Вы находитесь на странице: 1из 10

SimpleSEH © 2005 mcMike

Example of using SEH , detours and hardware-breakpoints


for writing injected DLL-hacks.

© 2005 mcMike
Many Thanks to bf194Lover from MPC for showing the path ;)

This tutorial will teach how to use StructuredExceptionHandling and Detours-library for writing
hacks. The hack will be built as a DLL-file and injected into target-process. From there it will add
new UnhandledExceptionFilter and set hardware-breakpoint to branch target-code execution to
our own function.

Tools needed: Visual Studio 6 (VC6++)


DLL-injector (example winject.exe from MPC-forums)
OllyDbg is helpful tool to check what’s happening.
OS: Windows XP SP2 (should work in others too)

The Example
As a learning target we use the most hardest , unbreakable, anti-hack-cabable Windows Notepad.
We will render useless it’s feared ability to display About-dialog with single hardware-breakpoint.

Below you see that in the address 0x0100334f the code will set some variables and call
ShellAboutW(). We set breakpoint to that address and make it jump 0x18 bytes forward to skip
that block all together. (similar to many common hacks where you would just NOP some lines).

We don’t actually patch the JMP instruction but set hardware-breakpoint and skip 0x18 bytes by
moving EIP (instruction pointer) forward. The effect is same than with jmp or simply NOPping
those lines.

Page 1 of 10
SimpleSEH © 2005 mcMike

The Background

“What the heck is SEH, Detours, Injecting ..?”

SEH
SEH stands for Structured Exception Handling. You can think it as Visualbasic’s ON ERROR -
GOTO method of handling runtime errors. There are several types of exceptions from floating-
point errors like division by zero to Access Violation etc. These exceptions also include
breakpoint-exceptions which are set by purpose.

The application (you) provides UnhandledExceptionFilter which is simply a callback-function to


be called when exception occurs. This function receives EXCEPTION_RECORD-structure as a
parameter which contains valuable information of exception-type, place where it occurred, all the
register-values among few other things. By modifying this struct you can for example change the
values of registers or change the EIP (Instruction Pointer) and let the process continue execution
from different place. (i.e skip some instructions like good’old NOP).

DEBUG-Registers
DEBUG-registers are physical registers inside processor (huh. really?) which provides several
helpful methods to debug programs at runtime. One of these methods is hardware-breakpoints.
See; software breakpoints (int3) are just a single byte written in the target code which will
replace original instruction under it. Because that byte will change the opcode(s) they are easy to
detect by game or external anti-cheat software. Also the debugger needs to handle restoring
those breakpoints at runtime.

Hardware-Breakpoints as name indicates are not written to code but kept in the DEBUG-registers.
There are 4 separate breakpoints available (by chaining you can have more). They are non-
intrusive i.e. do not change the code and therefore are not detected with same methods as
software breakpoints.
To access Debug-registers Windows API provides 2 functions SetThreadContext() and
GetThreadContext(). With first one you set the breakpoints and with latter you can read them.

// Let’s set some HW-breakpoints


// Create new CONTEXT stuct and set the flag for debug-registers altering.

CONTEXT ctx = {CONTEXT_DEBUG_REGISTERS};


ctx.Dr6 = 0x00000000;

ctx.Dr0 = 0x4001000; // Set Address of Breakpoint 1


ctx.Dr7 = 0x00000001; // Activate Breakpoint 1

ctx.Dr1=0x4002000; // Set Address of Breakpoint 2


ctx.Dr7 |= 0x00000004; // Activate Breakpoint 2

ctx.Dr2=0x4003000; // Set Address of Breakpoint 3


ctx.Dr7 |= 0x00000010; // Activate Breakpoint 3

ctx.Dr3=0x4004000; // Set Address of Breakpoint 4


ctx.Dr7 |= 0x00000040; // Activate Breakpoint 4

// Write the values to DEBUG-register. From now on the breakpoints are active
SetThreadContext(GetCurrentThread(), &ctx);

