How to reverse engineer a Windows 95 target
REVERSE ENGINEERING EXERCISES FOR THE MASSES - (2b)
by Fravia+ (MSRE), August 1997

HCU
(Part B: reverse engineering without source code - 04 August 1997)

Courtesy of Fravia's page of reverse engineering


Well, a very interesting essay... I wrote it myself! :-) This essay will be divided in four (or more) parts:
A = Introduction to filemon
B = reverse engineering without source code 
C = Filemon reversed
D = Back to Main
E = VXD vagaries and mysteries
Although already disponible, this essay is still under construction and will be modified and ameliorated until the wording below will disappear (I reckon until mid-september)


UNDER CONSTRUCTION

REVERSE ENGINEERING EXERCISES FOR THE MASSES - (2b)
How to reverse engineer a Windows 95 program
Filemon.exe Version 2
(Part B: Reverse engineering without source code)
by Fravia (MSRE), August 1997


Well, in the first part of this essay we have got an "introduction" to the structure of filemon.exe, let's have a "global" look before continuing...
The structure of our target
Have a look at your C source code, smack at the beginning:
usual #includes
Note that among the various "standard" includes there is an #include resource.h and an #include /vxd/ioctlcmd.h... there you'll find "homemade" IDs since, as applications grow, Micro$oft's developer studio defines for the programmers a number of new IDs (symbols). This are MOST important for reverse engineering purposes... have a look at the RESOURCE.H file, for instance.
(Note, moreover, that the "windowsx.h" included file is, among other API-macro functions, the official "window message crackers" file :-)
usual #defines
(check also the #defines inside the #includes)
usual variable declarations
usual procedure declarations
And then, the c code for the various functions of this winprogram:
01) FUNCTION Abort ;This, as we have already seen, starts at 1000
02) FUNCTION WinMain
03) FUNCTION InitApplication ;This, as we have already seen, starts at 10B0
04) FUNCTION InitInstance ;This, as we have already seen, starts at 1130
05) FUNCTION MainWndProc
06) FUNCTION Split
07) FUNCTION List_Append
08) FUNCTION UpdateStatistics
09) FUNCTION CreateListView
10) FUNCTION SaveFile ;This, as we have already seen, starts at 1C20
11) FUNCTION FilterProc
12) FUNCTION About

Where are the other corrispondences?
Let's examine the dead listing of our target... looking for "CALLS" we get the quite long (and therefore truncated) list below:
:Location_List
:00401000 ; START OF CODE SEGMENT (Error handling: Abort function)
:1020     ; (2626 calls)
:10B0     ; (102B calls) ;This is InitApplication
:1130     ; (1048 calls) ;This is InitInstance
:1750     ; (17A9 calls)   
:1790     ; (1AFB calls)
:1A40     ; (166E and 16E5 calling)
:1B50     ; (124A calls)
:1C20     ; (1579 and 1596 calling); This is SaveFile
:21CC     ; (1D0C calls)
:21E0     ; (1276, 1288, 129A, 20A2, 20B9, 20CB calling)
....
:004024E0 ;program entry point
...
And so on, and so on, there are many more routines (the target's listing reaches :0040696E) but, as you can see from the Location_List above, the FUNCTIONS we are looking for are evidently situated in the same (first) part of the code, in a sequence that recalls the C listing's one.
Besides, note how the routine starting at 21E0 is called from six different points... typical of a "inner working" routine as opposite to a function, that's the reason I did not listed the following routines beteen this 210 and "program entry point".
If you are unsure just have a look... examining routines, you'll get an idea of what they are about, and there is a quick "automated" routine sniffing trick ("climbing the tree"), that you'll learn in the third part of this essay...
Let's first of all discuss the "program entry point" sequence, a code snippet that you'll find ALWAYS, with the usual small variations, inside all windows programs.

Here at program entry, before calling WinMain, we have the "usual" preparation performed by all (compiled) windows programs.
See - at program entry - the following sequence of "catering" routines: GetVersion-HeapCreate-Variable setting-GetStartupInfo-GetFileType-SetHandleCount-GetCommandLineA (btw: you should investigate, on more obscure targets, this "GetCommandLine" part of the code, in order to see if there are, "secret" argument switches that you could trigger starting your target :-); GetStartupInfoA, GetModuleHandleA, and then we find following call:
:00402626 E8F5E9FFFF  call 00401020,
which smells from far away like the call to WinMain...

Indeed, if you look at our target's Location_List above, you'll see that this 1020 address is the only possible function's entry between 1000 (start of code and Abort function, as we have already seen) and 10B0 (InitApplication function). Then follow 1130 (InitInstance function), and what do we have thereafter? 1750, 1790, 1A40 1B50 and 1C20 (and this we know already! It's the SaveFile function!)
These four functions' entry points should correspond, respectively, to the C source code functions MainWndProc, Split, List_Append, UpdateStatistic and CreateListView... mmm... bad! 4 routines in assembly and 5 functions in C... let's have a closer look at everything!

