|
Theory and practice of menus reversing
|
Papers
|
June 1999
| by
+Spath.
|
|
|
Courtesy of Fravia's pages of reverse engineering
|
|
fra_00xx 980616 +Spath 0110 PA AD
|
A worthy addition for our papers section. I hope that many reversers will build on
this and offer us additions and ameliorations and suggestion, so that we will be able
to produce some 'basic' reversing papers (menus, save/save us crippled, internet background connections,
printer smearing, etcetera)
that could be used as 'quick reference' by fellolw reversers all around the word...
Note also the lookup table reversing and the
quick crack of Jeremy Likness' software cracking
challenge. Unfortunately the file I downloaded is corrupted, and therefore
you'll miss a small part at the bottom. I'm sure that +Spath will soon send the
missing snippet.
Enjoy!
| Advanced |
|
There is a crack, a crack in everything
That's how the light gets in
| |
Rating
|
( )Beginner (X)Intermediate (x)Advanced ( )Expert
| |
An essay to understand how menus and messages work, to be able to efficiently reverse
function-disabled programs.
Theory and practice of menus reversing
Written by
+Spath.
This text is an introduction to functions disabled programs ; I wrote it
because I did not find any essay describing both graphical (menus related)
and system (messages related) parts of this problem. My goal here was to
explain basically how menus work and to give a few tips and examples, so
that any beginner can start working in the right direction.
As you may have already understood, I'm not a native english speaker, so
please forgive my mistakes.
I) MENUS
1) Creating a menu
2) Modifying a menu
3) Reversing a menu definition
II) WINDOWS MESSAGES
1) Messages and Windows
2) Messages to an application
III) REVERSING A FUNCTION-DISABLED TARGET
1) Before you start
2) A few tips
IV) HANDS ON
1) CAP 1.0
2) Sigmastat 2.0
3) Bestwin
4) Flipper
One eye and one brain are required. This text is long, so some tea may help also.
See IV)
None.
I) WINDOWS MENUS
1) Creating a menu
Menus can be created using either a menu template or menu creation
functions.
- Creating menus using a menu template :
This is the most common method ; most of the times menu templates are
defined as resources and are contained in a separate file from real source
code. A menu template completely defines a menu, including all functions,
separators and all submenus. Here's a typical C/ASM menu template :
IDR_MENU1 MENU DISCARDABLE
BEGIN
POPUP "File"
BEGIN
MENUITEM "&Open...", ID_BROWSE
MENUITEM "&Execute", ID_EXECUTE GRAYED
MENUITEM SEPARATOR
MENUITEM "E&xit", ID_CANCEL
END
END
This defines a menu bar that contains one popup menu, named File (from
here on, popup menus will be refered to as menus). In this menu, some items
are defined, as well as a separator (that cause a horizontal line to be drawn
between two menu items). Each item is described using this format :
MENUITEM String, MenuID [Options]
String : the menu item's characters enclosed in " ". The '&' symbol before
a letter means that this letter will be underlined in the menu
(most of the times to indicate a keyboard shortcut).
MenuID : a number that becomes the low word of the wParam that is passed
with the WM_COMMAND message (I will discuss that in details
later).
Options : how the item is to appear : CHECKED, GRAYED (inactive and gray),
INACTIVE (inactive), MENUBARBREAK, MENUBREAK (both place item on
new line).
- Creating menus at run-time :
Using menu creation functions, you can create menus at run time or
add menu items to existing menus. You first can use the CreateMenu()
function to create an empty menu bar and the CreatePopupMenu() function
to create an empty menu. Then, to add items to a menu, AppendMenu(),
InsertMenuItem() and InsertMenu() can be used : AppendMenu() just
appends a menu item to the end of a menu or submenu, while the two
other ones let you insert a menu item at a specified position.
Both functions allow you to specify the attributes of the menu item,
including whether it is enabled, disabled, grayed, checked, or unchecked.
After a menu has been loaded or created, it must be assigned to a window.
The first method is to do it at class' registration : before creating a
window, a program must register its class to Windows via RegisterClass()
or RegisterClassEx(). The parameter of these functions is a pointer to a
WNDCLASS or WNDCLASSEX structure :
typedef struct _WNDCLASS { typedef struct _WNDCLASSEX {
UINT style; UINT cbSize;
WNDPROC lpfnWndProc; UINT style;
int cbClsExtra; WNDPROC lpfnWndProc;
int cbWndExtra; int cbClsExtra;
HANDLE hInstance; int cbWndExtra;
HICON hIcon; HANDLE hInstance;
HCURSOR hCursor; HICON hIcon;
HBRUSH hbrBackground; HCURSOR hCursor;
LPCTSTR lpszMenuName; HBRUSH hbrBackground;
LPCTSTR lpszClassName; LPCTSTR lpszMenuName;
} WNDCLASS; LPCTSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX;
The lpszMenuName parameter of this structure describes which menu will
be associated to a window of this class (created with CreateWindow() or
CreateWindowEx() ). The other method is to assign the menu after the
window has been created, which can be done with SetMenu().
Just a remark about context menus (also called floating menus), which are
the ones that are activated by right mouse button : they are most of the time
displayed via TrackPopupMenu() or TrackPopupMenuEx() when a WM_CONTEXTMENU
message is processed.
2) Modifying a menu
Several functions enable you to change menus or menuitems after they
have been loaded or created.
- SetMenuItemInfo() allows you to change the attributes of an existing
menu item ; attributes include its type, state, identifier, submenu,
bitmaps, item data, and text. Before modifying a menu, GetMenuItemInfo()
can be used to retrieve informations about an item. This function returns
a MENUITEMINFO structure, which specifies the attributes to retrieve and
receives their current values.
- ModifyMenu() replaces the specified menu item with a new item. It can
be used to enable, disable, gray, check or uncheck the text string or the
bitmap of a menu item.
- EnableMenuItem(), the very famous one : this function specifically exists
to enable, disable, or gray a menu item.
EnableMenuItem(
HMENU hMenu, // handle to menu
UINT uIDEnableItem, // menu item to enable, disable, or gray
UINT uEnable // menu item flags
);
The menu item flag contains two informations, the action to perform on
the item and the selecting method :
MF_ENABLED (00h) : the function can be used.
MF_GRAYED (01h) : the function is grayed and cannot be chosen.
MF_DISABLED (02h) : the function is not grayed, but clicking does
nothing (no WM_COMMAND message is sent).
MF_BYCOMMAND (00h) : uIDEnableItem gives the menuID of the menu item
(this is the default method).
MF_BYPOSITION (400h) : uIDEnableItem gives the zero-based relative
position of the menu item.
To gray an item, you usually use 03h (MF_GRAYED + MF_DISABLED)
- DeleteMenu() or RemoveMenu() to delete a menu item from a menu,
function. If the item being deleted is one that opens a submenu,
DeleteMenu() deletes the associated submenu, discarding the menu handle
and freeing the memory used by the submenu. The RemoveMenu() function
deletes a menu item, but if the item opens a submenu, the function
does not destroy the submenu or its handle, allowing the submenu to
be reused.
3) Reversing a menu definition
Let's take the sample resource file of 1) and compile it : here's how
it looks like in the EXE file :
10 00 46 00 69 00 6C 00-65 00 00 00 00 00 E8 03 ..F.i.l.e.......
26 00 4F 00 70 00 65 00-6E 00 2E 00 2E 00 2E 00 &.O.p.e.n.......
00 00 00 01 EA 03 26 00-45 00 78 00 65 00 63 00 ......&.E.x.e.c.
75 00 74 00 65 00 00 00-00 00 00 00 00 00 80 00 u.t.e...........
02 00 45 00 26 00 78 00-69 00 74 00 00 00 00 00 ..E.&.x.i.t.....
which can be decoded this way, if you remove the widechar format :
10h "File"
00h, 03E8h, "&Open..."
01h, 03EAh, "&Execute"
80h
00h, 0002h, "E&xit"
The main modification of the resources (apart from the wide char format)
is the inversion of the parameters (the order is now Options, MenuID,
String).
Before each option, you see the low word of the wParam value of the WM_COMMAND
message that will be sent to the application when the function is selected ;
this value is equal to the MenuID value defined in the resource file.
Documented options values correspond to the associated item flags : they
are grayed (01h), inactive (02h), checked (08h), menubarbreak (20h),
menubreak (40h). No option corresponds to value 00h, which means that the
item is active. As you see, each option use only one bit of the byte, to
ease combination (for instance, 09h is grayed + checked). Other values
are :
04h (MF_BITMAP) to use a bitmap as the menu item.
10h (MF_POPUP) must be used for menu definition, it just crash USER.EXE
when used with an item.
80h (MF_HILITE) is used for menuitem separators.
With the resource file of 1), the Execute option is grayed ; so to enable
it, we just have to replace the 01 (2 bytes before the "&Execute" string)
by 00.
Now let's see a VB menu definition :
...mnuFile.....& 05 07 00 6D 6E 75 46 69-6C 65 00 13 03 05 00 26
File.....&...... 46 69 6C 65 00 07 FF FF-02 26 00 00 00 06 0B 00
mnuFileSave..... 6D 6E 75 46 69 6C 65 53-61 76 65 00 13 03 0A 00
&Save Text...... 26 53 61 76 65 20 54 65-78 74 00 08 13 00 FF 02
.......mnuFileSe 1D 00 00 00 07 0C 00 6D-6E 75 46 69 6C 65 53 65
p_1.....-.....!. 70 5F 31 00 13 03 01 00-2D 00 06 FF FF 02 21 D1
.....mnuFileExit 00 00 08 0B 00 6D 6E 75-46 69 6C 65 45 78 69 74
.....E&xit...... 00 13 03 05 00 45 26 78-69 74 00 08 11 00 FF 03
........mnuAbout 02 1E 00 00 00 09 08 00-6D 6E 75 41 62 6F 75 74
.....&About..... 00 13 03 06 00 26 41 62-6F 75 74 00 07 FF FF 02
"......mnuAboutR 22 00 00 00 0A 0B 00 6D-6E 75 41 62 6F 75 74 52
eg.....&Register 65 67 00 13 03 09 00 26-52 65 67 69 73 74 65 72
..........mnuAbo 00 FF 02 1F 00 00 00 0B-0B 00 6D 6E 75 41 62 6F
utAbo.....A&bout 75 74 41 62 6F 00 13 03-06 00 41 26 62 6F 75 74
..........+@.... 00 FF 03 03 04 06 00 00-00 DC 2B 40 00 01 00 01
It can be interpreted this way :
05 0700 "mnuFile"00 1303 0500 "File"00 07FFFF02 2600 0000
06 0B00 "mnuFileSave"00 1303 0A00 "&Save Text"00 081300FF 021D00 0000
07 0C00 "mnuFileSep_1"00 1303 0100 "-"00 06FFFF02 21D1 0000
08 0B00 "mnuFileExit"00 1303 0500 "E&xit"00 081100FF 03021E00 0000
09 0800 "mnuAbout"00 1303 0600 "&About"00 07FFFF02 2200 0000
0A 0B00 "mnuAboutReg"00 1303 0900 "& Register"00 FF021F00 0000
0B 0B00 "mnuAboutAbo"00 1303 0600 "A&bout"00 FF030304 0600 0000
Here's a description of the fields :
1 : number of the item which will be defined.
2 : size of the next string.
3 : name of the menuitem as it has been entered during its creation in
VB ; this is a 00 terminated string.
4 : special delimiter between the two strings ; these values are used to
check that the strings' sizes are correct.
5 : size of the next string.
6 : name of the item as it will be displayed. Note that "-" is a special
value reserved for menuitem separators ; this is also a 00 terminated
string.
Other fields are left as targets for mighty reversers' curiosity. :)
Hey, but where are the MenuID values ? Well, they are not stored here, actually
we will see later that they are not stored at all...
II) WINDOWS MESSAGES
1) Messages and Windows
When you type a key or move the mouse, the corresponding device driver
converts the input into messages and places them in the system message
queue. Then Windows gets messages from this queue, determines what
window is currently active, and sends the message to the queue of the
thread that created this window.
A message is a structure declared as:
typedef struct MSG {
HWND hwnd; // handle of the window for whom the message is destined
WORD message; // type of message (WM_COMMAND, WM_PAINT,...)
WPARAM wParam; // first parameter, depends on the type of message
LONG lParam; // second parameter, depends on the type of message
DWORD time; // time when the message occured
POINT pt; // location (X,Y) of the mouse when the message occured
}
Depending on which API you use (Win32 or Win16) the contents of wParam and
lParam and the size of wparam can vary. For example, a WM_COMMAND message is :
in Win16 : wParam(16-bits) - Identifier for the command
lParam(32-bits) - LOWORD: Window handle, HIWORD: Special modifier
in Win32 : wParam(32-bits) - LOWORD: Identifier, HIWORD: modifier
lParam(32-bits) - Window Handle
Note that Softice does not handle these subtelties, thus it sometimes recognizes
wrong wParam and lParam values (with accelerators for instance, see 2) ).
Each thread of each application has an internal message queue, where Windows
can put one or more messages ; then, the application retrieves the messages
from the queue (in the same order they have been put into) and process them.
That means the application
- reads the message from the queue by using GetMessage().
- translates keyboard messages (key up/down) into char messages by TranslateMessage().
- dispatches them to the appropriate window procedure by using DispatchMessage().
Actually, there are two ways Windows can send a message to an application :
it can either place the message in the thread's queue with PostMessage()
(they are called queued messages) or it can send it directly to a window
procedure with SendMessage() (they are called nonqueued messages).
Queued messages include all user input messages (like WM_MOUSEMOVE,
WM_LBUTTONDOWN, WM_KEYDOWN, WM_CHAR, ...) and some system messages
(WM_TIMER, WM_PAINT, WM_QUIT). All these messages use a MSG structure.
For nonqueued messages, only the first four arguments (ie not time and mouse
position) are passed, but if the window procedure needs them, it can get them
via GetMessageTime() and GetMessagePos(). Windows typically sends nonqueued
messages to notify a window of events that affect it (WM_ACTIVATE,
WM_SETFOCUS, WM_SETCURSOR, ...) or when an application calls certain Windows
functions (for example, Windows directly sends the WM_WINDOWPOSCHANGED
message after an application uses the SetWindowPos() function to move
a window).
Note also that WM_PAINT is an exception to the FIFO message queue : this special
message has the lowest priority, that means it is sent to the window procedure
only when the queue contains no other message.
2) Messages to an application.
Now, the question is : how does an application know when Windows has
put a new message in its queue ? Well, it does not really know it.
Actually, the application is just stuck in an infinite loop where it keeps
on retrieving and dispatching messages. This is called the "message pump"
or "message loop", and its simplest form is :
; Process messages, quit when WM_QUIT received.
;
msg_loop:
push 0 ; maximum value of message to filter
push 0 ; minimum value of message to filter
push 0 ; handle of the window (0 = all messages)
push offset msgbuffer ; where to store the message
call GetMessageA ; get the message from the queue and store it
or eax,eax ; if eax=0, WM_QUIT as been posted
je end_loop ; in that case, we leave the app
push offset msgbuffer ; where the message is stored
call DispatchMessageA ; send message to window procedure
jmp msg_loop ; loop
; Terminate program.
end_loop:
push [msgbuffer.msgWparam]
call ExitProcess
A window procedure is a function that receives and processes messages sent
to the window. Every window class has a window procedure, and every window
created with that class uses that same window procedure to respond to messages.
Because a window procedure is shared by all windows belonging to the same
class, it can process messages for several different windows. To identify
the specific window affected by the message, a window procedure can examine
the window handle passed as first parameter of a message.
Window procedures are usually just a big 'CASE' statement, where all types
of messages are routed to different pieces of code :
; The window procedure, where messages are routed.
;
MainWndProc:
mov eax,[esp+8] ; get message type
cmp eax, 0000000F ; is it WM_PAINT ?
je paint_client ; yes, go and paint something
cmp eax, 00000111 ; is it WM_COMMAND ?
je execute_command ; yes, go to command procedure
cmp eax, 00000201 ; is it WM_LBUTTONDOWN ?
je LeftMouseDown ; do something
cmp eax, 00000001 ; is it WM_CREATE ?
je creating_window
cmp eax, 00000002 ; is it WM_DESTROY
je start_destroy
jmp DefWindowProcA ; delegate other message processing
Note that by default there is a call to DefWindowProcA(). This is the
Windows default window procedure which MUST be called to handle any
messages that a window procedure does not handle. A program can have
many windows and therefore many window procedures : when a message is
not processed by the main window procedure, the message is passed to
the next one via DispatchMessage().
We saw that a WM_KEYDOWN/WM_KEYUP combination of messages can be
translated into a WM_CHAR message (and reposted to the thread's own
queue) by TranslateMessage(). But a WM_KEYDOWN can also be translated
into a WM_COMMAND message by TranslateAccelerator(), the function which
processes accelerator keys for menu commands (like Ctrl+C for copy).
Also, if the keyboard combination corresponds to a menu, WM_INITMENUPOPUP
is sent to the application and the popup menu is opened. Note that you
can differentiate a WM_COMMAND message translated from a keyboard input
from a message triggered by the mouse, since TranslateAccelerator() sets
the high-order word of the wParam parameter to 1 ; in this case, Softice
wrongly thinks that it's the lparam hiword that is set.
In addition to the APIs we have covered, Windows has some other APIs that
you should be aware of that let you perform various actions on messages
and the message queue :
PeekMessage() - read messages in the queue without removing them.
RegisterWindowMessage() - to declare specific message for your application.
WaitMessage() - suspend the thread until a message is put on the queue.
C) Messages and Menus
When the user activates an item on the menu bar, the owner window first
receives a WM_SYSCOMMAND message. This message includes a flag that
indicates whether the user activated the menu by using the keyboard
(SC_KEYMENU) or the mouse (SC_MOUSEMENU).
Next, before displaying any menus, Windows sends the WM_INITMENU message
to the window procedure so that an application can modify the menus before
the user sees them. Windows sends the WM_INITMENU message only once per
menu activation.
When the user points to a menu item that opens a submenu, Windows sends
to the owner window the WM_INITMENUPOPUP message before displaying the
submenu. Again, this message allows the application to modify the submenu
before it is displayed. Each time the user moves the highlighting
from one item to another, Windows sends a WM_MENUSELECT message to the
window procedure of the menu's owner window. This message identifies the
currently selected menu item ; some applications (Words, Wordpad,...) handle
this message to display informations about the selected menu item at the
bottom of their main windows.
When the user chooses an item from a menu, Windows sends a WM_COMMAND
message to the window procedure. The low-order word of the WM_COMMAND
message's wParam parameter contains the identifier of the chosen item.
The window procedure should examine the identifier and process the message
accordingly.
For context menus (opened by right button clicks), a WM_CONTEXTMENU
message is sent, so that the application can process it and display
its menu.
III) REVERSING A FUNCTION DISABLED TARGET
1) Before you start
Before you even think about cracking a function-disabled program, you
should check a few things :
- is it the best solution ? If you can register with a code or a keyfile,
try this instead, because this will remove all visible (and invisible so
far) annoyances and/or limitations in one row.
- is the code of the disabled functions in the program ? If the code is
not there, you will have to add it, and that's a different challenge...
There are many clues that can lead you to a conclusion, like the registration
scheme, the imported APIs, some text strings, etc
Ok, for some reason you decided to crack it this way. Now you must always
keep in mind where you are and what you want to do ; basically, you will
have to :
- ungray the disabled function (or make it appear) in the menu.
- link this function to the correct piece of code.
Part one is most of the times fairly easy, and can lead to a typical
error : you put a breakpoint on EnableMenuItem, find where the flag is
stored and change it to MF_ENABLED. Now you have ungrayed the function,
so you're pretty confident, but when you click... nothing happens :(
That's the second part, which can be more or less complicated, depending
on how the program has been stripped : you must first find the piece of
code that corresponds to the function, then you must find and modify the
message loop or/and the window procedure.
2) A few tips
It's quite easy to ungray a item, just have a look at the imported APIs,
and find what is used to create and maybe modify the menus. The main
difficultys is to find where the message loop and the appropriate window
procedure are.
A few ways to find a message loop :
- via the the messages specific APIs, like GetMessageA(), DispatchMessageA()
or DefWindowProcA(), that are almost always called in each message loop.
- via the message values : some message are always processed, like
WM_COMMAND (0111h) or WM_DESTROY (0002h). So you can search for
00000111 in a disassembled listing to find a "cmp xxxx, 00000111h",
that may be the 'case WM_COMMAND' statement.
- in SoftIce, with a BMSG command
SoftIce's BMSG breakpoint is quite useful to break on specific messages.
You can also log without poping the messages going to a window procedure
with the 'L' option ; note that the messages are logged in the SoftIce
history buffer, so be sure to have a well chosen size. Also use WMSG to
have a list of messages recognised by SoftIce.
You can construct an equivalent BPX-style breakpoint using a conditional
expression. Use the HWND command to get the address of the window procedure,
then use the following BPX command :
bpx postmessagea if @(ss:esp+8)==WM_COMMAND
A few ways to find a window procedure :
- via the RegisterClass() and RegisterClassExA() functions.
The window procedure address is the second parameter (for WNDCLASS) and
the third parameter (for WNDCLASSEX), so some
BPX RegisterClassA do "u @(@(ss:(esp+4))+4) ; d @(@(ss:(esp+4))+24)"
BPX RegisterClassExA do "u @(@(ss:(esp+4))+8) ; d @(@(ss:(esp+4))+28)"
will display the registered class and unassemble the piece of code of
the corresponding window procedure. Also watch for CreateWindowA() and
CreateWindowExA(). Actually, this is interesting only if you find
window procedures that belongs to the target ; most of the times, it
will not be the case, because the application will use system or
application global classes (from Windows, MFC,...).
- with Softice, with HWND and CLASS : the first function gives informations
about all windows of a process, including the window procedure address :
:hwnd
Window Handle hQueue SZ QOwner Class Name Window Procedure
0080(0) 206F 32 MSGSRV32 #32769 (Desktop) 17CF:00004C44
^^^^^^^^^^^^^
The second function gives informations about the classes used by a process :
:class notepad
Handle Class Name Owner Wndw Proc Styles
-------------------------- Application Private -----------------------------
403C Notepad NOTEPAD 1467:00000350 07001000
--------------------------- Application Global -----------------------------
401C SysAnimate32 COMCTL32 1467:000000FE 03004008
400C msctls_hotkey32 COMCTL32 1467:000000E8 03004000
3A04 msctls_progress32 COMCTL32 1467:000000D2 03004003
...
A very underestimated command is "HWND -X", which gives you all the
informations you need about a window ; here's an example :
:hwnd -x notepad
Window Handle : (01B8) Level (1)
Parent : 16E7:000204CC
Child : 16E7:000235D8
Next : 16E7:00024F7C
Owner : NULL
Window RECT : (-4,-4) - (804,576)
Client RECT : (0,38) - (800,572)
hQueue : 238F
Size : 32
QOwner : NOTEPAD
hrgnUpdate : NULL
wndClass : 16E7:4184
Class : Notepad
hModule : NOTEPAD (1A07)
lpfnWndProc : 1467:0000033A
...
- with a BPR on the code of the application : when the DispatchMessage()
function is called, you leave the message loop and start executing some
code of the USER32 DLL. Most of the cases, the next piece of code of the
application that will be executed is the window procedure. Even if the
window procedure's entry point is not contained in the application, this
kind of breakpoint on the application is always interesting.
Note that you can easily find the memory space used as message queue,
since it uses its own selector. Here's an example :
:task
TaskName SS:SP StackTop StackBot StackLow TaskDB hQueue Events
Stat32d 0000:0000 00CFD000 00D00000 2D1E 2B27 0000
... ^^^^
:ldt 2b27
Sel. Type Base Limit DPL Attributes
2B27 Data16 80155B80 0000009F 3 P RW
The message queue for the Stat32d application start at address 80155B80
and is 160 bytes large. For details about how messages are stored in the
message queue's memory space, read Matt Pietrek's book (or files).
Finding the piece of code corresponding to a missing function can be
the most difficult part of the job, because here you have to mostly use
your intuition. Hopefully, most applications use the standard Windows
dialog boxes for opening, printing and saving files. These are the
COMDLG32 DLL's GetOpenFileName (for Open) , GetSaveFileName (for Save and
SaveAs), PrintDlgA (for Print).
IV) HANDS ON
1) Cryptographic Analysis Program (CAP) 1.0
Location: www.plu.edu/~spillmrj/cap.html
Description: a basic cryptanalyis tool
Limitations: Save, SaveAs and Print functions are disabled
Language: delphi
To register, you must send to the author a number (derived from your
HD number) and he sends you back the correct code : a very nice thing
for us, because we are sure we already have all the code.
Let's have a look at the imported APIs : for the "menus" part, we have
everything we want : CreateMenu(), EnableMenuitem(), InsertMenu(),
RemoveMenu(), SetMenu(),... The message part is interesting : we find
no GetMessage() imported, but the message queue is tested with
PeekMessage() (maybe a specificity of delphi ?)
Let's put a breakpoint on EnableMenuItem() to see what functions are
disabled : since MF_GRAYED value is 01, we just have to break for any
flag whose bit 0 is set, that means with a
:BPX EnableMenuItem IF ((@(ss:(esp+0c))&1)==1)
Here are the calls to EnableMenuItem() that trigger the breakpoint :
MenuId 0588 058C 058C 058C 05D4 05D4 0614 0614 0620 0620
FctId 20 04 05 07 49 4A 51 52 56 57
Flag 03 03 03 03 03 03 03 03 03 03
In this case, all these functions are grayed and disabled (03h).
He, but we see that only in menu 058C we find 3 disabled functions whose
FctId values (04, 05, 07) correspond to the order of the 3 disabled
functions we are interested in (Save, SaveAs, Print). With the STACK
command, we find out that EnableMenuItem is invoked from 42639B for
these 3 functions.
:00426383 mov eax, dword ptr [4*eax+004996D8] ; flag
:0042638A or eax, 00000000
:0042638D push eax
:0042638E movzx eax, word ptr [esi+34]
:00426392 push eax ; function id
:00426393 mov eax, edi
:00426395 call 00426194
:0042639A push eax ; menu handle
:0042639B Call EnableMenuItem
At line 426383, a lookup table seems to be used to get the flag
corresponding to every function ; for our 3 disabled functions,
eax=0 and ds:[4996D8]=03 is used each time. Let's take Hiew,
and change this byte at .996D8 into 00 : now, all these functions
are enabled.
Let's disassemble the program and search for an line containing 0111
(WM_COMMAND) in the disassembled listing ; here's what we find :
:00423177 cmp dword ptr [edx+04], 00000111 ; is it WM_COMMAND ?
:0042317E je 004231A0 ; yes : go on
:00423180 mov esi, dword ptr [edx+04]
:00423183 cmp esi, 00000200
:00423189 jle 00423193
:0042318B cmp esi, 0000020A
:00423191 jle 004231A0
Do you notice how the range 200-20A has a special meaning for this
piece of code ? Well, a quick look at our message reference shows
that this range contains all mouse related messages. This means that
this piece of code is very likely to be a part of the message processing.
So let just put a breakpoint to pop on any WM_COMMAND message :
:bpx 42317E if (ZFL==true)
Now we click on Save... the breakpoint is triggered.
Let's trace a bit to see how the message will be processed. As expected,
we soon reach a DispatchMessageA() procedure, where the message is
sent to the appropriate window procedure.
To find this procedure, let's do something elegant, that means a
little "breakpoint combo" :
:bc *
:map32 cap_prj
Owner Obj Name Obj# Address Size Type
CAP_PRJ CODE 0001 0137:00401000 0009753C CODE RO
...
So we know that the code of this program is stored in memory
between 137:401000 and 137:49853C ; I therefore put a bpr on this
zone so that any piece of code from CAP_PRJ that is executed will
trigger this breakpoint. Then,
:bpr cs:401000 cs:49853c r
:bd 0
:bpx 42317E if (ZFL==true) do "g DispatchMessageA ; be 0; g"
The breakpoint at 42317E will trigger first when a WM_COMMAND message
will be received. Then, it will process to the next DispatchMessageA
procedure, enable the first breakpoint and run. From here only the
code from DispatchMessageA() and its subprocedures will be executed,
so BP0 will trigger on the first opcode executed from CAP_PRJ, which
must be the window procedure entry point.
The breakpoint is triggered at 428F48 ; if we trace a bit, we find
this piece of code :
:004266B4 push ebx
:004266B5 cmp byte ptr [eax+2D], 00 ; test a value
:004266B9 je 004266CC ; leave if 0
:004266BB cmp word ptr [eax+62], 0000 ; test another value
:004266C0 je 004266CC ; leave if 0
:004266C2 mov ebx, eax
:004266C4 mov edx, eax
:004266C6 mov eax, dword ptr [ebx+64]
:004266C9 call [ebx+60]
:004266CC pop ebx
:004266CD ret
Let's see what happens at 4266B4 when we try a few options in the
menu :
Function Status EAX [EAX+2D] [EAX+62]
---------------------------------------------------
Open enabled 101ABD0 01 48
Save disabled 101AEDC 00 48
SaveAs disabled 101AE58 00 48
Print disabled 1019698 00 48
Exit enabled 1018108 01 48
It's quite clear now, we just have to change the values at [eax+2D]
from 0 to 1 to enable the registered functions.
2) SigmaStat 2.0
Location: www.spss.com
Description: a statistical tool
Limitations: Save disabled
Language: C++ / MFC
This is a typical C++ program, but it uses the famous Microsoft
Fundation Classes (MFC 3.0 here), and that changes everything ! MFCs
really add a shell around your application to completely handle
interaction with the user : the message loop, the window procedures and
even all the common functions of a File menu (New, Save, Print...) are
executed within the DLL.
Here's how menus are handled by MFC : every time you click on a menu,
- MFC gets the menu handle via GetMenu(), then the number of items in
this menu via GetMenuItemCount().
- for each item of this menu, it checks if this starts a submenu via
GetSubMenu(). If a submenu is here, another sub-loop is entered,
where it starts by getting the number of items via GetMenuItemCount().
- for each item in any menu/submenu, MFC gets the messageID associated
to this item via GetMenuItemID() and disable it if needed with
EnableMenuItem().
Here's this last part (the ORD_3BF procedure at offset 6854h of
MFC30.text segment) ; this piece of code is executed for each item
of each menu, and each time you select this menu :
:7FB27848 cmp dword ptr [esp+10],01 ; should we disable ?
:7FB2784D sbb eax,eax ; yes / no
:7FB2784F and eax,03 ; keep the two lower bits
:7FB27852 mov ah,04 ; set MF_BYPOSITION
:7FB27854 push eax ; pass the flag
:7FB27855 push dword ptr [esi+08] ; choose the function
:7FB27858 push dword ptr [ecx+04] ; choose the menu
:7FB2785B Call EnableMenuItem ; enable or disable it
See what happens ? If the value at [esp+10] is zero, then the carry flag
is set and the SBB EAX,EAX instruction sets EAX at 0 minus 1 = FFFFFFFFh.
If the value at [esp+10] is not zero, then the carry flag is not set and
EAX is set to 0. As a result, the EAX value passed as flag is either 0403h
(if [esp+10] was 0) or 0400h ; as usual, the two lower bits are the most
important ones for us.
Tracing back where the [esp+10] value is set to 0, we quickly find this
interesting piece of code :
:0040D599 mov [ebp-04],ecx
:0040D59C push 00 ; set the flag here !
:0040D59E mov eax,[ebp+08]
:0040D5A1 mov eax,[eax]
:0040D5A3 mov ecx,[ebp+08]
:0040D5A6 call [eax] ; call 7FB27836 (to EnableMenuItem)
:0040D5A8 jmp 0040D5AD ; stupid compiler flush the PIQ :(
:0040D5AD pop edi
:0040D5AE pop esi
:0040D5AF pop ebx
This piece of code can be found 3 times in the code of Stat32, that's
right 3 times the same piece of code, separated by a bunch of useless CCh
bytes ; obviously the Microsoft compiler use a standard "disable procedure"
that is just put as many times as necessary in the application's code.
Now there's something very interesting about that : if we replace the
"push 00" by a "push 01", not only the items are enabled, but the
corresponding functions are executed... the job is done :)
Just for fun (and knowledge), let's see how messages are processed by MFC ;
A breakpoint on GetMessageA() gives us the location of the message loop
(in MFC30 DLL) :
:7FB24CEF push edi
:7FB24CF0 lea esi,[ecx+30]
:7FB24CF3 mov edi,ecx
:7FB24CF5 push 00 ; (0,0,0) <=> get all the
:7FB24CF7 push 00 ; messages
:7FB24CF9 push 00
:7FB24CFB push esi ; message buffer offset
:7FB24CFC call GetMessageA ; get message from the queue
:7FB24D02 test eax,eax ; WM_QUIT received ?
:7FB24D04 jz 7FB24D31 ; yes : leave program
:7FB24D06 cmp dword ptr [edi+34],0000036A ; message 36A received ?
:7FB24D0D jz 7FB24D29 ; yes : ignore it
:7FB24D0F push esi
:7FB24D10 mov eax,[edi]
:7FB24D12 mov ecx,edi
:7FB24D14 call [eax+38] ; handling of 201h and 202h
:7FB24D17 test eax,eax ; (mouse-related messages)
:7FB24D19 jnz 7FB24D29
:7FB24D1B push esi
:7FB24D1C call TranslateMessage ; translate keyboard inputs
:7FB24D22 push esi
:7FB24D23 call DispatchMessageA ; send message to wndproc
:7FB24D29 mov eax,00000001
:7FB24D2E pop edi
:7FB24D2F pop esi
:7FB24D30 ret
Let's try to break in this message loop when a WM_COMMAND message is
received ; therefore we will watch the message buffer. The ESI value at
line 7FB24CFB is always 5D7048, and this is pointing to a data section
of Stat32d. There starts a MSG structure where every message taken from
the queue is temporarly stored before being processed ; at offset 4 of
this structure is stored wParam. So to break on a WM_COMMAND message we
can use a :
:bpx 7FB24D02 if (@(ds:(5D704C))==0111)
Now if we try the same combo as in CAP, we get lost, because we are just
bouncing between MFC30.DLL code and the program's lookup table.
Let's try the dead-listing method : we disassemble MFC30.DLL and use the
same method, that means searching for "00000111" ; here's the first hit :
:7FB21074 cmp ebx, 00000111 ; 111h = WM_COMMAND
:7FB2107A je 7FB2110C
:7FB21080 cmp ebx, 0000004E ; 4Eh = WM_NOTIFY
:7FB21083 je 7FB21138
:7FB21089 cmp ebx, 00000006 ; 06h = WM_ACTIVATE
:7FB2108C je 7FB21176
We can stop here, this is the good one. Why ? Because in every MFC
application, message processing is splitted between the CCmdTarget objects
that handles WM_COMMAND, WM_NOTIFY and WM_ACTIVATE (CN_UPDATE_COMMAND_UI)
and the CWin objects that process most of the other Windows messages. The
values of the 3 first messages are found here, so this must be the piece of
code we are looking for. If we look 7FB2110C, it's quite promising :
:7FB2110C mov ecx,[ecx]
:7FB2110E mov esi,[ebp+10] ; lParam is the first parameter
:7FB21111 push esi ;
:7FB21112 mov edi,[ebp+0C] ; wParam is the second one
:7FB21115 push edi ;
:7FB21116 mov [ebp-14],ecx
:7FB21119 mov ecx,[ebp-10]
:7FB2111C mov eax,[ebp-14]
:7FB2111F call [eax+48] ; call WM_COMMAND processing
Ok, now we trace a bit in the WM_COMMAND processing to find where all
different messages are routed, and we quickly find this piece of code :
:7FB29DBF push 00
:7FB29DC1 mov eax,[esi] ; get adress
:7FB29DC3 push 00
:7FB29DC5 mov ecx,esi
:7FB29DC7 push ebp
:7FB29DC8 push edi
:7FB29DC9 call [eax+14] ; execute the function
Look at this nice lookup table, it's just a pure array of code offsets, which
is the core of the message routing mechanism :
:005E7664 72 87 5A 00 32 82 5A 00-C0 F4 44 00 2C 82 5A 00 r.Z.2.Z...D.,.Z.
:005E7674 26 82 5A 00 20 82 5A 00-1A 82 5A 00 14 82 5A 00 &.Z. .Z...Z...Z.
:005E7684 6C 87 5A 00 10 F1 44 00-02 82 5A 00 FC 81 5A 00 l.Z...D...Z...Z.
:005E7694 F6 81 5A 00 66 87 5A 00-EA 81 5A 00 E4 81 5A 00 ..Z.f.Z...Z...Z.
:005E76A4 90 29 40 00 60 87 5A 00-DE 81 5A 00 5A 87 5A 00 .)@.`.Z...Z.Z.Z.
:005E76B4 D2 81 5A 00 CC 81 5A 00-C6 81 5A 00 80 EF 44 00 ..Z...Z...Z...D.
:005E76C4 BA 81 5A 00 54 87 5A 00-AE 81 5A 00 A8 81 5A 00 ..Z.T.Z...Z...Z.
:005E76D4 4E 87 5A 00 9C 81 5A 00-48 87 5A 00 42 87 5A 00 N.Z...Z.H.Z.B.Z.
A trained eye could recognise this kind of lookup tables just by looking at the
hex dump of the code section. Indeed, since these are all word-aligned offsets
to the application's code, so you will always find 4 pairs of columns equal to
"XX 00" (where XX is the first byte of the offset) at offsets 2, 6, 10 and 14
of this table.
:map32 stat32d
Owner Obj Name Obj# Address Size Type
STAT32D .text 0001 0137:00401000 001D5784 CODE RO
Here, XX can have any value between 40h and 5Dh, which are all printable
chars, but as you see most of the times you find ("Z",00h) pairs.
Reversing a MFC disabled target can be very painful, because most of the
work is done in the DLL while, even if you switch to/from the application
very often. Not to mention that both MFC DLL's code and the code inserted by
MFC in your application are ugly.
3) BestWin
Location: fravia.org/jn_essay.htm
Description: an encryption tool
Limitations: the Decrypt function is disabled (that's annoying :)
Language: C (compiled with a french version of lcc-win32 2.4 in his
c:\lcc\bestwin directory :)
Before the fortress' attack, Jeremy Likness proposed a software cracking
challenge, quite pretentiously called "New Chaos Protection", to protect
an implementation of his encryption algorithm (which is also weak btw,
but that's another story). Here follows a description and a quick crack
of his method.
A quick disassembly in W32dasm gives these interesting menu references :
MenuID_0258
File {Popup}
Encrypt [ID=00C8h]
Decrypt [ID=00D2h]
Set Key [ID=00DCh]
Exit [ID=012Ch]
The MenuID value for "Decrypt" is D2h, so let's search for "000000D2" in the
disassembled listing :
:00402677 cmp dword ptr [ebp+0C], 000000D2 ; Decrypt asked ?
:0040267E je 004026B1 ; yes : go on
...
:004026B1 push 755647E4
:004026B6 call PROT._pGetFuncbyCode ; which code to run ?
:004026BB add esp, 00000004
:004026BE mov dword ptr [ebp-04], eax ; EAX = code offset
:004026C1 call [ebp-04] ; execute our code
:004026C4 jmp 004026EA
When "Decrypt" is selected, BestWin asks PROT.DLL (via
PROT._pGetFuncbyCode) which piece of code is associated to this
function, then it runs it.
Let's load BestWin in SoftIce to have a closer look at this
_pGetFuncbyCode... what ? SoftIce is asking for source files ??
Looks like Jeremy forgot to remove the symbols :) Let's have a look :
:sym
.text(014F:00401000, 00002B88 bytes)
0137:00401ABA _Decrypt
0137:004028A4 _DefaultFunc
0137:00401E4F _EnableDecrypt
0137:00401CC4 _SetKey
...
Hehehe, our job is done : the _Decrypt label at 401ABA is the beginning
of the decryption routine. Therefore, we only need to associate this
piece of code to the "Decrypt" function of the menu in the WM_COMMAND
message processing, just like that :
004026BE E8F7F3FFFF call 401ABA ; _Decrypt procedure
004026C3 90
>
Transfer interrupted!
We also need to enable the Decrypt function in the menu ; _pGetFuncbyCode
is also used to "hide" functions that does not belong to any menu. For
instance, when you set the key, this piece of code is run :
:00401E1C push 75564714
:00401E21 call PROT._pGetFuncbyCode
:00401E26 add esp, 00000004
:00401E29 mov dword ptr [ebp+FFFFFBAC], eax
:00401E2F call dword ptr [ebp+FFFFFBAC]
If we are registered, the _EnableDecrypt function is executed, which
ungray Decrypt in the menu. If not, the infamous messagebox is displayed.
As you see, we did not even have to study PROT.DLL to nullify this protection,
we simply had to replace the relative calls by the absolute correct ones, just
like this :
00401E2F E81B000000 call 401E4F ; _EnableDecrypt procedure
00401E34 90 nop
His protection is based on a dialog between the main program (BestWin)
and its protection DLL (PROT.DLL) : first, the "you must register"
messagebox is set as default function (via PROT._SetDefaultFunc).
Then, when a registered item is selected, BestWin asks PROT.DLL (via
PROT._pGetFuncbyCode) which piece of code to run. If we are not
registered, the offset of the default function is returned, and
the evil message is displayed.
Another major error is that all the functions are first defined
(via PROT._AddFunc) with the offset of the correct piece of code
(so that _pGetFuncbyCode knows the correct offset to give back
to registered users).
A pretty interesting idea, badly implemented : quite disappointing
for such a hype name.
4) Flipper
Location: crackmes.cjb.net
Description: crackme
Limitation: File menu is grayed for the first 10 seconds
Language: Visual Basic 5.0
Well, this is not a full "disabled functions" protection, but it is
small and does everything we need to understand how VB handles menus.
Visual Basic handles all the GUI by itself, so that the coder has no
idea what is done ; so we can assume that all VB programs define
menus the same way. All is done in the __vbaI4ErrVar function.
First some empty menus are created with CreateMenu(). Then each menu and
each item are added with InsertMenuA() ; note that all the parameters
of InsertMenuA() are stored in a structure based on EBP ; actually, EBP
gets only two values in this piece of code, one for all the items and one
for all the menus.
:0F05191A push [ebp-10] ; address of menuitem text string
:0F05191D push [ebp-0C] ; handle of the new item
:0F051920 mov eax, dword ptr [ebp-08] ; get flag
:0F051923 or ah, 04 ; add MF_BYPOSITION
:0F051926 push eax ; pass the new flag
:0F051927 push [ebp+18] ; the position of the new item
:0F05192A push [ebp+0C] ; menu handle
:0F05192D call InsertMenuA
The first parameter (menu handle) is returned by a previous call to
CreateMenu(). The second parameter (position) is always 0FFFFFFFFh,
so that each new item is always appended to the existing menu.
The third parameter (flag) has a default value equal to 0, which is
reloaded after each item insertion. The fifth parameter (menuitem string)
is just copied from the menu definition (see "Reversing a menu definition").
The most interesting parameter is obviously the fourth one, the item's
message ID. As we saw before, it's not stored in the menu definition,
contrary to what most of the other languages do. Here VB is tricky, because
it distributes message IDs at run-time, by this piece of code :
:0F051834 mov eax, dword ptr [ebx] ; get previous ID message
:0F051836 inc eax ; increment
:0F051837 mov word ptr [edi+000000D0], ax ;
:0F05183E mov dword ptr [ebx], eax ; store new ID message
For instance, here's the message distribution for this application :
1 : Menu "File"
2 : Item "SaveText"
3 : Item Separator
4 : Item "Exit"
5 : Menu "About"
6 : Item "Register"
7 : Item "About"
It's clear and simple (something surprising from Visual Basic).
When you start the application, the "File" menu is grayed for 10 seconds.
A breakpoint on InsertMenuA() show that when the menu is created, the flag
used is 400h (MF_BYPOSITION), so that the menu is enabled ; later, the menu
is grayed using EnableMenuItem() with a 403h flag. After 10 seconds, the
same API function is used again to ungray the menu. For now, let's skip the
ungraying part and focus on the message routing (you'll see later why) :
:hwnd app1
Window Handle hQueue SZ QOwner Class Name Window Procedure
0E2C(1) 0D87 32 APP1 ThunderRT5Form 1497:000007DE
0E30(2) 0D87 32 APP1 Edit 1497:000007DE
0E34(2) 0D87 32 APP1 ThunderRT5Timer 1497:000007DE
...
All window procedures' adresses are pointing to the KERNEL DLL, so to find
which part of app1 is involved in menu handling, we will use again a breakpoint
on the application's code :
:map32 app1
Owner Obj Name Obj# Address Size Type
APP1 .text 0001 014F:00401000 000069A4 CODE RO
:bmsg 02EC WM_COMMAND do "bd * ; bpr 14f:401000 14f:408000 r ; g"
The breakpoint is triggered in MSVBVM50.DLL, at F03A2FD, where the
adress of the window procedure is fetched from a table in app1.
:0F03A2F8 mov ecx,[eax] ; get table base address
:0F03A2FA mov eax,[ebp+14] ; get item
:0F03A2FD push dword ptr [eax*4+ecx+0C] ; calculate address
:0F03A301 call 0F03A546
and if we trace a bit, we find this piece of code :
:0F01E597 mov eax, dword ptr [ebp+08] ; get address from stack
...
:0F01E5A7 call eax ; go !
All the EAX values lead to a kind of table structure, where we jump
to the real window's procedure, after the first stack parameter
is adjusted to a fixed value (4114C5) :
:004024FB sub dword ptr [esp+04], 00000033 ; About item
:00402503 jmp 00403B0A
:00402508 sub dword ptr [esp+04], 0000004F ; Register item
:00402510 jmp 00403CC9
:00402515 sub dword ptr [esp+04], 00000057 ; Exit item
:0040251D jmp 00403DDF
:00402522 sub dword ptr [esp+04], 00000047 ; SaveText item
:0040252A jmp 00403E43
:0040252F sub dword ptr [esp+04], 0000003F ; triggered each second
:00402537 jmp 004040A1 ; during 10 s countdown
:0040253C sub dword ptr [esp+04], 0000004B
:00402544 jmp 0040425E
For the SaveText item, we jump at 402508, then at 403CC9.
Now let's find which piece of code we would like to execute : well, the
rtcMsgBox is called at 404030, and just a bit upper, we find this
interesting API calls :
:00403FE0 call __vbaFileOpen
...
:0040400F call __vbaPrintFile
...
:00404029 call __vbaCloseFile
and again a bit upper, this part :
:00403E85 mov ax, word ptr [00408030] ; registered flag
...
:00403E8D cmp ax, 03FB ; bigger than 3FBh ?
:00403EC4 jnl 00404035 ; yes : do nothing
:00403ECA cmp ax, 001B ; smaller than 1Bh ?
:00403ECE jle 00404035 ; yes : do nothing
:00403ED4 cmp ax, 00FE ; min registered value
:00403ED8 jle 00404030 ; yes : go to rtcMsgBox
Just change [408030] into FFh, and it's done.
Ok, now let's ungray this File menu : we saw that the initial state of
this item is "enabled", and that it is grayed afterwards. And guess how ?
It's done by using the same table :
:004024D4 sub dword ptr [esp+04], 00000037 ; gray File menu
:004024DC jmp 00403A04
This is a nice place to patch ; let's replace this JMP to the graying function
by a harmless one. Oh, but look how the next lines look interesting :
:004024E1 sub dword ptr [esp+04], 0000FFFF
:004024E9 jmp 00403B00
:004024EE sub dword ptr [esp+04], 0000FFFF
:004024F6 jmp 00403B05
Not only is the FFFFh value a bit strange, but between the two JMPed addresses
there are only 5 bytes ; let's see what code is at 403B00 :
:00403B00 xor eax, eax
:00403B02 ret 0004
Well, it looks harmless enough to me, so we just patch at 4024DC to force a jump
to this address, and everything works fine.
Most of the times, programs that display a message box for disabled functions
are much easier to reverse than those that use grayed items. Indeed, in this
case, lazy protectionists will use a switch between real and fake code after
message decoding instead of trying to (for instance) modify the menu definition.
I hope that you now have a good overview of how menus work ; of course, comments
and corrections are welcome. This is really a large subject, and there are still
plenty of interesting things to talk about in this field (subclassing, superclassing,
adding functions, ...) ; if you're interested in this subject, I suggest that you have
a look at +HCU project 6 and Fravia+'s filemon essays. Good night, dear reader.
+Spath. (Spath@iname.com) (05/99)
Greetings:
+ORC, Fravia+, +Greythorne and all +HCU members.
+Frog's Print, BeLZeBuTH, Ethan, CyberbobJr, KellogS, Jeff, Rhayader, Eternal Bliss,
CrackZ, Iczelion, Virogen.
All my admiration goes to Razzia+, Stone, mammon_, Iceman, Quine and
especially to The Owl. Thanks for giving me inspiration.
I wont even bother explaining you
that you should BUY this target program if you intend to use it for a
longer period than the allowed one. Should you want to STEAL this
software instead, you don't need to crack its protection scheme at all:
you'll find it on most Warez sites, complete and already regged,
farewell, don't come back.
You are deep inside fravia's page of reverse engineering,
choose your way out:
homepage
links
anonymity
+ORC
students' essays
academy database
bots wars
antismut
tools
cocktails
javascript wars
search_forms
mail_fravia
Is reverse engineering illegal?