Page 2 of 10
SimpleSEH © 2005 mcMike

Injecting
In order to use SEH and HW-breakpoints we need to write a DLL and get it somehow inside
target-process. There are several methods to do it but here we use tool called winject.exe which
will handle the injecting for us. You can download it from MPC-forums.

Just select “Target Process” and “DLL to Inject” and click Inject –button. The injection is
automaticly verified. After you have injected the DLL you can also manually check that it indeed
is present in target process loaded modules. To do this just click […] –button in the rightside of
Target Process-combo. This will open Process Information –dialog to display some information
about selected process. From Loaded Modules-list you can see that the DLL we injected is loaded
by target.
TIP: With Winject you can also Eject the loaded modules. This is very useful since you don’t have
to terminate or close the target process before reinjecting. (For example while you are
developing new DLL you can have winject running with process and DLL selected. Once you build
new version just click inject and it’s ready to test).

All the magic (the hack) happens inside DLL and the launcher can be closed right after the
injecting is done.

Injecting Theory
Injecting a DLL means that the injector will allocate memory with VirtualAllocEx() inside target-
process for code and data-blocks. Then the function (code) and the data (name of the library to
be loaded) is written to those memory-areas with WriteProcessMemory(). Finally new thread is
started with CreateRemoteThread() and passing the pointer to written function and data.

Fortunately you can skip all this by using winjector but you need to understand the threading
model of it; This new thread will call LoadLibrary() and load our DLL into target-process. The OS-
loader will call our DLLmain() -function after it is loaded. From there our DLL will do some simple
initializing – but only simple. There are only very limited things we can do in DLLmain() for more
information read “DllMain : a horror story” from
http://blogs.msdn.com/oleglv/archive/2003/12/12/43069.aspx

Page 3 of 10
SimpleSEH © 2005 mcMike

Activating SEH
To activate exception handling we just add our new handler (function) to SEH-chain. This is done
with SetUnhandledExceptionFilter(fnName) API-function. The fnName is name of the function that
we provide.

// Add our ExceptionFilter to SEH-chain


SetUnhandledExceptionFilter(UnhandlerExceptionFilter);

// This is our function to be called when exception occurs.


LONG WINAPI UnhandlerExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo)
{
// HW-breakpoints DON'T generate EXCEPTION_BREAKPOINT but EXCEPTION_SINGLE_STEP
// so we check for that
if(ExceptionInfo->ExceptionRecord->ExceptionCode==EXCEPTION_SINGLE_STEP )
{
// Verify that the breakpoint was the one we set
if ((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress==dwBreakPoint)
{
// move instruction pointer forward to skip unwanted instructions and let
// the process continue as nothing has happened
ExceptionInfo->ContextRecord->Eip+=nBreakPointJump;
return EXCEPTION_CONTINUE_EXECUTION;
}
}

// Some other exception occured. Pass it to next handler


return EXCEPTION_CONTINUE_SEARCH;
}
}

Very simple but unfortunately we can’t simply do this in DLLMain() because we are doing the
injecting with CreateRemoteThread() –method.

Exception handling is per thread basis so we can’t add our new handler from DLLmain().
Remember it is called by OS-loader which is just a function that LoadLibrary() calls. AND we have
initiated Loadlibrary call from separate thread we created. So if we would add SEH from DLLmain
it would handle only exceptions for that thread – not the target process. That would not be very
useful. Also the loader thread is terminated right after the DLL is loaded.
So obviously we need to think something else. We need to force the target process to call a
function in our DLL in order to activate SEH from there. This is where we come to Api-hooking.

Page 4 of 10
SimpleSEH © 2005 mcMike

API-Hooking

API-hooking means that we hijack


some windows API-functions so
that when application is calling that
function it will jump to our function
instead. To put it simple; we will
write 5 byte long jumpgate in the
entrypoint of API-function. From
there we can simulate the original
API and interference the
parameters or return-value and
even do something fancy.
So how do we know what API’s is
target using? There is a nice tool
called Dependency Walker which is
included in the SDK-tools and new version can be downloaded from
http://www.dependencywalker.com/ . With this tool we simply open the target.exe and check
what API’s it is using.

