Results 1 to 1 of 1

Thread: Dll Cloaking

  1. #1
    Dwar
    Dwar is offline
    Veteran Dwar's Avatar
    Join Date
    2010 Mar
    Posts
    2,222
    Thanks Thanks Given 
    211
    Thanks Thanks Received 
    2,230
    Thanked in
    292 Posts
    Rep Power
    10

    Dll Cloaking

    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 independent 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.

    Original code by Darawk
     #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;
    }

    But CloakDLL_Stub does not work anymore with newer VS compilers.
    Modified CloakDll by Exzap
     //Modified CloakDll    -    original by Darawk, edited by Exzap
    #include<Windows.h>

    #pragma pack(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;

    #define UNLINK(x) (x).Blink->Flink = (x).Flink;
    (x).Flink->Blink = (x).Blink;


    bool HideDLL( HMODULE hDLL )
    {
    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 != hDLL)
    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
    for(int i=0; i<module->fullPath.Length; i++)
    ((unsigned char*)module->fullPath.Buffer)[i] = 0;
    // Zero out the memory of this module's node
    for(int i=0; i<sizeof(ModuleInfoNode); i++)
    ((unsigned char*)module)[i] = 0;
    return true;
    }
    Please, post your questions on forum, not by PM or mail

    I spend my time, so please pay a little bit of your time to keep world in equilibrium

  2. The Following User Says Thank You to Dwar For This Useful Post:


Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •