OEP

Comme d'habitude (avec le temps je deviens fainéant), rien de spécial, on lance le soft dans olly (pas de détection avec hide debugger et HideOD) on retrouve l'oep en regardant dans la pile, pas de stolen bytes rien de méchant, on est content.

Il est aussi possible de retrouver l'OEP rapidement à partir de la dernière exception (pour avoir son adresse il suffit de regarder dans le log)

Reconstruction des imports

Il y a deux grandes familles de redirection d'imports dans Armadillo, dans les 2 cas le code de l'ensemble des apis redirigées se trouve dans une page mémoire allouée et soit l'api est émulée soit elle est appelée dans le code de arma, chaque argument va être re-pushé dans la pile ce qui empêche l'utilisation de call fixer se basant sur la valeur de esp pour retrouver l'API. De plus il y a beaucoup de cas particuliers d'APIs qui nécessite un traitement spécial (car elles sont utilisées pour accéder aux APIs Armadillo) ce qui ne facilite pas la tâche.

Les deux familles se distinguent par le fait d'une partie du code est chiffré ou non, j'ai décidé de traiter la première famille avec un ollyscript (relativement rapide) et la seconde à l'aide d'une DLL (comme c'est bizarre et inattendu !)

Enfin Armadillo entre les imports de 2 modules différents remplace le DWORD nul par une fonction pointant sur du code qui fait du caca (loop infini, exit process etc.). Pour remettre les DWORD à 0 je regarde juste si l'adresse avant et l'adresse après appartiennent à un même module.

Voila le code qui fait ca :

  1. PDLL modPrev = GetModule(curseur[-1]);
  2. PDLL modNext = GetModule(curseur[1]);
  3. PAPI api;
  4. if ((! *curseur) || (((modPrev) && (modNext) && (! GetApi(curseur[1],modPrev)) && (! GetApi(curseur[-1],modNext))) || (!curseur[1])))
  5. curseur[0] = 0;

Reconstruction des imports : 1ère famille

Le code exécutant l'API étant chiffré il faut le déchiffrer ... le plus simple est de laisse Armadillo faire le travail à notre place en exécutant son code il ne reste plus qu'à savoir jusqu'où l'exécuter et comment reconnaitre les APIs redirigées de cette façon. Pour la signature j'utilise une série de 3 mov [EBP+X] , Y, Y étant une constante commune à toutes les APIs redirigées de cette façon mais qui change selon les programmes (normal ca pointe sur une zone mémoire allouée), X change selon les APIs. Pour savoir quand arrêter le trace c'est simple c'est quand on rencontre un call edx ou un call [address] et que ce call ne correspond pas à un appel à Enter/LeaveCriticalSection, GetProcAddress étant totalement émulé par Armadillo, on le reconnait justement quand on n'est tombé sur aucun call API. Enfin on oublie pas de rechiffrer le code en sautant au dessus du call sinon Armadillo n'est pas content.

Voila le code d'une fonction redirigée de cette façon :

  1. ;;;;;;;;;;;;;; initialisation ;;;;;;;;;;;;;;
  2. PUSH EBP
  3. MOV EBP,ESP
  4. SUB ESP,40
  5. PUSH EBX
  6. PUSH ESI
  7. PUSH EDI
  8. MOV EAX,1
  9. TEST EAX,EAX
  10. JE SHORT 012E7B9D
  11. PUSH 137BAA0
  12. CALL [1352348] ; ntdll.RtlEnterCriticalSection
  13. PUSH 137BAB8
  14. CALL [1352348] ; ntdll.RtlEnterCriticalSection
  15. MOV ECX,[136CAFC]
  16. MOV [137B790],ECX
  17. ;;;;;;;;;;;;;;;;;;;;; signature ;;;;;;;;;;;;;;;;;;;;;;
  18. MOV DWORD PTR [EBP-10],12C04D0
  19. MOV DWORD PTR [EBP-C],137AFE8
  20. MOV DWORD PTR [EBP-14],137B1E8
  21. ;;;;;;;;;;;;;;;; déchiffrement du code ;;;;;;;;;;;;;;;
  22. MOV EDX,[EBP-C]
  23. MOV EAX,[EDX+7C]
  24. MOV [EBP-18],EAX
  25. CMP DWORD PTR [EBP-18],0
  26. JE SHORT 012E7BFF
  27. MOV ECX,[EBP-18]
  28. CMP DWORD PTR [ECX],0
  29. JE SHORT 012E7BFF
  30. MOV EDX,[EBP-18]
  31. MOV EAX,[EDX]
  32. MOV ECX,[EAX]
  33. SUB ECX,[137B78C]
  34. MOV EDX,[EBP-18]
  35. MOV EAX,[EDX]
  36. MOV [EAX],ECX
  37. MOV ECX,[EBP-18]
  38. ADD ECX,4
  39. MOV [EBP-18],ECX
  40. JMP SHORT 012E7BD8
  41. [...]
  42. MOV EDX,[137B068]
  43. MOV [EBP-2C],EDX
  44. CMP DWORD PTR [EBP-2C],0
  45. JE SHORT 012E7D80
  46. MOV EAX,[EBP-2C]
  47. CMP DWORD PTR [EAX],0
  48. JE SHORT 012E7D80
  49. MOV ECX,[EBP-2C]
  50. MOV EDX,[ECX]
  51. MOV EAX,[EDX]
  52. ADD EAX,[137B78C]
  53. MOV ECX,[EBP-2C]
  54. MOV EDX,[ECX]
  55. MOV [EDX],EAX
  56. MOV EAX,[EBP-2C]
  57. ADD EAX,4
  58. MOV [EBP-2C],EAX
  59. JMP SHORT 012E7D59
  60. ;;;;;;;;;;;;;;; code chiffré ;;;;;;;;;;;;;;;;;;;;;;
  61. XCHG EAX,EDI
  62. 66:87DB XCHG BX,BX
  63. XCHG EAX,EDI
  64. 36:98 CWDE ; Superfluous prefix
  65. - E9 6ED68AD1 JMP D2B953FA
  66. IN AL,1A ; I/O command
  67. AND ECX,EBX
  68. SBB CH,[EDX+F2BC91C9]
  69. JE SHORT 012E7D70
  70. ??? ; Unknown command
  71. STOS DWORD PTR ES:[EDI]
  72. IMUL EDI,[EAX+29],15
  73. JECXZ SHORT 012E7D65
  74. PUSH ECX
  75. JECXZ SHORT 012E7D9D
  76. INC ECX
  77. LES EDX,EDX ; Illegal use of register
  78. MOV [EBP-4],EAX
  79. POPAD
  80. JMP SHORT 012E7DAF
  81. SALC
  82. SALC
  83. MOV AL,[B0680D8B]
  84. AAA
  85. ADD [ECX+7D83D04D],ECX
  86. ;;;;;;;;;;;;;;;; routine de rechiffrement ;;;;;;;;;;;;;
  87. ROL BYTE PTR [EAX],1
  88. JE SHORT 012E7DE5
  89. MOV EDX,[EBP-30]
  90. CMP DWORD PTR [EDX],0
  91. JE SHORT 012E7DE5
  92. MOV EAX,[EBP-30]
  93. MOV ECX,[EAX]
  94. MOV EDX,[ECX]
  95. SUB EDX,[137B78C]
  96. MOV EAX,[EBP-30]
  97. MOV ECX,[EAX]
  98. MOV [ECX],EDX
  99. MOV EDX,[EBP-30]
  100. ADD EDX,4
  101. MOV [EBP-30],EDX
  102. JMP SHORT 012E7DBE
  103. [...]
  104. ;;;;;;;;;;;;;;;;; fin de la fonction de redirection ... ;;;;
  105. PUSH 137BAB8
  106. CALL [135234C] ; ntdll.RtlLeaveCriticalSection
  107. MOV EAX,1
  108. TEST EAX,EAX
  109. JE SHORT 012E7E61
  110. PUSH 137BAA0
  111. CALL [135234C] ; ntdll.RtlLeaveCriticalSection
  112. MOV EAX,[EBP-4]
  113. POP EDI
  114. POP ESI
  115. POP EBX
  116. MOV ESP,EBP
  117. POP EBP
  118. RETN 0C

et voila le code de mon call fixer :

  1. var debIAT
  2. var finIAT
  3. var curIAT
  4. var API
  5. var i
  6. var addr
  7. var oriEip
  8. var ivan
  9. var oriEsp
  10. var addrSign
  11. var addrRet
  12. var enter
  13. var getProc
  14. var leave
  15. var espesp
  16.  
  17. mov debIAT , X
  18. mov finIAT , X
  19.  
  20. mov oriEip , eip
  21. mov oriEsp , esp
  22.  
  23. gpa "RtlLeaveCriticalSection" , "ntdll"
  24. mov leave , $RESULT
  25. gpa "RtlEnterCriticalSection" , "ntdll"
  26. mov enter , $RESULT
  27. gpa "GetProcAddress" , "kernel32"
  28. mov getProc , $RESULT
  29.  
  30.  
  31. mov curIAT , debIAT
  32.  
  33. sub curIAT , 4
  34.  
  35. // sert pour les appels à LoadLibrary et GetProcAddress, arma lisant les arguments ils doivent pointer sur une zone mémoire valide.
  36. sub esp , 04
  37. mov [esp] , 400000
  38. sub esp , 04
  39. mov [esp] , 400000
  40. sub esp , 04
  41. mov [esp] , 400000
  42.  
  43. MEGALOOP:
  44. add curIAT , 4
  45. cmp curIAT , finIAT
  46. ja jaifinimomaaaaan
  47. mov addr , [curIAT]
  48. gn addr
  49. cmp $RESULT , 0
  50. jnz MEGALOOP
  51. find addr , #C745??XXXXXXXXC745??YYYYYYYYC745??ZZZZZZZZ# // remplacer les X Y et Z par les constantes qui vont bien
  52. cmp $RESULT , 0
  53. je MEGALOOP
  54. mov addrSign , $RESULT
  55. findop addr , #C3#
  56. mov addrRet , $RESULT
  57. findop addr , #C2????#
  58. cmp addrRet , $RESULT
  59. jbe pouetpouet
  60. mov addrRet , $RESULT
  61. pouetpouet:
  62. cmp addrSign , addrRet
  63. ja MEGALOOP
  64. mov eip , addr
  65. eval "(eip > {addrSign})"
  66. TOCND $RESULT
  67. mov espesp , esp
  68. add addr , 0B0
  69. trace:
  70. eval "(([eip] & 0xFFFF) == 0D2FF) || (([eip] & 0xFF) == 0C3) || (([eip] & 0xFF) == 0C2) || ((([eip] & 0xFFFF) == 015FF) && (eip > {addrSign}))"
  71. TOCND $RESULT
  72. // si aucun call [X] ni call edx n'a été rencontré avant le retn alors c'est un call getprocaddress émulé
  73. mov ivan , [eip]
  74. and ivan , 0FF
  75. cmp ivan , 0C3
  76. je emugetproc
  77. cmp ivan , 0C2
  78. jne notemugetproc
  79. emugetproc:
  80. mov [curIAT] , getProc
  81. jmp MEGALOOP
  82.  
  83. notemugetproc:
  84. mov ivan , [eip]
  85. and ivan , 0FFFF
  86. cmp ivan , 0D2FF
  87. je calledx
  88. mov ivan , eip
  89. add ivan , 2
  90. mov ivan , [ivan]
  91. mov ivan , [ivan]
  92. cmp ivan , enter
  93. je trace
  94. cmp ivan , leave
  95. je trace
  96. mov [curIAT] , ivan
  97. add eip , 6
  98. mov esp , espesp
  99. jmp ok
  100. calledx:
  101. mov [curIAT] , edx
  102. add eip , 6
  103. mov esp , espesp
  104. ok:
  105. TOCND "(([eip] & 0xFF) == 0C3) || (([eip] & 0xFF) == 0C2)"
  106. jmp MEGALOOP
  107.  
  108. jaifinimomaaaaan:
  109.  
  110. mov eip , oriEip
  111. mov esp , oriEsp
  112. ret

Reconstruction des imports : 2ème famille

Bon maintenant on passe aux choses sérieuses ! Cette fois ci le code de redirection est en clair mais il est beaucoup plus changeant que la version chiffrée. Pour fixer ce type de calls mon call fixer effectue une sorte d'analyse heuristique du code de la redirection et travaille grosso modo comme un petit reverser :p. Il commence par désassembler le code et enregistre le nombre de call de chaque type (call reg, call [cst] et call cst), ainsi que le nombre de sauts inconditionnels. Si l'instruction désassemblée est un push cst et que la constante pointe sur une chaine correspondant à CreateThread ou IsDebuggerPresent il retourne l'API correspondante. Ensuite les données récoltées sont analysées.

Voila le code qui se charge de désassembler les instructions de la redirection (j'utilise encore et toujours BDLib, ma lib de disasm, qui est bien pratique pour l'étude de code) :

  1. typedef struct _INS2 {
  2. BDStruct ins ;
  3. struct _INS2* prev;
  4. struct _INS2* next;
  5. } INS2, *PINS2;
  6.  
  7. [...]
  8.  
  9. PAPI GetAddress(DWORD toTrace)
  10. {
  11. [...]
  12. do
  13. {
  14. ins = (PINS2)malloc(sizeof(INS2));
  15. result = BDasm2(address,&ins->ins);
  16. if (result == -1)
  17. DebugBreak();
  18. address += result;
  19. if (! instructions)
  20. instructions = ins;
  21. else
  22. prevIns->next = ins;
  23. ins->next = NULL;
  24. ins->prev = prevIns;
  25. prevIns = ins;
  26. if (ins->ins.instruction == INS_CALL)
  27. {
  28. PBDStruct call = &ins->ins;
  29.  
  30. switch(call->ArgT1)
  31. {
  32. case (TYPE_REG):
  33. nbCallReg ++;
  34. break;
  35. case (TYPE_CST):
  36. nbCallCst ++;
  37. break;
  38. case (TYPE_CPLX):
  39. if (ins->ins.Arg1.cplx.flags == CPLX_CST)
  40. nbCallBrack ++;
  41. break;
  42. default:
  43. DebugBreak();
  44. break;
  45. }
  46. }
  47. else if ((ins->ins.instruction == INS_PUSH) && (ins->ins.ArgT1 == TYPE_CST) && (ins->ins.Arg1.size == 32) && (! IsRealBadReadPtr((void*)ins->ins.Arg1.c.value.d,1)))
  48. {
  49. if (strcmp("CreateThread",(char*)ins->ins.Arg1.c.value.d) == 0)
  50. return GetApi(CrtThread,GetModule(CrtThread));
  51. if (strcmp("IsDebuggerPresent",(char*)ins->ins.Arg1.c.value.d) == 0)
  52. return GetApi(IsDbgPres,GetModule(IsDbgPres));
  53. }
  54. else if (ins->ins.instruction == INS_JMP)
  55. nbJmp ++;
  56. } while(ins->ins.instruction != INS_RETN);
  57.  
  58. [...]
  59. }

On rencontre grossièrement 4 sortes de codes différents :

les APIs émulées

Le plus simple à détecter et à fixer, c'est un simple mov eax , X / retn. Il suffit de chercher la valeur chargée dans eax et de la comparer à celles d'une liste

code d'exemple :

  1. PUSH EBP
  2. MOV EBP,ESP
  3. MOV EAX,[12345]
  4. POP EBP
  5. RETN

code fixant ce type d'import :

  1. static DWORD consts[5];
  2. static DWORD functs[5];
  3.  
  4. [...]
  5.  
  6. consts[0] = (DWORD)GetCurrentProcessId();
  7. consts[1] = (DWORD)GetVersion();
  8. consts[2] = (DWORD)GetCommandLineA();
  9. consts[3] = (DWORD)GetProcessHeap();
  10. consts[4] = (DWORD)GetACP();
  11. functs[0] = (DWORD)GetProcAddress(GetModuleHandleA("kernel32"),"GetCurrentProcessId");
  12. functs[1] = (DWORD)GetProcAddress(GetModuleHandleA("kernel32"),"GetVersion");
  13. functs[2] = (DWORD)GetProcAddress(GetModuleHandleA("kernel32"),"GetCommandLineA");
  14. functs[3] = (DWORD)GetProcAddress(GetModuleHandleA("kernel32"),"GetProcessHeap");
  15. functs[4] = (DWORD)GetProcAddress(GetModuleHandleA("kernel32"),"GetACP");
  16.  
  17. [...]
  18.  
  19. if ((nbCallReg == 0) && (nbCallCst == 0) && (nbCallBrack == 0) && (((*(PDWORD)(toTrace) == 0xA1EC8B55) && (*(PWORD)(toTrace + 0x8) == 0xC35D) && (address = *(PBYTE*)(toTrace + 0x4)) && (! IsRealBadReadPtr((void*)address,4))) || ((*(PBYTE)(toTrace) == 0xA1) && (*(PBYTE)(toTrace + 5) == 0xC3) && (address = *(PBYTE*)(toTrace + 1)) && (! IsRealBadReadPtr((void*)address,4)))))
  20. {
  21. int i;
  22. for (i = 0; i < 5 ; i++)
  23. if (consts[i] == *(PDWORD)address)
  24. return GetApi(functs[i],GetModule(functs[i]));
  25. return NULL;
  26. }
Les calls EDX

Armadillo charge dans edx l'adresse de l'API - 64 puis vérifie que le 1er octet n'est pas une int3 (si c'est le cas l'API n'est pas appelée) ensuite fait un call edx. Quelque chose que je ne capte pas trop c'est pourquoi il vérifie plusieurs fois le même octet ... une erreur de programmation ?

Un code d'exemple :

  1. PUSH EBP
  2. MOV EBP,ESP
  3. PUSH ECX
  4. PUSH EBX
  5. PUSH ESI
  6. PUSH EDI
  7. PUSHAD
  8. MOV EDX,[137B400] ; kernel32.7C830374
  9. ADD EDX,64
  10. MOV ECX,5
  11. CMP BYTE PTR [EDX],0CC
  12. JE SHORT 012E8AA8
  13. LOOPD SHORT 012E8A96
  14. PUSH DWORD PTR [EBP+10]
  15. PUSH DWORD PTR [EBP+C]
  16. PUSH DWORD PTR [EBP+8]
  17. CALL EDX
  18. MOV [EBP-4],EAX
  19. POPAD
  20. MOV EAX,[EBP-4]
  21. POP EDI
  22. POP ESI
  23. POP EBX
  24. MOV ESP,EBP
  25. POP EBP
  26. RETN 0C

Le code fixant ce type de calls :

  1. PINS2 getMov(PINS2 deb, DWORD reg)
  2. {
  3. PINS2 ins;
  4.  
  5. for(ins = deb ; ins ; ins = ins->prev)
  6. {
  7. if ((ins->ins.instruction == INS_MOV) && (ins->ins.ArgT1 == TYPE_REG) && (ins->ins.Arg1.size == 32) && (ins->ins.Arg1.r.regcode == reg) && (ins->ins.ArgT2 == TYPE_CPLX) && (ins->ins.Arg2.size == 32) && (ins->ins.Arg2.cplx.flags == CPLX_CST))
  8. return ins;
  9. }
  10. return NULL;
  11. }
  12.  
  13. PAPI GetAddress(DWORD toTrace)
  14. {
  15. [...]
  16. if ((nbCallReg == 1) && (! nbCallCst) && (! nbCallBrack))
  17. {
  18. DWORD address;
  19. for (ins = instructions; ins->ins.instruction != INS_CALL ; ins = ins->next);
  20. ins = getMov(ins,ins->ins.Arg1.r.regcode);
  21. if (! ins)
  22. DebugBreak();
  23. address = *(PDWORD)(ins->ins.Arg2.cplx.cst.value.d);
  24. if ((ins->next->ins.instruction == INS_ADD) && (ins->next->ins.ArgT1 == TYPE_REG) && (ins->ins.Arg1.r.regcode == ins->next->ins.Arg1.r.regcode))
  25. address += 0x64;
  26.  
  27. if ((module = GetModule(address)) && (api = GetApi(address,module)))
  28. return api;
  29. else
  30. DebugBreak();
  31. }
  32. [...]
  33. }
