BaDu

BaDu est basé sur un code que j'avais écrit il y a longtemps et que j'ai remanié pour l'occasion. Il se base sur le PE Header de l'executable en dur, ou en mémoire.

Le principe du dumper est simple :

  1. Récupération du PE Header à partir de l'exe en dur (GetModuleFileNameA + CreateFileA) ou en mémoire
  2. Récupération de la taille de l'exe en mémoire, création d'une page mémoire de même taille
  3. Copie de l'intégralité de l'exe en mémoire dans cette page, remplacement du PE en mémoire par le PE en dur si le flag GetPEFromDisk est set
  4. Recherche de la taille du PE Header et des sections à partir de la taille en mémoire décrémentée tant que le byte section[taille] == 0, alignement de la taille sur FileAlignement mis à 0x200
  5. Modification du PE Header (IT, Entry Point ...)
  6. Copie sur le disque

voila le code :

  1. // récupère une copie de l'executable en mémoire
  2. char* getPreDump(char* ImageBase, BOOL GetPEFromDisk)
  3. {
  4. char modulePath[MAX_PATH+1];
  5. HANDLE hFile;
  6. DWORD AllocSize = 0;
  7. char* Dump;
  8. PIMAGE_DOS_HEADER pDosHeader;
  9. PIMAGE_NT_HEADERS pPE;
  10. PIMAGE_SECTION_HEADER pSectionHeaders;
  11. DWORD SectionHeadersSize,NumberOfBytesRead;
  12. IMAGE_DOS_HEADER DosHeader;
  13. IMAGE_NT_HEADERS PE;
  14. int len;
  15.  
  16. if (GetPEFromDisk)
  17. {
  18. pDosHeader = &DosHeader;
  19. pPE = &PE;
  20. // on récupère le nom du fichier
  21. if (((len = GetModuleFileNameA((HMODULE) ImageBase, modulePath, MAX_PATH + 1)) >= MAX_PATH) || (!len))
  22. return NULL;
  23. // on récupère le PE Header depuis le disque
  24. if ((hFile = CreateFileA(modulePath,(GENERIC_READ),FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL)) == INVALID_HANDLE_VALUE)
  25. return NULL;
  26.  
  27. SetFilePointer(hFile,0,0,FILE_BEGIN);
  28. ReadFile(hFile,&DosHeader,sizeof(IMAGE_DOS_HEADER),&NumberOfBytesRead,NULL);
  29. if (NumberOfBytesRead != sizeof(IMAGE_DOS_HEADER))
  30. return NULL;
  31.  
  32. SetFilePointer(hFile,DosHeader.e_lfanew,0,FILE_BEGIN);
  33. ReadFile(hFile,&PE,sizeof(IMAGE_NT_HEADERS),&NumberOfBytesRead,NULL);
  34. if (NumberOfBytesRead != sizeof(IMAGE_NT_HEADERS))
  35. return NULL;
  36.  
  37. SectionHeadersSize = sizeof(IMAGE_NT_HEADERS)*PE.FileHeader.NumberOfSections;
  38. pSectionHeaders = (PIMAGE_SECTION_HEADER)malloc(SectionHeadersSize);
  39.  
  40. SetFilePointer(hFile,DosHeader.e_lfanew + sizeof(IMAGE_FILE_HEADER) + PE.FileHeader.SizeOfOptionalHeader + sizeof(DWORD),0,FILE_BEGIN);
  41. ReadFile(hFile,pSectionHeaders,SectionHeadersSize,&NumberOfBytesRead,NULL);
  42. if (NumberOfBytesRead != SectionHeadersSize)
  43. {
  44. free(pSectionHeaders);
  45. return NULL;
  46. }
  47. }
  48. else
  49. {
  50. pDosHeader = (PIMAGE_DOS_HEADER)&ImageBase;
  51. pPE = (PIMAGE_NT_HEADERS)(ImageBase + pDosHeader->e_lfanew);
  52. pSectionHeaders = (PIMAGE_SECTION_HEADER)(ImageBase + pDosHeader->e_lfanew + sizeof(IMAGE_FILE_HEADER) + pPE->FileHeader.SizeOfOptionalHeader + sizeof(DWORD));
  53. }
  54.  
  55. AllocSize = pSectionHeaders[PE.FileHeader.NumberOfSections-1].VirtualAddress + pSectionHeaders[PE.FileHeader.NumberOfSections-1].Misc.VirtualSize;
  56.  
  57. Dump = (char *)VirtualAlloc(NULL,AllocSize,MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE);
  58. if (! Dump)
  59. return NULL;
  60.  
  61. CopyMemory(Dump, ImageBase,AllocSize);
  62. if (GetPEFromDisk)
  63. {
  64. CopyMemory(Dump, &DosHeader, sizeof(IMAGE_DOS_HEADER));
  65. CopyMemory(Dump+DosHeader.e_lfanew, &PE, sizeof(IMAGE_NT_HEADERS));
  66. CopyMemory(Dump+DosHeader.e_lfanew + sizeof(IMAGE_FILE_HEADER) + PE.FileHeader.SizeOfOptionalHeader + sizeof(DWORD), pSectionHeaders, SectionHeadersSize);
  67.  
  68. free(pSectionHeaders);
  69. }
  70.  
  71. return Dump;
  72. }
  73.  
  74. DWORD AlignSize(DWORD size, DWORD alignement)
  75. {
  76. return (size%alignement == 0) ? size : ((size / alignement) +1)*alignement;
  77. }
  78.  
  79. BOOL Dump(char* ImageBase, DWORD ITRVA, DWORD ITLen, DWORD EntryPoint, BOOL GetPEFromDisk)
  80. {
  81. PIMAGE_DOS_HEADER pDosHeader;
  82. PIMAGE_NT_HEADERS pPE;
  83. PIMAGE_SECTION_HEADER pSection;
  84. DWORD NbByteWritten;
  85. DWORD curseur,i;
  86. char* Dump;
  87. char modulePath[MAX_PATH+8];
  88. int len;
  89. HANDLE hFile;
  90.  
  91. // on récupère le nom du fichier
  92. if (((len = GetModuleFileNameA((HMODULE) ImageBase, modulePath, MAX_PATH + 1)) >= MAX_PATH) || (!len))
  93. return FALSE;
  94.  
  95. Dump = getPreDump(ImageBase, GetPEFromDisk);
  96. if (! Dump)
  97. return FALSE;
  98.  
  99. pDosHeader = (PIMAGE_DOS_HEADER)Dump;
  100. pPE = (PIMAGE_NT_HEADERS)(Dump+pDosHeader->e_lfanew);
  101. pSection = (PIMAGE_SECTION_HEADER)((PCHAR)pPE + sizeof(IMAGE_FILE_HEADER) + pPE->FileHeader.SizeOfOptionalHeader + sizeof(DWORD));
  102.  
  103. pPE->OptionalHeader.FileAlignment = 0x200;
  104. for (curseur = AlignSize(pPE->OptionalHeader.SizeOfHeaders, pPE->OptionalHeader.FileAlignment)-1; ! Dump[curseur]; curseur --);
  105.  
  106. curseur = AlignSize(curseur+1, pPE->OptionalHeader.FileAlignment);
  107. pPE->OptionalHeader.SizeOfHeaders = curseur;
  108. //on copie maintenant les sections
  109. for (i = 0; i < pPE->FileHeader.NumberOfSections; i++)
  110. {
  111. CopyMemory(Dump+curseur,Dump+pSection[i].VirtualAddress,pSection[i].Misc.VirtualSize);
  112. pSection[i].PointerToRawData = curseur;
  113. curseur += pSection[i].Misc.VirtualSize-1;
  114. while((Dump[curseur] == 0) && (((int)curseur) >= -1))
  115. curseur --;
  116. curseur = AlignSize(curseur+1,pPE->OptionalHeader.FileAlignment);
  117. pSection[i].SizeOfRawData = curseur - pSection[i].PointerToRawData;
  118. }
  119.  
  120. pPE->OptionalHeader.DataDirectory[1].VirtualAddress = ITRVA;
  121. pPE->OptionalHeader.DataDirectory[1].Size = ITLen;
  122. pPE->OptionalHeader.AddressOfEntryPoint = EntryPoint;
  123.  
  124.  
  125. modulePath[len-4] = '-';
  126. modulePath[len-3] = 'd';
  127. modulePath[len-2] = 'u';
  128. modulePath[len-1] = 'm';
  129. modulePath[len] = 'p';
  130. modulePath[len+1] = 'e';
  131. modulePath[len+2] = 'd';
  132. modulePath[len+3] = '.';
  133. modulePath[len+4] = 'e';
  134. modulePath[len+5] = 'x';
  135. modulePath[len+6] = 'e';
  136. modulePath[len+7] = 0;
  137.  
  138. if ((hFile = CreateFileA(modulePath,(GENERIC_READ | GENERIC_WRITE),FILE_SHARE_READ | FILE_SHARE_READ,NULL,CREATE_ALWAYS,0,NULL)) == INVALID_HANDLE_VALUE)
  139. return FALSE;
  140.  
  141. WriteFile(hFile,Dump,curseur,&NbByteWritten,NULL);
  142. if (NbByteWritten != curseur)
  143. return FALSE;
  144. return TRUE;
  145. }

