Intel PIN, Cheatz, Hax, And Detection Part 2

Hi!

Today I’m going to go over more on intel’s PIN, more on cheats, and less on detection since I already covered that. I feel like I’ve spent way too much time on this and it’s a huge turn off against my productivity. And of course, I commit to a talk and it HAS to be done. Blech. At least I can now work on the talk more.

As I’ve gone over before, PIN allows us to dynamically instrument programs without source code. The documentation is nice, but leaves some things to be desired. For example – how in the hell do you stop on a specific memory address rather than some API name?

Cheats and PIN.

Traditionally a cheat will make use of (WriteProcessMemory) or something similiar to ‘peek’ and ‘poke’ memory addresses and with values of our choice. Once we’ve established the right memory addresses and values of course, we write our 3rd party ‘helper’ app (trainer) that attaches to a game and does our peek / poke. Here is what a typical cheat looks like in C# for windows. C# because not everything has to be C/C++, and managed code is awesome for those kick ass easy guis:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Trainer
{
    class Program
    {
        [DllImport("kernel32.dll")]
        internal static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, UInt32 nSize, ref UInt32 lpNumberOfBytesRead);
        
        [DllImport("kernel32.dll")]
        static extern bool CloseHandle(IntPtr hHandle);

        [DllImport("User32.dll")]
        private static extern short GetAsyncKeyState(System.Int32 vKey);

        [DllImport("kernel32.dll")]
        static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

        static int ProcID;
        static IntPtr Handle;

        public static class OutIgnore<T>
        {
            [ThreadStatic]
            public static T Ignored;
        }	
        public static void WriteMemory(int Address, byte[] data)
        {
            WriteProcessMemory(Handle, (IntPtr)Address, data, data.Length, out OutIgnore<IntPtr>.Ignored);
        }
		
        public static byte[] Read(IntPtr Address, int length)
        {
            byte[] ret = new byte[length];
            ReadProcessMemory(Handle, Address, ret, (UInt32)ret.Length, ref OutIgnore<uint>.Ignored);
            return ret;
        }
		
        public static int ReadInt32(IntPtr Address, int len)
        {
            return BitConverter.ToInt32(Read(Address, len), 0);
        }
		
        public static byte ByteReadOffset(IntPtr addr, int offset, int len)
        {
            int address = ReadInt32(addr, len);
            return Read((IntPtr)address + offset, 1)[0];
        }
		
        public static float FloatReadOffset(IntPtr addr, int offset, int len)
        {
            int address = ReadInt32(addr, len);
            byte[] naddr = Read((IntPtr)address + offset, sizeof(float));
            return BitConverter.ToSingle(naddr, 0);
        }
		
        public static int IntReadOffset(IntPtr addr, int offset, int len)
        {
            int address = ReadInt32(addr, len);
            byte[] naddr = Read((IntPtr)address + offset, sizeof(int));
            return BitConverter.ToInt32(naddr, 0);
        }
		
        public static void WriteOffset(IntPtr addr, int offset, byte[] res, int len)
        {
            int address = ReadInt32(addr, len);
            WriteMemory(address + offset, res);
        }
		
        static void Main(string[] args)
        {
            Console.WriteLine("Started. W8 process....");
            while (ProcID == 0)
            {
                foreach (Process id in Process.GetProcessesByName("AoK HD"))
                {
                    ProcID = id.Id;
                }
            }

            Handle = OpenProcess(0x001F0FFF, false, ProcID);

            
            bool makeitrain = false;
			
            Console.WriteLine("Injected");
			
            while (true)
            {
                string cmd = Console.ReadLine();

                if (cmd == "exit")
                    return;

                if (cmd == "makeitrain")
                {
                    makeitrain = (!makeitrain);
                    WriteMemory(0xA444A4, (!makeitrain) ? new byte[] { 0x0 } : new byte[] { 0x1 });
                }
                if (cmd == "get")
                {
                    Console.WriteLine("State: " + CPed.GetByteOffset(0x46F));
                    Console.WriteLine("Animation State: " + CPed.GetByteOffset(0x4DF));
                    Console.WriteLine("Gold: " + ReadInt32((IntPtr)0xB7CE50, 4));
                    
                }
            }
        }
    }
}

