banner
lca

lca

真正的不自由,是在自己的心中设下牢笼。

"Learning Notes on IDA Reverse Engineering from Scratch" - 12 (Program Registration Reverse Analysis)

image

12.1 Determine the main function through command line arguments#

The program TEST_REVERSER.exe in this chapter learns new knowledge about static reversing and debugging from this exercise.

First, let's look at the program architecture by running the IDE.

image

From the image above, we can see that this program has a 32-bit architecture and is compiled using VC++ 2015.

In the second step, try running the program, which prompts for a username and password. Enter any username and password, and it shows "bad reverser."

image

In the third step, open IDA and load the target program.

image

One method to go to the key part is to search for strings. Search for some command line arguments like argc, argv, etc. Since this is a C++ program, the function prototype is as follows:

int main(int argc, char *argv[])

Search for parameters like arg in the name, using ctrl + F to open the search box.

image

Double-click on p_argc, and the content is as follows:

image

Press the X key to search for references.

image

In the image above, the program calls the _p_argc_ and _p_argv functions, then passes the values to the main function.

Double-click to enter the main function.

image

Three parameters of the main function

image

Reference of the main function (signed)

12.2 Stack analysis of the main function#

Double-click on any function parameter or local variable to switch to the static stack view.

image

From the image above, we can see that the first thing at the bottom is the function parameters, which are always below the return address (r) because the parameters are first pushed onto the stack before calling the function, and then the return address is pushed.

Above that is the ebp value of the function that called the main function.

image

In the image above, when the main function executes the first instruction push ebp, it saves it onto the stack, then assigns the value of esp to ebp, using ebp as the base address for the function parameters below and local variable references above. Finally, sub esp,94h moves esp to create space for local variables and buffers. In this program, the distance moved is 0x94, and the compiler calculates the space occupied by local variables based on the source code.

The value of esp points to above the local variables, while ebp points to the base address. Above the base address are local variables, and below are the return address and function parameters, as shown in the image below.

image

So in functions with ebp as the base address, once the ebp value of the previous function is saved to the stack via push ebp, the value of esp is assigned to ebp. 00000000 serves as a baseline, with addresses above it being negative (-) and addresses below it being positive (+).

In the image above, the relative address of var_4 is -00000004. If we take the value of ebp as a reference, the actual address of var_4 is ebp-4.

In the disassembly view, right-click on any instance using var_4 to verify the above content.

image

Above var_4, there is a blank area without variables, which may be a buffer.

image

Move the view up a bit, and you can see the first variable Buf above the blank area, as shown in the image below.

image

Right-click and select ARRAY, and the following window pops up, indicating that the array consists of 120 one-byte elements, so the size of the array is 120.

image

image

Function stack view

The image above shows the ebp base address. After pointing to the mov ebp, esp instruction, esp is then reduced by 0x94, finally pointing to the top of the local variable area, as shown in the image below, after executing sub esp, 0x94, the value of esp.

image

In the image above, the left side's 00000094 represents esp=ebp-0x94. When calling other functions within the function, esp will move further up. Inside the main function, until exiting the main function, operations are still performed on local variables before -0x94.

12.3 Local variables of the main function#

Next, we will perform reverse analysis of local variables from the static stack view, with the parameters of the main function being known.

image

Local variables

In the image above, the program reads a certain value and performs an XOR operation with the value on ebp, saving the result to var_4, which serves to prevent stack overflow.

Double-click sub_4011B0 to enter, and you can see the sub_401040 function.

image

image

The sub_401040 function contains a printf function, indicating that this function is used to print characters.

image

After that, the size variable is assigned a value of 8. From the reference, we can see that there are two references, which only read the content without modification.

image

image

Next, there is a gets_s function, which restricts user input. The image above shows a maximum input of 8 characters. By passing parameters via push eax, it then uses lea to get the address of the variable buf, which is the buffer.

If the user inputs fewer than 8 characters and simply presses enter, the function will also interrupt input and return. Therefore, the Buf buffer can have a maximum of 8 characters.

Then the program passes the buffer address to the strlen() API function as a parameter through PUSH EDX, which retrieves the length of the string in Buf and saves the result to the var_90 variable.

12.4 Loop and code block grouping#

image

In the image above, the blue arrow pointing back may indicate a loop, and the var_84 variable starts as the counter for this loop. At 0x4019f5, there is a conditional jump that ends the loop if the condition is met. The counter starts from 0 and accumulates until it is greater than or equal to the var_90 variable, at which point the loop ends.

image

Counter increment by 1

The value of the counter variable is passed to EAX, which is incremented by 1 and then returned to the counter variable.

image

In the image above, the program retrieves the first character of BUFFER from EBP+EDX+BUF. EBP+BUF is added to the storage counter, starting at 0. Each time the loop iterates, it increments by 1 and reads the next character. This way, the loop adds the hexadecimal value of each byte in BUFFER to the var_88 variable (initially 0).

The content of this loop is character addition.

image

All marked with the same color, drag the bottom code blocks closer together.

You can group these three blocks by holding down ctrl and clicking on each block in the tab above, changing the color to cyan.

image

Then right-click to group the nodes.

image

Final effect

image

To see the specific content, right-click to ungroup the nodes.

image

12.5 Registration algorithm analysis#

Continue analyzing the code below the loop content.

image

In the image above, the program asks the user to input a username and password. The following sub_4011b0 is a printf function, which then calls the gets_s function. The username and password use the same buffer Buf with a maximum character limit of Size.

Since the program has already calculated the sum of each character in the username, it no longer uses the username string, so the password can use the same buffer.

image

Next, the atoi function is called to convert the input content to decimal and save it to the variable var_94, which is the password variable.

Then the program passes the var_94 password variable via push edx, and the var_88 variable via push eax, passing these two variables as parameters to the 0x401010 function.

image

Entering the 0x401010 function, we can see two parameters. arg_4 should be the password variable because it is pushed onto the stack first, while the arg_0 parameter above is the value of var_88.

So how does the 0x401010 function use these two parameters?

Before performing a cmp comparison, the program passes the password variable arg_4 to eax and executes shl eax,1.

shl shifts the bits in eax to the left, filling the rightmost low bits with 0. As a special case, shl reg,1 is equivalent to multiplying by 2.

Thus, the program multiplies the password variable by 2 and compares it with arg_0.

Using Python's ord function to calculate the corresponding decimal value of ASCII.

image

If "pepe" is used as the username, the sum of the characters in "pepe" is as follows:

image

The result is 0x1aa, and the input password multiplied by 2 is compared with 0x1aa. Therefore, the correct password should be one that, when multiplied by 2, equals 0x1aa, resulting in:

image

The result is 213.

At this point, you can open the program and enter the username "pepe" and the password "213."

image

It shows a success message.

image

From the image above, we can see that when these two values are not equal, it jumps to the red code block and returns 0. If they are equal, it jumps to the green code block and returns 1.

What is the purpose of the return value?

image

From the image above, we can see that the return value is passed to the var_7D variable.

image

From the image above, we can see that if the return value is 0, it jumps to "bad reverser," and if it is 1, it jumps to "good reverser."


This chapter mainly discusses how to reverse analyze and bypass registration, covering topics such as function stacks, local variables, and registration algorithm analysis.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.