Rien de transcendant comme vous pouvez le voir mais ca fait son boulot :P si vous voyez des trucs pas clairs ou des problèmes potentiels n'hésitez pas. Si vous rencontrez des problèmes avec l'utilisation de ce code, n'hésitez pas encore à me le signaler, je n'ai testé ce code qu'avec GetPEFromDisk à TRUE.

BINI

BINI est basé sur une lib que j'avais dev pour StarForce : BANG (Baboon Api Name Getter (on se refait pas ...)) Cette lib parse l'ensemble des modules chargés en mémoire et récupère l'ensemble des fonctions exportées associées aux modules qui les exportent. Cette lib supporte les forward exports et est donc toute indiquée pour faire un ImpRec like.

BINI fonctionne assez simplement elle aussi, on lui passe l'image base du module, l'adresse de l'IAT, sa taille, l'adresse à laquelle on veut que l'IT soit reconstruite et enfin un pointeur sur un DWORD qui contiendra la taille de l'IT. BINI ne permet pas de créer de nouvelle section, elle stocke les noms des APIs, des DLLs ainsi que toute la structure de l'IT dans une section existante. Je sais que ca fait une sacrée limitation mais bon ... je vous fais confiance pour arranger mon code :P

Avant de commencer à reconstruire l'IT, BINI commence par vérifier si l'IT est cohérente et calcule en même temps la taille que va prendre les noms des DLLs, des APIs ainsi que les thunks. Voila le fonctionnement générale de la fonction :

  • On récupère le module à partir de la première adresse
    • Pour chaque thunk :
      • Si l'API n'appartient pas au module :
        • on récupère le module de l'API
        • Si l'une des API précédentes n'appartient pas au même module : on retourne faux
        • Sinon on update la valeur du module
      • Si le thunk est nul c'est qu'on passe à un autre module :
        • On ajoute alors la taille du nom du module à la taille de l'ensemble des noms de modules
        • Pour chaque API on ajoute la taille du nom de l'API (si il existe) à la taille de l'ensemble des noms d'APIs
      • Sinon on passe au thunk suivant
      • On incrémente la taille prise par les thunks
  • En fin on retourne une liste chainée contenant les informations nécessaires à la reconstruction de l'IT

