CIS-77 Home http://www.c-jump.com/CIS77/CIS77syllabus.htm
The purpose of this assignment is to analyze step-by-step execution of real-world programs.
We will use run-time tracing techniques to understand how various C/C++ compilers handle registers, arithmetic expressions, and console I/O.
SYSENTER instruction (opcode 0Fh, 34h), the system call entry mechanism, is introduced.
The use of
To begin, download and unzip binary images for this lab exercise.
Consider:
#include <iostream> int main() { std::cout << "Hello, World!"; return 0; }
This is a canonical example of the first C++ program.
Is there a way to execute this program step-by-step at the CPU instruction level and see what is does?
Open OllyDbg and load program named hello.exe to start the debugging session.
This program was compiled with Microsoft Visual C++ 2005 Version 8.0.50727.867 on a Vista box (vsvista.050727-8600)
Resize your windows to make both console output and OllyDbg visible at the same time.
Use F8 to step through the application.
Note the place where the message Hello, World! is printed on the screen. Set a breakpoint at that location.
Restart the program (Debug -> Restart, or simply press Ctrl+F2.)
Choose Debug -> Run (F9). The execution stops as soon as the breakpoint is reached.
OllyDbg breakpoint is reached:
How does the console app handle character output?
OllyDbg makes the answer easy:
Open call stack window by clicking View -> Call stack (or simply Alt+K.)
Optional: Open run trace window by clicking View -> Run trace.
Click Debug -> Trace into (Ctrl+F11). The program resumes.
The execution stops prior to the first encountered SYSENTER instruction,
and the call stack looks like this:
When tracing the runtime execution, OllyDbg debugger stops before each SYSENTER instruction.
SYSENTER instruction, as Intel manual indicates, "is optimized to provide the maximum performance for transitions to protection ring 0", which is the way Windows XP/Vista and Linux applications invoke system services.
The OllyDbg call stack window indicates our current application state. It is a great opportunity for us to explore the functions of our application:
Clicking on the "Procedure" column of the stack window takes us to the beginning of a procedure being currently invoked (more precisely, the information about the procedure found in the corresponding stack frame.)
Clicking on the "Called from" column takes us to the responsible CALL instruction.
To step over SYSENTER, press F8.
Continue tracing into by pressing Ctrl+F11.
The debugger stops on each SYSENTER. Eventually, the first character, H, is displayed on the console. Set another breakpoint on the SYSENTER instruction which services the character ouput.
As it becomes visible in the stack window, kernel32.WriteFile and kernel32.WriteConsoleA system calls are employed to generate the console output.
With the new breakpoint in place, we can
Run command (F9) to execute until the next SYSENTER breakpoint.
Press F8 to step over each SYSENTER and watch the progress of characters appearing on the console screen:
Press F9 to continue (back to the step 1 above.)
See also the article about
Another incredibly useful feature of OllyDbg is right-clicking in the CPU window and choosing Search For -> All referenced text strings:
This gives us the opportunity to locate the "Hello, World!" text very easily:
Double clicking on the above line brings us to the CALL instruction that corresponds to the actual function call that initiates the console output:
Setting another breakpoint on the instruction next to the CALL validates the output of the entire text.
To continue experimenting with OllyDbg features, let's open another sample program, simple_IO_vc7.exe, compiled by MSVC++ version 8.0.
The text search for a particular string can be done manually as follows:
Click View -> Executable modules
Debug -> Run
The program runs.
Right-click executable module, and choose View memory
Double click the .rdata section of simple_I module (.rdata is the name of memory segment where all read-only constants are loaded.)
Right-click anywhere in the dump area, then click Search for -> Binary string. Look for the word "Please".
Once appropriate "Please..." text is found, write down its address.
Click View CPU to display the current instruction. (Clicking the EIP register in the register view usually takes you to the current instruction.)
Right-click the opcodes, Search for -> Binary String, and enter address in little endian format.
For example, if "Please" was found at 00407124, then you should search for the binary string 24 71 40 00.
Once the string is found, we should observe that it is used by the the PUSH command, preparing it for the console output call.
Right-click the PUSH command, and then Breakpoint -> Toggle.
Debug -> Run
The execution will stop at the PUSH instruction where the breakpoint was just set.
Debug -> Step over (or F8)
Debug -> Step over (or F8)
Click the console window. You should see "Please enter password:" prompt.
Go back to the CPU window (click View -> CPU.)
Use F8 to step until the the program begins to wait for the user input:
0040102C 68 40904000 PUSH simple_I.00409040
00401031 E8 24000000 CALL simple_I.0040105A
00401036 83C4 04 ADD ESP,4
Right-click the address 00401036 and Toggle breakpoint there (you can do this while the program is still waiting for user input.)
Type a single character, for example, 'A' (hexadecimal 41), and hit the Enter key.
The program returns back to the instruction
00401036 83C4 04 ADD ESP,4
Hit F8 once. OllyDbg moves to next line:
00401039 83F8 50 CMP EAX,50
The console input call returns the entered character code in EAX register. Apparently, the program is ready to examine the EAX value.
Double click the EAX register, and change it's value to hexadecimal 00000050.
What ASCII character corresponds to hex 50?
Use F8 a few more times to step through the program. Notice that it prints "password is correct, goodbye!"
Thus, the fragment
0040102C 68 40904000 PUSH simple_I.00409040 00401031 E8 24000000 CALL simple_I.0040105A 00401036 83C4 04 ADD ESP,4
is responsible for the input of the user password.
Since we know at this point that x50 is the correct password, we can permanently disable the above fragment by replacing it with
0040102C B8 50000000 MOV EAX,50
However, since the instruction MOV EAX,50 takes only 5 bytes of memory, the rest of the fragment must be patched by a series of NOP instructions.
Right click
0040102C 68 40904000 PUSH simple_I.00409040
and then Assemble. Enter MOV EAX,50.
Right click
00401031 E8 24000000 CALL simple_I.0040105A
and then Assemble. Enter NOP. Make sure Fill with NOP's option is checked.
Right click
00401036 83C4 04 ADD ESP,4
and then Assemble. Enter NOP. Make sure Fill with NOP's option is checked.
The resulting changes now look like this:
0040102C B8 50000000 MOV EAX,50 00401031 90 NOP 00401032 90 NOP 00401033 90 NOP 00401034 90 NOP 00401035 90 NOP 00401036 90 NOP 00401037 90 NOP 00401038 90 NOP
Click View menu -> Breakpoints, then right-click on each breakpoint and choose Remove. Since code changed, we no longer need those breakpoints.
Go back to the CPU window, right-click the code fragment containing the changes, and choose Copy to executable -> All modifications.
Click Copy All.
New D-window (disassembly window) opens. Close this window. The File changed prompt appears, indicating that the EXE indeed differs from the original.
Click YES and answer positively that you wish to overwrite the original executable.
Try to run the modified program from a command prompt. How does it behave now?
Note that simple_IO_gnu.exe is the same program compiled by GNU GCC version 3.4.5. Although the executable image is very diffrent, similar steps should get you the same results.
The original C++ source for both programs is simple_IO.cpp ( download ).
Open M12gcc.exe or M12vc8.exe in OllyDbg.
Both files are executable images of the same program compiled by GCC version 3.4.5 and MSVC++ version 8.0, respectively.
Using OllyDbg debugging techniques, solve the challenge imposed by the password prompt.
Hint: you need to understand the logic between the two consecutive prompts "password is incorrect, please re-enter".
What to submit: please email brief answers to the following questions:
(a) What key combinations do you type to enter the password?
(b) What is the location of the C++ source file for this program?
(c) What kind of logic is employed by this C++ program to hide/unhide the ASCII text?