Introduction
Yep, again. For those of you who liked my first article "Minesweeper, Behind the scenes" I present here
his natural sequel. The idea came when a friend of mine read the former article
and joked me about it, "now do hearts.." he said, so I did and this article is
the final result. At this point I would like to thank some friends who helped
me, Itay Langer, Michael Kuperstein and Yoav Sion (in alphabetical order).
Two weeks ago, when I wrote the first article, I've explained the use of API
functions with C# (using P/Invoke) to read another process memory. Also I've
written an example for using this class library in order to read the Minesweeper
mines map. In that article I've claimed and I still claim that the goal of the
article was not how to hack Minesweeper. The goal of that article was to provide
the reader a simple class library in C# that will allow them to read another
process memory. To my surprise, many people who read the article was very
interested in the example I've brought. Because of that I cant really claim that
the goal of this article is to learn how to use some new API functions in order
to have functionality unsupported by C#. The goal of this article is to see what
is happening behind the scenes of the two Microsoft card games: FreeCell and
Hearts. In spite of this goal my conscience cannot let me publish an article
with no new coding feature, So I've enhanced the library and now it supports
also writing into another process memory. I've even done a use of this function
in one of the examples.
Main Goals
- FreeCell behind the scenes and developing a cool app that uses this
knowledge.
- Hearts behind the scenes and developing a very cool app that uses this
knowledge.
- Write to another process memory using the
WriteProcessMemory
API function.
Note: The first two sections are using a debugger to investigate the games,
my personal favorite debugger is Olly Debugger v1.08
but any debugger will do.
Section 1: FreeCell, How does it work?
I imagine you ask yourself what is to crack in FreeCell? you already see all
the cards! Well the point is to understand what it does and how it gets these
cards. Also suppose you can read the cards, you can write a program that plays
Freecell by herself.. but you must have a way to read the freecell memory.
The first step, like always, is opening the file in the debugger. In this
case we need the freecell.exe file which is found in the windows\system32
folder. After opening the file we need to find an interesting starting point.
When you look at the beginning of the file you see a list of functions imported
from dll's as follows:
01001188 DD USER32.GetDlgItemInt
0100118C DD USER32.SetWindowTextW
01001190 DD USER32.wsprintfW
01001194 DD USER32.GetSysColor
01001198 DD USER32.GetWindowDC
0100119C DD USER32.IsIconic
010011A0 DD 00000000
010011A4 DD msvcrt._except_handler3
010011A8 DD msvcrt._controlfp
010011AC DD msvcrt.__set_app_type
010011B0 DD msvcrt.__p__fmode
010011B4 DD msvcrt.__p__commode
010011B8 DD OFFSET msvcrt._adjust_fdiv
010011BC DD msvcrt.__setusermatherr
010011C0 DD msvcrt._initterm
010011C4 DD msvcrt.__getmainargs
010011C8 DD OFFSET msvcrt._acmdln
010011CC DD msvcrt.exit
010011D0 DD msvcrt._cexit
010011D4 DD msvcrt._XcptFilter
010011D8 DD msvcrt._exit
010011DC DD msvcrt._c_exit
010011E0 DD msvcrt.isdigit
010011E4 DD msvcrt.time
010011E8 DD msvcrt.srand
010011EC DD msvcrt.rand
This is only the final part of the list, but If you look carefully you see
that the last imported function listed is the function rand from the msvcrt dll,
which is off course the randomize function from the microsoft visual c runtime
dll. So I've decided to search where this function is being used.
I've found two places where this function is being used. Then I put a
breakpoint on both places and run the program. After I've started a new game the
program stopped on the first breakpoint once and 52 times on the second
breakpoint. Here is the part where the brain needs to kick in, mine said that
the first breakpoint randomize the game number and the second breakpoint
randomize the position of each card (or the card of each position) (remember you
got 52 cards..).
So, I did a little test to prove me theory. I ran the program till the first
breakpoint, then step over the rand function and look at the eax register, the
value of this register was 0x00006A26, then I continue to run the program, the
game number (displayed in the caption) was 25254.. bingo, 0x6A26 = 25254. So now
I knew I was right I needed to find where freecell stores this value and the
cards values, so I could read them later with my program. I started a new game
and now I've investigated the second breakpoint. A few instructions before the
breakpoint I've found a very interesting piss of code. The code I've found store
a value in a specific place in the memory, and then formatted the string
"FreeCell Game #%d" with this same number, so THIS is where it stores the game
number.. The code is presented here:
010031D0 MOV DWORD PTR DS:[100834C],EAX
010031D5 PUSH ESI
010031D6 PUSH EDI
010031D7 PUSH 80
010031DC MOV ESI,freecell.01007880
010031E1 PUSH ESI
010031E2 PUSH 12F
010031E7 PUSH DWORD PTR DS:[1007860]
010031ED CALL DWORD PTR DS:[<&USER32.LoadStringW>>
010031F3 PUSH DWORD PTR DS:[100834C]
010031F9 PUSH ESI
010031FA MOV ESI,freecell.01007820
010031FF PUSH ESI
01003200 CALL DWORD PTR DS:[<&USER32.wsprintfW>]
01003206 ADD ESP,0C
01003209 PUSH ESI
0100320A PUSH DWORD PTR SS:[EBP+78]
0100320D CALL DWORD PTR DS:[<&USER32.SetWindowTex>
As you can see, the first line store the eax register value (which is the
randomized number from the first breakpoint) in the specific memory address
0x0100834C. By the way, there is no way you can tell from the code what the
value of eax is, But when you actually use the debugger, at runtime, you can see
this is the same value you got from the first rand function.
Back to the second breakpoint. As I said earlier the second breakpoint is
being heat 52 times which is exactly the number of cards we have. So what's now
interest me is where this code stores the randomize cards. So lets look at the
relevant code:
010032D3 PUSH EAX
010032D4 CALL DWORD PTR DS:[<&msvcrt.srand>]
010032DA POP ECX
010032DB XOR ESI,ESI
010032DD /CALL DWORD PTR DS:[<&msvcrt.rand>]
010032E3 |XOR EDX,EDX
010032E5 |DIV EBX
010032E7 |MOV ECX,ESI
010032E9 |AND ECX,7
010032EC |IMUL ECX,ECX,15
010032EF |DEC EBX
010032F0 |LEA EAX,DWORD PTR SS:[EBP+EDX*4-60]
010032F4 |MOV EDX,ESI
010032F6 |SHR EDX,3
010032F9 |ADD ECX,EDX
010032FB |MOV EDX,DWORD PTR DS:[EAX]
010032FD |MOV DWORD PTR DS:[ECX*4+1007554],EDX
01003304 |MOV ECX,DWORD PTR SS:[EBP+EBX*4-60]
01003308 |INC ESI
01003309 |CMP ESI,34
0100330C |MOV DWORD PTR DS:[EAX],ECX
0100330E \JB SHORT freecell.010032DD
So, what we see in this code is calling the srand function with the seed as
the game number randomize earlier. this is an important part to note. freecell
has game numbers, and you can select a specific game, so how is that possible
that the game is randomized but if you select over and over the same game
number, you get the same cards? well, the answer lies in the first two lines of
code, the "randomized" cards are randomized with the game number as a seed
number to the randomize function. If the rand function is initialized with the
same seed in the srand function, the randomized number it will creates will be
the same.
Line number 5 is a beginning of a loop, this loops iterate 52 times, and in
each iteration, randomize a card number, and puts it on the next location in the
memory. The line 010032FD is where the randomized card number is saved in the
memory. After tracing with the debugger a few timer on these addresses I can
tell you this, there is 8 arrays of cards, the first iteration put the card
number as the first item in the first array, the following iteration puts the
card number in the first item of the second array, and so forth. when it gets to
card number 9 it starts over, putting the card number as the second item in the
first array.. the following diagram should help to better understand it:
So what's the point with these 8 array? Well, the reason we got 8 array is
because freecell has 8 columns of cards. so each array is a column of cards.
Again, after tracing the loop a few times I've discovered that the formula for
the address where the card is saved is: CardAddress = BaseAddress + 0x54*I +
4*J, where BaseAddress is 0x1007554 (mentioned in line 010032FD), I is the
column number (0..7) and J is the row number.
So, now that we got the addresses we can go on and make the Freecell Memory
Reader app. The form contains 8 listbox controls, one for each column. The first
thing we do is checking which operating system are we on, the freecell I've
checked was on XP, I've also searched the addresses for win2K, but I couldn't
test the program, so if it doesn't work on win2K, please leave a comment and
I'll fix it. Anyway, I check the operating system and according to it I set the
addresses I'm going to use later. Then I search the process of freecell using
the static method Process.GetProcessesByName() and then I open a new instance of
my ProcessMemoryReader class. This class was introduced in my previous article
so check it for details explanation on the use of this class. After opening the
process I read the game number from the game number address. then I loop over
the cards array and for each place in the array I read its memory, convert the
card number to a normal card number and place it on the proper listview
(according to its column).
"Convert the card number"? Well, here I need to explain how the card is
stored in memory. The number that was randomized earlier was a number between
1..52, so I need to convert it to the card's number (1..13) and the card's kind
(1..4). At first I thought that the conversion is simple, I thought that it was
organized like this:
Kind | Number | original number
1 - 1 - 1
1 - 2 - 2
1 - 3 - 3
1 -
4 - 4
...
2 - 1 - 14
2 - 2 - 15
...
4 - 12
- 51
4 - 13 - 52
Well, it turns out that is not the case.. instead of specifying all the cards
from one kind and them the next kind and so forth (1..13,1..13,1..13,1..13) they
decided to specify the sets of card numbers. (1..4,1..4,1..4, 13 times), like
this:
Kind | Number | original number
1 - 1 - 1
2 - 1 - 2
3 - 1 - 3
4 - 1
- 4
1 - 2 - 5
2 - 2 - 6
...
3 - 13 - 51
4 -
13 - 52
I hope its clear now, That's why a conversion is needed. One Last thing to
mention is that when I read the game number from the memory I get a 4 bytes
array and I need to convert it to an Int32, there are better ways to do it then
how I did but I kept my way for simplicity. So the code is as follow:
IntPtr GameNumAddress;
IntPtr CardsAddress;
if (Environment.OSVersion.Version.Major == 5)
{
if (Environment.OSVersion.Version.Minor == 0)
{
GameNumAddress = (IntPtr)0x010071F8;
CardsAddress = (IntPtr)0x01007f74;
}
else
{
GameNumAddress = (IntPtr)0x0100834C;
CardsAddress = (IntPtr)0x01007554;
}
}
else
{
MessageBox.Show("Sorry, only winXP and win2K are supported!");
return;
}
Process[] pArray = Process.GetProcessesByName("freecell");
if (pArray.Length == 0)
{
MessageBox.Show("No Freecell process!");
return;
}
ProcessMemoryReaderLib.ProcessMemoryReader pReader =
new ProcessMemoryReaderLib.ProcessMemoryReader();
pReader.ReadProcess = pArray[0];
pReader.OpenProcess();
for(int i=0 ; i<8 ; i++)
listArray[i].Items.Clear();
int iGameNum;
int readBytes;
byte[] buffer;
int CardNum, CardKind;
string ItemString = "";
bool bAddCard;
buffer = pReader.ReadProcessMemory(GameNumAddress,4,out readBytes);
iGameNum = buffer[0] + 256*buffer[1] + 256*256*buffer[2] +
256*256*256*buffer[3];
txtGameNum.Text = iGameNum.ToString();
for (int i=0 ; i<8 ; i++)
{
for (int j=0 ; j<21 ; j++)
{
buffer = pReader.ReadProcessMemory((IntPtr)(
(int)CardsAddress + 0x54*i + 4*j),4,out readBytes);
CardNum = (buffer[0] / 4) + 1;
CardKind = (buffer[0] % 4) + 1;
bAddCard = true;
switch (CardNum)
{
case 11:
ItemString = "J";
break;
case 12:
ItemString = "Q";
break;
case 13:
ItemString = "K";
break;
case 64:
bAddCard = false;
break;
default:
ItemString = CardNum.ToString();
break;
}
if (bAddCard)
listArray[i].Items.Add(ItemString ,CardKind -1);
}
}
pReader.CloseHandle();
On to the next game..
Section 2: Hearts, How does it work?
This game was far more difficult to debug from the rest, because of the
complex ways the cards are stored in the memory. Also the example works only on
XP, cause I couldn't find Hearts on a win2K system, so I couldn't search for the
parallel addresses. Because of its complexity I'll will keep my explanations
nice an simple, and I won't get into details.
So, how many of you knows how do I start? That's right, we open the program
in the debugger (mshearts.exe) and search the file for the famous rand()
function. We will find to places, and we will put a breakpoint on each place.
Then we will run the program. As soon as we start a game, the first breakpoint
will pop once and the second breakpoint will pop 52 times.. I think we have
found the interesting part.. So as always we will look at the code and search
for the memory addresses where the values are being stored. Brought to you here
is the code of the second breakpoint:
01007FB6 /CALL DWORD PTR DS:[<&msvcrt.rand>]
01007FBC |CDQ
01007FBD |IDIV DWORD PTR SS:[EBP-18]
01007FC0 |MOV EAX,DWORD PTR SS:[EBP-14]
01007FC3 |PUSH 0D
01007FC5 |POP EDI
01007FC6 |PUSH 4
01007FC8 |POP EBX
01007FC9 |MOV ECX,EDX
01007FCB |CDQ
01007FCC |IDIV EDI
01007FCE |SUB EAX,DWORD PTR DS:[ESI+140]
01007FD4 |MOV EDI,EDX
01007FD6 |ADD EAX,4
01007FD9 |CDQ
01007FDA |IDIV EBX
01007FDC |LEA EAX,DWORD PTR SS:[EBP+ECX*4-110]
01007FE3 |MOV EBX,DWORD PTR DS:[EAX]
01007FE5 |SHL EDI,4
01007FE8 |LEA ECX,DWORD PTR DS:[ESI+EDX*4+130]
01007FEF |MOV EDX,DWORD PTR DS:[ECX]
01007FF1 |MOV DWORD PTR DS:[EDI+EDX+1C],EBX
01007FF5 |MOV ECX,DWORD PTR DS:[ECX]
01007FF7 |XOR EBX,EBX
01007FF9 |DEC DWORD PTR SS:[EBP-18]
01007FFC |INC DWORD PTR SS:[EBP-14]
01007FFF |CMP DWORD PTR SS:[EBP-14],34
01008003 |MOV DWORD PTR DS:[EDI+ECX+28],EBX
01008007 |MOV ECX,DWORD PTR SS:[EBP-18]
0100800A |MOV ECX,DWORD PTR SS:[EBP+ECX*4-110]
01008011 |MOV DWORD PTR DS:[EAX],ECX
01008013 \JL SHORT mshearts.01007FB6
Yea, I know, lots of ugly code.. But I've bolded the parts that interest us,
the first line is where we set out breakpoint, the rand() function is being
called. The second bolded line (01007FF1) is the line where the randomized value
is stored in the memory. note that this is the first operation of the kind: MOV
<memory address>,<value> ,So I've check carefully what is happening
in this command. After tracing it a few times I've found some logic in what
address are being used to store the values. The first 13 iterations of this loop
(by the way, the code brought here is a loop) are accessing a zone in the
memory, we will call it zone A, the next 13 iterations access a different zone
in the memory, zone B, and also the next 13 iterations and the last 13
iterations. so what do we got here. 52 iterations, each 13 we change to a
different zone in the memory. Also I've found that the difference between the
memory addresses in each set is equal to 16. So to simplify, I've made a little
diagram to better see the structure:
So, you see, we have 4 main base addresses, each one is an array, so the
first item is in ZoneA + 0, the second is in ZoneA + 16 and so forth. According
to the picture the 4 base addresses are stored in an array, this is correct.
I've discovered that there is an array of 4 addresses, each item of that array
is a pointer to another array of 13 items. All this is very nice, but I still
have a problem, all of the addresses involved in this structure are dynamic,
meaning they changed from run to run. I need to find an address that every time
the program starts, it will always be a static pointer to the base address (the
Base address is the address of the 4 pointers array). How do I know this kind of
static pointer does exist? well, this involves explanations on what happens when
you create a variable on the stack, and what happens when you create a variable
on the heap (I'm talking about unmanaged code off course). So when you create a
variable on the stack, its address is written to the file and so cannot change
when you run different instances, but if you create a variable dynamically, his
address is not known at design time and so cannot be written to the file. now
suppose you got a pointer to an array, so the array can be in every size you
want, and you cant create it dynamically and so on, BUT the pointer itself IS
static and declared on the stack so there is always a need for at least a static
pointer that holds the structure, and this static pointer has always the same
address. So now I know it exists, how do I find it? Well, I've just did a
search. I ran an instance of the program, and saw what was my Base address (the
address of the array of addresses) and then I've did a search on the memory to
find where is this base address is stored on the memory, and I've found only one
such address. bingo. Now, the trick is to know in which region of memory to look
for, cause how can you tell if the memory you are looking on is the dynamic
memory or static memory? Well, the region of memory that is between 0x0100D000
and 0x0100E000. This is only for this specific file. Each file has its own .data
section, and this is the section where all the static memory is stored.
If you didn't understand all the details it is not important, all you need to
know is that we got a Static address = 0x0100d514, this static address is a
pointer to another pointer we will call PointerArrayAddress, the
PointerArrayAddress is a pointer to an array of 4 pointers. Each pointer in the
array is a pointer to an array of 13 items. each item contains a card number.
each array of cards is a player. and now all I need to do is read this structure
and display it on the screen.
So what does out code do? First we find the Hearts process, then we read the
static pointer, then we read the 4 pointers of the arrays, and then we read the
52 items out of the arrays, for each item we convert its card number into a
normal representation. if you remember from the previous section the conversion
to the cards, here it's the same. and finally we add it to the proper list on
the screen (we got 4 lists). The code is as follows:
Process[] pArray = Process.GetProcessesByName("mshearts");
if (pArray.Length == 0)
return;
ProcessMemoryReaderLib.ProcessMemoryReader pReader =
new ProcessMemoryReaderLib.ProcessMemoryReader();
pReader.ReadProcess = pArray[0];
pReader.OpenProcess();
int i;
int readBytes;
byte[] buffer;
IntPtr StaticAddress = (IntPtr)0x0100d514;
buffer = pReader.ReadProcessMemory(StaticAddress,4,out readBytes);
IntPtr PointerArrayAddress = (IntPtr)(
buffer[0] +
256*buffer[1] +
256*256*buffer[2] +
256*256*256*buffer[3] + 0x130);
IntPtr[] aArray = new IntPtr[4];
buffer = pReader.ReadProcessMemory(
(IntPtr)PointerArrayAddress,16,out readBytes);
for (i=0 ; i<4 ; i++)
aArray[i] = (IntPtr)(
buffer[4*i] +
256*buffer[4*i + 1] +
256*256*buffer[4*i + 2] +
256*256*256*buffer[4*i + 3] + 0x1c);
for (i=0 ; i<4 ; i++)
listArray[i].Clear();
int CardNum, CardKind;
string ItemString = "";
bool bAddCard;
for (int j=0; j<13 ; j++)
{
for (i=0 ; i<4 ; i++)
{
buffer = pReader.ReadProcessMemory(
(IntPtr)((int)aArray[i] + (16 * j)),1,out readBytes);
CardNum = (buffer[0] / 4) + 1;
CardKind = (buffer[0] % 4) + 1;
bAddCard = true;
switch (CardNum)
{
case 11:
ItemString = "J";
break;
case 12:
ItemString = "Q";
break;
case 13:
ItemString = "K";
break;
case 64:
bAddCard = false;
break;
default:
ItemString = CardNum.ToString();
break;
}
if (bAddCard)
listArray[i].Items.Add(ItemString ,CardKind -1);
}
}
pReader.CloseHandle();
In the next section I will give an example of writing to a process memory
using in the Hearts game.
Section 3: WriteProcessMemory bonus
As I've said in the beginning, I could not write an article with no new
feature presented so I've enhanced the functionality of the class library so now
it can also write into another process memory. This is done using the API
WriteProcessMemory
. This function excepts a byte array, an address
and a process and writes the byte array to the address of the process. Simple as
that. Its normal header looks like this:
BOOL WriteProcessMemory(
HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesWritten );
So its C# equivalent is:
[DllImport("kernel32.dll")]
public static extern Int32 WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[In, Out] byte[] buffer,
UInt32 size,
out IntPtr lpNumberOfBytesWritten);
For this function to work, you
need to first open the process with the access permissions
PROCESS_VM_WRITE
and
PROCESS_VM_OPERATION
.
Brought to you here is an example of using this function, the example opens
the Hearts process again, only this time the goal is to write me a good game.
Here is the code:
Process[] pArray = Process.GetProcessesByName("mshearts");
if (pArray.Length == 0)
return;
ProcessMemoryReaderLib.ProcessMemoryReader pReader =
new ProcessMemoryReaderLib.ProcessMemoryReader();
pReader.ReadProcess = pArray[0];
pReader.OpenProcess();
int i;
int readBytes;
byte[] buffer;
IntPtr StaticAddress = (IntPtr)0x0100d514;
buffer = pReader.ReadProcessMemory(StaticAddress,4,out readBytes);
IntPtr PointerArrayAddress = (IntPtr)(
buffer[0] +
256*buffer[1] +
256*256*buffer[2] +
256*256*256*buffer[3] + 0x130);
IntPtr[] aArray = new IntPtr[4];
buffer = pReader.ReadProcessMemory(
(IntPtr)PointerArrayAddress,16,out readBytes);
for (i=0 ; i<4 ; i++)
aArray[i] = (IntPtr)(
buffer[4*i] +
256*buffer[4*i + 1] +
256*256*buffer[4*i + 2] +
256*256*256*buffer[4*i + 3] + 0x1c);
int writtenBytes;
buffer = new byte[1];
for (i=0 ; i<4 ; i++)
{
for (int j=0; j<13 ; j++)
{
buffer[0] = (byte)(j*4 + i);
pReader.WriteProcessMemory(
(IntPtr)((int)aArray[i] + (16 * j)),buffer,out writtenBytes);
}
}
pReader.CloseHandle();
btnReadMemory_Click(sender,null);
Note: the game might not be as much fun now. That's it. Hope you like it,
Don't forget to vote.
Arik Poznanski is a senior software developer at Verint. He completed two B.Sc. degrees in Mathematics & Computer Science, summa cum laude, from the Technion in Israel.
Arik has extensive knowledge and experience in many Microsoft technologies, including .NET with C#, WPF, Silverlight, WinForms, Interop, COM/ATL programming, C++ Win32 programming and reverse engineering (assembly, IL).