So in order to get target-process to call our function let’s pick GetTickCount() –API to hijack and
set our exception handler from there. We only need this “callback” once and after the SEH is set
we instantly remove the hijacking since it is no longer needed. We also hijack
GetThreadContext() to hide our altering of debug-registers (because we know that notepad is
active little devil to check itself for hacks ☺ ). The actual hooking we do with detours-library…

Detours
Detours is a library (detours.lib & detours.h) from Microsoft which provides easy and simple way
to hook (hijack) Windows API-functions. It contains lots of useful functions but here we
concentrate only couple of them. DetourFunction() is used when you simply want to Hijack some
API-function with your own Detour-function. This new function will provide the same functionality
than original did (+ maybe some other “useful” features ☺ ). To hijack GetTickCount() we just:
DetourFunction((PBYTE)GetTickCount,(PBYTE)GetTickCount_Detour);

The GetTickCount_Detour() is the function which we write. It needs to be exactly same type than
the original (i.e. return type, arguments and calling convention). After this hooking if and when
target-application (or we) calls GetTickCount() it will actually land to GetTickCount_Detour().
From there we will set the our SEH.

DWORD WINAPI GetTickCount_Detour()


{
Set_SEH_and_BreakPoints(); // Set the stuff in our other function

// remove hook by patching original bytes back


WriteProcessMemory(GetCurrentProcess(),(LPVOID)GetProcAddress(
GetModuleHandle("Kernel32"),"GetTickCount"),&opcodes,5,0);

return GetTickCount(); // return the original API-ret


}

Unfortunately Detours doesn’t provide function to remove hook if there wasn’t trampoline for it
(at least I’m not aware of it). So we will use our own method to remove it. Before hooking we
just read the 5 first bytes of original function’s entrypoint and store them. After the first call to
our detoured function we write those 5 bytes back to remove the hook.

Page 5 of 10
SimpleSEH © 2005 mcMike

DetourFunctionWithTrampoline() is used if you also wish to call original API-function within your
Detour-function. See; You can’t simply call original function directly from your detour-function
since that would lead to infinite recursive-loop. Thus we need Trampoline-function which is used
as a trampoline to original API (hence the name). Detours-library provides simple MACRO to
automaticly build “invisible” trampoline-function so you don’t actually have to write it by yourself.

We just define:
DETOUR_TRAMPOLINE(BOOL WINAPI GetThreadContext_Trampoline(HANDLE ,LPCONTEXT) ,GetThreadContext);

And then in the code we hook the API with:


DetourFunctionWithTrampoline((PBYTE)GetThreadContext_Trampoline,(PBYTE)GetThreadContext_Detour);

We don’t actually see the GetThreadContext_Trampoline() anywhere since it is build for us by


Detours. But obviously we need to write GetThreadContext_Detour() like we did for
GetTickCout_Detour(). Again it needs to be exactly same type as original API. In the hooked
GetThreadContext() we will fake the debug-registers:

BOOL WINAPI GetThreadContext_Detour (HANDLE hThread,LPCONTEXT lpContext)


{
// Get the Real values from original API-function. See we call the
// _Trampoline –function to access original API
BOOL ret=GetThreadContext_Trampoline( hThread, lpContext);

// If caller is interested in Debug-registers return fake values


if (lpContext->ContextFlags && CONTEXT_DEBUG_REGISTERS) {
lpContext->Dr0=0;
lpContext->Dr1=0;
lpContext->Dr2=0;
lpContext->Dr3=0;
lpContext->Dr6=0;
lpContext->Dr7=0;
}
return ret;
}

Whoooops…. That’s it !

We have covered how to use SEH , how to get DLL injected, how to set HW-breakpoints and how
to hijack API-functions. Next we will move to ultimate beginners guide for how to setup the
project in VC6++ and write a DLL.

