Detours, Trampolines, and Code Caves

Howdy fellow RE folk!

Today I’d like to cover the basics of detours, trampolines, and code caves.

Traditionally, if one wanted to expand functionality to a program, they have to look for whats called a ‘code cave’, which is just a contiguous piece of (executable) memory. You replace a piece of code (typically a function prologue) with a long jump to your code. Of course its never easy to do this – if the Import Address Table doesn’t have the reference to the function / dll pair, calling a function will fail. If you wanted to do this statically, you would have to modify the IAT (pain in the ass BTW) to include the functions you’re calling. This isn’t always feasible, especially with packed programs and other programs which depend on check sums. That said, its best to do this sort of thing at run time.

For those of you wondering how to do this statically, I figured out how to do modify the IAT in C#. You never know if you’ll need this code:

private void AddToIAT(byte[] codestuff, string path)
{
    PeHeaderReader lolpe = new PeHeaderReader(this.tbfilehere.Text);
    UInt32 ImportDirectoryRVA = lolpe.OptionalHeader32.ImportTable.VirtualAddress; // RVA
    FileStream fs = new FileStream(path, FileMode.Open);
    BinaryWriter bw = new BinaryWriter(fs);
    BinaryReader br = new BinaryReader(fs);
    fs.Seek(0x3c, SeekOrigin.Begin); // go to the value of e_lfanew
    int my_e_lfanew = br.ReadInt32();
    UInt32 joe = RvaToOffsetJoe(lolpe, ImportDirectoryRVA) + (UInt32)my_e_lfanew;
    // try seeking to the imports area first dumbass or better yet, importdirectoryVA + the 
	// length of the last entry (lolpe.OptionalHeader32.ImportTable.Size;)

    fs.Seek(RvaToOffsetJoe(lolpe, ImportDirectoryRVA), SeekOrigin.Begin); 
    bw.Write(ImportDirectoryRVA + 40);
    bw.Write(0);
    bw.Write(0);
    bw.Write(ImportHintNameTableRVA(lolpe) + 14);
    bw.Write(ImportDirectoryRVA);
    bw.Write(new byte[20]); 
    bw.Write(ImportHintNameTableRVA(lolpe));
    int size = 48;
	if (lolpe.FileHeader.Machine != 0x014C) // if not 32 bit
	{
	size += 4;
	bw.Write(0);
	}
	bw.Write(0);

	for (int i = (int)(ImportHintNameTableRVA(lolpe) - 
	(ImportDirectoryRVA + size)); i > 0; i--)
	{
	bw.Write((byte)0);
	}

    bw.Write((ushort)0);
    bw.Write(System.Text.Encoding.ASCII.GetBytes("FatalAppExitA"));
    bw.Write(0);
    bw.Write(System.Text.Encoding.ASCII.GetBytes("kernel32.dll"));
    bw.Write((ushort)0);
    fs.Close();
}

	
UInt32 RvaToOffsetJoe(PeHeaderReader lolpe, UInt32 Rva)
{
	int o;
	int secx = lolpe.FileHeader.NumberOfSections;
	for(o=0;o<secx;o++)
	{
		if (Rva >= lolpe.ImageSectionHeaders[o].VirtualAddress && Rva
		< lolpe.ImageSectionHeaders[o].VirtualAddress + 
		Math.Max(lolpe.ImageSectionHeaders[o].VirtualSize, lolpe.ImageSectionHeaders[o].SizeOfRawData))
		{
		return (UInt32)(Rva - lolpe.ImageSectionHeaders[o].VirtualAddress) + 
		(lolpe.ImageSectionHeaders[o].PointerToRawData);
		}
	
	}
    throw new Exception("Failed to convert RVA");
}

1453959088604

How would this be done dynamically? Many video game trainers do this kind of work. You’re basically obtaining the base address and calculating the offset of code in memory and performing peeks and pokes (Read/WriteProcessMemory) to modify program code. Unfortunately, this means you have to roll your own implementation system, essentially re-inventing the wheel. I ain’t about to do that. Enter detours.

The egg-heads at Microsoft have shared with us the Detours library. The library provides a way to patch binaries live in memory without the use of a debugger and all in glorious C. I’m all for that. How it works is it employs the use of ‘trampolines’ that patch the first 5 bytes of a target api or sub procedure or function pointer to perform a long ‘jmp’ to controlled code. The code of your choosing is patched, and when you return, the bytes are restored. Sounds simple enough right? Its much easier when you can just call a library function to do this for you. One of my friends is like “joe, just roll your own” and I hate re-inventing the wheel.

