Suite de l’analyse de la facture de chez DHL :) On le dépacke !
Le dit sample est dispo dans AvCaesar chez Malware.lu mais on n’y apprend pas grand chose de plus. A première vue, on n’arrivera pas a dépacker en statique. (Enfin pas moi quoi). Que ce soit radare ou Ida c’est mal partis.
[0x00408340]> pdf@0x00408340 Do you want to print 191849 chars? (y/N)
Voyons comment on peut louvoyer pour dépacker le bignou avec OllyDbg dans une VM sous Fusion. Si quelqu’un a des TIPs pour aller encore plus vite, je suis preneur ! En analysant la table d’import on tombe sur une ou deux fonctions chelou. C’est d’ailleurs étonnant qu’il y ait autant de fonctions déclarées pour un truc packé.
$ rabin2 -i 1.exe [Imports] ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=COMDLG32.dll_PageSetupDlgW ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=AVIFIL32.dll_AVISaveOptionsFree ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=SHELL32.dll_DragFinish ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_TerminateProcess ordinal=002 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_GetUserDefaultLCID ordinal=003 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_ReadFile ordinal=004 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_GetFileInformationByHandle ordinal=005 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_GetFileAttributesW ordinal=006 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_WriteFile ordinal=007 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_lstrcpynW ordinal=008 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_SetEndOfFile ordinal=009 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_GetCurrentProcess ordinal=010 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_GetLocaleInfoW ordinal=011 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_CreateFileW ordinal=012 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_IsDebuggerPresent ordinal=013 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_lstrcpyW ordinal=014 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_UnhandledExceptionFilter ordinal=015 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_SetUnhandledExceptionFilter ordinal=016 plt=0x00000000 bind=NONE type=FUNC name=KERNEL32.dll_IsProcessorFeaturePresent ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=WINSPOOL.DRV_ClosePrinter ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_UnhookWinEvent ordinal=002 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_GetWindowTextW ordinal=003 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_GetSystemMetrics ordinal=004 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_LoadImageW ordinal=005 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_CharLowerW ordinal=006 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_SetWinEventHook ordinal=007 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_TranslateMessage ordinal=008 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_MessageBeep ordinal=009 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_CreateWindowExW ordinal=010 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_EnableWindow ordinal=011 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_GetDlgItem ordinal=012 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_IsDialogMessageW ordinal=013 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_ShowWindow ordinal=014 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_GetParent ordinal=015 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_GetDesktopWindow ordinal=016 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_SetActiveWindow ordinal=017 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_LoadStringW ordinal=018 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_IsClipboardFormatAvailable ordinal=019 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_UpdateWindow ordinal=020 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_GetWindowLongW ordinal=021 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_GetCursorPos ordinal=022 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_GetDlgCtrlID ordinal=023 plt=0x00000000 bind=NONE type=FUNC name=USER32.dll_SetCursor ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=ADVAPI32.dll_RegOpenKeyExW ordinal=002 plt=0x00000000 bind=NONE type=FUNC name=ADVAPI32.dll_RegOpenKeyExA ordinal=003 plt=0x00000000 bind=NONE type=FUNC name=ADVAPI32.dll_RegQueryValueExA ordinal=004 plt=0x00000000 bind=NONE type=FUNC name=ADVAPI32.dll_RegCreateKeyW ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_StartPage ordinal=002 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_GetObjectW ordinal=003 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_CreateDCW ordinal=004 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_GetTextFaceW ordinal=005 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_GetDeviceCaps ordinal=006 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_StartDocW ordinal=007 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_EnumFontsW ordinal=008 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_TextOutW ordinal=009 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_CreatePalette ordinal=010 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_GetWindowOrgEx ordinal=011 plt=0x00000000 bind=NONE type=FUNC name=GDI32.dll_GetTextExtentPoint32W 58 imports
Donc je le renomme en 2.exe comme le veut le précédent batch au cas ou et je le charge dans Olly. Là on met un breakpoint dans kernel32.dll sur les fonctions habituelles.
- WriteProcessMemory
- NTWriteVirtualMemory
- VirtualAlloc
- VirtualAllocEx
- MapViewOfFile
- UnmapViewOfFile
- IsDebuggerPresent parce que vu dans la table d’import.
(Pour breaker on se rappelle, View > Executable Modules > Mickey Bouton Droit > Show Name > F2 pour breaker) En plus de cela à coté un Russinovich Process monitor afin de récolter les potentielles lectures/écriture dans la registry et/ou fichiers. Et c’est partis. Cela break en 0x422477 à l’appel à VirtualAlloc et un nouveau segment en 0x33000 est alloué en Read Write Execute. Coté registry de l’étrange, 200 clef de cet acabit on été testée. pas une n’a de sens.
07:05:56,6524924 2.exe 2412 RegQueryKey HKCU\Software\Classes SUCCESS Query: Name 07:05:56,6525133 2.exe 2412 RegOpenKey HKCU\Software\Classes\m7VBct NAME NOT FOUND 07:05:56,6525442 2.exe 2412 RegOpenKey HKCR\m7VBct NAME NOT FOUND 07:05:56,6525969 2.exe 2412 RegQueryKey HKCU\Software\Classes SUCCESS Query: Name 07:05:56,6526179 2.exe 2412 RegOpenKey HKCU\Software\Classes\eF5dl NAME NOT FOUND 07:05:56,6526496 2.exe 2412 RegOpenKey HKCR\eF5dl NAME NOT FOUND
A partir de là, (Snapshot vmware !!) et je vais lancer le mode trace de demander en plus à Olly de breaker quand il devra executer quelque chose dans le range nouvellement alloué. (ctrl-T puis hit trace dans «Trace») Quand enfin olly break, dans ce nouveau segment, on se dit qu’on est pas sortis du bois. nous voici dans le stage2 du packeur, une succession de “une a deux instructions, jump” bref du code spaghetti. Petit example remis dans l’ordre :
Address Hex dump Command Comments 003309A7 C0CE 18 ROR DH,18 ; Shift out of range 003309AA 0F9AC6 SETPE DH 003309AD D2E8 SHR AL,CL 003309AF 55 PUSH EBP 003309B0 ^ E9 71F6FFFF JMP 00330026 00330026 8BEC MOV EBP,ESP 00330028 E9 CE050000 JMP 003305FB 003305FB 81C4 F8DBFFFF ADD ESP,-2408 00330601 E9 F6190000 JMP 00331FFC 00331FFC 8B45 04 MOV EAX,DWORD PTR SS:[EBP+4] 00331FFF E9 72120000 JMP 00333276
Bref… c’est pas humain a suivre. Donc on fait le contraire, je trace et je demande à Olly de s’arrêter si on sort du segment avec le code spaghetti. Et je relance le hit trace. Et là PAF… on sombre dans l’étrange… Packer/Thanat0s 1-0 !… Là on apprend plein de chose; Premièrement Olly se plain qu’on lui à changé un breakpoint par un 0x00. Normalement le breakpoint software alias l’instruction Int3 , c’est 0xCC en hexa et il est posé en lieu et place de l’instruction que l’on veut arrêter. Ca c’est le signe que notre stage2 chipote dans le code. Amis polymorphes bonjour. Donc il va falloir faire gaffe avec les breakpoint software.
En plus le code tripoté est pas dans le segment que nous suivons mais dans le segment 0x400000 bref là ou il y avait le code loadé initialement. Cela veut dire que le code original de l’executable est remplacé.
Ensuite, l’EIP est arrivé en 0x7FFDE000 qui l’addresse notre PEB, le process environnement bloc, c’est un bout de mémoire avec une structure certes essentielle mais qui n’est absolument pas supposé être exécutée. Ca n’a pas de sens !
Coté registry cela comment à devenir compréhensible. Il semblerai que l’on soit dans les tests offensifs. Le packer cherche NOD32. Etonnant de la part d’un fichier issus d’un spam qui dit être déjà passé par cet antivirus, n’en doutons pas c’est surement pour nous éviter un second et inutile test :)
07:33:08,2232110 2.exe 2412 RegOpenKey HKLM\SOFTWARE\ESET NAME NOT FOUND Desired Access: Read
Quoi qu’il en soit.. Il a gagné, l’exe va au crash. Déjà, voyons ou s’est passé cet read dans la registry à la recherche de l’antivirus. Pour cela bonne nouvelle Russinovich sauve l’état de la stack à l’appel de la fonction le lecture de la clef de registre. Après l’appel à ADVAPI32 pour lire la registry le programme est supposé retourner en 0x33b5b il y a donc fort à parier que l’instruction de devant c’est notre fameux call. Et effectivement si on regarde le code, on tombe sur un call EAX
Address Hex dump Command Comments 00332B53 9BDAFFFF JMP 003305F3 00332B58 46 INC ESI 00332B59 FFD0 CALL EAX 00332B5B E9 A50A0000 JMP 00333605
On reprend donc notre snapshot vmware, . Dans la mesure ou le code se modifie de partout et qu’étonnamment 0x33B59 tombe en plein milieu de deux instructions on va utiliser un break point hardware ce coup ci (Shift+F5). Et on lance, cela confirme que c’est bien là qu’il faut breaker si on veut éviter le test avec node 32. Petit truc, en examinant un peut plus haut la stack on retrouve l’adresse de base de la DLL ADVAPI32. (Struct IMAGE_DOS_HEADER) cela est un bon indicateur que le packer parse lui meme les dll à la recherche des adresse des fonctions et que la table d’import originale n’est qu’un leurre. Bon, ca c’était pour le fun.. J’ai pas NOD32.
Maintenant gérons notre crash quand il nous a envoyé nous balader sur le PEB. On reprend le snapshot vmware au moment du crash et on va analyser la pile.
Address Value ASCII Comments 0012FFBC 00401235 5@ ; RETURN from 2.00401239 to 2.00401235 0012FFC0 7FFDE000 àý 0012FFC4 7C81776F ow| ; RETURN to kernel32.7C81776F 0012FFC8 0012ADB0 ° 0012FFCC 00140000 0012FFD0 7FFDE000 àý 0012FFD4 8054B6ED í¶T€ ; RETURN from 8054626C to 8054B6ED
Au dessus de 7FFDE00 on retrouve l’offset de retour d’une fonction en 0x401235 et on est passé par la juste avant le crash. Et oui vu qu’il modifie le code, il a réussis son coup le packeur, mais le breakpoint du trace n’a pas fonctionné. Pas grave on sait où regarder… Regardons la fonction autour de cette adresse.
CPU Disasm Address Hex dump Command Comments 00401235 BA DCEE0059 MOV EDX,5900EEDC 0040123A 8B09 .MOV ECX,DWORD PTR DS:[ECX] 0040123C 58 .POP EAX 0040123D 8B00 .MOV EAX,DWORD PTR DS:[EAX] 0040123F 29C8 .SUB EAX,ECX 00401241 C1E0 08 .SHL EAX,8 00401244 6A 03 .PUSH 3 00401246 0FC8 .BSWAP EAX 00401248 89C1 .MOV ECX,EAX 0040124A 58 .POP EAX 0040124B F7E1 .MUL ECX 0040124D 64:8B18 .MOV EBX,DWORD PTR FS:[EAX] 00401250 807B 02 00 .CMP BYTE PTR DS:[EBX+2],0 00401254 74 02 .JE SHORT 00401258 00401256 53 .PUSH EBX 00401257 C3 .RETN 00401258 BE 05124000 MOV ESI,00401205
On reprend notre snapshot, et comme le code se modifie, on break en hardware en 0x401235. Arrivé en 0x40124D tout s’explique. Toute la mamaille entre 0x401235 et 0x40124B sert uniquement à mettre 0x30 dans EAX. Et FS:[0x30] c’est à retenir, cela pointe le PEB. Du coup ce qu’il se passe en fait. C’est un test d’anti debugging. le fameux PEB!BEINGDEBUGGED C’est cela en clair:
.MOV EBX,DWORD PTR FS:[30] EBX = @PEB .CMP BYTE PTR DS:[EBX+2],0 @PEB+2 == 0 ? .JE SHORT 00401258 SAUTE si pas débuggé Dump - Process Environment Block Address Hex dump Decoded data Comments 7FFDE000 . 00 DB 00 ; InheritedAddressSpace = 0 7FFDE001 . 00 DB 00 ; ReadImageFileExecOptions = 0 7FFDE002 . 01 DB 01 ; BeingDebugged = TRUE 7FFDE003 . 00 DB 00 ; SpareBool = FALSE 7FFDE004 . FFFFFFFF DD FFFFFFFF ; Mutant = INVALID_HANDLE_VALUE 7FFDE008 . 00004000 DD OFFSET 2.<STRUCT IMAG ; ImageBaseAddress = 00400000
Donc si je débugge, il le sent,il pousse l’offset du PEB et jumpe dessus ce qui conduit à mon crash. Il faut donc sauter en 0x401258 pour passer outre l’antidebug. On recommence, snapshot, hdw breakpoint sur 0x401254 et on rebranche avec «bouton droit > New origin here »quand on à la main. Si on step un peu, on passe par une petite procédure qu’on va retrouver plusieurs fois encore et qui xor un énième pan de mémoire dans le bloc mémoire 0x40000 avec des 0x5E.
Address Hex dump Command Comments 0040125F 6A 22 PUSH 22 00401261 59 POP ECX 00401262 AC LODS BYTE PTR DS:[ESI] 00401263 34 5E XOR AL,5E 00401265 AA STOS BYTE PTR ES:[EDI] 00401266 E2 FA LOOP SHORT 00401262
Et enfin on retombe sur un peu le même soucis qui conduit au crash, la faute à ce bout de code :
Address Hex dump Command Comments 00401205 6A 70 PUSH 70 00401207 59 POP ECX 00401208 394B 68 CMP DWORD PTR DS:[EBX+68],ECX 0040120B 75 02 JNE SHORT 0040120F 0040120D 57 PUSH EDI 0040120E C3 RETN 0040120F BE 00104000 MOV ESI,00401000 00401214 89F7 MOV EDI,ESI
C’est un autre test d’anti debugging. le fameux PEB!NTGLOBALFLAG . arrivé en 0x401208, EBX pointe sur le PEB et quand on débuggue, les flags HEAP_ENABLE_TAIL_CHECK, HEAP_ENABLE_FREE_CHECK et HEAP_VALIDATE_PARAMETERS sont mis. Du coup on se fait gauler. Il faut jumper en 0x40120F pour bypasser ce test. Petite aparté non primordiale mais classique, quand juste après après avoir re xoré encore un coup un pan de code avec 0x5E on tombe sur ce genre de code, c’est intéressant.
Ce genre de code est un classique, Voici un exemple typique d’un parseur de EAT qui va s’occuper de chercher l’addresse d’une fonction dans une DLL. cette fonction est appelée avec un hash, ce hash est le nom de la fonction ou chaque lettre est décalée d’un octet et xorée avec ce qu’il y a avant (voir 0x401115 et 0x40118). Bref c’est une fonction obfusquée qui fait le même travail que GetProcAddress
Address Hex dump Command Comments 004010F1 60 PUSHAD 004010F2 89C5 MOV EBP,EAX 004010F4 89D3 MOV EBX,EDX 004010F6 8B7B 3C MOV EDI,DWORD PTR DS:[EBX+3C] 004010F9 8B7C1F 78 MOV EDI,DWORD PTR DS:[EBX+EDI+78] 004010FD 01DF ADD EDI,EBX 004010FF 57 PUSH EDI 00401100 8B4F 18 MOV ECX,DWORD PTR DS:[EDI+18] 00401103 8B57 20 MOV EDX,DWORD PTR DS:[EDI+20] 00401106 01DA ADD EDX,EBX 00401108 49 DEC ECX 00401109 51 PUSH ECX 0040110A 8B348A MOV ESI,DWORD PTR DS:[ECX*4+EDX] 0040110D 01DE ADD ESI,EBX 0040110F 89F0 MOV EAX,ESI 00401111 31C9 XOR ECX,ECX 00401113 3228 XOR CH,BYTE PTR DS:[EAX] 00401115 C1C1 08 ROL ECX,8 00401118 30E9 XOR CL,CH 0040111A 40 INC EAX 0040111B 8038 00 CMP BYTE PTR DS:[EAX],0 0040111E ^ 75 F3 JNE SHORT 00401113 00401120 39E9 CMP ECX,EBP 00401122 59 POP ECX 00401123 ^ 75 E3 JNE SHORT 00401108 00401125 5F POP EDI 00401126 8B47 24 MOV EAX,DWORD PTR DS:[EDI+24] 00401129 01D8 ADD EAX,EBX 0040112B 0FB70C48 MOVZX ECX,WORD PTR DS:[ECX*2+EAX] 0040112F 8B47 1C MOV EAX,DWORD PTR DS:[EDI+1C] 00401132 01D8 ADD EAX,EBX 00401134 8B0488 MOV EAX,DWORD PTR DS:[ECX*4+EAX] 00401137 01D8 ADD EAX,EBX 00401139 894424 1C MOV DWORD PTR SS:[ESP+1C],EAX 0040113D 61 POPAD
Il est toujours juteux de breaker sur cette fonction car on peut y apprendre quel sont les fonctions que le packers souhaite utiliser sans oser les déclarer. En breakant sur le mov avant le popad on découvre donc les fonctions :
- ntdll.NtAllocateVirtualMemory
- ntdll.NtTerminateProcess
Et maintenant on sait avec quoi il alloue ses pans de mémoire. Le debug continue et on s’arrête désormais (merci olly) au chargement de la DLL msasn1.dll qui est en fait appelée par le chargement de crypt32.dll. Si on prend les calltrace (View > CallStack) dans olly, on découvre que notre loadlibrary est appelé depuis 0x36343d. Fichtre encore un nouveau segment avec du code. Stage 3 ! Et là on est très heureux, il semblerai que le segment 0x360000 soit enfin le bon. En 0x361000 plein de strings bien claires et du code pile poil en 0x36500. Bon, on est presque au bout, mais il nous faut juste un entry point propre. Bref s’arrêter au plus tôt dans le code exécuté dans le segment 0x36000. On reprend le dernier snapshot et on ajoute un breakpoint sur le NTAllocateVirtualmemory découvert plus tot. Au terme d’un peu de steeping on arrive enfin au Graal
Address Hex dump Command Comments 004010DB 01DA ADD EDX,EBX 004010DD 6A 00 PUSH 0 004010DF 55 PUSH EBP 004010E0 68 3E4A4000 PUSH 00404A3E ; ASCII "00002" 004010E5 FFD2 CALL EDX 004010E7 6A 00 PUSH 0 004010E9 6A FF PUSH -1 004010EB FF15 09104000 CALL DWORD PTR DS:[401009]
EDX contient 0x364A00 notre EntryPoint ! Le call Dword[0x401009] d’après contient NTTerminateProcess.
VICTORY ! Au final on sais le dépacker avec ces 3 steps.
- Mettre 3 hardware Breakpoints en execution qui ne semblent pas n’avoir de sens en 0x401254 et 0x40120b et 0x4010E5
- On aide à passer les deux anti-debug
- Au break sur 0x4010E5 on dump le segment pointé par EDX et dont l’entry point est EDX.
Donc je repart depuis le début et je suis ces 3 steps.. ce coup-ci c’est le segment 0x9C0000 qui est alloué pour notre stage 3 (précédemment 0x360000)
Arrivé là on tape un peut dans le code et on assemble un mov edx pour aider a faire marcher le call EDX
Address Hex dump Command Comments 004010D2 90 NOP 004010D3 90 NOP 004010D4 BA 004A9C00 MOV EDX,9C4A00 004010D9 90 NOP 004010DA 90 NOP 004010DB 90 NOP 004010DC 90 NOP 004010DD 6A 00 PUSH 0 004010DF 55 PUSH EBP 004010E0 68 3E4A4000 PUSH 00404A3E ; ASCII "00002" 004010E5 FFD2 CALL EDX 004010E7 6A 00 PUSH 0 004010E9 6A FF PUSH -1 004010EB FF15 09104000 CALL DWORD PTR DS:[401009] 004010F1 60 PUSHAD
Et on dump avec le plugin OllyDumpEX le segment 40000 et 9C4A00 (ici celui pointé par EDX) Dépacké enfin ! On set l’entry point sur notre bidouille.
Bingo on l’a dépacké le bignou
$ strings final.exe | egrep "th|he|in|an|re|nd|at|on|nt|ha|es|http" shell32 urlmon HelpLink dbghelp vmware :Zone.Identifier cmd=getload&login= http://www.msn.com/ User-Agent: %s Connection: close User-Agent: %s Connection: close Content-Length: %d Content-Type: application/x-www-form-urlencoded &doubles=1 &personal=ok &removed=ok &admin= &hash= Software Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run Software\Microsoft\Windows\CurrentVersion\Run Software\Microsoft\Windows\CurrentVersion\Uninstall Location: http:// Shell_TrayWnd Location: plugin_size System\CurrentControlSet\Services\Disk\Enum AutoItv3CCleanerWIC
Allez bientôt on dépècera cet executable.