Our first loop
The first one, at 1750, is a routine that "counts" something, with a loop that increases the value bx, value that this routine returns to the caller:
:00401750 8B442404   mov eax, [esp + 04] ;get the 1st param from caller
:00401754 53         push ebx            ;save old bx
:00401755 56         push esi            ;save old esi
:00401756 33DB       xor ebx, ebx        ;clean bx
:00401758 0FBE742410 movsx byte ptr esi, [esp + 10] ;2nd param 
:0040175D 57         push edi            ;save old edi
:0040175E 8B7C2418   mov edi, [esp + 18] ;3rd param from caller

:loop_1762_call 2260
:00401762 8907         mov [edi], eax ;get ax in [edi] holder
:00401764 83C704       add edi, 4    ;increase holder 
:00401767 43           inc ebx       ;we are counting (cnt++)
:00401768 56           push esi      ;rightmost param for call
:00401769 50           push eax      ;leftmost param, loop increased
:0040176A E8F10A0000   call 00402260 ;call 2260 (with increased ax)
:0040176F 83C408       add esp, 8    ;correct stack
:00401772 85C0         test eax, eax ;is it 0?
:00401774 7406         je 0040177C   ;exit loop if 0 returned
:00401776 C60000       mov byte ptr [eax], 0 ;else zero *[ax]
:00401779 40           inc eax       ;returned value =returned value+1
:0040177A EBE6         jmp 00401762  ;loop back

:exit_loop
:0040177C 8BC3      mov eax, ebx ;this, ebx is the value
:0040177E 5F        pop edi      ;returned by this function,
:0040177F 5E        pop esi      ;it corresponds to cnt in the
:00401780 5B        pop ebx      ;c code and corresponds to the
:00401781 C3        ret          ;number of loops and 2260 calls
So, what does this function do? It's substantially a loop, calling ANOTHER routine until this one returns zero!... Looks like a for construction, therefore we are most probably looking at the SPLIT function (which is the first one in our c source code with such a construction!).
If we have a look at the 2260 routine that this function calls at each loop, we'll see that it's main purpose is to manipulate the value in ax (with a couple of loops).
Since the split function is in turn called from the List_Append function, let's go on, and examine the next function in our disassembled text, at 1790. This function is connected to the previous one (which is called after a couple of pushes), and it calls in turn a sequence of string operations (lstrlenA, wsprintfA) before sending a Message
We have obviously to do with a string manipulation function... we will in short imagine that we DO NOT have the source code of our target, and we'll see what we can figure out, but first, since in the reality we know that this is the List_Append function, let's have a look at the beginning of List_Append, here below, which coincides with the following three lines of c code:
itemcnt = itemcnt= Split( line, '\t', items );
	     if ( itemcnt == 0 )
	     return TRUE;
