Skip to content

Instantly share code, notes, and snippets.

@dwendt
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dwendt/07a8f685fff1bacdfe80 to your computer and use it in GitHub Desktop.
Save dwendt/07a8f685fff1bacdfe80 to your computer and use it in GitHub Desktop.
Ghost in the Shellcode 2015 Pwnadventure

Pwnadventure 2015

This year the Pwnadventure challenge for GitS was written using Unreal Engine 4. This was an interesting choice because this makes it one of the first few games published using the engine. I'm very familiar with UE3 hacking and how the engine works internally(drop-in cheat code for anything using the engine), so I had decided to try to find a game published on UE4 and get up to speed prior to the competition. There really wasn't anything decent available.

It should be noted that doing this challenge on the three different operating systems available conferred different avantages. Windows made it easy to understand the class structure, and linux/osx eliminated awful SSE instructions in Pirate Treasure.

Diving into pwnadv, there's a GameLogic.dll and GameLogic.pdb in the binary folder for the game. The first step to gamehacking is usually...

Reversing the classes

There's a PDB. No reversing necessary. Knowing how object oriented programming in C++ works is pretty important to this step. Classes with virtual functions have a virtual function table(VFT) stored in the binary when they are compiled. When a class is instantiated and put into memory, a pointer to the VFT is the first element in the class's memory space. In normal reversing, a VFT will look like:

VFT in normal disassembly

Since we have a PDB, the VFTs look like:

VFT with debug symbols, shows function names/what class they are from

This is the VFT for a single class, but it contains entries that are clearly from different classes! This is due to compiler optimizations that avoid generating the same function. The compiler will re-use existing functions it has already generated if the function to be generated does the same thing.

You'll know when a VFT function is being called because it'll usually look like the below, with registers aside from ecx varying.

mov eax, [ecx] -- ecx is a pointer to the object (object = instance of a class)
mov eax, [eax+0x28] -- 0x28 is the offset to the desired func from the start of the VFT
push 0 -- arguments for the function get pushed onto the stack
push 0x41414141
call eax

ecx in the above is usually important: if the function is the __thiscall calling convention, ecx should point to an instance of the class when the function is called. __thiscall is the default calling convention for members of a class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment