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.
(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.