These lines translate into:
:00401790 81ECA0000000    sub esp, 000000A0     ;adjust stack
:00401796 8D442450        lea eax, [esp + 50]   ;get items
:0040179A 53              push ebx              ;will pop
:0040179B 8B8C24B0000000  mov ecx, [esp + 000000B0] ;get line
:004017A2 56              push esi              ;will pop
:004017A3 57              push edi              ;will pop
:004017A4 55              push ebp              ;will pop
:004017A5 50              push eax              ;items param
:004017A6 6A09            push 00000009         ;\t (char delimiter)
:004017A8 51              push ecx              ;line param
:004017A9 E8A2FFFFFF      call 00401750=split   ;split(line, '\t', items)
:004017AE 83C40C          add esp, 0000000C     ;adjust stack
:004017B1 8BD8            mov ebx, eax      ;save return value cnt=itemcnt
:004017B3 85DB            test ebx, ebx     ;if itemcnt is NOT zero
:004017B5 7510            jne 004017C7      ;continue List_Append
:004017B7 B801000000      mov eax, 1        ;else return TRUE
:004017BC 5D              pop ebp           ; and poppall
:004017BD 5F              pop edi
:004017BE 5E              pop esi
:004017BF 5B              pop ebx
:004017C0 81C4A0000000    add esp, 000000A0 ;re-orden stack
:004017C6 C3              ret
Note, above at 17A6, the escape sequence 9 for "horizontal tab"... have a look at your ASCII table... 07 is bell, 08 is backspace and so on... basically, ANY TIME you see a "funny" value (i.e. not one and not zero) pushed for a call, you have the possibility to understand what the call itself is about using just a little "feeling", most of the time you wont even need to "climb" the calling tree (you'll anyway learn how to climb effectively in the third part of this essay). Here, the fact that one of the passed parameter is a TAB let's us immediately understand that the other two parameters for our split function must have to do with characters... as they indeed do

Cracking without source code
So, let's now imagine that we have NOT the source code of our target, and let's see what does this "mysterious" function do, once passed the "1050" call check.
We'll now study a little this function
A word about the three routines, that we will encounter in the snippet of code below: wsprintf, lstrlen and SendMessage
wsprintf(lpszOutput, lpszFormat, ...)
LPSTR lpszOutput;	/* address of string for output*/
LPSTR lpszFormat;	/* address of format-control string*/
. . .	             /* Specifies zero or more optional arguments*/
The wsprintf function formats and stores a series of characters 
and values in a buffer. Each argument (if any) is converted 
according to the corresponding format specified in the format 
string. The return value is the number of bytes stored in the 
lpszOutput string, not counting the terminating null character, 
if the function is successful.
lstrlen(lpszString) LPCSTR lpszString; /* address of string to count*/ The lstrlen function returns in bytes the length of the specified string (not including the terminating null character).
SendMessage(hwnd, uMsg, wParam, lParam) HWND hwnd; /* handle of destination window */ UINT uMsg; /* message to send */ WPARAM wParam; /* first message parameter */ LPARAM lParam; /* second message parameter */ The SendMessage function sends the specified message to the given window. The function calls the window procedure for the window and does not return until that window procedure has processed the message. The return value specifies the result of the message processing and depends on the message sent.
A big chunk of List_Append
Now be patient, there is a big chunk of code awaiting you, just read it slowly, and check my findings... you do not need to check the source c code yet, you'll (I hope) understand everything LOOKING AT THE DISASSEMBLED CODE, with the knowledge you already have if you followed me until this point. You'll see that the beginning of the code chunk below "turns" around a "big verification" and a "little loop". Let's have a good look at this mysterious (List_Append :-) routine
:004017C7 8B442460                mov eax, [esp + 60]    ;get parameter
:004017CB 803800                  cmp byte ptr [eax], 00 ;is it zero?
:004017CE 0F84B6000000            je 0040188A            ;yes do not big verify
:004017D4 BFFFFFFF7F              mov edi, 7FFFFFFF      ;no, big verify
                                                         ;with edi=0x7FFFFFFF
:004017D9 8BB424B4000000          mov esi, [esp + B4]    ;with this si
:004017E0 8BAC24B8000000          mov ebp, [esp + B8]    ;and these optional
                                                         ;arguments for wsprintf
:big verify from above or from Address:004018E6(C)
:004017E7 8B442460           mov eax, [esp + 60]         ;get parameter
:004017EB 803800             cmp byte ptr [eax], 00      ;is it zero?
:004017EE 0F8408010000       je 004018FC                 ;yes, exit big verify
:004017F4 55                 push ebp       ;optional arguments
:004017F5 68BC814000         push 004081BC  ;("%d") format-control string
:004017FA 68A0944000         push 004094A0  ;address of string for output
:004017FF FF15BCB24400       Call dword ptr [0044B2BC] ;USER32.wsprintfA output
:00401805 C744244405000000   mov [esp + 44], 5
:0040180D 897C2448           mov [esp + 48], edi
:00401811 83C40C             add esp, C
:00401814 C744244000000000   mov [esp + 40], 0
:0040181C C744244CA0944000   mov [esp + 4C], 004094A0 ;save address here
:00401824 68A0944000         push 004094A0         ;address of stringbuffer
:00401829 FF15D4B14400       Call dword ptr [0044B1D4] ;KERNEL32.lstrlenA, count
:0040182F 40                 inc eax               ;add one to count
:00401830 8D4C2438           lea ecx, [esp + 38]   ;get lMsgParam2
:00401834 89442450           mov [esp + 50], eax   ;save counted bytes
:00401838 51                 push ecx	              ;push lMsgParam2
:00401839 896C245C           mov [esp + 5C], ebp  ;save ebp there
:0040183D 6A00               push 0               ;push wMsgParam1
:0040183F 6807100000         push 00001007        ;push uMsg 1007 (see below)
:00401844 56                 push esi             ;push hWnd	
:00401845 FF15D8B24400       Call dword ptr [0044B2D8] ;USER32.SendMessageA
:0040184B 83F8FF             cmp eax, FFFFFFFF    ;returned -1?
:0040184E 8BF8               mov edi, eax	;save returned result (row)
:00401850 0F85A6000000       jne 004018FC	;-1, let's exit big verify
:00401856 55                 push ebp        ;else let's prepare error message
:00401857 6898814000         push 00408198   ;"Error adding item %d to list view"
:0040185C 68A0944000         push 94A0=Strings_buffer;address of output string
:00401861 FF15BCB24400       Call dword ptr [0044B2BC] ;USER32.wsprintfA, Ord:262h
:00401867 83C40C             add esp, C     ;correct stack
:0040186A 6A00               push 0         ;push MB_OK
:0040186C 6888814000         push 00408188  ;"filemon Error" is WMsgParam
:00401871 68A0944000         push 004094A0  ;previous string is the message
:00401876 56                 push esi       ;push usual hWnd
:00401877 FF1590B24400       Call dword ptr [0044B290] ;USER32.MessageBoxA, Ord:195h
:0040187D 33C0               xor eax, eax   ;exit routine with a return FALSE
:0040187F 5D                 pop ebp        ;pop them all
:00401880 5F                 pop edi
:00401881 5E                 pop esi
:00401882 5B                 pop ebx
:00401883 81C4A0000000       add esp, A0
:00401889 C3                 ret            ;exit FALSE

:here_from_start_if_no_need_to_big_verify
:0040188A 8BB424B4000000    mov esi, [esp + B4]  ;get hWnd
:00401891 6A00              push 0		    ;lMsgParam2
:00401893 C744243C04000000  mov [esp + 3C], 4
:0040189B C744244400000000  mov [esp + 44], 0
:004018A3 6A00              push 0		;wMsgParam1
:004018A5 6804100000        push 1004		;message 1004
:004018AA 56                push esi		;usual window
:004018AB FF15D8B24400      Call dword ptr [0044B2D8] ;USER32.SendMessageA, Ord:01D9h
:004018B1 8D78FF            lea edi, [eax-01] ;edi = return-1
:004018B4 8BAC24B8000000    mov ebp, [esp + B8]
:004018BB 85FF              test edi, edi     ;OK SendMessage?
:004018BD 7C24              jl 004018E3      ;do not little loop if lower

:little loop
:004018BF 8D442438          lea eax, [esp + 38]       ;get lMsgParam2
:004018C3 897C243C          mov [esp + 3C], edi       ;save edi
:004018C7 50                push eax                  ;lMsgParam2
:004018C8 6A00              push 0                    ;wMsgParam1
:004018CA 6805100000        push 1005                 ;message 1005
:004018CF 56                push esi                  ;usual window
:004018D0 FF15D8B24400      Call dword ptr [0044B2D8] ;USER32.SendMessageA, Ord:01D9h
:004018D6 85C0              test eax, eax             ;test result
:004018D8 7406              je 004018E0	         ;continue if zero
:004018DA 396C2458          cmp [esp + 58], ebp ;else check if [sp+58]=bp
:004018DE 7403              je 004018E3         ;and exit little loop if it is
:004018E0 4F                dec edi                   ;edi lowered
:004018E1 79DC              jns 004018BF              ;little loop 

:exit little loop
:004018E3 83FFFF                  cmp edi, FFFFFFFF
:004018E6 0F85FBFEFFFF            jne 004017E7 ;big verify if not
:004018EC B801000000              mov eax, 1 ;RETURN TRUE if edi=FFFFFFFF
:004018F1 5D                      pop ebp
:004018F2 5F                      pop edi
:004018F3 5E                      pop esi
:004018F4 5B                      pop ebx
:004018F5 81C4A0000000            add esp, A0
:004018FB C3                      ret
That was a big chunk of code! And we are not yet finished... but first let's have a closer look at one of our most interesting "findings":
:004017FA 68A0944000         push 004094A0  ;address of string buffer
As you'll learn with practice, all targets utilise part of their own data area as a "buffer" for strings, a sort of "blackboard" in order to prepare, update, modify and destroy string messages. Once you find it you may add as a tag "Strings_buffer", for instance, in the whole disassembled listing... In our target there are 16 locations... this helps a lot, as you'll see...
Exiting from the "big verify" above we land here, smack in the middle of a forest of switches:
:process block
:004018FC 85DB                test ebx, ebx          ;SWITCH ebx=0?
:004018FE 7E3B                jle 0040193B           ;go to next block if le 0
:00401900 8B442460            mov eax, [esp + 60]    ;get next Oemstring
:00401904 803800              cmp byte ptr [eax], 00 ;finish Oemstring?
:00401907 7432                je 0040193B            ;go to next block if so
:00401909 68A0944000          push 94A0=Strings_buffer   ;translate here
:0040190E 8B2DB0B24400        mov ebp, [0044B2B0]    ;OemToChar address
:00401914 50                  push eax               ;push Oemstring
:00401915 FFD5                call ebp               ;call OemToChar
:00401917 C744241801000000    mov [esp + 18], 1      ;PARAMETER 1     (PROCESS)
:0040191F C7442424A0944000    mov [esp + 24], 94A0=Strings_buffer
:00401927 8D4C2410            lea ecx, [esp + 10]
:0040192B 51                  push ecx
:0040192C 57                  push edi
:0040192D 682E100000          push 102E             ;message 102E (1=ROCESS)
:00401932 56                  push esi
:00401933 FF15D8B24400        Call dword ptr [0044B2D8] ;USER32.SendMessage_102E
:00401939 EB06                jmp 00401941              ;continue

:falls from process_block
:0040193B 8B2DB0B24400            mov ebp, [0044B2B0]   ;must have OemToChar address

:continue_request_block
:00401941 83FB01                  cmp ebx, 1              ;switch ebx=1
:00401944 7E33                    jle 00401979            ;next block if le 1
:00401946 8B442464                mov eax, [esp + 64]     ;get Oemstring
:0040194A 803800                  cmp byte ptr [eax], 00  ;finish Oemstring?
:0040194D 742A                    je 00401979             ;next block if so
:0040194F 68A0944000              push 94A0=Strings_buffer;traslate here
:00401954 50                      push eax                ;push Oemstring
:00401955 FFD5                    call ebp                ;OemToChar
:00401957 C744241802000000        mov [esp + 18], 2   ;PARAMETER 2	 (REQUEST)
:0040195F C7442424A0944000        mov [esp + 24], 94A0=DataAreaStrings
:00401967 8D442410                lea eax, [esp + 10]
:0040196B 50                      push eax
:0040196C 57                      push edi
:0040196D 682E100000              push 102E           ;message 102E	  (2=REQUEST)
:00401972 56                      push esi
:00401973 FF15D8B24400            Call dword ptr [0044B2D8] ;USER32.SendMessage_102E 
Well, you've seen already two of the switch blocks, there are three more, and they are all the same... therefore I'll print below only the first, the relevant and the last isntruction of each one of them...
:continue_path_block
:00401979 83FB03             cmp ebx, 3             ;switch ebx=3? See above comments
...
:0040198F C744241803000000   mov [esp + 18], 3    ;PARAMETER 3	 (PATH)
...
:004019AB FF15D8B24400       Call dword ptr [0044B2D8] ;USER32.SendMessage_1025

:continue_result_block
:004019B1 83FB02             cmp ebx, 2         ;switch ebx=2? See above comments
...
:004019C7 C744241804000000   mov [esp + 18], 4	;PARAMTER 4 (RESULT)
...
:004019E3 FF15D8B24400       call dword ptr [0044B2D8] ;USER32.SendMessage_1025

:continue_block_other
:004019E9 83FB03             cmp ebx, 3  ;switch ebx=3? See above comments
...
:004019FF C744241805000000   mov [esp + 18], 5  ;PARAMETER 5 (OTHER)
...
:00401A1B FF15D8B24400       Call dword ptr [0044B2D8] ;USER32.SendMessage_1025

:continue from block_other
:00401A21 B801000000         mov eax, 1  ;RETURN value=TRUE
:00401A26 5D                 pop ebp     ;popall
:00401A27 5F                 pop edi
:00401A28 5E                 pop esi
:00401A29 5B                 pop ebx
:00401A2A 81C4A0000000       add esp, A0  ;stack adjust
:00401A30 C3                 ret          ;end of "row"
So, what's happening here? Let's THINK a little... this is the most important "visualisation" function of our target... it is here that the target gets the data from the virtual device and through Message 1025 (which is SetItemText) adds various "rows" ("items") to the main activity window...
Well, this was the "gut" of our program... the Filevxd.vxd intercepted operations have been "translated" into strings on 5 different columns: Process, Request, Path, Result and Other. Note that after having done this we will return with one "written row", ready for the next one.

Yes... but return WHERE? Who called List_Append? Let's have a look... [1790-1A30]:List_Append... Therefore let's look who has called 1790, here you are:
:00401AFB E890FCFFFF  call 00401790List_Append
This is the only call to List_Append inside our target, and it comes from the NEXT routine, which starts at 1A40. We will examine it very soon, but what about the big MainWndProc?

MainWndProc did not disappear!
Let's go back to MainWndProc for a moment... you'll have noticed that we have reversed "Split" and "List_Append"... What about "MainWndProc"... she should come now, following the Location_List of "called" procedures that we have seen at the beginning... indeed! MainWndProc starts at 1190, as we have seen in the first part of this essay (through the WNDCLASS trick), as a matter of fact this procedure does not seem to be "called" by anybody at all:
 
:0040118D CC              int 03  ;filler: compiler must reach a correct location (90)
:0040118E CC              int 03  ;filler: compiler must reach a correct location (90)
:0040118F CC              int 03  ;filler: compiler must reach a correct location (90)
:00401190 81EC44010000    sub esp, 144   ; THIS IS MainWndProc start!
:00401196 53              push ebx
:00401197 56              push esi
:00401198 8BB42454010000  mov esi, [esp + 154]
:0040119F 57              push edi
:004011A0 83FE0F          cmp esi, F
...
The filler using, "snake" trick
All these int03 fillers, disseminated inside compiled code, can be very useful for "patching" other targets... say you want to add to a target a completely new function WITHOUT modifying its size (which is at times pretty important for applied reverse engineering :-)
Let's imagine that the function you have written is, say, 200 bytes long (which is quite a lot for a tight code function written in assembler)... ok, now imagine that you modify the little "about window" of your target, which usually has only a MB_OK button, adding a cancel button, or something more hidden, which WILL TRIGGER your completely new function, that you have "spread" along the many int03 "nuggets" of the program. Your function will run like a snake inside the code of your target, where you'll have substituted many "CC" instructions with your function's ones... something like Inst1, Ints2, Inst3, jmpInst4, Inst4, Inst5, Inst6, JmpInst7, Inst7...Inst197, JmpInst19A, Inst19A, ret... You dig it?

We could have found the beginning of MainWndProc also with a simpler method: checking THE END of the routines we have already investigated... if you remember the first part of this essay, InitInstance terminated with
UpdateWindow(hWndMain); 
	return hWndMain;
which translates into:
UpdateWindow(hWndMain);
:00401182 56              push esi ; hWndMain (handle was in esi)
:00401183 FF15F0B24400    Call dword ptr [0044B2F0] ;USER32.UpdateWindow, Ord:024Fh 
:00401189 8BC6            mov eax, esi ;return to WinMain with hWndMain in eax
:0040118B 5E              pop esi ;let's have the old esi back
:0040118C C3              ret
And, as you can see, the code that immediately follows (after three "CC") is, as it is logical to expect and as the information from WNDCLASS has already told us, the beginning of MainWndProc, which is "automatically" (and logically) initiated once the various InitInstance create, show and update window procedures have terminated...
Have windows... program may start!

Alas we are not yet ready for the reverse engineering of MainWndProc. We need some more "muscles" first. Let's go on with the function which follows (and calls) List_Append... we'll come back to MainWndProc don't worry: you'll master this application like nobody else!

How to sniff main functions (as opposed to routines)
We have seen how the Split function began at 1750 ending at 1781, and the List_Append function, began at 1790 ending at 1A30. Since "good" Address 1A30 is already taken by List_Append's last ret instruction, and since the compiler needs a "good" starting location for the next function, the compiler stuffs again a lot of (eventually useful for us) int 03 interrupts inside the code of our target, until the next "good" location:
:00401A40 8B44240C                mov eax, [esp + 0C]
You know already enough to grasp immediately that this is a function, not a "lower" routine: it gets in ax one of the passed parameters as first priority in order to test it... "lower" routines usually do not recover immediately passed parameter... they push values, or jump straight away, or compare flags, or perform other tasks... studying the beginning of routines you'll quickly develop the right "feeling" for the important ones.
You'll know that you have to do with an "important" function (as opposed to a "lower" routine) even if you don't understand WHAT the hell it does :-)
Let's recall the various beginnings of the functions we have reversed until now:
:00401000 8B442408     mov eax, [esp + 08]       Abort (get second par)
:00401020 83EC1C       sub esp, 0000001C        (WinMain)
:004010B0 8B442404     mov eax, [esp + 04]       InitApplication (get second par)
:00401130 8B442404     mov eax, [esp + 04]       InitInstance (get second par)
:00401190 81EC44010000 sub esp, 00000144        (WinMainProc)
:00401750 8B442404     mov eax, [esp + 04]       Split (get second par)
:00401790 81ECA0000000 sub esp, 000000A0         List_Append 1st line, (shuffle)
:00401796 8D442450     lea eax, [esp + 50]                   2nd line, (get par)
Well, with the exceptions of the two "Main" procedures of our target, ALL OTHER FUNCTIONS have as first (or second) instruction a
mov eax, [esp + nn]
instruction... and the "nn" location to be added to esp can give you often as well, by the way, an idea of HOW MANY arguments is the unknown function expecting...

