Taking Control of the Process
In the previous article, we learned about the basics of the Stack Based Buffer Overflow, such as analyzing the stack, creating breakpoints and analyzing the function call and registers. With the help of these skills, we will now see how we can manipulate the return addresses dynamically into the program. We will analyze a program in which a function 'do_not_call' is defined but has never been called throughout the program. So, our goal is to change the register address in a way so that this function is called and performs the operation which is given in the function. We will perform this task by manipulating the register addresses while the program is running.
Let's take a look at the program. We have already given the source code and the binary file of the program in the end of the article. You can download it here:
11 courses, 8+ hours of training
[download]
As can be seen in the above screenshot, we have mentioned a number for each function. The description for the same is given below.
-
First of all, we have created a function "do_not_call" which prints the message "Function do_not_call is called" by the printf command.
-
This is another function. The name of this function is 'function1', which has the integer return type with one argument. In the next line we have created a local variable. This function prints the message "function1 is called".
-
This is the main function of the program. The program execution will begin with this function. First, we gave a command to print the message "Main function is called", and in the next step we initialized the local variable. In the next line, we have called the 'function1'.
You must have noticed that we have not called the "do_not_call" function in our program. Let us verify it by running the program normally.
As we can see in the above screenshot, when we run the program normally, it prints two messages, 'Main Function is called' and 'Function1 is called'.
Now, we will open the program in Immunity Debugger.
After opening the program with Immunity Debugger, we can see four different screens. Each screen is showing a different kind of program data. If we scroll down the first screen we can see all assembly language instructions as well as the strings we have used in the program. You can see the same in the screenshot given below.
As can be seen in the above screenshot, the main function starts by pushing the EBP resister into the memory and ends at the RETN instruction. For a better understanding, we have created a table in which we have the starting and ending address of all the functions defined in our program.
Main Function
004013C4
00401416
Function 1
004013A4
004013C4
Do_not_call
00401390
004013A3
Now we will make a list of all the function calls taking place in the program. After analyzing the main program, we can see the following function calls:
After taking a close look at the above screenshot, we find that there are four function calls in the main program. If we closely look at the functions which are being called, then in the fourth function call, we can see that it is calling to 'Second.004013A4' in which the last 8 digit address is actually the starting address of 'function1'. We can verify the same by checking the table we have created above. In simple terms, this statement is calling function1 as we have defined in the program source code.
Now, we will create a break point to analyze the Stack, Return Addresses, etc. (We have already mentioned all the basics in the previous article, like what is a breakpoint and how do we create a breakpoint in the debugger, etc., so we are not going to cover this again in this article.) The following steps are mentioned below to further analysis.
- Create the breakpoint before the 4th function call. We can do this by clicking on the instruction and hitting the F2 key, after that run the program. We can run the program by pressing F9. Now, we can see the following screen in the debugger. As of now, the program execution control has reached that position where we have created the breakpoint.
In screen 1, we have created the breakpoint on "00401401" and now the program has paused on this instruction. We can see in screen 2 the EIP register is pointing to the address at which we have created the breakpoint.
- Now we will execute the instructions step by step for understanding the concept more clearly. Let us execute the next instruction; we can do this by pressing the F7 key, which is Step into.
As we can see, execution control is pointing to the next instruction in screen 1 and EIP is also pointing to the instruction address. Now, the execution control would go to address "004013A4" which is the starting address of the function1. We will now execute it and see what changes we come across.
- Again hit the F7 key to execute the next instruction. We get the following output screen.
This step is very critical and important to understand. As can be seen in the above screenshot, the execution pointer has switched from 00401408 (No-1) to 004013A4 (No-2) which is the starting point of 'function1'. Also, when we take a look at screen 4 we see that the Top of the Stack (No-3) is pointing the address 0040140D (No-4), which is the next instruction from the function call. This address is also called the return address to the main program. It means that after the execution of 'function1' is complete, then the execution control would switch to this address.
If we change this return address, we can actually change the order of execution of the program. So, let us change the return address in the debugger with the address of the 'do_not_call' function address which was in the above table.
The function "do_not_call" address is: "00401390"
To change the address, we will have to right click on the address and click on modify.
After that, change the Hexadecimal value and then click on OK. Now, we can see our changes are reflected on screen 4. The top of the stack is pointing to the address of the 'do_not_call' function. This will allow us to execute the 'do_not_call' function, which was not supposed to be executed in the actual program.
Now, we will execute each instruction step by step until we reach the RETN instruction.
As we can see, program execution control has reached the RETN instruction. It means the function1 execution has completed and now the execution control will go to the main program (according to our program), but we had changed the return address in the previous step to the function 'do_not_call', so the execution control will go to the 'do_not_call' function and execute the instruction that it is defined to execute.
So, let us execute next instruction step by step and we will see the 'do_not_call' function has successfully executed. We can verify the same by checking the output of the program.
As can be seen in the above screenshot, by dynamically changing the return address of the program, we are successfully able to execute the function the program was not supposed to execute.
So in this article we have learned…
11 courses, 8+ hours of training
- Monitoring and Analyzing the Register Value
- Analyzing Top of Stacks
- Changing the Return Address
References