Cette manière de faire permet de récupérer le module correspondant à un groupe d'API même si il contient des forward exports. voila le code correspondant :

  1. typedef struct _IMPORT_DIRECTORY {
  2. PDLL module;
  3. PDWORD start;
  4. struct _IMPORT_DIRECTORY *next;
  5. } IMPORT_DIRECTORY, *PIMPORT_DIRECTORY;
  6.  
  7. void FreeImportDirectories(PIMPORT_DIRECTORY importDirectories)
  8. {
  9. PIMPORT_DIRECTORY c;
  10. PIMPORT_DIRECTORY d;
  11.  
  12. for (c = importDirectories; c; c = d)
  13. {
  14. d = c->next;
  15. free(c);
  16. }
  17. }
  18.  
  19. PIMPORT_DIRECTORY CheckIAT(PDWORD start, PDWORD end, PDWORD ModuleNamesLength, PDWORD APINamesLength, PDWORD ThunksLen)
  20. {
  21. PDWORD thunk;
  22. PIMPORT_DIRECTORY retval = NULL;
  23. PIMPORT_DIRECTORY directory = NULL;
  24. PIMPORT_DIRECTORY prevdirectory;
  25.  
  26. *ModuleNamesLength = 0;
  27. *APINamesLength = 0;
  28. *ThunksLen = 0;
  29.  
  30. for(thunk = start; thunk <= end; thunk ++)
  31. {
  32. if (* thunk)
  33. {
  34. PDWORD t;
  35. prevdirectory = directory;
  36. directory = (PIMPORT_DIRECTORY)malloc(sizeof(IMPORT_DIRECTORY));
  37. if (! directory)
  38. {
  39. FreeImportDirectories(retval);
  40. return NULL;
  41. }
  42. if (! retval)
  43. retval = directory;
  44. if (prevdirectory)
  45. prevdirectory->next = directory;
  46.  
  47. directory->module = GetModule(*thunk);
  48. directory->start = thunk;
  49. directory->next = NULL;
  50. if (! directory->module)
  51. {
  52. FreeImportDirectories(retval);
  53. return NULL;
  54. }
  55. for(; *thunk; thunk++)
  56. {
  57. *ThunksLen += 4;
  58. if (! GetApi(*thunk, directory->module))
  59. {
  60. directory->module = GetModule(*thunk);
  61. if (! directory->module)
  62. {
  63. FreeImportDirectories(retval);
  64. return NULL;
  65. }
  66. for (t = directory->start; t < thunk; t ++)
  67. if (! GetApi(*t, directory->module))
  68. {
  69. FreeImportDirectories(retval);
  70. return NULL;
  71. }
  72. }
  73. }
  74. *ThunksLen += 4;
  75. *ModuleNamesLength += strlen(directory->module->Name) +1;
  76. for (t = directory->start; *t; t++)
  77. {
  78. PAPI api = GetApi(*t,directory->module);
  79. if (api->Name)
  80. *APINamesLength += strlen(api->Name)+3;
  81. }
  82. }
  83. }
  84. return retval;
  85. }