Your classic trainer in action – Find a memory address, poke it with a new value. Easy right? Can you guess the game? Hint: GetProcessesByName(“AoK HD”). That’s right, age of empires.

It’s pretty simple in action – writes itself directly to the address space of the process after opening a handle to it. This will work for a lot of games too. The problem is, many games no check for this sort of thing. We have to be more creative. But how?

Well, we could try DLL Injection. For those unfamiliar with DLL Injection, we basically write a dll, place code in the ‘DLL_PROCESS_ATTACH’ region, then when our dll is injected, our code is run in the address space of the game / process. This is a bonus because it makes the program think it belongs or something.

#include <windows.h>
#include <tlhelp32.h>
#include <Psapi.h>
#include <stdio.h> 

static const DWORD ammo = 0x0EB9B878;
HANDLE dllhandle = 0;

DWORD ThreadProc(LPVOID lpdwThreadParam);

MODULEINFO GetModuleInfo( char *szModule )
{
	MODULEINFO modinfo = {0};
	HMODULE hModule = GetModuleHandle(szModule);
	if(hModule == 0) 
		return modinfo;
	GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(MODULEINFO));
	return modinfo;
}

void WriteToMemory(DWORD* addressToWrite, char* valueToWrite, int byteNum)
{
	unsigned long OldProtection;
	VirtualProtect((LPVOID)(addressToWrite), byteNum, PAGE_EXECUTE_READWRITE, &OldProtection);
	memcpy( (LPVOID)addressToWrite, valueToWrite, byteNum);
	VirtualProtect((LPVOID)(addressToWrite), byteNum, OldProtection, NULL);
}


void ChangeMemory(DWORD baseadress, int value, DWORD offset1, DWORD offset2, BOOL msg)
{
	//DWORD d, ds;
	DWORD* adress = (DWORD*)((*(DWORD*)(baseadress + offset1)) + offset2);	
 
	if (msg)
	{
		char szTest[10] ;
		sprintf(szTest, "The final adress is : %X", adress);
		MessageBox(NULL,szTest , NULL, MB_OK);
	}
 
	*(int*)adress = value;
}

BOOL WINAPI DllMain
(
    HANDLE   hinstDLL,
    DWORD       fdwReason,
    LPVOID      lpvReserved
)
{
    if(fdwReason==DLL_PROCESS_ATTACH)
    {
 		DWORD threadId;
		dllhandle = hinstDLL;
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadProc, NULL, 0, &threadId);
	}
   
    return 1;
}
DWORD ThreadProc(LPVOID lpdwThreadParam)
{
	MODULEINFO mInfo = GetModuleInfo("fearxp.exe");
	DWORD base = (DWORD)mInfo.lpBaseOfDll;
	ChangeMemory(base,1234,ammo,ammo,FALSE);
   
    return 0;
}

The idea is pretty simple – poke a memory address after creating a new thread. The thread runs in the context of the running program. I know what you’re thinking – “Joe, isn’t this good enough for my cheats? surely the game won’t detect this? right?” and you’d be half right. Some game devs are clever assholes. We need to be more clever than them to inject code into their products and not have them caught. We can always use hooking and detours to do our injection.

Recall, I’ve talked about detours before. Yeah those, we can use those for game hacking. Given we have our function pointer address / memory address or function name, we can ‘detour’ to our code to peek / poke whatever address / value combo we want. Though I used detouring for more…evil purposes, however the concepts carry over.

#include "stdafx.h"
#include <windows.h>
#include <detours.h>
#pragma comment(lib, "detours.lib")

typedef void (WINAPI *pFunc)(void);
void WINAPI MyFunc(void);
pFunc FuncToDetour = (pFunc)(0x4BD11B); // Set it at address to detour in