Les calls précédés de SetLastWin32Error

Un simple appel à SetLastWin32Error est effectué avant d'appeler l'API ... Utilisé pour les APIs modifiant LastWin32Error (FindRessource etc.)

code d'exemple :

  1. PUSH EBP
  2. MOV EBP,ESP
  3. PUSH ECX
  4. PUSH 0
  5. CALL [135214C] ; ntdll.RtlSetLastWin32Error
  6. MOV EAX,[EBP+10]
  7. PUSH EAX
  8. MOV ECX,[EBP+C]
  9. PUSH ECX
  10. MOV EDX,[EBP+8]
  11. PUSH EDX
  12. CALL [13521EC] ; kernel32.FindResourceA
  13. MOV [EBP-4],EAX
  14. MOV EAX,[EBP-4]
  15. MOV ESP,EBP
  16. POP EBP
  17. RETN 0C
Les calls spéciaux (APIs utilisées par Armadillo)

Assez casse burne à fixer il y a d'ailleur dans mon call fixer des signatures qui risquent de ne pas matcher dans les prochaines versions de Armadillo. Le code devrait pouvoir marcher sans, elles servaient à l'origine à être sur que tel schémas de redirection n'était utilisé que pour telle API à tel endroit, bref vous verrez bien ;)

Un code d'exemple (pour ce type de redirection mon call fixer se débrouille très bien) :

  1. PUSH EBP
  2. MOV EBP,ESP
  3. SUB ESP,8
  4. CALL [13520E4] ; kernel32.GetCurrentProcess
  5. CMP [EBP+8],EAX
  6. JNZ SHORT 012E55A2
  7. CALL [13520E4] ; kernel32.GetCurrentProcess
  8. CMP [EBP+10],EAX
  9. JNZ SHORT 012E55A2
  10. MOV EAX,[EBP+C]
  11. PUSH EAX
  12. CALL 012E4FD0
  13. ADD ESP,4
  14. MOVZX ECX,AL
  15. TEST ECX,ECX
  16. JE SHORT 012E55A2
  17. MOV EDX,[EBP+C]
  18. PUSH EDX
  19. CALL 012E5080
  20. ADD ESP,4
  21. MOV [EBP-4],EAX
  22. MOV EAX,[EBP-4]
  23. IMUL EAX,EAX,0C
  24. ADD EAX,1374CA8
  25. MOV [EBP-8],EAX
  26. MOV ECX,[EBP-8]
  27. MOV EDX,[ECX]
  28. ADD EDX,1
  29. MOV EAX,[EBP-8]
  30. MOV [EAX],EDX
  31. MOV ECX,[EBP+14]
  32. MOV EDX,[EBP+C]
  33. MOV [ECX],EDX
  34. PUSH 0
  35. CALL [135214C] ; ntdll.RtlSetLastWin32Error
  36. MOV EAX,1
  37. JMP SHORT 012E55C4
  38. JMP SHORT 012E55C4
  39. MOV EAX,[EBP+20]
  40. PUSH EAX
  41. MOV ECX,[EBP+1C]
  42. PUSH ECX
  43. MOV EDX,[EBP+18]
  44. PUSH EDX
  45. MOV EAX,[EBP+14]
  46. PUSH EAX
  47. MOV ECX,[EBP+10]
  48. PUSH ECX
  49. MOV EDX,[EBP+C]
  50. PUSH EDX
  51. MOV EAX,[EBP+8]
  52. PUSH EAX
  53. CALL [1352140] ; kernel32.DuplicateHandle
  54. MOV ESP,EBP
  55. POP EBP
  56. RETN 1C

