Reversing 'dihux's ReverseMe #1 '
Required Knowledge
Basic assembly knowledge and
knowledge of OllyDbg will prove useful.
Reversing 'dihux's ReverseMe #1'
Level : Beginner
Author : minderbinder
Target : dhx.reme.1.exe
Tools :
OllyDbg with
CommandBar Plugin
The reverseme files∞
Open the readme in the ReverseMe's zip file to find out what we are meant to do:
-Tasks-
- 1. Enable the "Do" button
- 2. Make it compare the text that is taken from
the first edit box with 0xA, and if above then
message the user that max is 10 characters
- 3. When you have enabled the "Do" button you
have to make it set the generated serial
to the second edit box
- 4. Make the second edit box locked so
that the user can't modify its contents
but the user should be able to copy it
- 5. Write a tutorial and give it to me
Ok, I'll break this tutorial down into the individual tasks.
Step 1: Enable the "Do" button
Open the reverseme in Olly, run it and we see that the 'Do' button is disabled. Lets rewind Olly and take a look at the code. Take a look at the intermodular calls for calls to the CreateWindow by right clicking in the main Olly window area, selecting Search for, then select All intermodular calls, like this:
After you click on this, a screen displaying all intermodular calls should pop up. Click on the label 'Destination' at the top of the destination column so that the API calls are sorted alphabetically.
Click on the first call to CreateWindowExA, at address 004013D5 and you will see the call in the code. Scroll up just a little to see what was pushed onto the stack before CallWindowExA was called. You see, windows programs work by calling functions stored within windows DLL's, functions which do things such as compare strings (eg. lstrcmpA), draw a window (eg. CreateWindowExA) or get the system time (eg. GetSystemTimeA), there are hundreds of windows functions available for programs to use. How programs call windows functions is to push the parameters onto the stack, then call the function, and if the function returns a value, it will be stored in eax. The easiest way to know what the parameters a particular windows function requires, as well as the return values of windows functions and what they mean is to get a copy of the WIN32.HLP file, which lists all of the windows API functions and their prototypes, and a list of windows API constants and their values.. Open up the WIN32.HLP file (or just look below if you don't have one) and search for CreateWindowEx and you will find this:
HWND CreateWindowEx(
DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // pointer to registered class name
LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // handle to menu, or child-window identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam // pointer to window-creation data
);
Now the stack is a FILO (first in, last out) style memory system, which means that the last thing that is pushed on the stack is the first thing to be popped off. If you don't know what the hell I'm talking about, I recommend that you read up on assembly language. What this means for us is that the parameters to windows functions are pushed in the reverse order that they appear in the function prototypes in the WIN32.HLP file. So compensate for this when analyzing files, although Olly lists the parameters that are pushed before API calls, which makes things easier, however, other debuggers such as soft-ice do not.
Read the WIN32.HLP file for the flags which may render the button disabled, because this is the one that we want to change. At the top of the CreateWindowEx page in the API help file (WIN32.HLP), you will see the line 'For more information about creating a window and for full descriptions of the other parameters of CreateWindowEx, see CreateWindow.', click on the CreateWindow link to go to that function prototype. You see, CreateWindowEx is the same function as CreateWindow, it just has some additional functionality, the Ex standing for Extended. Scroll down and have a look at the styles available for use when calling the CreateWindow function. You should find 'WS_DISABLED - Creates a window that is initially disabled. A disabled window cannot receive input from the user.' This is what we're looking for. The two calls to CreateWindowEx are not what we are looking for, as the class parameter is not 'Button', like the call we are after. That's ok, we will just have to look a bit harder. Scroll up in Olly to offset 4011C4, this is the call to CreateWindowEx that we're looking for, however, have a look at the parameter pushed as the window style. The value that is pushed onto the stack as the style parameter is 50000000, if the button was rendered disabled when it was created, the window style would have to be 8000000 or 58000000, as long as the 7th digit (from right to left) was an 8, or an OR'ed value of 8.
So if the button isn't rendered disabled when it is created, then it must be rendered disabled after it is created, right? Scroll down and just below the function where the window is created, you will see some values pushed and then a call to EnableWindow. It should look something like this:
As you can see, at offset 004011F2, the Enable param. pushed is FASLE (0), this means that the window is rendered disabled, so, if you change this value to TRUE (1) it will mean that the window (button) will stay enabled, but because the window is enabled when it is created, changing the value to TRUE would make this code redundant, so we can simply NOP this section of code (from offset 004011ED to 004011FA). NOP this code by highlighting the 4 lines of code highlighted below and right-clicking on it and selecting Binary - NOP.
Once you have done this, run the program by clicking on the play icon in Olly, or pressing F9 and you will see that the 'Do' button is now enabled, and we have got created some space inside the program for use later on.
When you patch code in Olly, you are not actually patching the executable file, you are patching the file as it is stored in memory. This means that when you restart Olly or run the program outside of Olly, the changes to the code that you made will no longer be there. What you can do when cracking or reversing a file is to test your patch by patching the code in Olly, and if the patch has the desired effect, then make the patch permanent by right clicking in Olly and selecting:
When you click on 'All modifications', select 'Copy All'. A window with the file displayed in hex, with the changed bytes highlighted should pop up. Close this window and you will be prompted as to whether you want to save the changes made to the file to disk. Select yes, and enter a filename for the patched version. Something like dhx.reme.1.cracked.exe should do. This way, you will retain an original copy of the exe, which is always a good idea when patching files. Unless you save the changes over the original copy of the program, you will need to open the new file in Olly and continue with that.
Step 2: Make it compare the text that is taken from the first edit box with 0xA, and if above then message the user that max is 10 characters.
This step is fairly simple. The call at address 00401053 to GetDlgItemText will return the length of the string in the edit box in eax, so all we need to do is compare eax to 0xA and jump if above to a message box telling the user that the max is 10 chars. The coding of this is easy, the only thing we have to do is find the space to put the code.
This program was coded in Microsoft Visual C++ 7.0, which means that there's a heap of wasted space ;) which is good for us reversers. Directly beneath the call to GetDlgItemText, there are 7 NOP instructions (7 bytes) that we can use, however, this space by itself is not enough to compare eax to 0xA (that's 10 in decimal), jump if above to a message box, and display the message box code. This doesn't matter though, as there's plenty of room to put our code without removing any of the current functionality of the program. Scroll to offset 00401110 and you will see the following:
Now the highlighted code checks if eax is 0 (NULL), which this windows function return if there is an error. If eax does not equal 0, then the code follows on at offset 0040112B, however, if eax does equal 0, then the message box is created explaining the error. This is perfect for us, as we can put a jump instruction in at offset 00401115, making the code always jump to 0040112B, removing the error message and changing the message box to say what we want. This may sound bad, the idea of removing error handling code, but it doesn't really do anything if there is an error, it just tells the user what went wrong.
Pictured below are the 7 NOP instructions immediately below the call to GetDlgItemText:
We want to change the NOP instructions, starting at offset 401059, to a 'JMP 00401117'. This will make the code jump to offset 00401117, where our code will start to check if eax is greater than 0xA and if it is, message to the user. To do this in Olly, select the first NOP instruction, hit the space bar and write a jump instruction like this:
Hit assemble and you should see your jump instruction at offset 401059. The code should now look like this:
Now all we need to do is add our code in the space. To do this, change the code to the code below:
The changed bytes/lines are highlighted red/purple.
Note: When assembling the call to MessageBox, typing 'call dword ptr [405100]' will do fine.
As you can see, I added comments to the code to make it a bit easier to read. The first lines of the 2 sections of our code are mov dword ptr [xxxxxxxx], eax. Remember, if you read the CreateWindowEx function prototype, that the function returns a handle to the window created in eax. It is a good idea to leave the mov dword ptr... instructions in the program, so that if the program references the windows handle, it will be there. I don't think that this program uses either of the handles that we kept the mov instructions in there for, but it's still a good idea to have them in there.
Once you have made the modifications to the code, save the changes to the file (by right clicking, selecting 'Copy to executable', then 'All modifications' and follow the prompts), then run the program and you should be messaged if you enter more than 10 characters into the first edit box and click on the 'Do' button. The only problem is that the message says 'Couldn't create edit box.hName', wouldn't it be better if it said something more appropriate? To do this, dump the address pushed as the text parameter to the MessageBox function. You can do this by typing 'd 405194' in the CommandBar plugin, or if you don't have the CommandBar plugin installed, left click anywhere in the hex dump window in Olly (usually bottom left) and then press Ctrl+G and type in the address of the string, 405194 and click OK. Highlight the whole message in the ASCII column of the hex display window in Olly, then right press Ctrl+E to go into the binary edit window. You should see this:
Right click in the ASCII edit box and change the message to something like this:
As you can see, I highlighted the few 0's at the end of the hex edit box of the window. This is because after typing the message that we want to display in the ASCII edit box, we have to add NULL terminating characters (00h) to the end of the string, this can only be done in the hex box by clicking the hex bytes and changing them to 00's. Once you have done this, click OK, then right click on the highlighted text, which should be the new message we want displayed, click on 'Copy to executable'. As before, a new window should pop up, simply close this window and follow the prompts to save the changes to the file. Run the program and it should display the new message when we enter more than 10. characters into the first edit box and click the 'Do' button.
Step 3: Set the generated serial to the second edit box
The first thing that we need to do for this step is to find the serial generation routine in the program. The logical place to look is at the code just after the call to GetDlgItemText. Go to offset 401060. The code from this offset down to 401088 looks like the serial generation routine, followed by a call to wsprintf, presumably to format the serial generated to ASCII, then a call to SetDlgItemText. This is what we are looking for. The call to SetDlgItemText is already there, and is what we need to set the generated serial to the second edit box, the only problem is that the parameters pushed are all 0's, so at the moment, this call does nothing. What we need to do is push the required parameters before the call to SetDlgItemText and that should cover this step. There are only 6 bytes that we can change before the call to SetDlgItemText. This in itself is not enough room, not that it really matters, we'll just have to find some more space :). Scroll down to offset 4011ED, there you should find 14 bytes of NOP instructions from when we NOP'ed the EnableWindow(FALSE) call. Just above this is another error message. Change the JNZ (Jump if Not Zero) instruction at offset 4011D9 to a 'JMP 4011FB' instruction (note the change to the offset the jump jumps to). Once you have done this, NOP the error message and the 4 push instructions before it (from offset 4011DB to 4011EC). This should free up more than enough space for us to use. Save the changes to the file.
We now have all of the space that we will need to push the required parameters for the call to SetDlgItemText. What we need to do is NOP the 3 push instructions before the call to SetDlgItemText and put a jump to our space of code where we will be pushing the parameters. Scroll back up to offset 4010AA and NOP the 3 push instructions. Then, select the first NOP (at offset 4010AA), click space and assemble a 'JMP 4011DB' instruction. Save the changes to the file. It should look like this:
The function prototype for SetDlgItemTextA is:
BOOL SetDlgItemText(
HWND hDlg, // handle of dialog box
int nIDDlgItem, // identifier of control
LPCTSTR lpString // text to set
);
Parameters:
- hDlg: Identifies the dialog box that contains the control.
- nIDDlgItem: Identifies the control with a title or text that is to be set.
- lpString: Points to the null-terminated string that contains the text to be copied to the control.
Remembering that parameters are pushed in reverse order in ASM, our code should be like this:
push lpString
push nIDDlgItem
push hDlg
call SetDlgItemTextA
Let's find the value we need to push as the first parameter. It should be lpString, a pointer to the string that we want to make the edit box display. If you look up wsprintf in the windows API help file, you will discover that the last parameter pushed before the call to wsprintf is the buffer (address) to store the output of the function. This is the address that we will need to push first for our call to SetDlgItemText. The address of the string we want to set the edit box to display is 407474.
The value that we need to push as the second parameter (nIDDlgItem) is the ID of the control we want to set. To find this value, we need to find the value of the hMenu parameter pushed for the call to CreateWindowEx when the edit box is created, as this is the ID of the edit box that we need to push. Scroll to offset 4010E1 and you will see the following:
004010E1 . 6A 00 PUSH 0 ; /lParam = NULL
004010E3 . 8B1D FC504000 MOV EBX,DWORD PTR DS:[<&USER32.""CreateWin"">; |USER32.""CreateWindowExA""
004010E9 . 8BF8 MOV EDI,EAX ; |
004010EB . 57 PUSH EDI ; |hInst
004010EC . 6A 65 PUSH 65 ; |hMenu = 00000065
004010EE . 56 PUSH ; |hParent
004010EF . 6A 14 PUSH 14 ; |Height = 14 (20.)
004010F1 . 68 AA000000 PUSH 0AA ; |Width = AA (170.)
004010F6 . 6A 0A PUSH 0A ; |Y = A (10.)
004010F8 . 6A 0A PUSH 0A ; |X = A (10.)
004010FA . 68 00000050 PUSH 50000000 ; |Style = WS_CHILD|WS_VISIBLE
004010FF . 68 C1514000 PUSH dhx_reme.004051C1 ; |""WindowName"" = ""
00401104 . 68 BC514000 PUSH dhx_reme.004051BC ; |Class = "EDIT"
00401109 . 68 00000200 PUSH 20000 ; |""ExtStyle"" = WS_EX_STATICEDGE
0040110E . FFD3 CALL EBX ; \""CreateWindowExA""
As you can see by the 'Style' parameter pushed, the window created is an edit box, however, look at the X and Y values pushed (both 10.) and you will see that this has to be the first edit box (the top one) as it is as the coordinate (10,10) in the main window, with the top left of the window being origin. What we are looking for is a call to CreateWindowEx where the style parameter is EDIT, as with this call, however, the Y coordinate of the edit box created should be a higher number, something like, 40 for example. Scroll down a few lines, to offset 40112B and you will see this:
0040112B > 6A 00 PUSH 0
0040112D . 57 PUSH EDI
0040112E . 6A 66 PUSH 66
00401130 . 56 PUSH ESI
00401131 . 6A 14 PUSH 14
00401133 . 68 AA000000 PUSH 0AA
00401138 . 6A 23 PUSH 23
0040113A . 6A 0A PUSH 0A
0040113C . 68 00000058 PUSH 58000000
00401141 . 68 C1514000 PUSH dhx_reme.004051C1
00401146 . 68 BC514000 PUSH dhx_reme.004051BC ; ASCII "EDIT"
0040114B . 68 00000200 PUSH 20000
00401150 . FFD3 CALL EBX
This should be it, as there should only be one more edit box created, and the Y value pushed here is higher, meaning that the edit box created appears lower down in the main window. The hMenu parameter pushed here, 66h is the second value that we want to push before our call to SetDlgItemText.
The last parameter we need to push (hDlg) is a handle to the dialog window in which the dialog item we want to change exists. We can find this value by looking at the hParent parameter. The parent window of the second edit box created is the same as the first edit box created, so to make it easier (as Olly labels the param's of the first edit box), scroll back up to offset 4010E1. As you can see, the param. pushed is stored in ESI, scroll up to offset 4010DE and you will see this:
004010DE . 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8]
The value pushed is the DWORD stored at [EBP+8], so for the last parameter that we need to push before our call to SetDlgItemText is [EBP+8]. Now all we need to do is push the values.
Scroll back to our space of code starting at offset 4011D9 and change the code to the following.
All we do here is push the 3 parameters that we just found the values of, and then code a jump instruction back to offset 4010B0, where the call to SetDlgItemText resides. Save the changes to the program. Run the program and you will see that when you enter something in the top edit box and click the 'Do' button, the serial generated will appear in the second edit box. Also, if you enter more than 10 characters, a message box will appear. The only thing that I don't like about the reversed program at the moment is that at the moment, when a user enters more than 10 characters, after the message appears, the program still runs through the serial generation routine and sets the second edit box to the generated serial. It would be better if the program cleared the second edit box if the user enters more than 10 characters, this should be fairly easy to do. All we need to do is push a pointer to a NULL character and then jump to the instruction where the second parameter to SetDlgItemText is pushed.
We need to find a space in memory that holds a NULL character (00h). The first place that comes to mind is at the end of the message text that we edited to say 'Maximum of 10 characters.' There should be a few NULL characters after this. Find the address of where the string is stored, if you remember, its offset 405194. The NULL characters start at offset 4051AD, so we want to push 004051AD and then jump to the instruction where the second parameter is pushed before the call to SetDlgItemText. Scroll to the MessageBox code we created at offset 401160. Change the jump instruction at offset 401166 from 'jmp 00401060' to a 'jmp 004011EA' instruction. After you have done this, scroll down to offset 4011EA and assemble a 'push 4051AD' instruction at this offset, then assemble at the next line of code a 'jmp 4011E0' instruction. Save the changes and run the program and you will see that when you enter more than 10 characters, a message will be displayed, and after you click 'ok', the edit box displaying the generated serial will be cleared.
Step 4: Make the second edit box locked
Look at the possible edit box styles in the windows API help file and you will see 'ES_READONLY - Prevents the user from typing or editing text in the edit control.'. All we have to do is OR the value of ES_READONLY (800) with the current value pushed as the window style parameter when the second edit box is created with CreateWindowEx to make the second edit box 'locked'. Remember in the last step when we had to find the code where the second edit box is created to find its ID and discovered it at offset 40112B to 40114F. The style parameter is the 4th last parameter pushed according to the CreateWindowEx prototype. You will find where the style is pushed at offset 40113C. If you look, the value is 50000000, so we need to change the value pushed from this to 50000800 and when the second edit box is created, it will be rendered disabled. To change the value pushed, select the line of code at offset 40113C, press the space bar and assemble a 'push 50000800' instruction. Save the modifications to the file and run the program and you will find that we have a fully reversed program. ;)
Any questions can be sent to minderbin@gmail.com or minerbin on EFnet
Thanks to dihux for coding the reverseme ;)
Related pages
Categories
CategoryTutorialsBasics
There are no comments on this page. [Add comment]