Inspired by: http://sgros-students.blogspot.com/2014/09/reversing-with-immunity-debugger.html
We see that in case when our password is incorrect we get message Bad password. So lets try to find where that message is loaded up and displayed.
To do that we shall search binary file for string Bad password, or to be more precise, we shall search binary for all strings which are referenced to in the code. This method is effective only if binary isn't packed, obfuscated or encrypted in any way.
There is a good chance we will find JMP (some variant of it) couple of instructions before these instructions and that they are in fact part of a if-else statement. Let's follow any one of the two instructions in disassembly and try to find out how to get the CPU to execute instruction with Good job! instead of the one with Bad password. Double click on any of those two lines or right click and select Follow in Dissasembler.
At the address 00401B6E we see JE (jump equal) instruction and line above is TEST instruction with AL register (AL register is the one which holds the lowest 8 bits of EAX register) as an argument.
Destination of JE instruction is just behind the code that loads string Good job! and calls some function. So in order to get the Good job! message this instruction must never jump. TEST instruction is actually logic AND instruction and only way JE instruction is going to jump after TEST instruction which has both arguments same is if argument is equal to zero. We can deduce that AL register holds boolean value which determines if our password is equal to the real password.
Function which is being called before TEST instruction is probably one which is comparing our password with the real password. Let's check that, right click on that CALL instruction and press Follow.
First two instructions are standard function prologue, saving old frame pointer and setting new one. Then 24 bytes (18 hex = 24 decimal) of memory are allocated, enough for 6 variables (under assumption each variable takes 4 bytes). To know what next four instructions are doing you need to understand stack and stack(function) frame.
In short, addresses that are higher than EBP are parameters to the function (with the first one being at the offset +8) and addresses that are lower than EBP are functions local variables. So the next four instructions are actually transferring this functions first two parameters to be parameters to the next called function. Sometimes (with right compiling options) compilers are going to reserve place in advance for function parameters and then load them up with MOV instruction instead of using PUSH instruction (its faster).
Let's first focus on the code after the CALL instruction and then we'll go and see what happens in the called function. First instruction saves value of EAX register as the first local variable. So the EAX register holds the return value from the function. Then that value is being compared with the zero, and if it is equal AL register is set to the value of 1 (see SETE). MOVZX instruction fills the rest of the EAX instruction with the zeroes.
Next instruction saves that value in the first local variable and the one after that does nothing practically since its moving same value in the EAX register. So the actual comparison isn't done in this function but the boolean value which we test to determine if we have got the right password is set in this function. It's a good guess that next function does the actual comparing, and it's also good guess that our two parameters are actually pointers to our typed in password and the real password. Let's move on to the next function.
Again we have our standard function prologue and then allocating 32 bytes of space. Next instruction is saving value of EBXregister on the stack (again substitution for PUSH), then first parameter is loaded into EAX and then ESI and EDI registers are saved on the stack. Our suspicion that arguments are pointers (at least first one) is confirmed in the next instruction. Value which is located at the address pointed to by first argument is loaded into EAX. That value is then saved as the 8th local variable. Next instruction loads the value located 12 bytes behind current value, that tells us we'r probably dealing with some kind of the data structure.
Then second argument is loaded and its first member is saved in EDX register. Address of the fifth local variable is saved in the EAX and second member of second data (its actually the fourth since it is 12 bytes after the first one but because those 2 in the middle aren't used I'm going to refer to it as the second) structure is saved in the EBX. In the next 4 instructions values of the second members of data structures are compared and if necessary EAX is adjusted to be pointer to the member of the first data structure (if the member of the first data structure is lesser than member of the second data structure). Note that JB instruction is one that checks the status of the unsigned values, so that tells us that second members of the data structure are unsigned values.
We will ignore CLD instruction for a moment. In the next 3 instructions registers EDI and ESI are loaded with our first members of data structures and in ECX register is put lesser from the two unsigned values. REPE CMPS instruction compares bytes pointed to by EDI and ESI registers as long as they are equal. That is a string instruction so there is a good probability we found place where our password is compared to the real one. CLD instruction which we ignored before is used to determine whether EDI and ESI registers are incremented or decremented. Since ECX holds the value which determines how many times REPE instruction is going to run (until ECX is zero, it is one of the 2 conditions, other one being bytes at the locations are equal) we can assume that the ECX holds the number of characters of the string which is shorter, meaning second member of the data structure is number of the characters. In the rest of the function register EAX is set to hold the return value (0 if the strings are equal) and starting values are returned to their registers.
Now to verify if our static analysis was correct and to get the better understanding of functions go back to the address 00401B67, put breakpoint on that instruction and step through these 2 functions. Let's stop on address 00412C8E and look at the registers ECX, EDI and ESI to see if our guess about values in these registers was correct (although you can see that before this instruction). As we can see ECX register holds value 4, which is length of our typed in password. ESI register holds the address of our typed in password "test", and EDX register holds the value of the string "dQhdhhbhb" which is probably the real password.
Now try restarting the program and for username input test and for password dQhdhhbhb. We have got the message Good job!. We were successful in discovering the password. But restart the program and for username type test1 and for the password dQhdhhbhb. Once again we have got the message Bad password. It's obvious that password is generated from username somehow and it would be good to discover how it is being generated. But for now let's try something else, let's see if we can change some part of the code so that our "good" message would always be displayed. Good place to do something like that would be part of the code where it is decided which string is going to be loaded up and displayed.
Go back to address 00401B6E, where it is decided which message is going to be displayed. We want to never jump on this instruction, but rather to continue down to the good message. This can be achieved by multiple ways. One of those is simply to change JE instruction to NOP (no operation).
It will ask you if you want to save all changes, press Copy all. If you want to copy only selection of the binary file, highlight the part you want to copy and instead of All modifications choose Selection.
Leave a Reply.