1457578413917

Here’s a very basic program (compiled to a dll) that illustrates the use of the detours library:

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


// Target pointer for the uninstrumented Sleep API.
static VOID(WINAPI * TrueSleep)(DWORD dwMilliseconds) = Sleep;
// Detour function that replaces the Sleep API.
VOID WINAPI AltSleep(DWORD dwMilliseconds)
{
	FatalAppExitA(0, "I HATE SLEEPZ!");
}

// DllMain function attaches and detaches the AltSleep detour to the
// Sleep target function.  The Sleep target function is referred to
// through the TrueSleep target pointer.
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&)TrueSleep, AltSleep);
		DetourTransactionCommit();
	}
	else if (dwReason == DLL_PROCESS_DETACH) {
		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourDetach(&(PVOID&)TrueSleep, AltSleep);
		DetourTransactionCommit();
	}
	return TRUE;
}

Note the dummy external function ‘DoNothingAlready’ in the dll. You need at least 1 export present for this to work.

The program I’m going to be modifying is my old keygen solution from an older project. I’ll be catching the Sleep API within the ‘GetKey’ function and replacing it with a call to ‘FatalAppExitA’.

#include <windows.h>
#include <stdio.h>
#define LoWord(l) ((WORD)(l))
#define HiWord(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF)) // pelles doesnt support hi / lo words 
// which just splits and shifts the double word. whatever. 
unsigned long GenKey(DWORD);

int main(int argc, char *argv[])
{
	char root[MAX_PATH] = "C:\\";
	char volname[MAX_PATH];
	DWORD serial, maxname, flags;
	char fsName[128];
	DWORD fsNameLength;	fsNameLength = sizeof(fsName)-1;
	GetVolumeInformation(root, volname, MAX_PATH,&serial, &maxname, &flags, fsName, fsNameLength);
	printf("Name of System: %s\r\n",fsName);
	if(volname[0] == '\0') // lol null
	{
	printf("No Volume name present\r\n");
	}
	else
	{
	printf("Volume Name: %s\r\n",volname);
	}
	printf("Volume Serial Number: %x\r\n",serial);
	printf("Your Special key: %x\r\n",GenKey(serial));
	Sleep(50000);
	system("pause");
	return 0;
}

unsigned long GenKey(DWORD serial)
{
Sleep(100000);
DWORD seed1 = 4096;
DWORD seed2 = HiWord(serial);
DWORD retme = seed1 ^ serial * seed2;
return retme;
}

How do we get our dll to run inside this program? Basic dll injection wont work, so we have to use a special API to launch the process along side our dll to make this run.

Our launcher employs the ‘DetourCreateProcessWithDllEx’ function from the detours lib to launch our special dll and program side by side:

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

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc < 3)
	{
		printf("Error, usage is %s 'target process' 'dll'\r\n", argv[0]);
		return 1;
	}
	_TCHAR *prog = argv[1];
	
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&si, sizeof(si));
	ZeroMemory(&pi, sizeof(pi));
	si.cb = sizeof(si);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = SW_SHOW;
	if (!DetourCreateProcessWithDllEx(prog,
		NULL, NULL, NULL, TRUE,
		CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED,
		NULL, NULL, &si, &pi,
		"ayy.dll", NULL))
	{
		MessageBox(0, L"failed", 0, 0);
		return 1;
	}
	else
	{
		ResumeThread(pi.hThread);

		WaitForSingleObject(pi.hProcess, INFINITE);

		CloseHandle(&si);
		CloseHandle(&pi);
	}

	
	return 0;
}

Here’s a quick video of this in action:

This example shows how you modify programs where you have API and function names as well as source. Where’s the fun in that? Let’s modify some stuff without the benefit of source code.

Here we have Audacity open in IDA. I chose the sub routine responsible for when you hit the about button.

idad

Now that we have our address ‘0x40c910’, we can throw that into our detour dll and rock / roll. The code looks something like this:

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

typedef int (WINAPI *pFunc)(int,int);
int WINAPI MyFunc(int,int);
pFunc FuncToDetour = (pFunc)(0x40C910); //Set it at address to detour in

int WINAPI MyFunc(int a, int b)
{
	MessageBox(NULL, L"Audacity rocks!", L"Joe was here", MB_OK);
	return 4;
}
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;
}

Here we again have a video of what this looks like:

There you have it. Maybe later I’ll show you all how to use detours to cheat at age of empires and piss off family members.

1449004430529

One thought on “Detours, Trampolines, and Code Caves

Leave a Reply

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