Let's have a look at the next function after List_Append, it's of course the "UpdateStatistics" function, but ignore the C source code alone for a while, just follow the disassembled code... I'll keep your hand:

The UpdateStatistics function
:00401A40 8B44240C          mov eax, [esp + 0C] ;get Main_param (third one)
:00401A44 53                push ebx            ;save bx
:00401A45 56                push esi            ;save si
:00401A46 85C0              test eax, eax       ;Main_param=0?
:00401A48 57                push edi            ;save di
:00401A49 55                push ebp            ;save bp
:00401A4A 7511              jne 00401A5D        ;have Main_param, let's go on
:00401A4C 833DB496400006    cmp dword ptr [004096B4], 6 ;is [96B4]<6? :00401A53 0F82E3000000 jb 00401B3C ;param="0" and [96B4]<6: ret :00401A59 85C0 test eax, eax ;[96B4]>6 Main_param sure zero?
:00401A5B 7468              je 00401AC5         ;Main_param zero but [96B4]>6

:Have Main_Param, let's go on
:00401A5D 8B442414          mov eax, [esp + 14]       ;get hWnd
:00401A61 50                push eax                  ;hWnd for all mouse input
:00401A62 FF15B4B24400      Call dword ptr [0044B2B4] ;USER32.SetCapture (hWnd)
:00401A68 8B0DB0964000  **  mov ecx, [004096B0]  ;get parameter for next call
:00401A6E 8B3DB8B24400  **  mov edi, [0044B2B8]  ;get next call name
:00401A74 51            **  push ecx             ;push handle of cursor
:00401A75 FFD7          **  call edi   ;Call [0044B2B8] ;USER32.SetCursorA
The "indirect function call" trick
Watch the typical construction above (the four lines with **)... every time the programmer builds an own "homemade" function (here hSaveCursor), the compiler will resolve it like that.
The four lines above correspond to hSaveCursor = SetCursor(hHourGlass); but -alas- you'll not find, in your disassembled dead listing, the useful tag
:00401A75 FF15B8B24400 Call dword ptr [0044B2B8] ;USER32.SetCursorA... you'll only have a forsaken line
:00401A75 FFD7 call edi... it's up to you to "solve" it (which is easy, given the corrispondence between 44B2B8 and SetCursorA :-)
Let's go on
:00401A77 8B742418           mov esi, [esp + 18]    ;get hWnd for next call
:00401A7B 6A00               push 00000000          ;push rightmost NULL
:00401A7D 6A00               push 00000000          ;push next NULL
:00401A7F 8B2DD8B24400       mov ebp, [0044B2D8]    ;get real call
:00401A85 6804100000         push 00001004          ;push 1004
:00401A8A A3BC964000         mov [004096BC], eax    ;save ax in [96BC]  
:00401A8F 56                 push esi     ;push leftmost parameter
:00401A90 FFD5               call ebp     ;call ebp (si, 1004, 0, 0)
This function is SendMessage, can you feel it? (If not ook inside your disassembled listing for "44B2D8" :-) Therefore the above call corresponds to SendMessage(hwnd, 1004, NULL, NULL)
:00401A92 C6056C80400001 mov byte ptr [0040806C], 01 ;[806CSEndMsg1008]=TRUE
:00401A99 85C0           test eax, eax               ;if zero returned
:00401A9B 7E11           jle 00401AAE                ;don't loop_SendMessage_1008
:00401A9D 8BD8           mov ebx, eax                ;else get counter