le code les fixant (les codes pour fixer les autres sortes d'APIs redirigées sont dans l'archive en annexe) :

  1. if (nbCallReg || nbCallCst || nbCallBrack)
  2. {
  3. DWORD address = 0;
  4. DWORD address2;
  5. for (ins = instructions ; ins ; ins = ins->next)
  6. {
  7. if ((ins->ins.instruction == INS_CALL) && (ins->ins.ArgT1 == TYPE_CPLX) && (ins->ins.Arg1.cplx.flags == CPLX_CST) && (*(PDWORD)ins->ins.Arg1.cplx.cst.value.d) && (*(PDWORD)ins->ins.Arg1.cplx.cst.value.d != SLW32Error) && (*(PDWORD)ins->ins.Arg1.cplx.cst.value.d != GetCurProc) && (*(PDWORD)ins->ins.Arg1.cplx.cst.value.d != LeaveCrit) && (*(PDWORD)ins->ins.Arg1.cplx.cst.value.d != EnterCrit))
  8. {
  9. address2 = *(PDWORD)ins->ins.Arg1.cplx.cst.value.d;
  10. if ((module = GetModule(address2)) && (api = GetApi(address2,module)))
  11. {
  12. if ((address) && (address != address2))
  13. DebugBreak();
  14. address = address2;
  15. }
  16. }
  17. else if ((ins->ins.instruction == INS_CALL) && (ins->ins.ArgT1 == TYPE_REG))
  18. {
  19. ins2 = getMov(ins,ins->ins.Arg1.r.regcode);
  20. if (!ins2)
  21. DebugBreak();
  22. address2 = *(PDWORD)(ins2->ins.Arg2.cplx.cst.value.d);
  23. if ((ins2->next->ins.instruction == INS_ADD) && (ins2->next->ins.ArgT1 == TYPE_REG) && (ins2->ins.Arg1.r.regcode == ins2->next->ins.Arg1.r.regcode))
  24. address2 += 0x64;
  25. if ((address2 != GTC) && (address2 != SLW32Error) && (address2 != LeaveCrit) && (address2 != GetCurProc) && (address2 != EnterCrit) && (module = GetModule(address2)) && (api = GetApi(address2,module)))
  26. {
  27. if ((address) && (address != address2))
  28. DebugBreak();
  29. address = address2;
  30. }
  31. }
  32. }
  33. if ((nbJmp) && (address == SetErrMde))
  34. {
  35. if (*(PDWORD)(toTrace+0x25) == 0x00FC6583)
  36. return GetApi(ExtProc,GetModule(ExtProc));
  37. else
  38. DebugBreak();
  39. }
  40. if (address)
  41. return api;
  42. }

