My dad is awesome. He always beats me at chess. With a huge nod to this uninformed post – introduction to reverse engineering win32 applications where they debug minesweeper, I decided to dive into the windows 7 chess game and see if I could give myself a bit of an advantage. I wasn’t sure exactly what I wanted to do other than that. I’ll be using Windows 7 32 bit, and the file is at C:\Program Files\Microsoft Games\Chess\. This tutorial will probably not work with anything but Windows 32 bit. This is a beginner tutorial.
Recon and Defining what we want to do
Following the uninformed post, I wondered if chess might contain symbols also, as this would make my life easier. I have this set in my config, but if you don’t then you will want to set your symbol path.
0:000> .sympath srv*c:\debug*http://msdl.microsoft.com/download/symbols Symbol search path is: srv*c:\debug*http://msdl.microsoft.com/download/symbols Expanded Symbol search path is: srv*c:\debug*http://msdl.microsoft.com/download/symbols 0:000> .reload /f * 0:000> lm start end module name 001b0000 00474000 Chess (pdb symbols) c:\debug\Chess.pdb\1467728C9EEA429C9FA465213785E17C1\Chess.pdb 6e030000 6e06c000 OLEACC (pdb symbols) c:\debug\oleacc.pdb\DC8A57A3E8C648228F2C3650F2BE1D672\oleacc.pdb 6f900000 6f972000 DSOUND (pdb symbols) c:\debug\dsound.pdb\F38F478065E247C68EDA699606F56EED2\dsound.pdb
Awesome, we have a chess.pdb. In the uninformed post they use windbg to look at functions, but I find IDA Pro easier to read. Loading chess.exe into IDA we see quite a few functions right off the bat that look interesting. It looks like there’s a Pawn class, a knight class, a bishop class, etc
Pawn::GetCaptureMoves(int const * *) .text 0102D605 00000017 R . . . B . . Pawn::GetShadowRadius(void) .text 0102D621 00000007 R . . . . . . Knight::Knight(ESide) .text 0102D62D 0000001D R . . . B . . Knight::Clone(void) .text 0102D64F 0000002B R . . . . . . Knight::GetPassiveMoves(int const * *) .text 0102D67F 00000017 R . . . B . . Knight::CanJump(void) .text 0102D69B 00000003 R . . . . . . Knight::GetPieceType(void) .text 0102D6A3 00000004 R . . . . . . Knight::GetShadowRadius(void) .text 0102D6AC 00000007 R . . . . . . Bishop::Bishop(ESide) .text 0102D6B8 0000001D R . . . B . . Bishop::Clone(void) .text 0102D6DA 0000002B R . . . . . . Bishop::GetPassiveMoves(int const * *) .text 0102D70A 00000017 R . . . B . . Bishop::GetPieceType(void) .text 0102D726 00000004 R . . . . . . Rook::Rook(ESide) .text 0102D72F 0000001D R . . . B . . Rook::Clone(void) .text 0102D751 0000002B R . . . . . .
So there seem to be two outliers, knights and pawns. Knights have extra moves like canjump, and pawns can move certain places depending on other pieces, so this makes sense. Also, this gives us a big clue that these classes contain some of the logic we can use to determine which piece can move where.
So how should I beat my dad? He’s not a grandmaster, so maybe if I made bishops move like queens for me that would do the trick. There is also a board class, so another idea I had was to replace the bishops with queens when the board was setup, but that’s not the route I went.
There’s this function getpassivemove common to all the classes
0:010> x chess!*getpassivemove* 009bd781 Chess!Rook::GetPassiveMoves = <no type information> 009bd7f8 Chess!Queen::GetPassiveMoves = <no type information> 009bd67f Chess!Knight::GetPassiveMoves = <no type information> 009bd5d6 Chess!Pawn::GetPassiveMoves = <no type information> 009bd87b Chess!King::GetPassiveMoves = <no type information> 009bd70a Chess!Bishop::GetPassiveMoves = <no type information>
Setting a bp here it’s tough to tell what’s going on because it’s hit so frequently, but the functions are really simple, and for the most part they look VERY similar between pawn/rook/knight/king/etc classes
So let’s just replace the first instruction to jump to the other function. I had mona loaded into windbg here, but you can also do this with the metasploit asm shell or nasm.
What this does is modify the Chess!Bishop::GetPassiveMoves function and has it immediately jump to Chess!Queen::GetPassiveMoves. (The addresses on your box will certainly be different)
0:010> !py mona asm -s "mov eax, 0x0076d7f8#jmp eax" Hold on... Opcode results : ---------------- mov eax, 0x0076d7f8 = \xb8\xf8\xd7\x76\x00 jmp eax = \xff\xe0 Full opcode : \xb8\xf8\xd7\x76\x00\xff\xe0 [+] This mona.py action took 0:00:02.172000 0:010> eb 0076d5d6 b8 f8 d7 76 00 ff e0 0:010> uf Chess!bishop::GetPassiveMoves Flow analysis was incomplete, some code may be missing Chess!bishop::GetPassiveMoves: 0076d5d6 b8f8d77600 mov eax,offset Chess!Queen::GetCaptureMoves (0076d7f8) 0076d5db ffe0 jmp eax 0:010> g
Sure enough, this works. When we run we can move anywhere with our bishops
Problems
At this point, even though we can move anywhere, we still have two problems we need to solve. 1) both black and white can move anywhere, so this doesn’t give me an advantage. What I really want is just white to be able to move anywhere 2) We can’t just write to this address because of ASLR and also because it’s a read only section of memory.
What does it mean for us that ASLR is enabled? Any static addresses will likely change from run to run of the chess game. Looking for non-aslred modules, there are none. By the way, I’m using mona here.
0:000> !py mona noaslr Hold on... No aslr & no rebase modules : [+] Generating module info table, hang on... - Processing modules - Done. Let's rock 'n roll. ---------------------------------------------------------------------------------------------------------------------------------- Module info : ---------------------------------------------------------------------------------------------------------------------------------- Base | Top | Size | Rebase | SafeSEH | ASLR | NXCompat | OS Dll | Version, Modulename & Path ---------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------
So for us, we can’t really rely on any hard coded addresses.
Additionally, even if we solved ASLR, our hard jump strategy will also fail because both white and black call the GetPassiveMoves function. We need a way to only modify that function for white.
Figuring out Whose Turn it is
Getting turn info took a bit of shooting in the dark also, but because of symbols it was relatively easy to track down.
First I put a breakpoint here:
bp Chess!GameState::GetTurn +3 "r eax; g"
This is called a lot, and it seems to return 2 or 0 for white, and 0, 1, or 2 for black. This function will probably work, but there’s another turn function too named toggleturn, so lets try that. This function seems perfect – it’s called once after every move. We can see it’s testing the value in [ecx+4] so we inspect that, and sure enough it’s 1 before a white move and 0 before a black move
bp Chess!GameState::toggleturn dd ecx + 4
Programatically Changing the Game
I’m going to programattically debug the process. The way the uninformed post did things was cool, but it’s (more) difficult to go route way because we’re not messing with data, we’re messing with the program which is non writable. So how do we programatically debug?
There are a ton of ways. Mona uses this and it looks awesome: http://pykd.codeplex.com/. I’m a python guy, so usually I’d go that route, but I’m trying to learn powershell so I decided to try going that route and use this http://powerdbg.codeplex.com/. For the powershell to work you need to install this module.
The first thing I want to do is change the hard coded value to something I can switch back and forth. So I tried setting a breakpoint that I could disable per turn
bp Chess!Bishop::GetPassiveMoves "r eip=Chess!Queen::GetPassiveMoves;g"
This was waaaay too slow for the game to be playable. I had to figure out something else. This is when I noticed just how similar the getpassivemoves functions are
0:012> uf Chess!Bishop::GetPassiveMoves Chess!Bishop::GetPassiveMoves: 00c5d70a 8bff mov edi,edi 00c5d70c 55 push ebp 00c5d70d 8bec mov ebp,esp 00c5d70f 8b4508 mov eax,dword ptr [ebp+8] 00c5d712 c700f07ec300 mov dword ptr [eax],offset Chess!Bishop::sPassiveMoves (00c37ef0) 00c5d718 a1e87ec300 mov eax,dword ptr [Chess!Bishop::sPassiveMovesCount (00c37ee8)] 00c5d71d 5d pop ebp 00c5d71e c20400 ret 4 0:012> uf Chess!queen::GetPassiveMoves Chess!Queen::GetCaptureMoves: 00c5d7f8 8bff mov edi,edi 00c5d7fa 55 push ebp 00c5d7fb 8bec mov ebp,esp 00c5d7fd 8b4508 mov eax,dword ptr [ebp+8] 00c5d800 c700b880c300 mov dword ptr [eax],offset Chess!Queen::sPassiveMoves (00c380b8) 00c5d806 a1b080c300 mov eax,dword ptr [Chess!Queen::sPassiveMovesCount (00c380b0)] 00c5d80b 5d pop ebp 00c5d80c c20400 ret 4
They’re very close, and they’re the exact same number of bytes. We can just edit things on the fly, replacing the queen’s code with the bishop’s code and back again.
Import-Module PowerDbg #global vars, populated later $bishop_code = "" $queen_code = "" function bishop_to_queen { $command = "eb Chess!Bishop::GetPassiveMoves+a " + $queen_code Invoke-DbgCommand $command } function bishop_restore { $command = "eb Chess!Bishop::GetPassiveMoves+a " + $bishop_code Invoke-DbgCommand $command } New-DbgSession -command 'C:\Program Files\Microsoft Games\Chess\Chess.exe' Load-PowerDbgSymbols "srv*c:\debug*http://msdl.microsoft.com/download/symbols" #get the bytes for the different bishop and queen functions $bishop_array = (Invoke-DbgCommand "db Chess!Bishop::GetPassiveMoves+a L7").Split(" ")[2..8] $bishop_code = [string]::join(" ", $bishop_array) $queen_array = (Invoke-DbgCommand "db Chess!queen::GetPassiveMoves+a L7").Split(" ")[2..8] $queen_code = [string]::join(" ", $queen_array) bishop_to_queen $white_turn = $true Invoke-DbgCommand "bp Chess!GameState::ToggleTurn" #this loops once per turn while($true) { if ($white_turn -eq $true) { $white_turn = $false bishop_to_queen } else { $white_turn = $true bishop_restore } $ret_error = Invoke-DbgCommand "g" if ($ret_error.Contains("No runnable debugees")) { break; } }
And there we go, a runnable chess game where white bishops are super powerful. There are a few quirks, like if a bishop gets a king into checkmate with a queen move it doesn’t seem to register and you can kill the king and keep playing, but overall pretty good :)
I am still a noob at reversing, but this was still a fun afternoon :)
