Introduction to Reverse Engineering Win32 Applications trew trew@exploit.us 1) Foreword Abstract: During the course of this paper the reader will be (re)introduced to many concepts and tools essential to understanding and controlling native Win32 applications through the eyes of Windows Debugger (WinDBG). Throughout, WinMine will be utilized as a vehicle to deliver and demonstrate the functionality provided by WinDBG and how this functionality can be harnessed to aid the reader in reverse engineering native Win32 applications. Topics covered include an introductory look at IA-32 assembly, register significance, memory protection, stack usage, various WinDBG commands, call stacks, endianness, and portions of the Windows API. Knowledge gleaned will be used to develop an application designed to reveal and/or remove bombs from the WinMine playing grid. Thanks: The author would like to thank thief, skape, arachne, H D Moore, h1kari, Peter, warlord, west, and everyone else that participated in the initial release of the Uninformed Journal. 2) Introduction Games can often times be very frustrating. This frustration stems from the inherent fact that games, by design, present many unknowns to the player. For example, how many monsters are lurking behind door number three, and are these eight clips of 90 50 caliber rounds going to be enough to kill this guy? Ten lives and a broken keyboard later, acquiring the ability to not only level the playing field, but get even, grows extremely attractive, at any cost. Some people risk reputational and karma damage to acquire that edge -- by cheating. Many develop cheats for this very reason, to obtain an unfair advantage. Others, however, have an entirely different motivation -- the challenge it involves. Motivations aside, the purpose of this document is to familiarize the reader with basic methodologies and tools available that aid in the practice of reverse engineering native Windows applications. Throughout, the reader will be introduced to WinDBG, IA-32 assembler, and portions of the Windows API. These concepts will be demonstrated by example, via a step by step navigation through the portions of WinMine that are pivotal in obtaining the coveted unfair advantage. 3) Getting Started Although this document is designed to speak at an introductory level, it is expected that the reader satisfies the following prerequisites: 1. Understanding of hexadecimal number system 2. The ability to develop basic C applications 3. The ability to install and properly configure WinDBG 4. Access to a computer running Windows XP with WinMine installed The following are suggested materials to have available while reading this document: 1. IA-32 Instruction Set Reference A-M [7] 2. IA-32 Instruction Set Reference N-Z [7] 3. IA-32 Volume 1 - Basic Architecture [7] 4. Microsoft Platform SDK [4] 5. Debugger Quick Reference [8] First, WinDBG and the Symbol Packages http://msdl.microsoft.com/download/symbols/packages/windowsxp/WindowsXP-KB835935-SP2-slp-Symbols.exe need to be properly installed and configured. WinDBG is part of The Debugging Tools Windows http://msdl.microsoft.com/download/symbols/debuggers/dbg_x86_6.4.7.2.exe package. While these download, the time will be passed by identifing potential goals, articulating what a debugger is, what abilities they provide, what symbols are, and how they are useful when debugging applications. 3.1) Identifying Goals The basic strategy behind WinMine is to identify the location of bombs within a given grid and clear all unmined blocks in the shortest duration of time. The player may track identified bomb locations by placing flags or question marks upon suspect blocks. With this in mind, one can derive the following possible goals: 1. Control or modify time information 2. Verify the accuracy of flag and question mark placement 3. Identify the location of bombs 4. Remove bombs from the playing grid In order to achieve these goals, the reader must first determine the following: 1. The location of the playing grid within the WinMine process 2. How to interpret the playing grid 3. The location of the clock within the WinMine process For the scope of this paper, the focus will be on locating, interpreting, and revealing and/or removing bombs from the playing grid. 3.2) Symbols and Debuggers A debugger is a tool or set of tools that attach to a process in order to control, modify, or examine portions of that process. More specifically, a debugger provides the reader with the ability to modify execution flow, read or write process memory, and alter register values. For these reasons, a debugger is essential for understanding how an application works so that it can be manipulated in the reader's favor. Typically, when an application is compiled for release, it does not contain debugging information, which may include source information and the names of functions and variables. In the absence of this information, understanding the application while reverse engineering becomes more difficult. This is where symbols come in, as they provide the debugger, amongst other things, the previously unavailable names of functions and variables. For more information on symbols, the reader is encouraged to read the related documents in the reference section.[3] 3.3) Symbol Server Hopefully by now both the Debugging Tools for Windows and the Symbols Packages have finished downloading. Install them in either order but take note of the directory the symbols are installed to. Once both are installed, begin by executing WinDBG, which can be found under Debugging Tools for Windows, beneath Programs, within the Start Menu. Once WinDBG is running click on File, Symbol File Path, and type in the following: SRV**http://msdl.microsoft.com/download/symbols For example, if symbols were installed to: C:\Windows\Symbols then the reader should enter: SRV*C:\WINDOWS\Symbols*http://msdl.microsoft.com/download/symbols This configuration tells WinDBG where to find the previously installed symbols, and if needed symbols are unavailable, where to get them from -- the Symbol Server. For more information on Symbol Server, the reader is encouraged to read the information in the reference section.[2] 4) Getting Familiar with WinDBG Whether the reader points and clicks their way through applications or uses shortcut keys, the WinDBG toolbar will briefly act as a guide for discussing some basic debugging terminology that will be used throughout this document. From left to right, the following options are available: 1. Open Source Code Open associated source code for the debugging session. 2. Cut Move highlighted text to the clipboard 3. Copy Copy highlighted text to the clipboard 4. Go Execute the debugee 5. Restart Restart the debugee process. This will cause the debugee to terminate. 6. Stop Debugging Terminate the debugging session. This will cause the debugee to terminate. 7. Break Pause the currently running debugee process The next four options are used after the debugger has been told to break. The debugger can be issued a break via the previous option, or the user may specify breakpoints. Breakpoints can be assigned to a variety of conditions. Most common are when the processor executes instructions at a specific address, or when certain areas of memory have been accessed. Implementing breakpoints will be discussed in more detail later in this document. Once a breakpoint has been reached, the process of executing individual instructions or function calls is referred to as stepping through the process. WinDBG has a handful of methods for stepping, four of which will be immediately discussed. 1. Step Into Execute a single instruction. When a function is called, this will cause the debugger to step into that function and break, instead of executing the function in its entirety. 2. Step Over Execute one or many instructions. When a function is called, this will cause the debugger to execute the called function and break after it has returned. 3. Step Out Execute one or many instructions. Causes the debugger to execute instructions until it has returned from the current function. 4. Run to Cursor Execute one or many instructions. Causes the debugger to execute instructions until it has reached the addresses highlighted by the cursor. Next, is Modify Breakpoints which allows the reader to add or modify breakpoints. The remainder of the toolbar options is used to make visible and customize various windows within WinDBG. 4.1) WinDBG Windows WinDBG provides a variety of windows, which are listed beneath the View toolbar option, that provide the reader with a variety of information. Of these windows, we will be utilizing Registers, Disassembly, and Command. The information contained within these three windows is fairly self describing. The Registers window contains a list of all processor registers and their associated values. Note, as register values change during execution the color of this value will turn red as a notification to the reader. For the purpose of this document, we will briefly elaborate on only the following registers: eip, ebp, esp, eax, ebx, ecx, edx, esi, and edi. eip: Contains the address of the next instruction to be executed ebp: Contains the address of the current stack frame esp: Contains the address of the top of the stack. This will be discussed in greater detail further in the document. The remaining listed registers are for general use. How each of these registers are utilized is dependant on the specific instruction. For specific register usage on a per instruction basis, the reader is encouraged to reference the IA-32 Command References [7]. The Disassembly window will contain the assembly instructions residing at a given address, defaulting at the value stored within the eip register. The Command window will contain the results of requests made of the debugger. Note, at the bottom of the Command window is a text box. This is where the user issues commands to the debugger. Additionally, to the left of this box is another box. When this box is blank the debugger is either detached from a process, processing a request, or the debugee is running. When debugging a single local process in user-mode, this box will contain a prompt that resembles "0:001>". For more information on interpreting this prompt, the reader is encouraged to read the related documentation in the reference section [9]. There exists three classes of commands that we can issue in the Command window; regular, meta, and extension. Regular commands are those commands designed to allow the reader to interface with the debugee. Meta commands are those commands prefaced with a period (.) and are designed to configure or query the debugger itself. Extension commands are those commands prefaced with an exclamation point (!) and are designed to invoke WinDBG plug-ins. 5) Locating the WinMine Playing Grid Let's begin by firing up WinMine, via Start Menu -> Run -> WinMine. Ensure WinMine has the following preferences set: Level: Custom Height: 900 Width: 800 Mines: 300 Marks: Enabled Color: Enabled Sound: Disabled Once this is complete, compile and execute the supplemental SetGrid application[12] found in the reference section. This will ensure that the reader's playing grid mirrors the grid utilized during the writing of this paper. Switch over to WinDBG and press F6. This will provide the reader with a list of processes. Select winmine.exe and press Enter. This will attach WinDBG to the WinMine process. The reader will immediately notice the Command, Registers, and Disassembly windows now contain values. 5.1) Loaded Modules If the reader directs attention to the Command window it is noticed that a series of modules are loaded and the WinMine process has been issued a break. ModLoad: 01000000 01020000 C:\WINDOWS\System32\winmine.exe ModLoad: 77f50000 77ff7000 C:\WINDOWS\System32\ntdll.dll ModLoad: 77e60000 77f46000 C:\WINDOWS\system32\kernel32.dll ModLoad: 77c10000 77c63000 C:\WINDOWS\system32\msvcrt.dll ... ModLoad: 77c00000 77c07000 C:\WINDOWS\system32\VERSION.dll ModLoad: 77120000 771ab000 C:\WINDOWS\system32\OLEAUT32.DLL ModLoad: 771b0000 772d4000 C:\WINDOWS\system32\OLE32.DLL (9b0.a2c): Break instruction exception - code 80000003 (first chance) eax=7ffdf000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 eip=77f75a58 esp=00cfffcc ebp=00cffff4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246 ntdll!DbgBreakPoint: 77f75a58 cc int 3 The two 32-bit addresses following "ModLoad:" represent the virtual memory address range the corresponding module is mapped to. These loaded modules contain functionality that WinMine is dependant upon. To get a list of loaded modules, the reader may issue either of the following commands: lm, !lm, !dlls The reader should also notice that WinDBG, by default, articulates register values within the Command window upon reaching a breakpoint or at the completion of each step. 5.2) Loaded Symbols Time was spent to download and install the Symbols Packages, so let's see what hints they provide. Issue the following within the Command window get a list of all available symbols for WinMine. x WinMine!* The e(x)amine command interprets everything to the left of the exclamation point as a regular expression mask for the module name, and everything to the right as a regular expression mask for the symbol name. For more information on regular expression syntax, the reader is encouraged to read the related documents in the reference section [10]. A list of symbols will scroll within the Command window. ... 01003df6 winmine!GetDlgInt = 010026a7 winmine!DrawGrid = 0100263c winmine!CleanUp = 01005b30 winmine!hInst = 01003940 winmine!Rnd = 01001b81 winmine!DoEnterName = ... From this listing, it is not possible to positively ascertain which symbols represent functions or variables. This is due, as WinDBG has pointed out, to the absence of type information. This is typical of public symbol files. Thankfully, methodologies exist that allow the reader to, at a minimum, distinguish functions from non-functions. Assuming the reader is not well versed reading assemblies, methods requiring that skill set will be for a short time avoided. An alternative technique, examining virtual memory protections, will be investigated that is relatively easy to comprehend and apply, 5.3) Memory Protection Thus far, discussions related to application memory have been sufficiently neglected, until now. This is not to say the interworkings of Windows memory management are about to be revealed, vice, a fairly pigeon holed approach will be taken for the sake of brevity and to satisfy our immediate utilitarian needs. When an application requests memory, a region is allocated provided the requested amount is available. If the allocation is successful, this region of memory can, amongst other things, be protected. More specifically, the region has psuedo access control lists applied to it that deny or permit certain access types. A couple examples of these access types are the ability to read information from, write information to, and execute instructions at, the given region. It is these access types that will provide the ability to quickly determine with relatively high probability whether a symbol is a function or non-function. By virtue of being a function, these memory regions allow execution. Conversely, memory regions allocated for classic variables do not allow instruction execution. All memory pages on the IA-32 architecture are executable at the hardware level despite memory protections.. Conveniently, WinDBG is shipped with an extension that allows the user the retrieve memory protection attributes for a given address. This extension command is !vprot. Let's select aptly named symbols to demonstrate this functionality. Type the following in the Command window: !vprot WinMine!ShowBombs ShowBombs was chosen as the name implies (to me) that it's a function. Let's see what !vprot says: BaseAddress: 01002000 AllocationBase: 01000000 AllocationProtect: 00000080 PAGE_EXECUTE_WRITECOPY RegionSize: 00003000 State: 00001000 MEM_COMMIT Protect: 00000020 PAGE_EXECUTE_READ Type: 01000000 MEM_IMAGE At first glance this might appear contradictory. However, the AllocationProtect field denotes the default protection for the entire memory region. The Protect field speaks to the current protections on the specific region specified in the first argument. This, as one would expect, is set to execute and read as denoted by PAGE_EXECUTE_READ. Next, look at the memory protection for a region allocated for a suspected variable, such as WinMine!szClass. !vprot WinMine!szClass The expectation is !vprot will return page protection that only allows read and write access to this region. BaseAddress: 01005000 AllocationBase: 01000000 AllocationProtect: 00000080 PAGE_EXECUTE_WRITECOPY RegionSize: 00001000 State: 00001000 MEM_COMMIT Protect: 00000004 PAGE_READWRITE Type: 01000000 MEM_IMAGE So be it. Considering the naming convention (sz preface), which implies a string type, one could easily validate the assumption by examining the data at this memory location. To do this, the display memory command can be utilized. Type the following in the Command window: du WinMine!szClass The 'u' modifier tells the (d)isplay memory command to interpret the string as Unicode. The results of this are: 01005aa0 "Minesweeper" I'm convinced. 5.4) Understanding Assemblies The goal for this chapter is to simply locate where the playing grid resides. With that in mind, revisit the previously identified ShowBombs function. Logically, it wouldn't be that long of a jump to assume this function will lead to the playing grid. Set a breakpoint on this function by issuing the following command: bp WinMine!ShowBombs WinDBG provides no positive feedback that the breakpoint was successfully set. However, WinDBG will alert the user if it is unable to resolve the name or address being requested. To obtain a list of set breakpoints, issue the following command: bl The Command window should reflect: 0 e 01002f80 0001 (0001) 0:*** WinMine!ShowBombs The leading digit represents the breakpoint number, which can be used as a reference for other breakpoint aware commands. The next field depicts the status of the breakpoint, as either (e)nabled or (d)isabled. This is followed by the virtual address of the breakpoint. The next four digits speak to the number of passes remaining until this breakpoint will activate. Adjacent, in parentheses, is the initial pass count. Next, is the process number, not to be confused with process ID, a colon acting as a separator between what would be a thread ID, if this was a thread specific breakpoint. It's not, hence three asterisks. Lastly, at least in the above example, is the module name and symbol/function where WinDBG will break at. Set WinMine in motion by hitting F5 (Go) or type 'g' in the Command window. The reader should notice that WinDBG informs the user the "Debugee is running". Switch to the WinMine window and click on the upper left box, which should reveal the number two. Next, click the box to the right and it will be very apparent that a bomb has been selected, as the reader will no longer be able to interact with WinMine. This is due to the fact that WinDBG recognized that a breakpoint condition has been met and is waiting for instruction. When back in the WinDBG, the Command window has highlighted the following instruction: 01002f80 a138530001 mov eax,[WinMine!yBoxMac (01005338)] This is the first instruction within the ShowBombs function, which corresponds to the address previously identified when current breakpoints were listed. Before attempting to understand this instruction, let's first cover a few functional and syntactical aspects of IA-32 assembly. It is recommended that the reader make available the aforementioned supplemental material mentioned in Chapter 2. Each line in the disassembly describes an instruction. Use the above instruction to identify the major components of an instruction without getting distracted by how this instruction relates to the ShowBombs function. If distilled, the previous instruction can be abstracted and represented as:
, The
represents the virtual location of the . Opcodes are literal instructions that the processor interprets to perform work. Everything to the right of represents a translation of these opcodes into assembly language. The can be thought of as a verb or function that treats each operand as an argument. It's of importance to note that in Intel Opposed to ATT style, which is utilized by GCC style assembly, these operations move from right to left. That is, when performing arithmetic or moving data around, the result typically finds its destination at . Looking back at the original instruction one can determine that the 32-bit value, or word, located at 0x01005338 is being copied into the eax register. Brackets ([]) are used to deference the address contained in an operand, much like an asterisk (*) does in C. Let's focus on the opcodes for a moment. If the reader looks up the opcode for a mov instruction into the eax register, the value 0xa1 will be found. Opcode Instruction Description ... A0 MOV AL,moffs8* Move byte at (seg:offset) to AL. A1 MOV AX,moffs16* Move word at (seg:offset) to AX. A1 MOV EAX,moffs32* Move doubleword at (seg:offset) to EAX. ... It is not by coincidence that the first byte of is also 0xa1. This leaves for the remainder, which brings us to the short discussion in endianness. 5.5) Endianness Endianness refers to the order by which any multi-byte data is stored. There exists two commonly referred conventions: little and big. Little endian systems, which includes the IA-32 architecture, store data starting with the least significant byte, through the most significant byte. Big endian systems do the opposite, storing the most significant byte first. For example, the value 0x11223344 would be stored as 0x44332211 on a little endian system, and 0x11223344 on a big endian system. Notice the value in is 0x01005338 and the remainder of is 0x38530001. If is rewritten and expressed in little endian order one can see these values are equal. 0x01005338, rewritten for clarity: 0x01 0x00 0x53 0x38 | | | | | | +----|-+ | +--------|-|-+ +--------------|-|-|-+ V V V V 0x38530001 With this information, one can see exactly how the processor is instructed to move the value stored at 0x01005338 into the eax register. For more information on endianness, the reader is encouraged to read the related documents in the reference section [5]. 5.6) Conditions Let's see if this new information can be applied to aid in reaching the goal of locating the playing grid. Start by hitting F10, or by typing 'p' in the Command window, to execute the current instruction and break. There are a couple of things to notice. First, the previously magenta colored bar that highlighted the examined instruction from above is now red and the instruction just below this is now highlighted blue. WinDBG, by default, denotes instructions that satisfy a breakpoint condition with a red highlight and the current instruction with a blue highlight. Additionally, a handful of values in the Registers window have been highlighted in red. Remember from Chapter 4 that this signifies an updated register value. As one would expect, the eax register has been updated, but what does its new value represent? 0x18, which now resides in eax, can be expressed as 24 in decimal. Note that our playing grid, even though previously specified at 800x900, was rendered at 30x24. Coincidence? This can be validated by restarting WinMine with varying grid sizes, but for the sake of brevity let the following statement evaluate as true: winmine!yBoxMac == Height of Playing Grid The following instructions: 01002f85 83f801 cmp eax,0x1 01002f88 7c4 jl winmine!ShowBombs+0x58 (01002fd8) compare this value, the maximum height, to the literal numeric 0x1. If the reader visits the description of the cmp instruction in the reference material it can be determined that this command sets bits within EFLAGS[6] based on the result of the comparison. Logically, the next instruction is a conditional jump. More specifically, this instruction will jump to the address 0x01002fd8 if eax is "Less, Neither greater nor equal" than 0x1. One can come to this conclusion by first recognizing that any mnemonic starting with the letter 'j' and is not jmp is a conditional jump. The condition by which to perform the jump is represented by the following letter or letters. In this case an 'l', which signifies "Jump short if less" per the definition of this instruction found in the instruction reference and the previously mentioned EFLAGS definition. This series of instructions can be expressed in more common terms of: if(iGridHeight < 1) { //jmp winmine!ShowBombs+0x58 } Translating assembly into pseudo code or C may be helpful when attempting to understand large or complex functions. One can make the prediction that the conditional jump will fail, as eax is currently valued at 0x18. But, for the mere academics of it, one can determine what would happen by typing the following in the Command window: u 0x1002fd8 This will show the reader the instructions that would be executed should the condition be met. 5.7) Stacks and Frames The 'u' command instructs WinDBG to (u)nassemble, or translate from opcodes to mnemonics with operands, the information found at the specified address. 01002fd8 e851f7ffff call WinMine!DisplayGrid (0100272e) 01002fdd c20400 ret 0x4 From this, one can see that the DisplayGrid function is called and the ShowBombs function subsequently returns to the caller. But what is call actually doing? Can one tell where ret is really returning to and what does the 0x4 represent? The IA-32 Command Reference states that call "Saves procedure linking information on the stack and branches to the procedure (called procedure) specified with the destination (target) operand. The target operand specifies the address of the first instruction in the called procedure." The reader may notice that the command reference has variable behaviors for the call instruction depending on the type of call being made. To further identify what call is doing, the reader can examine the opcodes, as previously discussed, and find 0xe8. 0xe8, represents a near call. "When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) onto the stack (for use later as a return-instruction pointer)." This is the first step in building a frame. Each time a function call is made, another frame is created so that the called function can access arguments, create local variables, and provide a mechanism to return to calling function. The composition of the frame is dependant on the function calling convention. For more information on calling conventions, the reader is encouraged to read the relevant documents in the reference section[1]. To view the current call stack, or series of linked frames, use the 'k' command. ChildEBP RetAddr 0006fd34 010034b0 winmine!ShowBombs 0006fd40 010035b0 winmine!GameOver+0x34 0006fd58 010038b6 winmine!StepSquare+0x9e 0006fd84 77d43b1f winmine!DoButton1Up+0xd5 0006fdb8 77d43a50 USER32!UserCallWinProcCheckWow+0x150 0006fde4 77d43b1f USER32!InternalCallWinProc+0x1b 0006fe4c 77d43d79 USER32!UserCallWinProcCheckWow+0x150 0006feac 77d43ddf USER32!DispatchMessageWorker+0x306 0006feb8 010023a4 USER32!DispatchMessageW+0xb 0006ff1c 01003f95 winmine!WinMain+0x1b4 0006ffc0 77e814c7 winmine!WinMainCRTStartup+0x174 0006fff0 00000000 kernel32!BaseProcessStart+0x23 With this, the reader can track the application flow in reverse order. The reader may find it easier to navigate the call stack by pressing Alt-6, which will bring up the Call Stack window. Here, the call stack information is digested a bit more and displayed in a tabular manner. With the call stack information, one can answer the second question regarding where ret is really headed. For example, once ShowBombs returns, eip will be set to 0x010034b0. Finally, the significance of 0x4 can be learned by reading the ret instruction definition, which states that this value represents "...the number of stack bytes to be released after the return address is popped; the default is none. This operand can be used to release parameters from the stack that were passed to the called procedure and are no longer needed." More specifically, when the processor encounters a ret instruction it pops the address stored at the top of the stack, where esp is pointing, and places that value in the eip register. If the ret instruction has an operand, that operand represents how many additional bytes should be removed from the top of the stack. With this information, and knowing that 32-bit addresses are four bytes long, one can determine that the ShowBombs function accepts one argument. This is dependant upon calling convetion, which will be discussed later in this document. Returning to the task at hand, the following represents the current picture of what the ShowBombs function is doing: if(iGridHeight < 1) { DisplayGrid(); return; } else { //do stuff } Continuing on, one can see the following in the Disassembly window, which will take the place of "//do stuff". 01002f8a 53 push ebx 01002f8b 56 push esi 01002f8c 8b3534530001 mov esi,[winmine!xBoxMac (01005334)] 01002f92 57 push edi 01002f93 bf60530001 mov edi,0x1005360 Begin by stepping WinDBG twice (by pressing 'p' twice) so that eip is set to 0x1002f8a. The next two instructions are storing the ebx and esi registers on the stack. This can be demonstrated by first viewing the memory referenced by esp, identifying the value stored in ebx, pressing 'p' to execute push ebx, and revisiting the value stored at esp. The reader will find the value of ebx stored at esp. 0:000> dd esp 0006fd38 010034b0 0000000a 00000002 010035b0 0006fd48 00000000 00000000 00000200 0006fdb8 ... 0:000> r ebx ebx=00000001 0:000> p eax=00000018 ebx=00000001 ecx=0006fd14 edx=7ffe0304 esi=00000000 edi=00000000 eip=01002f8b esp=0006fd34 ebp=0006fdb8 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000206 winmine!ShowBombs+0xb: 01002f8b 56 push esi 0:000> dd esp 0006fd34 00000001 010034b0 0000000a 00000002 0006fd44 010035b0 00000000 00000000 00000200 Notice, that esp has been decremented by four (the size of a 32-bit pointer) and the value of ebx is at that location. The behavior can be observed again by stepping to execute push esi. Again, the reader will notice the value of esp decrement by four and the value within esi is at this new location. This is the basic principal of how the stack works. The stack pointer is decremented and the value being push'd onto the stack is placed at the new address esp is pointing at. It is also important to note that the stack grows down. That is, as values are placed on the stack, the stack pointer decreases to make room. Which begs the question, what are the upper and lower limits of the stack? It can't keep on growing for ever, can it? The short answer is no, the stack has a floor and ceiling. Which can be identified by examining the Thread Environment Block or TEB. Luckily, WinDBG comes with an extension command to accomplish this, !teb. 0:000> !teb TEB at 7ffde000 ExceptionList: 0006fe3c StackBase: 00070000 StackLimit: 0006c000 SubSystemTib: 00000000 FiberData: 00001e00 ArbitraryUserPointer: 00000000 Self: 7ffde000 EnvironmentPointer: 00000000 ClientId: 00000ff4 . 00000ff8 RpcHandle: 00000000 Tls Storage: 00000000 PEB Address: 7ffdf000 LastErrorValue: 183 LastStatusValue: c0000008 Count Owned Locks: 0 HardErrorMode: 0 Note the values for StackBase and StackLimit, which refer to the stack's ceiling and floor, respectively. For more information on the TEB, the reader is encouraged to read the related documents in the reference section [11]. That was an exciting tangent. Circling back, the reader is found at the following instruction: 01002f8c 8b3534530001 mov esi,[winmine!xBoxMac (01005334)] This, if convention holds true, will store the width of the playing grid in esi. By single stepping ('p'), the reader will notice the esi register is denoted in red within the Registers window and now contains the value 0x1e. 0x1e is 30 in decimal, which, if the reader recalls, is the width of the current playing grid. Hence, one can make the educated determination that xBoxMac represents the width of the playing grid. The next instruction, push edi, is saving the value in the edi register on the stack in preparation for the subsequent instruction; mov edi,0x1005360. This is were things get a bit more interesting, as this instruction begs the question; what is the significance of 0x1005360? Considering the previous instructions gathered prerequisite information about the playing grid, perhaps this address is indeed the playing grid itself! To determine this, the reader should examine some aspects of this memory address. The aforementioned !vprot extension command will provide information regarding the type of access permitted to this memory address, which is PAGEREADWRITE. This information isn't overly valuable but is favorable in the sense that this address does not reside within an executable portion of the application space and is therefore likely a variable allocation. If this area is truly the playing grid one should be able to identify a pattern while viewing the memory. To accomplish this, type 0x1005360 in to the Memory window. The following should appear: 01005360 10 42 cc 8f 8f 8f 8f 0f 8f 8f 8f 8f 0f 0f 8f 0f .B.............. 01005370 0f 8f 8f 8f 8f 8f 8f 0f 0f 0f 8f 0f 0f 8f 8f 10 ................ 01005380 10 8f 0f 0f 8f 8f 0f 0f 0f 0f 0f 8f 0f 0f 8f 8f ................ 01005390 0f 8f 0f 0f 0f 8f 8f 8f 0f 0f 8f 8f 8f 8f 8f 10 ................ 010053a0 10 0f 0f 8f 0f 0f 8f 0f 0f 0f 0f 0f 8f 0f 0f 0f ................ 010053b0 8f 0f 0f 0f 8f 8f 0f 0f 8f 0f 8f 0f 8f 8f 0f 10 ................ 010053c0 10 0f 0f 8f 0f 0f 8f 0f 0f 0f 8f 0f 0f 8f 0f 0f ................ 010053d0 8f 0f 0f 8f 0f 0f 0f 8f 0f 0f 0f 8f 0f 0f 0f 10 ................ 010053e0 10 0f 0f 8f 0f 8f 8f 0f 0f 8f 8f 0f 0f 8f 0f 0f ................ 010053f0 0f 0f 0f 0f 8f 0f 0f 0f 0f 0f 0f 0f 8f 0f 0f 10 ................ 01005400 10 8f 0f 0f 0f 0f 0f 0f 8f 8f 0f 8f 8f 0f 0f 8f ................ 01005410 0f 8f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 8f 8f 0f 10 ................ 01005420 10 8f 0f 8f 8f 0f 8f 8f 0f 0f 0f 8f 8f 0f 8f 0f ................ 6) Interpreting the Playing Grid The reader may make the immediate observation that this portion of memory is littered with a limited set of values. Most notably are 0x8f, 0x0f, 0x10, 0x42, 0xcc. Additionally, one may notice the following repeating pattern: 0x10 <30 bytes> 0x10. The number 30 may ring familiar to the reader, as it was encountered earlier when discovering the grid width. One may speculate that each pattern repetition represents a row of the playing grid. To aid in confirming this, switch to WinDBG and resume WinMine by pressing 'g' in the Command window. Switch to WinMine and mentally overlay the information in the Memory window with the playing grid. A correlation between these can be identified such that each bomb on the playing grid corresponds to 0x8f and each blank position on the playing grid corresponds to 0x0f. Furthermore, one may notice the blown bomb on the playing grid is represented by 0xcc and the number two is represented by 0x42. To confirm this is indeed the playing grid, it is essential to test the lower bound by performing simple arithmetic and exercising the same technique employed to identify the suspected beginning. The current hypothesis is that each aforementioned pattern represents a row on the playing grid. If this is true, one can multiply 32, the length of our pattern, by the number of rows in the playing grid, 24. The product of this computation is 768, or 0x300 in hexadecimal. This value can be added to the suspected beginning of the grid, which is located at 0x01005360, to derive an ending address of 0x01005660. Restart WinMine by clicking the yellow smiley face, rerun the SetGrid helper application, and click the bottom right square on the playing grid. Coincidentally, the number two will appear. Next, click on the position to the immediate left of the number two. This position contains a bomb and will trigger a breakpoint in WinDBG. Switch over to WinDBG and direct attention to the Memory window. Press 'Next' in the Memory window twice to bring this range into focus. 01005640 10 8f 0f 0f 0f 8f 0f 0f 8f 0f 8f 0f 0f 0f 0f 8f ................ 01005650 0f 8f 0f 0f 0f 8f 0f 0f 0f 0f 0f 0f 0f cc 42 10 ..............B. 01005660 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 ................ 01005670 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 ................ Following the same overlay as before, the reader will notice that the previous correlations can be made between the last row of the playing grid and the information located at 0x01006540, the start of the previously identified 32 byte pattern. Notice, again, each bomb is represented by 0x8f. With this information the reader can reasonably conclude that this is indeed the playing grid. 7) Removing Mines Before venturing into a programmatic method of instrumenting the playing grid the reader will first be introduced to tools provided by WinDBG, more specifically, the (e)nter values command. This command allows the reader to manipulate specific portions of virtual memory and can be utilized to, amongst other things, remove bombs from the WinMine playing grid. First, reset the grid by resuming the WinMine process in WinDBG, clicking on the yellow smiley face in WinMine, and running the SetGrid application. Next, click on the top left position to expose the two and break the WinMine process within WinDBG by pressing Control+Break. The reader should recall that the address 0x01005362, to the immediate right of the two, contains a bomb. To demonstrate the enter values command perform the following in the Command window. eb 0x01005362 0x0f Resume WinMine in WinDBG and click on the position to the right of the two. Notice, instead of a bomb being displayed, the number two is revealed. The reader could perform the tedious task of performing this function manually throughout the grid, or, one could develop an application to reveal and/or remove the bombs. 7.1) Virtual Mine Sweeper In this section, the reader will be introduced to portions of the Windows API that will allow one to develop an application that will perform the following: 1. Locate and attach to the WinMine process 2. Read the WinMine playing grid 3. Manipulate the grid to either reveal or remove hidden bombs 4. Write the newly modified grid back into WinMine application space To accomplish the first task, one can enlist the services of the Tool Help Library, which is exposed via Tlhelp32.h. A snapshot of running processes can be obtained by calling CreateToolhelp32Snapshot, which has the following prototype This information can be obtained by referencing the Platform SDK: HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID ); This function, when called with dwFlags set to TH32CSSNAPPROCESS will provide the reader with a handle to the current process list. To enumerate this list, the reader must first invoke the Process32First function, which has the following prototype: BOOL WINAPI Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe ); Subsequent iterations through the process list are accessible via the Process32Next function, which has the following prototype: BOOL WINAPI Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe ); As the reader surely noticed, both of these functions return a LPPROCESSENTRY32, which includes a variety of helpful information: typedef struct tagPROCESSENTRY32 { DWORD dwSize; DWORD cntUsage; DWORD th32ProcessID; ULONG_PTR th32DefaultHeapID; DWORD th32ModuleID; DWORD cntThreads; DWORD th32ParentProcessID; LONG pcPriClassBase; DWORD dwFlags; TCHAR szExeFile[MAX_PATH]; } PROCESSENTRY32, *PPROCESSENTRY32; Most notably of which is szExeFile, which will allow the reader to locate the WinMine process, and th32ProcessID, which provides the process ID to attach to once the WinMine process is found. Once the WinMine process is located, it can be attached to via the OpenProcess function, which has the following prototype: HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId ); Once the WinMine process has been opened, the reader may read the current playing grid from its virtual memory via the ReadProcessMemory function, which has the following prototype: BOOL ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead ); After the grid is read into the buffer, the reader may loop through it replacing all instances of 0x8f with either 0x8a to reveal bombs, or 0x0f to remove them. This modified buffer can then be written back into the WinMine process with the WriteProcessMemory function, which has the following prototype: BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten ); With this information, the reader has the tools necessary to develop an application that to reach the ultimate goal of this paper, to reveal and/or remove bombs from the WinMine playing grid. The source code for a functioning demonstration of this can be found in the reference section.[13] 8) Conclusion Throughout this document the reader has been exposed to portions of many concepts required to successfully locate, comprehend, and manipulate the WinMine playing grid. As such, many details surrounding these concepts were neglected for the sake of brevity. In order to obtain a more holistic view of the covered concepts, the reader is encouraged to read those items articulated in the reference section and seek out additional works. References 1. Calling Conventions http://www.unixwiz.net/techtips/win32-callconv-asm.html 2. Symbol Server http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx 3. Symbols ms-help://MS.PSDK.1033/debug/base/symbolfiles.htm 4. Platform SDK www.microsoft.com/msdownload/platformsdk/sdkupdate/ 5. Endianness http://www.intel.com/design/intarch/papers/endian.pdf 6. EFLAGS ftp://download.intel.com/design/Pentium4/manuals/25366514.pdf Appendix B 7. Intel Command References http://www.intel.com/design/pentium4/manuals/indexnew.htm 8. Debugger Quick Reference http://www.tonyschr.net/debugging.htm 9. WinDBG Prompt Reference WinDBG Help, Search, Command Window Prompt 10. Regular Expressions Reference WinDBG Help, Search, Regular Expression Syntax 11. TEB http://msdn.microsoft.com/library/en-us/dllproc/base/teb.asp 12. SetGrid.cpp /********************************************************************** * SetGrid.cpp - trew@exploit.us * * This is supplemental code intended to accompany 'Introduction to * Reverse Engineering Windows Applications' as part of the Uninformed * Journal. This application sets the reader's playing grid in a * deterministic manner so that demonstrations made within the paper * correlate with what the reader encounters in his or her instance of * WinMine. * *********************************************************************/ #include #include #include #pragma comment(lib, "advapi32.lib") #define GRID_ADDRESS 0x1005360 #define GRID_SIZE 0x300 int main(int argc, char *argv[]) { HANDLE hProcessSnap = NULL; HANDLE hWinMineProc = NULL; PROCESSENTRY32 peProcess = {0}; unsigned int procFound = 0; unsigned long bytesWritten = 0; unsigned char grid[] = "\x10\x0f\x8f\x8f\x8f\x8f\x8f\x0f\x8f\x8f\x8f\x8f\x0f\x0f\x8f\x0f" "\x0f\x8f\x8f\x8f\x8f\x8f\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x8f\x8f\x10" "\x10\x8f\x0f\x0f\x8f\x8f\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x0f\x8f\x8f" "\x0f\x8f\x0f\x0f\x0f\x8f\x8f\x8f\x0f\x0f\x8f\x8f\x8f\x8f\x8f\x10" "\x10\x0f\x0f\x8f\x0f\x0f\x8f\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x0f\x0f" "\x8f\x0f\x0f\x0f\x8f\x8f\x0f\x0f\x8f\x0f\x8f\x0f\x8f\x8f\x0f\x10" "\x10\x0f\x0f\x8f\x0f\x0f\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x8f\x0f\x0f" "\x8f\x0f\x0f\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x10" "\x10\x0f\x0f\x8f\x0f\x8f\x8f\x0f\x0f\x8f\x8f\x0f\x0f\x8f\x0f\x0f" "\x0f\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x0f\x10" "\x10\x8f\x0f\x0f\x0f\x0f\x0f\x0f\x8f\x8f\x0f\x8f\x8f\x0f\x0f\x8f" "\x0f\x8f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x8f\x8f\x0f\x10" "\x10\x8f\x0f\x8f\x8f\x0f\x8f\x8f\x0f\x0f\x0f\x8f\x8f\x0f\x8f\x0f" "\x0f\x0f\x0f\x8f\x0f\x8f\x0f\x8f\x0f\x0f\x8f\x8f\x0f\x8f\x0f\x10" "\x10\x8f\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x0f\x8f\x8f\x8f\x8f\x0f" "\x0f\x0f\x0f\x0f\x0f\x8f\x8f\x8f\x0f\x0f\x0f\x0f\x8f\x8f\x8f\x10" "\x10\x8f\x0f\x8f\x8f\x8f\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x8f\x0f\x0f" "\x8f\x8f\x0f\x0f\x0f\x8f\x0f\x8f\x0f\x8f\x0f\x0f\x0f\x0f\x0f\x10" "\x10\x0f\x0f\x8f\x8f\x0f\x8f\x8f\x8f\x8f\x0f\x0f\x0f\x0f\x0f\x0f" "\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x10" "\x10\x0f\x0f\x0f\x8f\x8f\x8f\x0f\x8f\x8f\x0f\x0f\x0f\x8f\x0f\x0f" "\x0f\x8f\x0f\x8f\x0f\x0f\x0f\x8f\x8f\x0f\x0f\x0f\x0f\x8f\x8f\x10" "\x10\x0f\x8f\x8f\x0f\x8f\x0f\x8f\x0f\x8f\x0f\x8f\x8f\x0f\x0f\x8f" "\x0f\x0f\x0f\x0f\x0f\x0f\x8f\x8f\x0f\x0f\x8f\x0f\x8f\x0f\x0f\x10" "\x10\x0f\x0f\x8f\x8f\x0f\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x8f\x0f" "\x8f\x0f\x8f\x8f\x8f\x0f\x0f\x8f\x0f\x8f\x0f\x8f\x8f\x8f\x8f\x10" "\x10\x8f\x8f\x0f\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x8f\x0f\x0f\x8f\x0f" "\x0f\x0f\x8f\x8f\x8f\x8f\x8f\x0f\x0f\x8f\x8f\x0f\x0f\x8f\x8f\x10" "\x10\x8f\x0f\x0f\x0f\x8f\x0f\x8f\x8f\x8f\x8f\x0f\x0f\x8f\x8f\x0f" "\x0f\x8f\x0f\x0f\x8f\x8f\x8f\x8f\x0f\x8f\x0f\x8f\x0f\x8f\x8f\x10" "\x10\x0f\x8f\x8f\x0f\x0f\x8f\x8f\x8f\x0f\x8f\x0f\x0f\x0f\x0f\x0f" "\x0f\x8f\x8f\x8f\x0f\x0f\x8f\x0f\x8f\x8f\x8f\x0f\x8f\x8f\x0f\x10" "\x10\x8f\x0f\x0f\x8f\x8f\x8f\x8f\x0f\x0f\x8f\x0f\x0f\x0f\x8f\x8f" "\x8f\x8f\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x8f\x8f\x0f\x0f\x8f\x0f\x10" "\x10\x0f\x8f\x8f\x0f\x0f\x0f\x0f\x8f\x0f\x8f\x0f\x8f\x0f\x0f\x0f" "\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x8f\x0f\x8f\x0f\x10" "\x10\x0f\x0f\x0f\x0f\x8f\x8f\x8f\x8f\x8f\x0f\x0f\x0f\x8f\x0f\x0f" "\x8f\x8f\x8f\x0f\x0f\x8f\x8f\x8f\x0f\x0f\x8f\x0f\x0f\x8f\x0f\x10" "\x10\x8f\x8f\x0f\x8f\x8f\x0f\x8f\x8f\x0f\x0f\x0f\x0f\x8f\x8f\x8f" "\x8f\x0f\x8f\x0f\x0f\x0f\x8f\x0f\x8f\x8f\x8f\x0f\x8f\x0f\x0f\x10" "\x10\x0f\x8f\x8f\x0f\x0f\x8f\x8f\x8f\x0f\x0f\x8f\x0f\x0f\x0f\x0f" "\x0f\x0f\x8f\x8f\x0f\x8f\x0f\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x8f\x10" "\x10\x0f\x0f\x8f\x0f\x8f\x0f\x8f\x8f\x0f\x0f\x0f\x0f\x0f\x0f\x0f" "\x0f\x8f\x0f\x0f\x0f\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x0f\x0f\x8f\x10" "\x10\x0f\x8f\x8f\x8f\x0f\x8f\x0f\x8f\x0f\x0f\x8f\x0f\x0f\x8f\x0f" "\x0f\x8f\x8f\x0f\x0f\x0f\x0f\x8f\x0f\x8f\x8f\x0f\x0f\x0f\x8f\x10" "\x10\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x8f\x0f\x8f\x0f\x0f\x0f\x0f\x8f" "\x0f\x8f\x0f\x0f\x0f\x8f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x8f\x8f\x10"; //Get a list of running processes hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(hProcessSnap == INVALID_HANDLE_VALUE) { printf("Unable to get process list (%d).\n", GetLastError()); return 0; } peProcess.dwSize = sizeof(PROCESSENTRY32); //Get first process in list if(Process32First(hProcessSnap, &peProcess)) { do { //Is it's winmine.exe? if(!stricmp(peProcess.szExeFile, "winmine.exe")) { printf("Found WinMine Process ID (%d)\n", peProcess.th32ProcessID); procFound = 1; //Get handle on winmine process hWinMineProc = OpenProcess(PROCESS_ALL_ACCESS, 1, peProcess.th32ProcessID); //Make sure the handle is valid if(hWinMineProc == NULL) { printf("Unable to open minesweep process (%d).\n", GetLastError()); return 0; } //Write grid if(WriteProcessMemory(hWinMineProc, (LPVOID)GRID_ADDRESS, (LPCVOID)grid, GRID_SIZE, &bytesWritten) == 0) { printf("Unable to write process memory (%d).\n", GetLastError()); return 0; } else { printf("Grid Update Successful\n"); } //Let go of minesweep CloseHandle(hWinMineProc); break; } //Get next process } while(Process32Next(hProcessSnap, &peProcess)); } if(!procFound) printf("WinMine Process Not Found\n"); return 0; } 13. MineSweeper.cpp /********************************************************************** * MineSweeper.cpp - trew@exploit.us * * This is supplemental code intended to accompany 'Introduction to * Reverse Engineering Windows Applications' as part of the Uninformed * Journal. This application reveals and/or removes mines from the * WinMine grid. Note, this code only works on the version of WinMine * shipped with WinXP, as the versions differ between releases of * Windows. * *********************************************************************/ #include #include #include #pragma comment(lib, "advapi32.lib") #define BOMB_HIDDEN 0x8f #define BOMB_REVEALED 0x8a #define BLANK 0x0f #define GRID_ADDRESS 0x1005360 #define GRID_SIZE 0x300 int main(int argc, char *argv[]) { HANDLE hProcessSnap = NULL; HANDLE hWinMineProc = NULL; PROCESSENTRY32 peProcess = {0}; unsigned char procFound = 0; unsigned long bytesWritten = 0; unsigned char *grid = 0; unsigned char replacement = BOMB_REVEALED; unsigned int x = 0; grid = (unsigned char *)malloc(GRID_SIZE); if(!grid) return 0; if(argc > 1) { if(stricmp(argv[1], "remove") == 0) { replacement = BLANK; } } //Get a list of running processes hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //Ensure the handle is valid if(hProcessSnap == INVALID_HANDLE_VALUE) { printf("Unable to get process list (%d).\n", GetLastError()); return 0; } peProcess.dwSize = sizeof(PROCESSENTRY32); //Get first process in list if(Process32First(hProcessSnap, &peProcess)) { do { //Is it's winmine.exe? if(!stricmp(peProcess.szExeFile, "winmine.exe")) { printf("Found WinMine Process ID (%d)\n", peProcess.th32ProcessID); procFound = 1; //Get handle on winmine process hWinMineProc = OpenProcess(PROCESS_ALL_ACCESS, 1, peProcess.th32ProcessID); //Make sure the handle is valid if(hWinMineProc == NULL) { printf("Unable to open minesweep process (%d).\n", GetLastError()); return 0; } //Read Grid if(ReadProcessMemory(hWinMineProc, (LPVOID)GRID_ADDRESS, (LPVOID)grid, GRID_SIZE, &bytesWritten) == 0) { printf("Unable to read process memory (%d).\n", GetLastError()); return 0; } else { //Modify Grid for(x=0;x<=GRID_SIZE;x++) { if((*(grid + x) & 0xff) == BOMB_HIDDEN) { *(grid + x) = replacement; } } } //Write grid if(WriteProcessMemory(hWinMineProc, (LPVOID)GRID_ADDRESS, (LPCVOID)grid, GRID_SIZE, &bytesWritten) == 0) { printf("Unable to write process memory (%d).\n", GetLastError()); return 0; } else { printf("Grid Update Successful\n"); } //Let go of minesweep CloseHandle(hWinMineProc); break; } //Get next process } while(Process32Next(hProcessSnap, &peProcess)); } if(!procFound) printf("WinMine Process Not Found\n"); return 0; }