Voila c'est fini pour la reconstruction des imports :D pour fixer l'IT j'ai utilisé BINI vous trouverez tous ca dans l'annexe avec l'ensemble des codes utilisés.

Redirection de code

On passe maintenant à la redirection de code. Mon call fixer est relativement vieux et je n'ai pas touché à cette partie depuis un moment, de plus je n'ai pas touché de soft utilisant cette protection depuis un moment donc si je dis des conneries excusez moi :D

Armadillo insère au début de plusieurs fonctions (assez pour vous décourager de le fixer à la main) un JMP long pointant sur une zone mémoire allouée. Le début du code de la fonction étant (un peu) polymorphisé puis re-sautes sur le reste du code de la fonction.

Récupérer l'adresse de la page mémoire allouée contenant le code redirigé

flemme d'expliquer le code parle de lui même :)

  1. typedef struct _MEM {
  2. MEMORY_BASIC_INFORMATION memInfo;
  3. DWORD n;
  4. struct _MEM* next;
  5. } MEM, *PMEM;
  6.  
  7. PMEMORY_BASIC_INFORMATION getRedirMem(void)
  8. {
  9. PMEM mem = NULL;
  10. PBYTE curseur;
  11. PMEM curMem;
  12. DWORD max = 0;
  13. PMEMORY_BASIC_INFORMATION retVal;
  14.  
  15. for (curseur = debut_scan; curseur <= fin_scan; curseur ++)
  16. {
  17. if (*curseur == 0xE9)
  18. {
  19. PBYTE destination = *(PDWORD)(curseur+1) + curseur + 5;
  20. PDLL module = GetModule((DWORD)destination);
  21. if ((! module) && (IsExecutable(destination)))
  22. {
  23. for (curMem = mem ; (curMem) && (((PBYTE)curMem->memInfo.BaseAddress > destination) || ((PBYTE)curMem->memInfo.BaseAddress + curMem->memInfo.RegionSize <= destination)) ; curMem = curMem->next);
  24. if (curMem)
  25. curMem->n ++;
  26. else
  27. {
  28. curMem = (PMEM)malloc(sizeof(MEM));
  29. curMem->n = 1;
  30. curMem->next = mem;
  31. VirtualQuery(destination,&curMem->memInfo,sizeof(MEMORY_BASIC_INFORMATION));
  32. mem = curMem;
  33. }
  34. }
  35. }
  36. }
  37.  
  38. for (curMem = mem ; curMem ; curMem = curMem->next)
  39. if (curMem->n > max)
  40. max = curMem->n;
  41.  
  42. for (curMem = mem ; curMem ; curMem = curMem->next)
  43. if (curMem->n == max)
  44. {
  45. retVal = (PMEMORY_BASIC_INFORMATION)malloc(sizeof(MEMORY_BASIC_INFORMATION));
  46. memcpy(retVal,&curMem->memInfo,sizeof(MEMORY_BASIC_INFORMATION));
  47. break;
  48. }
  49. for (curMem = mem ; curMem ;)
  50. {
  51. PMEM next = curMem->next;
  52. free(curMem);
  53. curMem = next;
  54. }
  55.  
  56. return retVal;
  57. }

Reconstuire le code

Armadillo insère des saut inutiles du type jnz short $+4 | jnz short random ou jcc/jmp $+2 le tout avec des préfixes inutiles. Il polymorphise aussi un peu le code mais pas de manière très violente. De plus Aramadillo s'arrêtant à la première instruction permettant de sauter (call, jmp, retn) cela nous facilite pas mal le boulot.

Voila la liste des règles de poly :

  1. PUSH R32A | POP R32B
  2. =
  3. MOV R32B , R32A
  4.  
  5. ; instructions nop-like :
  6. MOV R32A , R32A
  7. MOV R16A , R16A
  8. NOP
  9. XCHG R32A , R32A
  10. XCHG R16A , R16A
  11. XCHG R32A , R32B | XCHG R32A , R32B
  12. XCHG EAX , R32A | XCHG EAX , R32A
  13. XCHG R16A , R16B | XCHG R16A , R16B
  14. XCHG AX , R16A | XCHG AX , R16A
  15. BSWAP R32A | BSWAP R32A
  16. NOT R32A | NOT R32A