void WINAPI MyFunc(void)
{
	MessageBox(NULL, L"NONO", L"TEST", MB_OK);
	unsigned char buf[] =
		"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
		"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
		"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
		"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
		"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
		"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
		"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
		"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
		"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
		"\x8d\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c"
		"\x77\x26\x07\xff\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
		"\x29\x80\x6b\x00\xff\xd5\x6a\x08\x59\x50\xe2\xfd\x40\x50\x40"
		"\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x68\x02\x00\x1f\x90\x89"
		"\xe6\x6a\x10\x56\x57\x68\xc2\xdb\x37\x67\xff\xd5\x57\x68\xb7"
		"\xe9\x38\xff\xff\xd5\x57\x68\x74\xec\x3b\xe1\xff\xd5\x57\x97"
		"\x68\x75\x6e\x4d\x61\xff\xd5\x68\x63\x6d\x64\x00\x89\xe3\x57"
		"\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c"
		"\x01\x01\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56\x56\x46"
		"\x56\x4e\x56\x56\x53\x56\x68\x79\xcc\x3f\x86\xff\xd5\x89\xe0"
		"\x4e\x56\x46\xff\x30\x68\x08\x87\x1d\x60\xff\xd5\xbb\xaa\xc5"
		"\xe2\x5d\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb"
		"\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5";
	
}
extern "C" __declspec(dllexport) void DoNothingAlready(void)
{
	DWORD ayylmao = 20345;
	_asm
	{
		xor eax, eax
			xor ecx, ecx
			mov eax, ayylmao
			mov ecx, 0
		testd:
		fnop
			inc ecx
			cmp eax, ecx
			jnz testd
			pop ebx
			nop
	}
	return;
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
	if (DetourIsHelperProcess()) {
		return TRUE;
	}

	if (dwReason == DLL_PROCESS_ATTACH) {
		DetourRestoreAfterWith();
		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourAttach(&(PVOID&)FuncToDetour, MyFunc);
		DetourTransactionCommit();
	}
	else if (dwReason == DLL_PROCESS_DETACH) {
		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourDetach(&(PVOID&)FuncToDetour, MyFunc);
		DetourTransactionCommit();
	}
	return TRUE;
}

This is certainly something that would be hard to detect right? So what else is there to inject code in? Enter binary instrumentation and PIN. A mad man could possibly conceive a way to inject code and hack games using a framework that’s been used for all kinds of things from unpacking malware, to finding bugs and memory leaks. It seems game hacking with PIN is just inevitable.

But joe you might say, “how the hell do you stop on a particular memory address in PIN? I don’t see a way to specify a pointer like with detours, only function names!” and you’d be right. But keep in mind, with PIN we have full control of the context / registers. It’s a simple matter of halting on a particular instruction pointer address. Easy peasy. Observe:

/*
alexander hanel - thanks alex, much love. 
log IP of all instructions that do not reside in a module. 
*/
#include <stdio.h> 
#include <iostream> 
#include "pin.H" 
namespace WINDOWS
{
#include <Windows.h>
}
#include <string>
#include <algorithm>


// bool started = FALSE;
ADDRINT addr;
vector<string> modules;
char othercompare[32]  = "0x000000014000112A";
//__int64 curIP = 0;

FILE * trace;

// This function is called before every instruction is executed
// and prints the IP
VOID printip(VOID *ip) 
{
	char joincompare[32] = "";
	sprintf(joincompare, "0x%p",ip);
	
	if(strcmp(joincompare,othercompare))
	{
	// we do our hax here
	printf("******************************************\n");
	printf("******************************************\n");
	printf("****************Found it!!!***************\n");
	printf("******************************************\n");
	printf("******************************************\n");
	}
	fprintf(trace, "Instruction Pointer: 0x%p\n", ip ); 
	
}

// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
	IMG img = IMG_FindByAddress(INS_Address(ins));
	string path = (IMG_Valid(img) ? IMG_Name(img) : "InvalidImg");
	auto it = std::find(modules.begin(), modules.end(), path);
	if (it != modules.end())
		return;
	else
	{
		fprintf(trace, "Instruction: %s\n", INS_Disassemble(ins)); // also print the decoded instructions
		INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printip, IARG_INST_PTR, IARG_END);
	}
}

VOID Image(IMG Img, VOID *v)
{ 
	if (IMG_IsMainExecutable(Img))
		printf("Main module loading...\n");
	else
		modules.push_back(IMG_Name(Img).c_str());
}

VOID Fini(INT32 code, VOID *v) 
{ 
	printf("The following modules were loaded: \r\n");
	int ii;
	for (ii = 0; ii < modules.size(); ii++)
	{
		cout << modules[ii] << endl;
	}
} 

INT32 Usage() 
{ 
	return -1; 
}