:loop_1A9F_call_SendMessage_1008
:00401A9F 6A00                push 00000000      ;since ebp has not changed,
:00401AA1 6A00                push 00000000      ;we use it to SendMessage
:00401AA3 6808100000          push 00001008      ;Number 1008
:00401AA8 56                  push esi           ;always to the same window
:00401AA9 FFD5                call ebp  ;call SendMessage(hWnd,1008,NULL,NULL)
:00401AAB 4B                  dec ebx            ;cnt--
:00401AAC 75F1                jne 00401A9F       ;loop until cnt=0

:after_loop_1A9F_call_SendMessage_1008
:00401AAE C6056C80400000  mov byte ptr [0040806C], 00 ;[806CSEndMsg1008]=FALSE
:00401AB5 A1BC964000      mov eax, [004096BC]    ;Want my old ax back
:00401ABA 50              push eax               ;as parameter for next call
:00401ABB FFD7            call edi               ;call SetCursorA(ax)
:00401ABD FF15C4B24400    Call dword ptr [0044B2C4] ;USER32.ReleaseCapture 
:00401AC3 EB0A            jmp 00401ACF           ;continue without updating

:From_entrance_if_Main_param=zero_but_[96B4]>6
:00401AC5 8B742418                mov esi, [esp + 18] ;update esi
:00401AC9 8B2DD8B24400            mov ebp, [0044B2D8] ;ebp must point to SendMessage