Page 6 of 10
SimpleSEH © 2005 mcMike

The Project setup


We start from the ground. Get your VC++ up and running and let’s begin.

1) Create new Project “Win32 Dynamic-Link Library”. Choose name and location for project and
click OK. Next select “An empty DLL project” and click Finish.

2) Copy included SEH_example.cpp to your project-directory which project-wizard created.

3) From http://research.microsoft.com/sn/detours/ download latest detours-library and unpack


it. Copy the detours.h and detours.lib to your project-directory.

4) Add SEH_example.cpp to your project. Right-click over source-files and choose “Add Files to
Folder”. Pick SEH_example.cpp –file.

6) From Project->Settings->C/C++->Precompiled Headers choose “Not using precompiled


headers” and from Project->Settings->Link->General add detours.lib to “Object/library
modules:”

Page 7 of 10
SimpleSEH © 2005 mcMike

7) Select build target: “Win32 Release” and build the SimpleSEH.dll from build-button or from
Build-menu “Rebuild all”.

You should now see message in the bottom of visual studio that SimpleSEH.dll is build. By default
it will placed in your project-directory\release –subdir.

8) Start winject and select simple_SEH.dll for “DLL to Inject” also start notepad and select it as
“Target Process”. Click Inject-button and you should see following messagebox popping up. This
indicates that our DLL is loaded in target-
process and our DLLmain() –function has
been called.

9) You propably realized by now that our hardware-breakpoints nor SEH-handler were not set
yet. So you can still launch about-menu as usual. This is because Notepad have not called
GetTickCount() yet. To make it happen
just invoke help from help-menu. You
should see the next message box
popping out telling that it finally has set
the breakpoints and SEH-handler. Now
try again the About-box and see that we
have beaten the feared notepad with our
Structured-Exception- Handling-
Hardware-Breakpoint-API-Hooking-hack.

Page 8 of 10
SimpleSEH © 2005 mcMike

The CODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "detours.h"

// Function defines
LONG WINAPI UnhandlerExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo);
DWORD WINAPI GetTickCount_Detour(void);
BOOL WINAPI GetThreadContext_Detour (HANDLE hThread,LPCONTEXT lpContext);
void Set_SEH_and_BreakPoints(void); // Set the SEH and breakpoints
LPTOP_LEVEL_EXCEPTION_FILTER oldHandler=NULL; // Pointer to existing exception handler

// Detour MACRO
DETOUR_TRAMPOLINE(BOOL WINAPI GetThreadContext_Trampoline(HANDLE ,LPCONTEXT) ,GetThreadContext);

// Global variables
DWORD dwBreakPoint=0x100334f; // The hardware-breakpoint (4 available)
int nBreakPointJump=0x18; // How many bytes we make EIP to skip from this breakpoint
BYTE opcodes[5]; // Original opcodes in GetTickCount() entry-point to be stored for restoring

// DLL entrypoint which OS-loader calls for us after injecting


BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(GetModuleHandle(NULL));

// Store original opcodes under GetTickCount()


ReadProcessMemory(GetCurrentProcess(), (LPVOID)GetProcAddress(
GetModuleHandle("Kernel32"),"GetTickCount"),&opcodes,5,0);

// Hijack GetTickCount to jmp to our GetTickCount_Detour


DetourFunction((PBYTE)GetTickCount,(PBYTE)GetTickCount_Detour);

// Hijack also GetThreadContext() to hide debug-registers altering


DetourFunctionWithTrampoline((PBYTE)GetThreadContext_Trampoline,
(PBYTE)GetThreadContext_Detour);
MessageBox(NULL,"We are in and hooked !","SEH_example",0);
break;
case DLL_PROCESS_DETACH:
// Try to remove all hooks and handlers.
DetourRemove((PBYTE) GetThreadContext_Trampoline,(PBYTE) GetThreadContext_Detour);
WriteProcessMemory(GetCurrentProcess(),(LPVOID)GetProcAddress(
GetModuleHandle("Kernel32"),"GetTickCount"),&opcodes,5,0);
if (oldHandler) SetUnhandledExceptionFilter(oldHandler);
break;
}
return true;
}

