10101010101010
010101010101000010100
10100101
10101010100101010
10101000010010110
00101000101000010

-extasy's OpenMe #2-
solution of the reverseme
~by tank_~

10101000100011111001
101000110010101100
1000100001111111011
10001000011111101010
1000111000101100100
1001111110001000100

Wise quote

Section:
Contents:
1.Tools used

SoftIce (for tracing/debugging) [not really necessary, but usefull for tracing errors]
IDA / W32Dasm (for dead-listings)
Hiew (for modifying the exe's instructions/opcodes)
PeBuild (for modifying the exe's sections and writing strings)
IID King (by SantMat, for adding apis to a PE file)
OpGen (by NeuraL_NoiSe, for getting the right calls/jmps in the exe)
MASM & Api reference (for the coding part of the reverseme)

2.Initial approach (Introduction)

Hello there! It's been quite a while since I've last written a tutorial, sorry 4 that, here is on on extasy's OpenMe #2. It can be found at reversemes.immortaldescendants.org. Now, let's take a look at the task of the reverseme:
"The only goal you have is to make this program save and open the state of the checkboxes, but, IT HAS TO SAVE THEM IN THE EXE ITSELF ! It means that when you will run this program, it will restore the state of them when it initialises, and it won't need a savefile to restore the state. " (as taken from the readme)
The idea I had was: when the reme starts, it also starts a child/sister/utility program that would store the state of the checkboxes and write them to the reme after its execution stops.
Now, as I've conceived the solution, it has four main parts: adapting the exe, adding the code used on exe initialisation (that restores the state of the checkboxes), adding the code used on exe exit (that saves the new state of the checkboxes) and coding the little util that will write the bytes to the exe (util.exe).

Following, the definitions of four apis:

BOOL CheckDlgButton(
HWND hDlg, // handle to dialog box
int nIDButton, // button-control identifier
UINT uCheck // check state
);


HWND FindWindow(
LPCTSTR lpClassName, // pointer to class name
LPCTSTR lpWindowName // pointer to window name
);


UINT IsDlgButtonChecked(
hWND hDlg, // handle of dialog box
int nIDButton // button identifier
);


LRESULT SendMessage(
HWND hWnd, // handle of destination window
UINT Msg, // message to send
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);



HINSTANCE ShellExecute(
HWND hwnd, // handle to parent window
LPCTSTR lpOperation, // pointer to string that specifies operation to perform
LPCTSTR lpFile, // pointer to filename or folder name string
LPCTSTR lpParameters, // pointer to string that specifies executable-file parameters
LPCTSTR lpDirectory, // pointer to string that specifies default directory
INT nShowCmd // whether file is shown when opened
);

 

3.Essay
(steps)

PART I
The first and foremost thing to do: start IDA and disassemble the exe. At this point I had to decide what apis I'd need that arent included in the openme2.exe file. Those that came to my mind were: CheckDlgButton (check/uncheck a checkbox), IsDlgButtonChecked (determine the state of a checkbox), SendMessageA (we need a way to tell our utility exe "stuff"), FindWindowA (to get the window handle of our utilily proggie) [all from user32.dll] and ShellExecuteA (run a file, in this case our util.exe) [from shell32.dll]. So, run IID King and add those functions. In openme2.exe.txt you'll get the code needed to access these apis during runtime. In my case these were:
CheckDlgButton: call dword ptr [405064]
FindWindowA: call dword ptr [405068]
IsDlgButtonChecked: call dword ptr [40506C]
SendMessageA: call dword ptr [405070]
ShellExecuteA: call dword ptr [40606C]

After adding them, we need to establish where to place our code/data. I've taken the first .IIDKing section and renamed it to .ourdata, then I've placed the following data (using PEBuild):
"open" (4050E0) (parameter for ShellExecuteA)
"util.exe" (4050F0)
(parameter for ShellExecuteA)
"OurClass" (405100)
(parameter for FindWindowA)
"OurWindow"(405110)
(parameter for FindWindowA)

Also, I've established the places where I'll keep some uninitialised data (variables)
:
405120 for the handle of util.exe window
405130-405138 for the 1/0 of the 9 checkboxes (according to the checked/unchecked state)
405140 for is/isnt a certain checkbox checked (the result of IsDlgButtonChecked).

Third step: take the second .IIDKing section, rename it to .ourtext and determine where our code can start - I found 4060A0.

Note: All the offsets are virtual offsets. Try to figure out the physical ones by yourself if you'd like :P

PART II
What do we need to do at this point? Add code that reads the bytes 405130-405138 and checks a checkbox if the corresponding byte is not null, then start our util.exe. At this point, take a look at the disassembly and try to figure out where to jump to the first part of our code that, as I've said, starts at 4060A0. I've found 401037 to be a suitable location. Replace the 6 bytes there with jmp 4060A0; inc eax; dec eax. Now, go at 4060A0 and assemble:

.ourtext:004060A0 6A 00 push 0 ;SW_HIDE
.ourtext:004060A2 6A 00 push 0 ;NULL
.ourtext:004060A4 6A 00 push 0 ;NULL
.ourtext:004060A6 68 F0 50 40 00 push offset aUtil_exe ; "util.exe"
.ourtext:004060AB 68 E0 50 40 00 push offset aOpen ; "open"
.ourtext:004060B0 FF 35 40 30 40 00 push dword_403040 ;hInstance of openme2.exe, determined using the disassembly
.ourtext:004060B6 FF 15 6C 60 40 00 call ds:ShellExecuteA
.ourtext:004060BC B8 30 51 40 00 mov eax, offset dword_405130 ;the bytes of the checkboxes
.ourtext:004060C1 B9 09 00 00 00 mov ecx, 9 ;number of checkboxes
.ourtext:004060C6 BA 65 00 00 00 mov edx, 65h ;ID of first checkbox, again determined using the disassembly
.ourtext:004060CB 8A 18 mov bl, [eax] ;value of checkbox byte
.ourtext:004060CD 80 FB 01 cmp bl, 1 ; is checkbox with id edx to be checked
.ourtext:004060D0 75 0E jnz short loc_4060E0
.ourtext:004060D2 60 pusha ;push registers to stack
.ourtext:004060D3 6A 01 push 1 ;BST_CHECKED
.ourtext:004060D5 52 push edx ; id of checkbox
.ourtext:004060D6 FF 75 08 push dword ptr [ebp+8] ;handle of our exe window
.ourtext:004060D9 FF 15 64 50 40 00 call ds:CheckDlgButton
.ourtext:004060DF 61 popa ;pop registers from stack
.ourtext:004060E0 40 inc eax ;next byte
.ourtext:004060E1 42 inc edx ;next id
.ourtext:004060E2 49 dec ecx ;next checkbox
.ourtext:004060E3 0F 85 E2 FF FF FF jnz loc_4060CB ;if it's not the last, do code allover again
.ourtext:004060E9 E9 AD AF FF FF jmp loc_40109B ;go back to normal program flow

PART III
We can add our code for this part starting with 406100.
Okay, now we need to determine the part of the code that's responsable with the code for the 'Save&Quit' button. Take a look here:

.text:00401050 81 7D 0C 11 01 00+ cmp [ebp+arg_4], 111h ;is it WM_COMMAND? ie. click etc.
.text:00401057 75 39 jnz short loc_401092
.text:00401059 8B 45 10 mov eax, [ebp+arg_8] ;wParam of message (id of control issuing the message)
.text:0040105C 8B 55 10 mov edx, [ebp+arg_8];this
.text:0040105F C1 EA 10 shr edx, 10h;code
.text:00401062 66 0B D2 or dx, dx;is rather
.text:00401065 75 34 jnz short loc_40109B;useless
.text:00401067 66 83 F8 76 cmp ax, 76h
.text:0040106B 75 0C jnz short loc_401079
.text:0040106D 6A 00 push 0
.text:0040106F FF 75 08 push [ebp+arg_0]
.text:00401072 E8 33 00 00 00 call j_EndDialog
.text:00401077 EB 17 jmp short loc_401090
.text:00401079 66 83 F8 6E cmp ax, 6Eh
.text:0040107D 75 11 jnz short loc_401090
.text:0040107F 6A 00 push 0
.text:00401081 6A 00 push 0
.text:00401083 68 05 30 40 00 push offset aSaveAndOpen2Re ; "Save and Open 2\r\nRead the readme.txt fo"...
.text:00401088 FF 75 08 push [ebp+arg_0]
.text:0040108B E8 20 00 00 00 call j_MessageBoxA
.text:00401090 jmp short loc_40109B

As I see it, the code starting from 40105C to 40106B can be replaced with cmp eax,76 ; jne 401079 ; jmp 406100. Now, go at 406100 and assenble:

.ourtext:00406100 68 10 51 40 00 push offset aOurwindow ; "OurWindow"
.ourtext:00406105 68 00 51 40 00 push offset aOurclass ; "OurClass"
.ourtext:0040610A FF 15 68 50 40 00 call ds:FindWindowA
.ourtext:00406110 A3 20 51 40 00 mov ds:dword_405120, eax ;store value of window handle
.ourtext:00406115 BA 65 00 00 00 mov edx, 65h ;again, id of first checkbox
.ourtext:0040611A B9 09 00 00 00 mov ecx, 9 ;number of dialog boxes
.ourtext:0040611F 60 pusha ;store registers
.ourtext:00406120 52 push edx ;id of checkbox
.ourtext:00406121 FF 75 08 push dword ptr [ebp+8] ;window handle
.ourtext:00406124 FF 15 6C 50 40 00 call ds:IsDlgButtonChecked
.ourtext:0040612A A3 40 51 40 00 mov ds:dword_405140, eax ;store result
.ourtext:0040612F 61 popa ;restore registers
.ourtext:00406130 60 pusha ;store registers
.ourtext:00406131 FF 35 40 51 40 00 push ds:dword_405140 ;result of IsDlgButtonChecked
.ourtext:00406137 52 push edx ;id of checkbox
.ourtext:00406138 68 00 05 00 00 push 500h ;fictive message
.ourtext:0040613D FF 35 20 51 40 00 push ds:dword_405120 ;handle of util.exe window
.ourtext:00406143 FF 15 70 50 40 00 call ds:SendMessageA ;issue the message
.ourtext:00406149 61 popa ;restore registers
.ourtext:0040614A 42 inc edx ;next id
.ourtext:0040614B 49 dec ecx ;next checkbox
.ourtext:0040614C 0F 85 CD FF FF FF jnz loc_40611F ;if ecx>0 verify next checkbox
.ourtext:00406152 E9 16 AF FF FF jmp loc_40106D ;go back to normal program flow

Ah, but what happens if the user doesn't close the window via the 'Save&Quit' button. Oops! Forgot about that. But it's easy to fix. We'll simply redirect the code for WM_CLOSE (10h) to our code at 406100. So, at 401044 do a jmp 406100 in hiew. That's it!

PART IV

Now, all we have left to do is code our util.exe and we're all set. Here are the parts of interest:

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY ; if the user closes our window
invoke PostQuitMessage,NULL ; quit our application
.ELSEIF uMsg==1280 ;if message is 1280=500h
mov ecx, OFFSET boxes ;move the starting offset of the boxes buffer in ecx
add ecx,wParam; add the wParam (id of checkbox)
sub ecx,101 ;delete 101 (now in ecx we'll have the offset of ecx + i, where i=0..8, i=number of checkbox-1)
.IF lParam==1 ;is it checked?
mov byte ptr [ecx],1
.ENDIF
.IF wParam==6Dh ;was this message issued for the last checkbox? If so, that means that the openme2.exe will soon terminate.
invoke SetTimer,hWnd,NULL,1000,NULL ;set a timer for one second
.ENDIF
.ELSEIF uMsg==WM_TIMER ;if one second elapsed, it must mean that OpenMe #2 is finished, so we can write to it
invoke CreateFile,OFFSET openme_filename,GENERIC_WRITE,NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
mov esi,eax
invoke SetFilePointer,eax,3888,NULL,FILE_BEGIN ;move to 3888=F30h=physical offset for 405130
invoke WriteFile,esi,OFFSET boxes,9,OFFSET dummybuffer,NULL ;write to the file the buffer with the bytes
invoke CloseHandle,esi
invoke PostMessage,hWnd,WM_CLOSE,NULL,NULL ;close our app
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ; Default message processing
ret

.ENDIF
xor eax,eax
ret
WndProc endp

Note:
I set a delay of one second from the time util.exe receives the message from the last checkbox to the time it tries to write to the openme2.exe file. This implies two things: the write process could fail on slow/busy systems since the reverseme process could still be open, resulting in a sharing error(not much of an issue though) and that you must wait for about a second from the time you close openme to the time you run it again. Also, multiple instances can cause (and probably will :P ) problems.

4.Final words
Nice reme, I really enjoyed it, although the "This should take you even longer" remark... I don't know, it took me about a day to do it (not counting the present essay). Well, maybe that's just me :P

5.Greets
(Acknowledgements)

Greets and thankyou's go to:
Crudd, extasy, amante4, SantMat, noptical, Iczelion, NeuRaL_NoiSE, the guys at #Cracking4Newbies, #win32asm, the guys in ID (your database is really great), and everyone else I forgot.
6.Contact me
You can mail me with questions/problems/queries/threaths and so on at tank__@hotmail.com