:continue_from_SendMessage_1008_loop
:00401ACF BBF0994000      mov ebx, 004099F0   ;pointer to the data area
:00401AD4 A1B4964000      mov eax, [004096B4] ;get this value in ax
:00401AD9 03C3            add eax, ebx        ;check and
:00401ADB 3BC3            cmp eax, ebx        ;if [4096B4]=0
:00401ADD 7632            jbe 00401B11        ;do not loop_1ADF_List_Append

:loop_1ADF_List_Append
:00401ADF 8D5304     lea edx, [ebx+04]     ;get parameter
:00401AE2 B9FFFFFFFF mov ecx, FFFFFFFF	;prepare counter
:00401AE7 8BFA       mov edi, edx  ;get correct value for scasb
:00401AE9 2BC0       sub eax, eax  ;repnz condition
:00401AEB F2         repnz     ;repeat while not equal
:00401AEC AE         scasb     ;this compare string operation
:00401AED 52         push edx  ;parameter (char*line)
:00401AEE 8B03       mov eax, [ebx]  ;get parameter (seg)
:00401AF0 F7D1       not ecx   ;count how many scasb
:00401AF2 50         push eax  ;parameter (seq)
:00401AF3 56         push esi  ;parameter (hWndList)
:00401AF4 8D5C1904   lea ebx, [ecx + ebx + 04] 
:00401AF8 8D79FF     lea edi, [ecx-01]         
:00401AFB E890FCFFFF call 00401790List_Append
:00401B00 83C40C     add esp, 0000000C   ;correct stack
:00401B03 A1B4964000 mov eax, [004096B4] ;check [96B4]
:00401B08 05F0994000 add eax, 004099F0   ;add to it "DATAbottom"
:00401B0D 3BC3       cmp eax, ebx        ;check against bx and if lower
:00401B0F 77CE       ja 00401ADF  ;loop1ADF ;exit loop_List_Append