Une fois cette étape faite, la reconstruction de l'IT est très simple, il suffit de copier les noms des modules un par un ainsi que les noms des APIs et de construire les structures IMAGE_IMPORT_DESCRIPTOR et IMAGE_IMPORT_BY_NAME. encore une fois voici le code correspondant :

  1. DWORD ReconstructImportTable(PBYTE ImageBase, PDWORD IATAddress, DWORD len, PBYTE ITaddress, PDWORD ITLen)
  2. {
  3. PIMPORT_DIRECTORY directories;
  4. PIMPORT_DIRECTORY directory;
  5. DWORD ModuleNamesLength;
  6. DWORD APINamesLength;
  7. DWORD ThunksLen;
  8. char* ModuleName = ITaddress;
  9. char* APIName;
  10. PDWORD originalthunk;
  11. PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
  12.  
  13. *ITLen = 0;
  14. directories = CheckIAT(IATAddress, IATAddress + (len >> 2), &ModuleNamesLength, &APINamesLength, &ThunksLen);
  15. if (!directories)
  16. return 0;
  17.  
  18. APIName = ModuleName + ModuleNamesLength;
  19. originalthunk = (PDWORD)(APIName + APINamesLength);
  20. ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(((char*)originalthunk) + ThunksLen);
  21.  
  22. for (directory = directories; directory; directory = directory->next)
  23. {
  24. PDWORD thunk;
  25. int i = strlen(directory->module->Name)+1;
  26.  
  27. ImportDescriptor->Name = (DWORD)(ModuleName-ImageBase);
  28.  
  29. CopyMemory(ModuleName, directory->module->Name, i);
  30. ModuleName += i;
  31.  
  32. ImportDescriptor->OriginalFirstThunk = (DWORD)(((PBYTE)originalthunk)-ImageBase);
  33. ImportDescriptor->TimeDateStamp = 0;
  34. ImportDescriptor->ForwarderChain = 0;
  35. ImportDescriptor->FirstThunk = (DWORD)(((PBYTE)directory->start)-ImageBase);
  36.  
  37. for (thunk = directory->start; *thunk; thunk ++, originalthunk++)
  38. {
  39. PAPI api = GetApi(*thunk, directory->module);
  40.  
  41. if (api->Name)
  42. {
  43. int i = strlen(api->Name) +1;
  44. *(PWORD)APIName = api->Ordinal;
  45. CopyMemory(APIName+2, api->Name, i);
  46. *thunk = (DWORD)(APIName - ImageBase);
  47. *originalthunk = (DWORD)(APIName - ImageBase);
  48. APIName += i+2;
  49. }
  50. else
  51. {
  52. *thunk = 0x80000000 | (((DWORD)api->Ordinal) & 0xFFFF);
  53. *originalthunk = 0x80000000 | (((DWORD)api->Ordinal) & 0xFFFF);
  54. }
  55. }
  56. *originalthunk = 0;
  57. originalthunk++;
  58. ImportDescriptor ++;
  59. *ITLen += sizeof(IMAGE_IMPORT_DESCRIPTOR);
  60. }
  61. FreeImportDirectories(directories);
  62.  
  63. return (DWORD)(ITaddress + ModuleNamesLength + APINamesLength + ThunksLen - ImageBase);
  64. }

L'ensemble des codes ainsi que les libs sont disponibles en annexe. Si vous avez des idées d'améliorations, si vous rencontrez des bugs ou si vous voulez me donner plein de sousous pour que je puisse me payer un conseiller en com qui me trouvera des noms moins foireux : [mon super nick de singe] {at} lyua {point} org