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 Fravias 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 Fravias 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

Theory and practice of menus reversing



Written by +Spath.

Introduction

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

Tools required

Target's URL/FTP

Program History

Essay

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 Fravias' 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.

Final Notes

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.

Ob Duh

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:



