How to reverse engineer a Windows 95 target
REVERSE ENGINEERING EXERCISES FOR THE MASSES - (2a)
Version 0.01
by Fravia+ (MSRE), August 1997
Part A: Introduction to filemon - 01 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 - (2a)
How to reverse engineer a Windows 95 program
~
Part A: Introduction to filemon.exe
(c) Fravia (MSRE), 1997. All rights reserved
Print as html document, else use courier 8
Sorry for the language, I'm not a native English speaker.
Sorry for the "rough" version, it's still under construction... I am publishing this essay in its incomplete form, only because so many have insisted. The complete version will not be ready before mid-September and will contain many changes and improvements .
This essay is a "quick and dirty" introduction to Windows 95 reverse
engineering, it requires almost NO knowledge of windows programming, and a
low to
moderate knowledge of assembler coding. If you are already a good
software reverse engineer this essay may disappoint you, being a little
too much on the elementary side, yet I believe that a good comprehension of the basic of this trade is the main secret for advanced reverse
engineering.
Introduction
You may have already read the short essay (divided in two parts) that
I published one year ago, reverse engineering Filemanager for Windows 3.1. Since we
are all now dealing mostly with Windows 95 programs (it's not our choice
- alas - but a Micro$oft's imposition that everybody accepts, against
any sound logic :-) it suits us well to examine the "deep" structure
of filemon.exe, Version 2.0, by By Mark Russinovich
and Bryce Cogswell, a pretty useful program, released with its c source code at
the beginning of the year. You may want to download also the LAST version of this
good tool (version 3.0), released in July, at http://www.ntinternals.com,
where you'll find also its companion utility regmon.exe and the Windows NT versions
of both tools with complete c++ source code. Yet for this essay download from
my site version 2 of filemon.exe with its source code, this is all what you'll need.
As usual, when you start a cracking session, first of all run the program, try all its options (there are not many options inside this target) and, last
but not least,
print the complete
C source code (15 "A4" sheets).
Since we have already the C source code of this program this lesson
will be a "false" reverse engineering exercise: we are not going to
find anything hidden or new, nor many secret tricks in here... yet I
believe that many of you will find pretty useful our work
below, since analogous structures will more or less be present inside
UNKNOWN code, inside other targets, that you'll try to reverse engineer on your
own.
Another (very) interesting point in this program is its use of a virtual
device driver (VXD) for the filtering of all file system accesses... VXD reverse
engineering is a branch in its own rights, as you will see.
Here you go: all
the files you'll find inside filsrc.zip:Searching ZIP: FILSRC.ZIP
Length Method Size Ratio Date Time CRC-32 Attr Name
------ ------ ----- ----- ---- ---- -------- ---- ----
0 Stored 0 0% 07-03-97 13:16 00000000 --wD GUI/
0 Stored 0 0% 07-03-97 13:16 00000000 --wD VXD/
766 DeflatN 350 55% 28-03-96 08:55 cc319149 --w- GUI/APPICON.ICO
16015 DeflatN 7090 56% 16-03-97 20:47 ce4c6ba6 --w- GUI/FILEVXD.VXD
58368 DeflatN 4905 92% 16-03-97 20:49 75a1fd27 --w- GUI/FILEMON.NCB
0 Stored 0 0% 07-03-97 13:16 00000000 --wD GUI/RELEASE/
38912 DeflatN 1764 96% 16-03-97 20:49 266898dd --w- GUI/FILEMON.MDP
7176 DeflatN 1522 79% 27-11-96 14:34 4ccc82e0 --w- GUI/FILEMON.MAK
4786 DeflatN 1396 71% 16-03-97 20:48 54b79e51 --w- GUI/FILEMON.RC
2312 DeflatN 623 74% 16-03-97 20:13 fea19b03 --w- GUI/RESOURCE.H
22356 DeflatN 6422 72% 16-03-97 20:22 58b0c5ab --w- GUI/FILEMON.C
16015 DeflatN 7090 56% 16-03-97 20:47 ce4c6ba6 --w- GUI/RELEASE/FILEVXD.VXD
38912 DeflatN 18466 53% 16-03-97 20:48 4d4d1cee --w- GUI/RELEASE/FILEMON.EXE
420 DeflatN 255 40% 24-11-96 21:09 bd0b63d3 --w- VXD/MAKEFILE
1199 DeflatN 259 79% 16-03-97 20:47 1c1c6d4c --w- VXD/FILEVXD.DEF
1620 DeflatN 961 41% 16-03-97 20:47 20fd9fad --w- VXD/FILEVXD.SYM
1480 DeflatN 373 75% 16-03-97 20:47 031f6484 --w- VXD/FILEVXD.EXP
16015 DeflatN 7090 56% 16-03-97 20:47 ce4c6ba6 --w- VXD/FILEVXD.VXD
6212 DeflatN 1699 73% 16-03-97 20:47 ce4845f1 --w- VXD/FILEVXD.MAP
15675 DeflatN 6009 62% 16-03-97 20:47 7702cbad --w- VXD/FILEMON.OBJ
1384 DeflatN 364 74% 16-03-97 20:47 1ec43719 --w- VXD/FILEVXD.LIB
313 DeflatN 194 39% 05-12-95 02:01 317f17cc --w- VXD/FILEVXD.VRC
452 DeflatN 283 38% 23-11-96 18:36 0ea560c5 --w- VXD/FILEVXD.RES
82944 DeflatN 5012 94% 16-03-97 20:47 f7db1ed0 --w- VXD/FILEVXD.PDB
84457 DeflatN 12255 86% 24-11-96 04:47 6f9a8ab8 --w- VXD/TEST.FIL
35430 DeflatN 7703 79% 16-03-97 20:45 2fc85672 --w- VXD/FILEMON.C
1139 DeflatN 509 56% 16-03-97 20:24 e642c40b --w- VXD/IOCTLCMD.H
1557 DeflatN 582 63% 16-03-97 20:25 395d7317 --w- VXD/FILEMON.H
------ ------ --- -------
455915 93176 80% 28
We'll reverse engineer two files: filemon.exe and filevxd.vxd. We'll begin with filemon.exe. The reverse engineering of this program will be COMPLETE, since its various parts will be useful -for you- in order to learn some of the different aspects and techniques (and tricks) of our trade. Be patient and wade slowly through the code of this target, I'll keep you on the right path.
'Dead listing' reverse engineering, as +ORC calls it, is a slow "puzzle solving" process: the intellectual challenge can be extremely interesting, btw.
We will NEVER use Winice in this essay, as it is NOT NECESSARY to use our powerful debugger to understand EVERYTHING a target does, as you'll see reading this essay.
Elementary must know, the SaveFile approach
Some elementary MUST KNOW that you should head before starting a cracking session:
At the beginning there are no names... only a sea of numbers, hundred of
different locations... that's your target "in the wild", roaming around with unnamed procedures, before you tame it to clarity.
Soon some little islands will appear... their form still indeterminate... slowly you'll understand what some procedures of your target (should) do... for instance here
in filemon (as in almost all programs you'll disassemble) it's pretty easy to
individuate the "FileSaving" function, using simple search masks inside the
dead listing.
Searching you'll quickly get to this part of your dead listing:
:00401CF3 C744244804824000 mov [esp + 48], 00408204 ;"Save File Info..."
:00401CFB C7442454FC814000 mov [esp + 54], 004081FC ;"*.fil"
Now just dead list "back", to the beginning of this function:
:00401C20 81EC7C060000 sub esp, 0000067C ;correct stack
Since this function starts at :00401C20, we can at once substitute (search and replace) any "call 00401C20"
(which is not a very useful tag for our dead listing perusing) with a much
more meaningful tag: "call
00401C20=savefile".
Note how our substitution did NOT eliminate the location number, you better
keep always such number locations together with your new tags, because during your cracking sessions you will necessarily
commit quite
a lot of mistakes, that you'll correct later. Keeping the original location
numbers together with your new assigned 'provisory' names will help you a lot
when needed.
Inside
filemon's dead listing we will find only two occurrences of a call to our "FileSaving" routine but working
on your own targets, later, you'll soon discover how abstruse (and puzzling)
code
snippets will suddenly be comprehensible thank to these - very simple -
substitutions
Let's have a look at the relevant filemon's code:
This snippet of code calls twice the SaveFile function of our target...
by the way, since this kind of routines are typically called from the main menu
of the main window ("Save" and "Saveas" inside the "File" main menu option), this snippet will be very probably inside
a WM_COMMAND structure... more about this later)... here is the part of code calling
SaveFile::
:00401569 6A00 push 00000000 ;BOOLEAN FALSE
:0040156B A1B8964000 mov eax, [004096B8] ;get second par
:00401570 8BB42458010000 mov esi, [esp + 00000158] ;get HWND hWnd
:00401577 50 push eax ;push second par
:00401578 56 push esi ;push HWND hWnd
:00401579 E8A2060000 call 00401C20=savefile
:0040157E 83C40C add esp, 0000000C
:00401581 E97E010000 jmp 00401704
:00401586 6A01 push 00000001 ;BOOLEAN TRUE
:00401588 A1B8964000 mov eax, [004096B8] ;get second par
:0040158D 8BB42458010000 mov esi, [esp + 00000158] ;get HWND hWnd
:00401594 50 push eax ;push second par
:00401595 56 push esi ;push HWND hWnd
:00401596 E885060000 call 00401C20=savefile
:0040159B 83C40C add esp, 0000000C
:0040159E E961010000 jmp 00401704
You notice that I have already transformed "call 00401C20" in "call 00401C20=savefile".
You may use the same "search and replace" technique also for memory
locations you have understood the significance of. Usually you'll be lucky every
time that a KNOWN return value of a KNOWN windows function will be stored
in a specific memory location. This will allow you to prepare easily an immediate
"search and replace" of the same location in the whole dead listing, whereby you'll
substitute awkward number-locations with your tags, explaining
their exact meaning
Yet you'll be able to clear the meaning of quite a lot of
code even
if you DO NOT KNOW the exact meaning of a value stored inside a memory
location... the important thing is that you know where that value is
used... let's make an example, look at the code above once more.
This small code snippet let us understand
that the "homemade" function SaveFile
of our target accepts THREE parameters (note the three pushes before each
call).
One of the three parameters is, clearly, a boolean parameter, either 0 or 1...
can you guess what this could be... in a "save
file" operation? It's the "saveas" parameter in alternative to "save",
a typical boolean
parameter for saving operations... we don't even need the confirmation
of the c code...
Nice... and the other two parameters? The first one (in the C
call, the last one in assembly) is HWND hWnd, of course, and the other
one, the "middle" one? We know, from the C source, that's HWND ListBox,
but we could ALREADY
have searched and replaced all memory locations "[004096B8]" -
in the whole dead
listing - with something like "[004096B8]=SaveFileSecondPar", and believe
me, this would have made quite a BIG difference in a huge 7-8 megabytes
dead listing where you don't even understand what the hell the programmer
was trying to do, nor where have been hidden, inside the huge codewoods, the
snippets of the target's code you are looking for.
OK, we have finished our quick examination of the small snippet above... would
you like to
know what it was exactly? It correspond to the following 6 lines of "c" code,
placed inside the main "switch" tree (for WM_COMMAND) of the MainWndProc:
case IDM_SAVE:
SaveFile( hWnd, hWndList, FALSE );
return 0;
case IDM_SAVEAS:
SaveFile( hWnd, hWndList, TRUE );
return 0;
Let's start cracking: the first function
Now let's start together anew, take your sheets with the C source code have
a general look, prepare your favourite cocktail (may I suggest a
traitor?) and then jump with me inside the disassembled
target...
If you just read the disassembled code that follows, with my comments, you'll
notice pretty easily how the c source code has been "translated" in assembler.
The first windows' function in the C source code is ABORT, let's examine first of all its "C" code:
/********************************************************************
* FUNCTION: Abort:
* PURPOSE: Handles emergency exit conditions.
*********************************************************************/
void Abort( HWND hWnd, TCHAR * Msg )
{ MessageBox( hWnd, Msg, "filemon", MB_OK );
PostQuitMessage( 1 );
}
Note the 4 parameters of the Messagebox function: from left to right:
hWnd, Msg, "progname", MB_OK... as you'll now see, in assembly they will be pushed in
REVERSE ORDER: MB_OK, "progname", Msg, hWnd,
And here is the code of our target
//********************** Start of Code in Object .text **************
Program Entry Point = 004024E0 (Filemon.exe File Offset:000018E0)
:00401000 8B442408 mov eax, [esp + 08] ;get msg in eax
:00401004 6A00 push 0 ;push right parameter: MB_OK (=0)
:00401006 8B4C2408 mov ecx, [esp + 08] ;get hWnd in ecx
:0040100A 68C0804000 push 004080C0 ;push StringData "filemon"
:0040100F 50 push eax ;push msg
:00401010 51 push ecx ;push hWnd
:00401011 FF1590B24400 Call dword ptr [0044B290] ;call USER32.MessageBoxA
:00401017 6A01 push 1 ;push 1 for PostQuit
:00401019 FF1588B24400 Call dword ptr [0044B288] ;call USER32.PostQuitMessage
:0040101F C3 ret ;finis
What does this little introductory example mean from a reverse engineering point
of view?
It means, for a start, that EVERY TIME you find a "call USER32.MessageBoxA"
function, in
your disassembled listing you may substitute IMMEDIATELY the 4 pushes preceding
it
with:
First push: whatever MessageBoxStyle has been called (Here 0 = MB_0K)... see below
the complete list
Second push: Title of the MsgBox
Third push: Msg
Fourth push: hWnd
You dig it?
It's the same old story we already (should) know from dos reverse
engineering actually:
All it happens when passing parameters to a C++ function is that you push the
rightmost parameter first, then the next rightmost parameter, and so on, until
the leftmost parameter has been pushed. Then the function is called... say you
call the C library function strcpy to copy SourceString to
DestString... in c++ you would type:
strcpy (DestString, SourceString);
The same call in assembler works like this
lea ax,SourceString ;rightmost parameter
lea bx,DestString ;leftmost parameter
push ax ;push rightmost first
push bx ;push next one
call _strcpy ;copy the string using pre-made code
add sp,4 ;DISCARD used parameters
Everything depends from the CALLING CONVENTION!
The C calling convention pushes rightmost first
and discards parameters from stack;
The Pascal calling convention pushes leftmost
first and the called program
discards the parameter from the stack.
It's therefore quite important to understand first of all wich convention uses your
target, which is pretty easy, since you just need to have a look to a known windows
function.
The old good MessageBox function
But we are not yet finished with our messagebox function,, I'll use this very
function in order to explain you "in the deep" a single Windows' function, it's
up to you, obviously, to learn as much as you can about the more important
windows' functions...
I know, I know, it's an awful operating system, yet we MUST STUDY IT, unfortunately,
in order to reverse it whenever we feel like it. In the following example, regarding
MessageBox, you'll find a description useful for reverse engineering purposes,
the descriptions you'll find inside the WinAPI references of the main languages
compilers are similar, but they are aimed at programmers that usually DO NOT
need to know how to disassemble their program effectively and therefore are
not always useful, nor complete. In fact the basical
syntax for messagebox is the following:
int MessageBox(hwndParent, lpszText, lpszTitle, fuStyle)
HWND hwndParent; /* handle of parent window */
LPCSTR lpszText; /* address of text in message box */
LPCSTR lpszTitle; /* address of title of message box */
UINT fuStyle; /* style of message box */
The MessageBox function creates, displays, and operates a message-box window.
The message box contains an application-defined message and title, plus any
combination of the predefined icons and push buttons described in the fuStyle
parameter.
Parameter Description
hwndParent Identifies the parent window of the message box to be created.
If this parameter is NULL, the message box will have no parent
window.
LpszText Points to a null-terminated string containing the message to
be displayed.
LpszTitle Points to a null-terminated string to be used for the dialog
box title. If this parameter is NULL, the default title Error
is used.
fuStyle Specifies the contents and behavior of the dialog box.
This parameter can be a combination of the following values:
Value Meaning
MB_ABORTRETRYIGNORE The message box contains three push buttons:
Abort, Retry, and Ignore.
This value is 0x00000002L
MB_APPLMODAL The user must respond to the message box before
continuing work in the window identified by the
hwndParent parameter. However, the user can move
to the windows of other applications and work in
those windows. MB_APPLMODAL is the default if
neither MB_SYSTEMMODAL nor MB_TASKMODAL is
specified.
This value is 0x00000000L
MB_DEFBUTTON1 The first button is the default. Note that the
first button is always the default unless MB_DEFBUTTON2
or MB_DEFBUTTON3 is specified.
This value is 0x00000000L
MB_DEFBUTTON2 The second button is the default.
This value is 0x00000100L
MB_DEFBUTTON3 The third button is the default.
This value is 0x00000200L
MB_ICONASTERISK Same as MB_ICONINFORMATION.
This value is 0x00000040L
MB_ICONEXCLAMATION An exclamation-point icon appears in the message box.
This value is 0x00000030L
MB_ICONHAND Same as MB_ICONSTOP.
This value is 0x00000010L
MB_ICONINFORMATION An icon consisting of a lowercase letter "I" in a
circle appears in the message box.
This value is 0x00000040L
MB_ICONQUESTION A question-mark icon appears in the message box.
This value is 0x00000020L
MB_ICONSTOP A stop-sign icon appears in the message box.
This value is 0x00000010L
MB_OK The message box contains one push button: OK.
This value is 0x00000000L
MB_OKCANCEL The message box contains two push buttons: OK and Cancel.
This value is 0x00000001L
MB_RETRYCANCEL The message box contains two push buttons: Retry and Cancel.
This value is 0x00000005L
MB_SYSTEMMODAL All applications are suspended until the user responds to
the message box. Unless the application specifies MB_ICONHAND,
the message box does not become modal until after it is
created; consequently, the parent window and other windows
continue to receive messages resulting from its activation.
System-modal message boxes are used to notify the user of
serious, potentially damaging errors that require immediate
attention (for example, running out of memory).
This value is 0x00001000L
MB_TASKMODAL Same as MB_APPLMODAL except that all the top-level windows
belonging to the current task are disabled if the hwndParent
parameter is NULL. This flag should be used when the calling
application or library does not have a window handle available
but still needs to prevent input to other windows in the
current application without suspending other applications.
This value is 0x00002000L
MB_YESNO The message box contains two push buttons: Yes and No.
This value is 0x00000004L
MB_YESNOCANCEL The message box contains three push buttons: Yes, No, and
Cancel.
This value is 0x00000003L
As you can see, the possible values are 0,1,2,3,4,5,10,20,30,40,100,200,100, 2000
(there are also other values, more rare: F, FO, F00, 3000, 8000, C000, 20000... you'll
find them out either experimenting a little or reverse engineering a lot :-)
Returns
The return value is zero if there is not enough memory to create the message box.
Otherwise, it is one of the following menu-item values returned by the dialog box:
Value Real value Meaning
ERROR (0) fcked
IDOK (1) OK button was selected.
IDCANCEL (2) Cancel button was selected.
IDABORT (3) Abort button was selected.
IDRETRY (4) Retry button was selected.
IDIGNORE (5) Ignore button was selected.
IDYES (6) Yes button was selected.
IDNO (7) No button was selected.
If a message box has a Cancel button, the IDCANCEL value will be returned
if either the ESC key is pressed or the Cancel button is selected. If the
message box has no Cancel button, pressing ESC has no effect.
Comments
When a system-modal message box is created to indicate that the system is
low on memory, the strings pointed to by the lpszText and lpszTitle
parameters should not be taken from a resource file, because an attempt
to load the resource may fail.
When an application calls the MessageBox function and specifies the
MB_ICONHAND and MB_SYSTEMMODAL flags for the fuStyle parameter, Windows
displays the resulting message box regardless of available memory.
When these flags are specified, Windows limits the length of the
message-box text to three lines. Windows does not automatically break
the lines to fit in the message box, however, so the message string
must contain carriage returns to break the lines at the appropriate
places.
If a message box is created while a dialog box is present, use the
handle of the dialog box as the hwndParent parameter. The hwndParent
parameter should not identify a child window, such as a control in
a dialog box.
See Also
FlashWindow, MessageBeep
OK, we have seen "in the deep" a single Windows' function, you would be well
advised to prepare yourself some "information sheets", like the above one, for your
own use, about the most important and more frequent windows functions, WITH the
values of the constants that windows uses... you'll see how easy it is to
understand what an unknown part of a program is doing just examining how it handles
the DIFFERENT possible return values...
This is obviously not the case here...
remember what we are doing, we are just examining an "ABORT" error function, an
anormal function that will show the user only a short error message and offer him
the OK button to click onto... you could modify the code at
:00401004 6A00 push 0 ;push right parameter: MB_OK (=0)
into::00401004 6A01 push 1 ;push right parameter: MB_OKCANCEL (=1)
Yet modifying this code would not make much sense: you would see two push buttons: OK and Cancel, only in the event of an error (a
pretty futile reverse engineering exercise :-)
The InitApplication function of filemon
Now that we have seen the ABORT function of filemon, let's work on the next
routines of our target... be patient and follow me: if you read carefully
this short essay you'll master the rudiments of windows reverse engineering.
The following function, inside our C source code, is WinMain... since WinMAin is a KNOWN function (which usually calls InitInstance and InitApp before entering a ghetmaessage loop), WinMain will be one of the LAST code snippets that
we'll reverse, we'll see first a lot of other, more or less "home-made" procedures that we'll "solve" first (once more: we have the c
source code of this target, yet my aim is to teach you how to reverse engineer targets you
DO NOT have the source code of, we'll soon operate AS IF we did not have any source code at all, bear with me :-)
We'll pass to the next procedure, the one after Winmain. This is a standard InitApp procedure (as you'll see in the FOURTH) part of this lesson) here is its C source code:
/****************************************************************************
* FUNCTION: InitApplication(HANDLE)
* PURPOSE: Initializes window data and registers window class
****************************************************************************/
BOOL InitApplication( HANDLE hInstance )
{ WNDCLASS wc;
// Fill in window class structure with parameters that describe the
// main (statistics) window.
wc.style = 0;
wc.lpfnWndProc = (WNDPROC)MainWndProc; !!!!
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon( hInstance, "ICON" );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = GetStockObject( LTGRAY_BRUSH );
wc.lpszMenuName = "LISTMENU";
wc.lpszClassName = "filemonClass";
if ( ! RegisterClass( &wc ) )
return FALSE;
return TRUE;
}
FUNCTION: InitApplication(HANDLE)
* Referenced by a CALL at Address:0040102B
BOOL InitApplication( HANDLE hInstance)
This function fills in the window class structure with parameters that describe the
main (statistics) window of our target... it's one of the main "initializing" functions of our target
:004010B0 8B442404 mov eax, [esp + 04] ;get hInstance
:004010B4 83EC28 sub esp, 00000028
:004010B7 C744240000000000 mov [esp], 00000000
:004010BF 89442410 mov [esp + 10], eax
:004010C3 56 push esi ;save esi
:004010C4 C744240890114000 mov [esp + 08], 00401190 ;See below what is this
:004010CC C744240C00000000 mov [esp + 0C], 00000000
:004010D4 C744241000000000 mov [esp + 10], 00000000
:004010DC 68E4804000 push 004080E4 ;StringData "ICON"
:004010E1 50 push eax ;push hInstance
:004010E2 FF15F4B24400 Call dword ptr [0044B2F4] ;USER32.LoadIconA
:004010E8 89442418 mov [esp + 18], eax ;save return value
:004010EC 68007F0000 push 00007F00 ;7F=IDC_ARROW
:004010F1 6A00 push 00000000 ;NULL
:004010F3 FF15F8B24400 Call dword ptr [0044B2F8] ;USER32.LoadCursorA
:004010F9 8944241C mov [esp + 1C], eax ;save return value
:004010FD 6A01 push 1 ;1=LTGRAY_BRUSH
:004010FF FF15CCB14400 Call dword ptr [0044B1CC] ;GDI32.GetStockObject
:00401105 C7442424D8804000 mov [esp + 24], 004080D8 ;StringData "LISTMENU"
:0040110D C7442428C8804000 mov [esp + 28], 004080C8 ;StringData "filemonClass"
:00401115 89442420 mov [esp + 20], eax ;save return in esp+20
:00401119 8D442404 lea eax, [esp + 04] ;get WNDCLASS wc
if ( ! RegisterClass( &wc ) )
return FALSE;
return TRUE
:0040111D 50 push eax ;push WNDCLASS wc
:0040111E FF15FCB24400 Call dword ptr [0044B2FC] ;USER32.RegisterClassA
:00401124 663D0100 cmp ax, 0001 ;did we get it through?
:00401128 5E pop esi
:00401129 1BC0 sbb eax, eax ;if zero return false
:0040112B 83C428 add esp, 00000028
:0040112E 40 inc eax ;else return true
:0040112F C3 ret
Well, let's see what happens when we get back from this procedure:
:WinMain of filemon calls InitApplication
:00401020 83EC1C sub esp, 0000001C
:00401023 53 push ebx
:00401024 56 push esi
:00401025 8B742428 mov esi, [esp + 28]
:00401029 57 push edi
:0040102A 56 push esi
:0040102B E880000000 call 004010B0 ;call InitApplication(HANDLE)
:00401030 83C404 add esp, 4 ;correct esp
:00401033 85C0 test eax, eax ;was it zero?
:00401035 750B jne 00401042 ; if InitApplication(hInstance) OK
; continue WinMain
:00401037 33C0 xor eax, eax ;else return FALSE
:00401039 5F pop edi
:0040103A 5E pop esi
:0040103B 5B pop ebx
:0040103C 83C41C add esp, 1C
:0040103F C21000 ret 10
Therefore the above snippet is:
if (! InitApplication(hInstance))
return FALSE;
Which is a part of WinMain, btw.
The trick for finding MainWndProc
God, I realize now that I should begin to explain the whole WNDCLASS structure...
please
study it yourself... if you bought (as you should have done) the COMPLETE Borland
C++ Version
4.52 for less than 4 UK pounds (see here), you'll have
all
important specs at your fingertips from the huge API helpfiles (7 million bytes
for Win32 and 3 million
bytes for Win31).
I'll explain here only part of the API calls... The most
important element here, for us, is that WNDCLASS' member lpfnWndProc
POINTS TO THE CALLBACK WINDOW PROCEDURE!
Let's approach the above code (of InitApplication) slowly... What was the value "401190" at 10C4?
:004010C4 C744240890114000 mov [esp + 08], 00401190
That is the location of the MainWndProc!
Windows is so kind to tell us, in
many
occasions, WHERE the "obligatory" functions of an unknown program start!
If
Peter
Urbanik, the author of Wdasm, would listen to us, instead of uselessly updating
his program every
couple of weeks, he would work on this to get a spectacular tool for reverse
engineering!
OK, every single WNDCLASS call of a windows program carries
inside itself
the location of the caller... in this case (as in most initialization parts of code)
WNDCLASS is called at initialization by a little initialization routine (here in
filemon called InitInstance) which is in turn called by the main "homemade"
procedure of our target, here in filemon called MainWndProc... nice to know,
isn't it?
There is more: since WNDCLASS has a parameter lpszClassName, which
points to a null-terminated string that specifies the name of the window class
(in the case of filemon
"filemonClass"), it's pretty easy to find all occurrences of WNDCLASS inside any
unknown target just examining its strings (and you can use good old Frattaroli's strings.zipto do it) ... nice isn't it?
What have we more up there? Let's see
:004010EC 68007F0000 push 00007F00 ;IDC_ARROW
hCursor Identifies the class cursor. This member must be a handle to a cursor
resource. There are many resources of each type, for the joy of a good reverse
engineer...
Here you go! experiment a little (change it with Hexworkshop inside filemon.exe, play
with your targets! In this specific case you wont see much, though, because this
is the "ghost" "loaded" cursor of filemon... you should change the SetCursor
function's parameter to change the cursor of an application)
32512 (0x7F00) = IDC_ARROW ;that's what we have
32513 (0x7F01) = IDC_IBEAM ;Text I-beam cursor.
32514 (0x7F02) = IDC_WAIT ;that's the hourglass
...
32560 = IDC_APPSTARTING
Another parameter:
:004010FD 6A01 push 00000001 ;LTGRAY_BRUSH
Since GRAY BRUSH is 2 and DARKGRAY BRUSH is 3, you may experiment as well with
some colors... If you substitute :004010FD 6A01 with :004010FD 6A03 you'll indeed
see (for a moment) your DKGRAY_BRUSH "behind" the filling of the main window of
filemon, once more this is the "initializing" routine, which is called at the
beginning of our target's life, many parameter will be "reconfirmed" later on.
What's more up there? Yes: RegisterWindowClass... once created, the WNDCLASS data
must be "registered" in order to pass to the subsequent CreateWindow function...
Let's have a look at the code of WinMain that will be performed if the InitApplication
routine returns successful...
:WinMain after InitApplication
:00401042 8B442438 mov eax, [esp + 38]
:00401046 50 push eax
:00401047 56 push esi
:00401048 E8E3000000 call 00401130=InitInstance
:0040104D 83C408 add esp, 00000008
:00401050 85C0 test eax, eax
:00401052 750B jne 0040105F ;if InitInstance successful
;continue WinMain
:00401054 33C0 xor eax, eax ;else return FALSE
:00401056 5F pop edi
:00401057 5E pop esi
:00401058 5B pop ebx
:00401059 83C41C add esp, 0000001C
:0040105C C21000 ret 0010
A Windows is born
This huge operating system
will now perform its most characteristic work: create a Window. Prepare yourself
another
cocktail, this will take quite a while...
/****************************************************************************
* FUNCTION: InitInstance(HANDLE, int)
* PURPOSE: Saves instance handle and creates main window
****************************************************************************/
HWND InitInstance( HANDLE hInstance, int nCmdShow )
{ HWND hWndMain;
hInst = hInstance;
hWndMain = CreateWindow(
"filemonClass", "Win95 File Monitor", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL );
// if window could not be created, return "failure"
if (! hWndMain)
return NULL;
// make the window visible; update its client area; and return "success"
ShowWindow(hWndMain, nCmdShow);
UpdateWindow(hWndMain);
return hWndMain;
}
This HWND "hWndMain" translates to: CreateWindow (...)
:00401130 8B442404 mov eax, [esp + 04] ;get hInstance
:00401134 56 push esi ;save esi
:00401135 6A00 push 00000000 ;last NULL lpvparameter
:00401137 A3E8994000 mov [004099E8], eax ;save hInstance (HEY! A memory loc!)
:0040113C 50 push eax ;push hInstance
:0040113D 6A00 push 00000000 ; NULL hmenu
:0040113F 6A00 push 00000000 ; NULL hwndparent
:00401141 6800000080 push 80000000 ; CW_USEDEFAULT
:00401146 6800000080 push 80000000 ; CW_USEDEFAULT
:0040114B 6800000080 push 80000000 ; CW_USEDEFAULT
:00401150 6800000080 push 80000000 ; CW_USEDEFAULT
:00401155 680000CF00 push 00CF0000 ; WS_OVERLAPPEDWINDOW
:0040115A 68EC804000 push 004080EC ; "Win95 File Monitor"
:0040115F 68C8804000 push 004080C8 ; "filemonClass"
:00401164 6A00 push 00000000
:00401166 FF15E8B24400 Call dword ptr [0044B2E8]; USER32.CreateWindowExA, Ord:55h
:0040116C 8BF0 mov esi, eax ;get the handle to the new window in esi
:0040116E 85F6 test esi, esi ;test it
:00401170 7504 jne 00401176 ;if created OK, continue to showwindow
:00401172 33C0 xor eax, eax ;else return NULL (i.e. FALSE)
:00401174 5E pop esi
:00401175 C3 ret
Ok, let's have a look at this important function:
HWND CreateWindow(lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight,
hwndParent, hmenu, hinst, lpvParam)
LPCSTR lpszClassName; /* address of registered class name */
LPCSTR lpszWindowName; /* address of window text */
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 of parent window */
HMENU hmenu; /* handle of menu or child-window identifier */
HINSTANCE hinst; /* handle of application instance */
void FAR* lpvParam; /* address of window-creation data */
lpszClassName is "filemonClass" (what we have registered)
lpszWindowName is "Win95 File Monitor" (what you see in the main window of
filemon)
dwStyle is WS_OVERLAPPEDWINDOW = 00CF0000 (which creates an overlapped
window having the WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU,
WS_THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles)
int x is CW_USEDEFAULT
This value specifies the initial x-position of the window. For an overlapped or pop-up
window, the x parameter is the initial x-coordinate of the window's upper-left corner,
in screen coordinates. For a child window, x is the x-coordinate of the upper-left
corner of the window in the client area of its parent window. If, like here in filemon,
this value is CW_USEDEFAULT, Windows selects the default position for the window's
upper-left corner and ignores the y parameter. CW_USEDEFAULT is valid only for
overlapped windows... if you firmly believe that Mark Russinovich and Bryce
Cogswell should have let their program appear in the top left corner of the
screen instead of using the default position then go ahead! Modify whatever
you want!Int y is CW_USEDEFAULT, as above for the y-position
nWidth is CW_USEDEFAULT
This value specifies the width, in device units, of the window. For overlapped windows,
the nWidth parameter is either the window's width (in screen coordinates) or
CW_USEDEFAULT. If nWidth is CW_USEDEFAULT, like here in filemon, Windows selects
a default width and height for the window (the default width extends from the
initial x-position to the right edge of the screen, and the default height extends
from the initial y-position to the top of the icon area). CW_USEDEFAULT is valid only
for overlapped windows.nHeight is CW_USEDEFAULT, as above for the
height
hwndParent is NULL. This value identifies the parent or owner window of the
window being created. Overlapped windows must NOT have a parent
(hParent must be NULL)
hMenu is NULL, handle of menu identifier
hInstance is the value in eax, and identifies the instance of the module to
be associated with the window.
LpvParam is the WM_CREATE param
Well, what returns CreateWindow? The return value is the handle of the new window if
the function is successful. Otherwise, it is NULL. Everything is OK with old good
filemon, let's continue...
A window has been "made" it's name is hWndMain let's show it to the world
:ShowWindow(hWndMain, nCmdShow); make the window visible & update its client area
:00401176 8B44240C mov eax, [esp + 0C] ;get nCmdShow
:0040117A 50 push eax ; nCmdShow
:0040117B 56 push esi ; hWndMain (handle was in esi)
:0040117C FF15ECB24400 Call dword ptr [0044B2EC] ;USER32.ShowWindow, Ord:022Ch
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
If you never programmed before, you could legitimately ask yourself why the hell we
have to show and update a window we have created a minute ago... see: ShowWindow
specifies how the window is to be shown... hide=0, normal=1, otherzoom=2,
maximize=3, otherunzoom=4, show=5 etc... therefore the value in esp+0C determines HOW
the windows will appear, and it has been already determined calling WinMain, which
has following parameters: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE
hPrevInstance, LPSTR lpCmdLine, int nCmdShow), the last "int" one being the
nCmdShow... have a look in the code following the program entry point for this.
Once show, the window must be updated. The UpdateWindow function updates the client
area of our window by sending a WM_PAINT message to the window if the update region
for the window is not empty. The function sends a WM_PAINT message directly to the
window procedure, bypassing the application queue. If the update region is empty,
no message is sent.
We are now finished with the InitInstance procedure, have our nice main window,
must move on: back to WinMain!
:WinMain continued
:0040105F 8D44240C lea eax, [esp + 0C] ;
:00401063 6A00 push 00000000
:00401065 6A00 push 00000000
:00401067 8B3500B34400 mov esi, [0044B300]
:0040106D 6A00 push 00000000
:0040106F 50 push eax
:00401070 FFD6 call esi
:00401072 85C0 test eax, eax
:00401074 742B je 004010A1
:00401076 8B3D94B24400 mov edi, [0044B294]
:0040107C 8B1D8CB24400 mov ebx, [0044B28C]
Well, we'll continue with another lesson, we have almost 50.000 bytes here!
(c) Fravia+ 1997. All rights reserved.
You are deep inside Fravia's page of reverse engineering,
choose your way out:
filemon2
filemon3
filemon4
filemon5
homepage
links
anonymity
+ORC
students' essays
tools cocktails
antismut
search_forms
mailFravia
is reverse engineering legal?