Et le code qui va bien :

  1. PBYTE removePrefix(PBYTE ins)
  2. {
  3. PBYTE opcode;
  4. for (opcode = ins ; (*opcode == 0xF2) || (*opcode == 0xF3) || (*opcode == 0x2E) || (*opcode == 0x36) || (*opcode == 0x3E) || (*opcode == 0x26) || (*opcode == 0x64) || (*opcode == 0x65) || (*opcode == 0x66) || (*opcode == 0x67) ; opcode ++);
  5. return opcode;
  6. }
  7.  
  8. DWORD IsBranchIns(PBYTE ins)
  9. {
  10. PBYTE opcode;
  11.  
  12. opcode = removePrefix(ins);
  13.  
  14. if ((*opcode == 0x0F) && (*(opcode+1) >= 0x80) && (*(opcode+1) <= 0x8F))
  15. return JCC_LONG;
  16. if (((*opcode >= 0x70) && (*opcode <= 0x7F)) || (*opcode == 0xE3))
  17. return JCC_SHORT;
  18. else if ((*opcode >= 0xE0) && (*opcode <= 0xE2))
  19. return LOOP;
  20. else if (*opcode == 0xE9)
  21. return JMP_LONG;
  22. else if (*opcode == 0xEB)
  23. return JMP_SHORT;
  24. else if ((*opcode == 0xC3) || (*opcode == 0xC2))
  25. return RETN;
  26. else if (*opcode == 0xE8)
  27. return CALL_CST;
  28. else if (*opcode == 0xFF)
  29. {
  30. if ((*(opcode+1) & 0x38) == 0x20)
  31. return JMP_BRCK;
  32. else if ((*(opcode+1) & 0x38) == 0x10)
  33. return CALL_BRCK;
  34. }
  35. return NORM_INS;
  36. }
  37.  
  38. BOOL dejunk(PINS* instructions)
  39. {
  40. PINS ins;
  41. BOOL modified = FALSE;
  42.  
  43. for(ins = *instructions; ins; )
  44. {
  45. if ((ins->next) && (
  46. // XCHG R32A , R32B | XCHG R32A , R32B
  47. ((*ins->address == 0x87) && (*(PWORD)ins->address == *(PWORD)ins->next->address)) ||
  48. // XCHG EAX , R32A | XCHG EAX , R32A
  49. ((*ins->address >= 0x90) && (*ins->address <= 0x97) && (*ins->address == *ins->next->address)) ||
  50. // XCHG R16A , R16B | XCHG R16A , R16B
  51. ((*ins->address == 0x66) && (*ins->next->address == 0x66) && (*(ins->address+1) >= 0x90) && (*(ins->address+1) <= 0x97) && (*(ins->address+1) == *(ins->next->address+1))) ||
  52. // XCHG AX , R16A | XCHG AX , R16A
  53. ((*ins->address == 0x66) && (*ins->next->address == 0x66) && (*(ins->address+1) == 0x87) && (*(PWORD)(ins->address+1) == *(PWORD)(ins->next->address+1))) ||
  54. // BSWAP R32A | BSWAP R32A
  55. ((*ins->address == 0xF) && (*(ins->address+1) >= 0xC8) && (*(ins->address+1) <= 0xCF) && (*(PWORD)ins->address == *(PWORD)ins->next->address)) ||
  56. // NOT R32A | NOT R32A
  57. ((*ins->address == 0xF7) && ((*(ins->address+1) & 0xF8) == 0xD0) && (*(PWORD)ins->address == *(PWORD)ins->next->address))
  58. ))
  59. {
  60. PINS nextIns = ins->next->next;
  61. if (nextIns)
  62. nextIns->prev = ins->prev;
  63. if (ins->prev)
  64. ins->prev->next = nextIns;
  65. else
  66. * instructions = nextIns;
  67. free(ins->next);
  68. free(ins);
  69. ins = nextIns;
  70. modified = TRUE;
  71. }
  72. else if ((ins->next) && (
  73. // PUSH R32A | POP R32B = MOV R32B , R32A
  74. (*ins->address >= 0x50) && (*ins->address <= 0x57) &&
  75. (*ins->next->address >= 0x58) && (*ins->next->address <= 0x5F)
  76. ))
  77. {
  78. BYTE r32a,r32b;
  79. PINS newIns = (PINS)malloc(sizeof(INS)+2);
  80. r32a = *ins->address - 0x50;
  81. r32b = *ins->next->address - 0x58;
  82. newIns->address = (PBYTE)newIns + sizeof(INS);
  83. newIns->len = 2;
  84. newIns->prev = ins->prev;
  85. newIns->next = ins->next->next;
  86. if (ins->prev)
  87. ins->prev->next = newIns;
  88. else
  89. * instructions = newIns;
  90. if (ins->next->next)
  91. ins->next->next->prev = newIns;
  92. free(ins->next);
  93. free(ins);
  94. *newIns->address = 0x8B;
  95. *(newIns->address + 1) = 0xC0 | (r32b << 3) | r32a;
  96. ins = newIns;
  97. modified = TRUE;
  98. }
  99. else if (
  100. // MOV R32A , R32A
  101. ((*ins->address == 0x8B) && ((*(ins->address+1) & 0xC0) == 0xC0) && ((*(ins->address+1) & 0x7) == ((*(ins->address+1) >> 3)& 0x7))) ||
  102. // MOV R16A , R16A
  103. ((*ins->address == 0x66) && (*(ins->address+1) == 0x8B) && ((*(ins->address+2) & 0xC0) == 0xC0) && ((*(ins->address+2) & 0x7) == ((*(ins->address+2) >> 3)& 0x7))) ||
  104. // NOP
  105. (*removePrefix(ins->address) == 0x90) ||
  106. // XCHG R32A , R32A
  107. ((*ins->address == 0x87) && ((*(ins->address+1) & 0xC0) == 0xC0) &&((*(ins->address+1) & 0x7) == ((*(ins->address+1) >> 3)& 0x7))) ||
  108. // XCHG R16A , R16A
  109. ((*ins->address == 0x66) && (*(ins->address+1) == 0x87) && ((*(ins->address+2) & 0xC0) == 0xC0) && ((*(ins->address+2) & 0x7) == ((*(ins->address+2) >> 3)& 0x7)))
  110. )
  111. {
  112. PINS nextIns = ins->next;
  113. if (nextIns)
  114. nextIns->prev = ins->prev;
  115. if (ins->prev)
  116. ins->prev->next = nextIns;
  117. else
  118. * instructions = nextIns;
  119. free(ins);
  120. ins = nextIns;
  121. modified = TRUE;
  122. }
  123. else
  124. ins = ins->next;
  125. }
  126. return modified;
  127. }
  128.  
  129. PBYTE passNopJmps(PBYTE ins)
  130. {
  131. PBYTE nextIns = ins;
  132. DWORD inst;
  133. while(inst = IsBranchIns(nextIns))
  134. {
  135. PBYTE opcode = removePrefix(nextIns);
  136. DWORD len = LDE(nextIns,LDE_X86);
  137. if ((inst == JCC_SHORT) && ((*(opcode+1) == 0) || ((*opcode == *(removePrefix(nextIns+len))) && (*(opcode+1) == LDE(nextIns+len,LDE_X86)))))
  138. nextIns += len + (signed char)*(nextIns+len-1);
  139. else if ((inst == JMP_LONG) && (*(PDWORD)(opcode+1) == 0))
  140. nextIns += len;
  141. else if ((inst == JMP_SHORT) && (*(opcode+1) == 0))
  142. nextIns += len;
  143. else
  144. break;
  145. }
  146. return nextIns;
  147.  
  148. }
  149.  
  150. void fixRedir(PBYTE address)
  151. {
  152. PBYTE debRedir = address;
  153. PBYTE endRedir;
  154. PINS instructions = NULL;
  155. PINS curIns = NULL;
  156. PINS next = NULL;
  157. PBYTE curseur;
  158. DWORD inst;
  159. DWORD len;
  160.  
  161. for(curseur = passNopJmps(address + 5 + *(PDWORD)(address+1)) ; (curseur < debut_scan) || (curseur > fin_scan) || (!IsBranchIns(curseur)) ;)
  162. {
  163. PBYTE opcode;
  164. len = LDE(curseur,LDE_X86);
  165. inst = IsBranchIns(curseur);
  166.  
  167. if (!inst)
  168. {
  169. PINS ins = (PINS)malloc(sizeof(INS));
  170. ins->address = curseur;
  171. ins->len = len;
  172. if (! instructions)
  173. instructions = ins;
  174. else
  175. curIns->next = ins;
  176. ins->next = NULL;
  177. ins->prev = curIns;
  178. curIns = ins;
  179. curseur = passNopJmps(curseur+len);
  180. }
  181. else if (*(opcode = removePrefix(curseur)) == 0xE9)
  182. curseur = passNopJmps(curseur + len + *(PDWORD)(curseur+len-4));
  183. else
  184. DebugBreak();
  185. }
  186. endRedir = curseur;
  187.  
  188. while(dejunk(&instructions));
  189.  
  190. for(curIns = instructions , curseur = debRedir ; curIns ; curIns = next)
  191. {
  192. next = curIns->next;
  193. if (((int)(curIns->address - curseur) < 4) && ((int)(curIns->address - curseur) > 0) && (! memcmp(curIns->address, curseur, curIns->len)))
  194. {
  195. for (;curseur < curIns->address;curseur ++)
  196. *curseur = 0x90;
  197. break;
  198. }
  199. memcpy(curseur,curIns->address,curIns->len);
  200. curseur += curIns->len;
  201. free(curIns);
  202. }
  203. }

