Copy Link
Add to Bookmark
Report
uninformed 01 01
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*<path to symbols>*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 = <no type information>
010026a7 winmine!DrawGrid = <no type information>
0100263c winmine!CleanUp = <no type information>
01005b30 winmine!hInst = <no type information>
01003940 winmine!Rnd = <no type information>
01001b81 winmine!DoEnterName = <no type information>
...
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:
<address> <opcodes> <mnemonic> <operand1>, <operand2>
The <address> represents the virtual location of the <opcodes>.
Opcodes are literal instructions that the processor interprets to perform work.
Everything to the right of <opcodes> represents a translation of these
opcodes into assembly language. The <mnemonic> 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 <operand1>.
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 <opcodes> is also 0xa1.
This leaves <operand2> 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 <operand2> is 0x01005338 and the remainder of
<opcodes> is 0x38530001. If <operand2> 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 <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#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 <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#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;
}