// Hijacked GetTickCount. This is called when target-app (Notepad) is calling GetTickCount()


DWORD WINAPI GetTickCount_Detour()
{
// From here we add our Structured Exception Handler
// We can't add it in the DLLmain since that function is called in
// different thread-context and the SEH and Breakpoints are per thread basis
Set_SEH_and_BreakPoints();

// Return original bytes to GetTickCount() i.e. unhook it. We only need this "callback" once.
WriteProcessMemory(GetCurrentProcess(),(LPVOID)GetProcAddress(
GetModuleHandle("Kernel32"),"GetTickCount"),&opcodes,5,0);
MessageBox(NULL,"HW-breakpoints are set !","SEH_example",0);

// Return actual function result


return GetTickCount();
}

Page 9 of 10
SimpleSEH © 2005 mcMike

// Add the SEH-handler and set HW-breakpoint(s)


void Set_SEH_and_BreakPoints()
{
// Store existing handler to global variable to reset later
oldHandler=SetUnhandledExceptionFilter(UnhandlerExceptionFilter);

// Set debug-registers for HW-breakpoint and activate it


CONTEXT ctx = {CONTEXT_DEBUG_REGISTERS};
ctx.Dr6 = 0x00000000;

ctx.Dr0 = dwBreakPoint; // Set Address of Breakpoint 1


ctx.Dr7 = 0x00000001; // Activate Breakpoint 1
/*
use these for setting more breakpoints
ctx.Dr1=address; // Set Address of Breakpoint 2
ctx.Dr7 |= 0x00000004; // Activate Breakpoint 2
ctx.Dr2=address; // Set Address of Breakpoint 3
ctx.Dr7 |= 0x00000010; // Activate Breakpoint 3
ctx.Dr3=address; // Set Address of Breakpoint 4
ctx.Dr7 |= 0x00000040; // Activate Breakpoint 4
*/

// Write the values to registers. From now on the breakpoint is active


SetThreadContext(GetCurrentThread(), &ctx);
}

// Our ExceptionHandler
// study the ExceptionInfo-struct for stuff you need
LONG WINAPI UnhandlerExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo)
{
// HW-breakpoints DON'T generate EXCEPTION_BREAKPOINT but EXCEPTION_SINGLE_STEP so we check for that
if(ExceptionInfo->ExceptionRecord->ExceptionCode==EXCEPTION_SINGLE_STEP )
{
// Verify that the breakpoint was the one we set
if ((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress==dwBreakPoint)
{
// move instruction pointer forward to skip unwanted instructions and let
// the process continue as nothing has happened
ExceptionInfo->ContextRecord->Eip+=nBreakPointJump;
return EXCEPTION_CONTINUE_EXECUTION;
}
}

// Some other exception occured. Pass it to next handler


return EXCEPTION_CONTINUE_SEARCH;
}

// Hijacked GetThreadContext(). We don't actually need this in our notepad-example


// I included it just for help since for a real hack you need to fake
// DEBUG-registers so that the game doesn't see they that are altered
BOOL WINAPI GetThreadContext_Detour (HANDLE hThread,LPCONTEXT lpContext)
{
// Get the Real values from original API-function (see the _trampoline)
BOOL ret=GetThreadContext_Trampoline( hThread, lpContext);

// If target is interested in Debug-registers return fake values


if (lpContext->ContextFlags && CONTEXT_DEBUG_REGISTERS) {
lpContext->Dr0=0;
lpContext->Dr1=0;
lpContext->Dr2=0;
lpContext->Dr3=0;
lpContext->Dr6=0;
lpContext->Dr7=0;
}
return ret;
}

Page 10 of 10

Вам также может понравиться