Solution to Extasy's OpenMe

by Lord Rhesus (27/11/2000)

Difficulty Level

Intermediate

Target

This is a reverseme that has been coded by Extasy of the Immortal Descendants.

What Needs to be Done

The program is a small program which contains a series of check boxes in a three by three grid. The checkboxes are checked and unchecked by clicking on them and when the user presses the save button a file is created called 'save.sav' which contains the details of whether each box is checked or not. The challenge here is to implement an open function in the program without patching any part of the program except the PE header. Writing to the process in memory however is allowed!

Tools Used

Ida Pro Resource Editor (not really necessary but useful for exploring)
Iczelion's Code Snippet Creator (only used for testing and compiling. Cheat!!!)
Hex Editor
Masm
TRW 2000 (to debug our code)

The Essay

You will need to download some of my source code from here in order to understand fully what is going on.

Method of Approach

Before we jump in at the deep end it is a good idea to plan how we are going to achieve the reverseme's objectives. As suggested by Extasy it would be a good idea to write a dll containing the code to open the saved file. The reason why we would do this is because the imports which we need aren't in the main program. Actually this isn't true, I gave it some thought and realised this after I had solved the reverseme. We could implement the code because the main program contains the LoadLibrary and GetProcAddress imports which we will use anyway to get to our own dll functions. After we have written the dll we need to find a zero padded space in OpenMe.exe where we can place our code. The steps we need to take are as follows:

  1. Poking around in the program's code
  2. Find a zero padded space where we can insert our code to call the dll
  3. Write the dll
  4. Determine what code we need to use to call the dll
  5. Write a loader to insert our code
  6. Turn on the TV and see if the Americans have learned how to run a fair, democratic election yet :P

Step 1 (Poking around)

Have a look at the program in a resource editor and find out what ID the open button has. I found it out to 117 (or 75h). Disassemble the file OpenMe.exe in Ida Pro or something else, it should only take about 3 seconds to disassemble as the program is so small. If you scroll down you will get to an interesting section of code as shown below:

00401088         cmp     ax, 75h            ; Hmm, is that the ID of the Open Button?
0040108C         jnz     short loc_401093   ; Jump to see if other button pressed
0040108E         jmp     loc_401184         ; Skip to end if button pressed
00401093 ; ---------------------------------------------------------------------------
00401093 
00401093 loc_401093:                             ; CODE XREF: sub_401040+4Cj
00401093         cmp     ax, 76h            ; ID of the Save button
00401097         jnz     short loc_4010E0   ; Skip if save not pressed

We have now found out where the check is to see if the Open button has been selected. If Extasy hadn't included this check in the program then we could have just redirected the program from here and done the check ourselves. That would have been a bit tedious, but it's worth noting for other reversemes and programs.

Anyway. If we look further down the code we come to the next section of interest:

004010E0 ; ---------------------------------------------------------------------------
004010E0 
004010E0 loc_4010E0:                             ; CODE XREF: sub_401040+57j
004010E0         cmp     ax, 65h            ; 65h = ID of first checkbox
004010E4         jnz     short loc_4010F2   ; Jump if box not clicked
004010E6         xor     dword_4020A8, 1    ; Reverse value at 4020A8, hmm
004010ED         jmp     loc_401184         ; Jump to end
004010F2 ; ---------------------------------------------------------------------------
004010F2 
004010F2 loc_4010F2:                             ; CODE XREF: sub_401040+A4j
004010F2         cmp     ax, 66h            ; ID or second checkbox
004010F6         jnz     short loc_401104
004010F8         xor     dword_4020AC, 1    ; hmm, location is now a dwords length away
004010FF         jmp     loc_401184
00401104 ; ---------------------------------------------------------------------------

Code exactly like the above occurs 9 times! The same number of times as the number of checkboxes! From this we can see that the current state of the checkboxes is stored at location 4020A8 in memory and a dword is used to store the state of each checkbox, highly efficient usage of memory by Extasy there ;-). We should make a note of the location where this information is kept I think.

Now we have to find the handle of the dialog box. We need this so we can change the states of the checkboxes after we've opened and processed the file. This is fairly straight forward, all we have to do is look in our disassembled listing for the call to CreateDialogParam. Take a note of the address that is pushed onto the stack 4 pushes before the call (at memory location 401013). This is the call to the dialog procedure which handles all api calls to do with the dialog box. Ida Pro will show you the local variables (arguments) passed to the procedure as below:

00401040 sub_401040      proc near               ; DATA XREF: start+13o
00401040 
00401040 arg_0           = dword ptr  8
00401040 arg_4           = dword ptr  0Ch
00401040 arg_8           = dword ptr  10h