int main(int argc, char * argv[]) 
{ 
	trace = fopen("itrace.out", "w");
	if (PIN_Init(argc, argv)) 
		return Usage();
	IMG_AddInstrumentFunction(Image, 0);
	INS_AddInstrumentFunction(Instruction, 0); 
	PIN_AddFiniFunction(Fini, 0); 
	PIN_StartProgram(); 
	return 0; 
}

Where do we obtain the instruction pointer address? That’s up to you(me), and the disassembler. Let’s go over an example.

Here we have a simple C program I wrote (in 64 bit no less) that uses the MessageBox API.

#include <windows.h>
int main(void)
{
	char *msg1 = "this will work";
	char *msg2 = "this too";
	MessageBox(NULL,msg1,msg2,MB_YESNOCANCEL);
	return 0;
}

Here we run the fucker:

And here’s what it looks like in IDA:

We would want to instrument at the address 0x0000000140001000. To modify the call to MessageBox, we need to alter the contents of the registers. The register ‘r9d’ contains the value 3 or MB_YESNOCANCEL. With what? How about something simple like changing the MessageBox ‘uType’ to ‘MB_OK’ or 0. This means we would need to modify the ‘r9d’ register at the address 0x0000000140001004 or just before the call to MessageBox. This means we have to halt on this address and modify the CONTEXT structure. For those who don’t know, the CONTEXT structure is a windows term for the state of registers. PIN has my back when it comes to modifying this structure.

This code we use to modify the context structure. using some creative C++ hax, I’m able to stop on a certain address and call modify at the right time:

#include <iostream>
#include <fstream>
#include "pin.H"

vector<string> modules;
//REG testedRegister = REG_INVALID();

BOOL hitit = false;
REG testedRegister = REG_R9D;

char stop_on_me[32]  = "0000000140001004";
char joincompare[32] = "";

VOID show_instruction_pointer(ADDRINT rip, ADDRINT r9) 
{
	sprintf(joincompare, "%p",Addrint2VoidStar(rip));
	if(memcmp(joincompare,stop_on_me,sizeof(joincompare)))
	{
	printf("Instruction pointer is %p\r\n",Addrint2VoidStar(rip));
	//printf("Value of R9 is %p\r\n",Addrint2VoidStar(r9));
	hitit = true;

	}

}

void afterfunc_call(CONTEXT * ctxt)
{
	if(hitit)
	{
	printf("Setting the value of R9 to 0");
	PIN_SetContextRegval(ctxt, testedRegister, 0x0000000000000000);
	}
}
    
// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
	IMG img = IMG_FindByAddress(INS_Address(ins));
	string path = (IMG_Valid(img) ? IMG_Name(img) : "InvalidImg");
	auto it = std::find(modules.begin(), modules.end(), path);
	if (it != modules.end())
		return;
	else
	{
	
	 REGSET regsin;
     REGSET regsout;
     REGSET_Clear(regsin);
     REGSET_Clear(regsout);
     REGSET_Insert(regsout, testedRegister);
	 
	 // insert call to check for instruction pointer
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)show_instruction_pointer, IARG_REG_VALUE, REG_INST_PTR, IARG_END);
	 // insert call to modify register value
	INS_InsertCall(ins, IPOINT_BEFORE, AFUNPTR(afterfunc_call),IARG_PARTIAL_CONTEXT, &regsin, &regsout, IARG_END);
	
	}
}

VOID Image(IMG Img, VOID *v)
{ 
	if (IMG_IsMainExecutable(Img))
		printf("Main module loading...\n");
	else
		modules.push_back(IMG_Name(Img).c_str());
}

VOID Fini(INT32 code, VOID *v)
{
    
  
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "Just more code hax by joe" << endl;
    
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */
/*   argc, argv are the entire command line: pin -t <toolname> -- ...    */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();
	IMG_AddInstrumentFunction(Image, 0); // remove other modules, except main module, let that run
    INS_AddInstrumentFunction(Instruction, 0);
    PIN_AddFiniFunction(Fini, 0);
    PIN_StartProgram();
    
    return 0;
}

Does it work? Hell yeah!

So how the hell would you cheat like this? Well I’ve already demonstrated register manipulation, memory manipulation, so the rest should be trivial.

I’m sorry it took so long to go through this framework and make it into something useful. I’m not done with PIN just yet, I plan to release something awesome using it soon. Stay tuned!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.