:Exit_loop_List_Append
:00401B11 C705B496400000000000 mov dword ptr [004096B4], 0 ;zero [96B4]location
:00401B1B 803D6880400000       cmp byte ptr [00408068], 00 ;is [8068]=0?
:00401B22 7418                 je 00401B3C                 ;return if so
:00401B24 6A00                 push 00000000    ;NULL for the second call
:00401B26 6A00                 push 00000000
:00401B28 6A00                 push 00000000
:00401B2A 6804100000           push 00001004
:00401B2F 56                   push esi
:00401B30 FFD5                 call ebp  ;CALL SendMessage(hWnd,1004,NULL,NULL)
:00401B32 48                   dec eax   ;result value=resultvalue-1
:00401B33 50                   push eax  ;will be pushed
:00401B34 6813100000           push 00001013    ;along with message 1013
:00401B39 56                   push esi         ;same hWnd of course
:00401B3A FFD5                 call ebp  ;CALL SendMessage(hWnd,1013,eax,NULL)

:return_home_Lassie
:00401B3C 5D                      pop ebp
:00401B3D 5F                      pop edi
:00401B3E 5E                      pop esi
:00401B3F 5B                      pop ebx
:00401B40 C3                      ret
Well, what ARE actually these mysterious messages that we are continuously finding inside our target?
What did we have until now? Let's see:
Message 1007 in List_Append at 183F 
Message 1004 in List_Append at 18A5 
Message 1005 in List_Append at 18CA 
Message 102E in List_Append at 192D, 196D, 19A5, 19DD and 1A15
(and List_Append is called inside a loop of UpdateStatistics)
Message 1004 in UpdateStatistics at 1A85, 1B2A
Message 1008 in UpdateStatistics at 1AA3 (a loop)
Message 1013 in UpdateStatistics at 1B34
Since these messages are all correlated with the main window of our target, we would be well advised to use as part of their new tags something like "Main_Wnd_Msg", or a similar name (at least until we FIND OUT what's the real name of the main window of our target :-)
Basically the whole "spirit" of windows programming dwells inside such messages: any window procedure must examine messages it receives from the system or from other windows, or from the user, and determine what action, if any, to take
We must delve a little deeper inside Windows messages... here you go:
Window Messages
A window message is a set of values that Windows sends to a window procedure 
as input or requesting the window to carry out some action. Windows includes 
a wide variety of messages that it (or any other application) can send to a 
window procedure. Most messages are sent to a window as a result of a given 
function being executed or as a result of input from the user.
 Every message consists of four values: a handle that identifies the window 
(hWnd), a message identifier (msg), a 16-bit message-specific value, and a 
32-bit message-specific value. These values are passed as individual parameters 
to the window procedure. The window procedure then examines the message 
identifier to determine what response to make and how to interpret the 16- 
and 32-bit values.
Although Windows generates most messages, an application can create its own 
messages and place them in its own message queue or that of another 
application. 
Seems like we are at a dead point of our cracking session... without knowing what these "private" and "home-made" ID_ mean, we'll not figure them out...
And yet... let's THINK a little: When we have run our target we have SEEN what it does: it writes in the main window all system activities... it get's them from its VXD driver, ok, we'll examine that later on... but substantially it writes row after row of data inside the window, and it has an "autoscroll" function to keep new added items visible, and a "clear display" option to delete all items... mmm... one of the message must be Main_Window_Delete_Items... will it be the "1008" one in the UpdateStatistics loop? (Yes! Have a look at the C source code of UpdateStatistics NOW :-)
Hey, 42000 bytes, time to switch over to a new lesson!
(c) Fravia+ 1997. All rights reserved.

You are deep inside Fravia's page of reverse engineering, choose your way out:
filemon1 filemon3 filemon4 filemon5
homepage links red anonymity +ORC students' essays tools cocktails
antismut search_forms mailFravia
is reverse engineering legal?