The first variable is what we want as it points to the dialogs handle when combined with ebp. Therefore provided no more procedures are called [EBP+8] will point to the dialog's handle.

Step 2 (Finding a zero padded area)

Some rules when looking for zero padded areas in a file:

  1. The most important is don't fiddle with the zero padded areas in the resource sections as this usually (always in my experience) trashes the program.
  2. A good place to look is the data section as there is always unused space at the end of this.

(quick amendment by the author here: the two rules above are not the best way to find a usable zero padded area, the correct method is described in other parts of this web site, or alternatively look at SantMat's solution as he uses a better method)

Just open up the program in a hex editor (I use Cygnus Hex Editor but everyone else seems to think it's lame!). Have a look for a section filled with lots of zeros. If it has a bunch of strings above it then it is probably the data section. Check this location now in Ida to see if it is being used at all. The zeros from memory location 4020CC seem to be ok. A good place to put the code would be 4020D0 as it is a nice round number. Funnily enough I was reading through SantMat's essay on this reverseme and 4020D0 is exactly the same place where he chose to place his code! You know what they say about great minds and thinking alike, although my mind is probably superior ;-).

Step 3 (Writing the dll)

I won't bother explaining this much as you can just look at the source code. The below pseudo code will show the stages taken:

    Open the file for reading
    if file not found then
        Messagebox with "File not found" message
    else
        Read 36 bytes (24h) from file save.sav and store them in mem location 4020A8
        Close the file
        Update checkbox display
    end if

Step 4 (Writing code to call the dll)

I won't bother explaining this as well as you can see from the source code below how to call the program. I cheated a bit and when testing hard patched the code into the executable to see if it worked using Iczelion's Snippet Creator. Here is the code as pasted into the creator's text box:

@top:
jmp @start
LibName             db "open.dll",0
FunctionName        db "OpenSavedFile",0
DllNotFound         db "open.dll not found",0
AppName             db "OpenMe",0
FunctionNotFound    db "OpenSavedFile function not found",0

hLib                dd ?
FunctionAddr        dd ?

@start:
    invoke LoadLibrary,addr LibName
    .IF eax==NULL
        invoke MessageBox,NULL,addr DllNotFound,addr AppName,MB_OK
    .ELSE
        mov hLib,eax
        invoke GetProcAddress,hLib,addr FunctionName
        .IF eax==NULL
            invoke MessageBox,NULL,addr FunctionNotFound,addr AppName,MB_OK
        .ELSE
            mov FunctionAddr,eax
            mov eax, [ebp+8]     ;[ebp+8] contains the handle of the main dialog. It
                                 ;is moved into eax so it can be passed to the dll
            call [FunctionAddr]
        .ENDIF
    .ENDIF
    jmp @top                     ;jump put here so it can be edited later to point to
                                 ;memory location 401184 which it returns to

For the snippet creator to insert the code where we want it, we have to tell it to place the code in an existing section at location 4020D0. When this is done we have to redirect the jumps. The first jump we have to redirect to is the link to the code which calls our dll. If you look at the first disassembly snippet in Step 1 then the jump we need to change is the jump from 40108E. Here is the calculation for working out the jump:

4020d0 - 401093 = 103d

So in hex we change the jump at file location (just for testing) 48e to e93d100000. Now we have to jump back from the end of our code snippet to memory location 401184. We can work out the hex of our code by the method below:

NOT((402190 - 401184) - 1) = ffffeff4

In hex we patch file location 78B to E9F4EFFFFF.

If we run the program and now save a pattern like below:

X
X
XXX

Then change it to something else and click on Open the pattern above will be retrieved from the file.

Step 5 (Write a loader to insert code)

After cheating and writing a hard patch we can easily take the hex bytes it was compiled to and stick them in the data section of a loader, and then use them to patch the main program at run time. Again I'm not going to explain the loader so just look at the source code. There we go, we're finished and we didn't have to edit the PE file.

Step 6 (Watching democracy in action, the American style)

I would write something witty and clever here about the state of American politics but I lack a sense of humor, Volatility would probably kick my arse and American politics is too fucking boring.

Final Notes

That was easy enough wasn't it.

Greetings

Everyone in the reversing scene, no one in the warez scene coz they are no better than the lusers who make the porn sites they advertise and Adolf Hitler, for failing to take over the world.

If you wish to contact me my e-mail address is: lordrhesus@yahoo.co.uk

As an end note I would like to make a brief statement explaining why reversing papers are more interesting than the boring papers you have to write in industry and university:

In industry you can't end your paper with the word fuck!!!