Reconstruction de l'IAT

Encore une fois le code est vieux et en le reprenant récemment je ne me souvenais même pas avoir codé ca donc encore une fois soyez indulgents :D

Quand Armadillo le peu, il détruit complètement l'IAT et remplace les call [API] et les jmp [API] par des jmp et calls qui vont chercher leurs adresses dans des zones mémoires allouées avec toutes les adresses éparpillées un peu partout, de plus certaines de ces adresses sont elles aussi redirigées. Pour retrouver et fixer ces adresses j'ai ajouté un VectorExceptionHandler au chargement de ma dll, il se charge de chercher des signatures dans le code et de poser des HBP aux endroits qu'il faut. Il enregistre alors une liste chainée d'APIs associées à l'adresse de leur call ensuite une fois le programme loadé il suffit de reconstruire l'IAT dans un coin (nous verrons après où)

Pour reconstruire l'IAT je commence par les modules contenant des forward exports pour être sur de faire une IAT la plus petite possible.

On voit bien dans les calls ci dessous que les adresses sont éloignées l'une de l'autre et située dans une zone mémoire allouée :

  1. CALL [1C7DAB4] USER32.WindowFromPoint
  2. CALL [1C7D80C] USER32.WinHelpA

Le code de reconstruction :

  1. // mise en place du vectored exception handler au chargement de la DLL (il faut l'injecter AVANT de lancer le programme, la redirection se faisant dans le loader de Arma
  2. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
  3. {
  4. switch (fdwReason)
  5. {
  6. case DLL_PROCESS_ATTACH:
  7. AddVectoredExceptionHandler(0,ExcHandler);
  8. break;
  9. [...]
  10. }
  11. }
  12.  
  13. // l'exception handler :
  14. LONG CALLBACK ExcHandler(PEXCEPTION_POINTERS ExceptionInfo)
  15. {
  16. static BOOL HBPset = FALSE;
  17. static PBYTE addrRedirAdd;
  18. static PBYTE addrJmp;
  19.  
  20. if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
  21. {
  22. if (ExceptionInfo->ExceptionRecord->ExceptionAddress == addrRedirAdd)
  23. {
  24. PDLL module;
  25. PAPI api;
  26. PDWORD destination = (PDWORD)ExceptionInfo->ContextRecord->Ecx;
  27. DWORD toTrace = *destination;
  28.  
  29. InitAPIiinj();
  30.  
  31. module = GetModule(toTrace);
  32. if ((! module) || (module->Base != (DWORD)GetModuleHandleA(NULL)))
  33. {
  34. if ((! module) || (! (api = GetApi(toTrace,module))))
  35. {
  36. api = GetAddress(toTrace);
  37. if (! api)
  38. DebugBreak();
  39. else
  40. {
  41. PMAGICSTRUCT mstruct = (PMAGICSTRUCT)malloc(sizeof(MAGICSTRUCT));
  42. *destination = api->Address;
  43. mstruct->next = APIs;
  44. if (APIs)
  45. APIs->prev = mstruct;
  46. APIs = mstruct;
  47. mstruct->address = (PDWORD)ExceptionInfo->ContextRecord->Eax;
  48. mstruct->api = api;
  49. }
  50. }
  51. else
  52. {
  53. PMAGICSTRUCT mstruct = (PMAGICSTRUCT)malloc(sizeof(MAGICSTRUCT));
  54. mstruct->next = APIs;
  55. if (APIs)
  56. APIs->prev = mstruct;
  57. APIs = mstruct;
  58. mstruct->address = (PDWORD)ExceptionInfo->ContextRecord->Eax;
  59. mstruct->api = api;
  60. }
  61. }
  62. }
  63. else
  64. return EXCEPTION_CONTINUE_SEARCH;
  65.  
  66. ExceptionInfo->ContextRecord->Eip = (DWORD)addrJmp;
  67. ExceptionInfo->ContextRecord->Dr3 = (DWORD)addrRedirAdd;
  68. ExceptionInfo->ContextRecord->Dr7 |= DR7flag(OneByteLength,BreakOnExec,LocalFlag,3);
  69.  
  70. return EXCEPTION_CONTINUE_EXECUTION;
  71. }
  72. else if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0xC0000096)
  73. {
  74. PBYTE addrSign;
  75. CONTEXT context;
  76.  
  77. InitAPIiinj();
  78.  
  79. if (addrSign = SearchSign(signAddresses,sizeof(signAddresses)-1))
  80. {
  81. addrRedirAdd = addrSign + 0x86;
  82. addrJmp = addrSign - 0x31;
  83. }
  84. else if (addrSign = SearchSign(signAddresses2,sizeof(signAddresses2)-1))
  85. {
  86. addrRedirAdd = addrSign + 0xA8;
  87. addrJmp = addrSign - 0x6;
  88. }
  89. else
  90. DebugBreak();
  91.  
  92. context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
  93.  
  94. GetThreadContext(GetCurrentThread(),&context);
  95.  
  96. context.Dr3 = (DWORD)addrRedirAdd;
  97. context.Dr7 |= DR7flag(OneByteLength,BreakOnExec,LocalFlag,3);
  98.  
  99. // ExceptionInfo->ContextRecord->Dr7 |= DR7flag(OneByteLength,BreakOnExec,LocalFlag,3);
  100.  
  101. SetThreadContext(GetCurrentThread(),&context);
  102.  
  103. return EXCEPTION_CONTINUE_SEARCH;
  104. }
  105. else if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
  106. {
  107. if (! HBPset)
  108. {
  109. PBYTE addrSign;
  110.  
  111. InitAPIiinj();
  112.  
  113. if (addrSign = SearchSign(signAddresses,sizeof(signAddresses)-1))
  114. {
  115. addrRedirAdd = addrSign + 0x86;
  116. addrJmp = addrSign - 0x31;
  117. }
  118. else if (addrSign = SearchSign(signAddresses2,sizeof(signAddresses2)-1))
  119. {
  120. addrRedirAdd = addrSign + 0xA8;
  121. addrJmp = addrSign - 0x6;
  122. }
  123. else
  124. DebugBreak();
  125.  
  126. ExceptionInfo->ContextRecord->Dr3 = (DWORD)addrRedirAdd;
  127. ExceptionInfo->ContextRecord->Dr7 |= DR7flag(OneByteLength,BreakOnExec,LocalFlag,3);
  128.  
  129. HBPset = TRUE;
  130.  
  131. return EXCEPTION_CONTINUE_EXECUTION;
  132. }
  133. else
  134. return EXCEPTION_CONTINUE_SEARCH;
  135. }
  136. else
  137. {
  138. return EXCEPTION_CONTINUE_SEARCH;
  139. }
  140. }
  141.  
  142. // la fonction à appeler une fois arriver à l'oep pour reconstruire une IAT :
  143.  
  144. DWORD RebuildIAT(PMAGICSTRUCT toRebuild, PDWORD ITLen)
  145. {
  146. PMAGICSTRUCT APIs = toRebuild;
  147. PBYTE BaseAddress = (PBYTE)GetModuleHandleA(NULL);
  148. PIMAGE_SECTION_HEADER armaSection = getArmaSections((PIMAGE_DOS_HEADER)BaseAddress);
  149. PDWORD curseur = (PDWORD)(armaSection->VirtualAddress + BaseAddress);
  150. cellule* c;
  151.  
  152. for(c = DLLs->tete ; c ; c = c->suivant)
  153. {
  154. PDLL module = (PDLL)c->valeur;
  155. if (module->forwardExports)
  156. {
  157. BOOL yadesapis = FALSE;
  158. PMAGICSTRUCT mstruct;
  159. for(mstruct = APIs ; mstruct ; )
  160. {
  161. if (GetApi(mstruct->api->Address,module))
  162. {
  163. DWORD address = mstruct->api->Address;
  164. yadesapis = TRUE;
  165. *curseur = address;
  166. for (mstruct = APIs ; mstruct ; )
  167. {
  168. if (mstruct->api->Address == address)
  169. {
  170. PMAGICSTRUCT next;
  171. *mstruct->address = (DWORD)curseur;
  172. if (mstruct->next)
  173. mstruct->next->prev = mstruct->prev;
  174. if (mstruct->prev)
  175. mstruct->prev->next = mstruct->next;
  176. else
  177. APIs = mstruct->next;
  178. next = mstruct->next;
  179. free(mstruct);
  180. mstruct = next;
  181. }
  182. else
  183. mstruct = mstruct->next;
  184. }
  185. curseur ++;
  186. mstruct = APIs;
  187. }
  188. else
  189. mstruct = mstruct->next;
  190. }
  191. if (yadesapis)
  192. {
  193. *curseur = 0;
  194. curseur ++;
  195. }
  196. }
  197. }
  198.  
  199. for(c = DLLs->tete ; c ; c = c->suivant)
  200. {
  201. PDLL module = (PDLL)c->valeur;
  202. if (! module->forwardExports)
  203. {
  204. BOOL yadesapis = FALSE;
  205. PMAGICSTRUCT mstruct;
  206. for(mstruct = APIs ; mstruct ; )
  207. {
  208. if (GetApi(mstruct->api->Address,module))
  209. {
  210. DWORD address = mstruct->api->Address;
  211. yadesapis = TRUE;
  212. *curseur = address;
  213. for (mstruct = APIs ; mstruct ; )
  214. {
  215. if (mstruct->api->Address == address)
  216. {
  217. PMAGICSTRUCT next;
  218. *mstruct->address = (DWORD)curseur;
  219. if (mstruct->next)
  220. mstruct->next->prev = mstruct->prev;
  221. if (mstruct->prev)
  222. mstruct->prev->next = mstruct->next;
  223. else
  224. APIs = mstruct->next;
  225. next = mstruct->next;
  226. free(mstruct);
  227. mstruct = next;
  228. }
  229. else
  230. mstruct = mstruct->next;
  231. }
  232. curseur ++;
  233. mstruct = APIs;
  234. }
  235. else
  236. mstruct = mstruct->next;
  237. }
  238. if (yadesapis)
  239. {
  240. *curseur = 0;
  241. curseur ++;
  242. }
  243. }
  244. }
  245. SecureZeroMemory(curseur, (DWORD)armaSection->Misc.VirtualSize - (DWORD)curseur + (DWORD)armaSection->VirtualAddress + (DWORD)BaseAddress);
  246. SecureZeroMemory(armaSection[1].VirtualAddress + BaseAddress, armaSection[1].Misc.VirtualSize);
  247. SecureZeroMemory(armaSection[2].VirtualAddress + BaseAddress, armaSection[2].Misc.VirtualSize);
  248. SecureZeroMemory(armaSection[3].VirtualAddress + BaseAddress, armaSection[3].Misc.VirtualSize);
  249.  
  250. return ReconstructImportTable(BaseAddress, (PDWORD)(armaSection[0].VirtualAddress + BaseAddress), ((PBYTE)curseur)-(armaSection[0].VirtualAddress + BaseAddress), (PBYTE)curseur, ITLen);
  251. }

Nettoyage de l'exe

Maintenant que notre exécutable est unpacké, les sections de armadillo ne servent plus à rien et peuvent être supprimées. Vous avez d'ailleurs peut être remarqué que c'est ce qui était fait à la fin du code ci dessus. L'espace libéré par le code de Armadillo peut alors être utilisé pour caser l'IAT et / ou l'IT.

On ne peut par contre directement supprimer complètement ces sections qui devront rester en mémoire, Armadillo se chargeant de relocaliser les ressources en fin de fichier, il faudrait faire le travail inverse pour se retrouver avec un exécutable ayant le même nombre de section que l'original. Ayant un peu la flemme je ne l'ai pas fait ...

Enfin une petite précision, dans ma précipitation je me suis trompé de fichier pour ma librairie BANG dans l'article publié hier donc re-téléchargez les libs ;)

Le code comme d'hab est en annexe :D

(PS : Kaine si tu passes par là viens donc nous faire coucou sur IRC !)