EBFE vs CC
Introduction
Tout droit revenu d'un entretien pour un stage, et oui je ne suis encore qu'un étudiant, je m'empresse de revenir à mes activités de reverse de vx.
Je fais un billet sur ce que j'ai reverse car j'ai utilisé une technique (EBFE tricks )tout droit sorti du grenier.
Technique utilisé pour vaincre safedisc v1 et 2, si je dis pas de bêtises. Je l'avais utilisé pour Sim City 3000 ;).
La cible
> md5sum porno-rolik12.avi.exe
726537d1ea6b871a2db9b0a3e2098ce7 porno-rolik12.avi.exe
> sha1sum porno-rolik12.avi.exe
79458bdc8bc60faf5198b9b60f2c2657b4f5c3a8 porno-rolik12.avi.exe
> sha256sum porno-rolik12.avi.exe
6f91c648ee422571225a1839395118c2ac706ac83c7d0bc64e491e75e8c08bf1 porno-rolik12.avi.exe
Un winlocker plus ou moins original où j'ai dû déjouer certaines choses :
Il m'informe que si je veux supprimer leur widget je dois envoyer au compte beeline 9099416525 un montant de 500 USD.
First stage, First failed
On ouvre la bête avec Olly et là c'est le drame, on tombe sur un crypter en VB, sérieusement le vb à reverse c'est juste chiant ( voir même de la merde ).
La personne qui a inventé ce langage j'ai tout simplement envie de la tuer, car sur le marché un paquet de crypter sont écrits en vb et c'est à la porté de tout le monde de le faire.
Les mecs qui ont codés ce WinLocker doivent être juste idiot :
HKU\S-1-5-21-1004336348-1580436667-1708537768-1003\Software\Microsoft\Windows\CurrentVersion\Run\711527436: "C:\Documents and Settings\analys\711527436.exe"
C:\Documents and Settings\analys\711527436.exe
Après avoir copié le réel Winlocker, et changer la clef de registre pour se start au démarrage, ils font un appel à ShellExecuteW pour lancer "shutdown /r /f /t 3" :
Nous avons exactement 3 secondes pour ouvrir une invite de commande et taper "shutdown -a" pour annuler la mise hors tension.
Nous sommes donc maitenant en mesure de travailler avec le réel WinLocker.
Dans un précédent billet, je parlais d'injection dans des processus, ici c'est la même, sauf qu'au lieu de passer par kernel32, il passe direct pas ntdll, du coup il use ZwWriteVirtualMemory, ZwSetContextThread ... :
Une fois le thread du processus resume, ces saguouins font appel à SystemParametersInfo, afin de bloquer la souris au sein de la fenetre du ransomware, faire semblant qu'on est dans un screensaver, du coup pas de Alt-Tab, ni Ctrl-Alt-Suppr possible.
p0wn stage 2
Il nous faut trouver un moyen pour essayer de prendre la main sur le processus dans lequel le processus actuel injecte du code, mais qui dit code injecté dit code qu'on peut modifier.
On va donc modifier le buffer et écrire notre propre code en breakant sur ZwWriteProcessMemory.
C'est là qu'arrive 2 instructions qui vont nous être bien utiles :
> rasm2 -d EBFE
jmp 0x8048000
> rasm2 -d CC
int3
- 0xEBFE : C'est un simple jmp short 0, une boucle infinie
- 0xCC : Interruption 3, trap to debugger
Il faut trouver l'endroit (EIP), où le thread va reprendre son éxécution, un bp sur ZwSetContextThread, et regarder ce qu'il y a dans la structure CONTEXT :
Ok, notre thread reprendra en 0x0040f160, revenons en arrière afin maitenant de breaker sur ZwWriteVirtualMemory, et modifier le buffer d'écriture.
Nous devons calculer où se situe le future eip du thread dans le buffer :
> hex(0x40F160 - 0x40A000) # new eip - adresse ou il start a ecrire
'0x5160'
> hex(0x182408 + 0x5160) # buffer + offset new eip
'0x187568'
Si vous regardez bien la stack :
- 0x60 : Handle du processus
- 0x0040A000 : Adresse de départ où on va écrire
- 0x00182408 : Buffer à écrire (placement d'un int3 0xCC)
- 0x5E00 : Taille à écrire
On continue l'éxécution ( après avoir bien configuré Olly : Just-in-time debugging ) :
On nous invite gentiment à debugger le programme (int3 ftw), à partir de là c'est gagné, on tombe sur du upx, la routine habituel, dump processus, reconstruction de l'IAT. Et on peut travailler tranquilement avec le locker et le p0wn.
Car malheuresement ils usent l'API StrCmpW, pour checker le code, un bp dessus est :
Le code est : "SEXXX".
MBRLocker
Introduction
En ce moment je m'amuse à reverse quelques vx. Mais aujourd'hui c'est jour de fête, je suis tombé sur mon premier MBRLocker :
Reverse de la bête
Le problème que j'avais c'était comment dump mon mbr alors que j'étais infécté, revenir au snapshot précédent, attendre que le malware écrive le nouveau mbr et en 1 ligne de python dump le nouveau mbr :
file("vuln_mbr", "wb").write(file(r"\\.\PhysicalDrive0","rb").read(0x200))
Mais j'ai préféré opté pour une autre solution, convertir le disque de ma vm et allé récupérer le mbr à la main :
> vboxmanage clonehd --variant static analyse.vdi temp.vdi
> head -c 348 temp.vdi | hexdump -C
00000000 3c 3c 3c 20 4f 72 61 63 6c 65 20 56 4d 20 56 69 |<<< Oracle VM Vi|
00000010 72 74 75 61 6c 42 6f 78 20 44 69 73 6b 20 49 6d |rtualBox Disk Im|
00000020 61 67 65 20 3e 3e 3e 0a 00 00 00 00 00 00 00 00 |age >>>.........|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 7f 10 da be 01 00 01 00 90 01 00 00 02 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000150 00 00 00 00 00 10 00 00 00 b0 00 00 |............|
0000015c
On récupère l'offset de notre disque dans le fichier : 0x0000b000.
On go chercher le mbr :
> echo $((0x0000b000))
45056
dd if=./temp.vdi of=./vuln_mbr bs=512 count=1 skip=$((45056/512))
On regarde que c'est bien ca :
> file vuln_mbr
mbr_vuln.bin: x86 boot sector; partition 1: ID=0x7, active, starthead 1, startsector 63, 20948697 sectors, code offset 0x80
Bon bah reste plus qu'à reverse le truc, moins de 512 octets de code ca devrait allé vite :
> objdump -D -b binary -mi386 -Maddr16,data16 mbr_vuln
Je vais pas tout vous balancer tout le disas mais juste des lignes inéressantes :
88: b4 00 mov ah,0x0
8a: 57 push di
8b: cd 16 int 0x16
8d: 5f pop di
8e: eb 37 jmp 0xc7
...
c7: 3c 0d cmp al,0xd
c9: 75 55 jne 0x120
cb: be 8c 7d mov si,0x7d8c
ce: bf 00 6c mov di,0x6c00
d1: b5 00 mov ch,0x0
d3: ea 72 7d 00 00 jmp 0x0:0x7d72
Ici on fait appel à l'interrupt 0x16 avec ah = 0 afin de lire un caractère, si ce caractère est égale à 0xd (Carriage Return)
alors on part en 0x172 ( 0x7d72 - 0x7C00 ), le bootloader est chargé en 0x7C00 par le BIOS.
Surement la routine de vérifiction de notre code :
172: 8a 0e 8b 7d mov cl,BYTE PTR ds:0x7d8b
176: ac lods al,BYTE PTR ds:[si]
177: 8a 15 mov dl,BYTE PTR [di]
179: 80 f2 ed xor dl,0xed
17c: 47 inc di
17d: 3a c2 cmp al,dl
17f: 0f 85 58 ff jne 0xdb
183: 49 dec cx
184: 75 f0 jne 0x176
186: ea 36 7d 00 00 jmp 0x0:0x7d36
On peut voir clairement que dans %cl, il y aura la taille du code attendu, %si lui pointe vers le serial auquel on va être comparé, %di pointe sur ce qu'on a rentré, donc tous nos carac vont être xored avec 0xED.
Il suffit d'aller voir ce qui traine en 0x18B ( 0x7d8B - 0x7C00 ) :
> hexdump -s 0x18B mbr_vuln
0000018c 07 a0 ad dc d9 d8 bf b9 00 00 00 00 00 00 00 00
>>> chr(0xa0 ^ 0xED)
'M'
>>> chr(0xad ^ 0xED)
'@'
>>> chr(0xdc ^ 0xED)
'1'
>>> chr(0xd9 ^ 0xED)
'4'
>>> chr(0xd8 ^ 0xED)
'5'
>>> chr(0xbf ^ 0xED)
'R'
>>> chr(0xb9 ^ 0xED)
'T'
Le code serait donc : "M@145RT", on teste et ca boot :]
Bonus
Je trouvais leur message, un peu triste, alors j'ai voulu pour le fun coder un petit bootloader qui déssine une bière, mais qui ne fait rien au final, car 512 octets pour pouvoir caller un dession c'est hard, du coup j'ai 2 stage, le stage 1 qui init la gdt, passer en mode protected, on load le printer de la biere en memoire (0x1000), et on finira par l'exec, voici le résultat :
Le code :
Faire du re et apprendre des choses
Introduction
L'autre jour sur IRC, on me balance un link vers un VX qui aboutit sur le download d'un FakeAV.
Le truc fun quand j'ai reverse la bête, j'ai appris une technique d'injection dans un processus qui est
oldschool mais n'étant pas expert dans la matière, j'ai voulu la recoder.
Injection
Voici un extrait du disas commenté :
00403ACE |. FF90 70080000 CALL DWORD PTR DS:[EAX+870] ; kernel32.CreateProcessA
00403AD4 |. 85C0 TEST EAX,EAX
00403AD6 |. 0F84 9C000000 JE usps.00403B78
00403ADC |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403AE1 |. 53 PUSH EBX
00403AE2 |. 57 PUSH EDI
00403AE3 |. FF90 74080000 CALL DWORD PTR DS:[EAX+874] ; kernel32.GetModuleHandleA
00403AE9 |. 8BF0 MOV ESI,EAX
00403AEB |. 8B46 3C MOV EAX,DWORD PTR DS:[ESI+3C]
00403AEE |. 8B5C30 50 MOV EBX,DWORD PTR DS:[EAX+ESI+50]
00403AF2 |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403AF7 |. 6A 40 PUSH 40 ; flProtect = PAGE_EXECUTE_READWRITE
00403AF9 |. 68 00300000 PUSH 3000 ; flAllocationType = MEM_COMMIT | MEM_RESERVE
00403AFE |. 53 PUSH EBX ; dwSize = 0xE000
00403AFF |. 56 PUSH ESI ; lpAddress = 0x00400000
00403B00 |. FF75 EC PUSH DWORD PTR SS:[EBP-14] ; hProcess = PROCESS_INFORMATION->hProcess
00403B03 |. FF90 78080000 CALL DWORD PTR DS:[EAX+878] ; kernel32.VirtualAllocEx
00403B09 |. 3BC7 CMP EAX,EDI
00403B0B |. 74 60 JE SHORT usps.00403B6D
00403B0D |. 8D4D E8 LEA ECX,DWORD PTR SS:[EBP-18]
00403B10 |. 51 PUSH ECX ; *lpNumberOfBytesWritten = 0x00B8FC6C
00403B11 |. 53 PUSH EBX ; nSize = 0xE000
00403B12 |. 56 PUSH ESI ; lpBuffer = 0x00400000
00403B13 |. 50 PUSH EAX ; lpBaseAddress = 0x00400000
00403B14 |. FF75 EC PUSH DWORD PTR SS:[EBP-14] ; hProcess = PROCESS_INFORMATION->hProcess
00403B17 |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403B1C |. FF90 88080000 CALL DWORD PTR DS:[EAX+888] ; kernell32.WriteProcessMemory
00403B22 |. 8D85 D4FBFFFF LEA EAX,DWORD PTR SS:[EBP-42C]
00403B28 |. 50 PUSH EAX ; lpContext = 0x00B8F858
00403B29 |. FF75 F0 PUSH DWORD PTR SS:[EBP-10] ; hThread = PROCESS_INFORMATION->hThread
00403B2C |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403B31 |. FF90 84080000 CALL DWORD PTR DS:[EAX+884] ; kernel32.GetThreadContext
00403B37 |. 8D85 D4FBFFFF LEA EAX,DWORD PTR SS:[EBP-42C]
00403B3D |. 50 PUSH EAX ; lpContext = 0x00B8F858
00403B3E |. FF75 F0 PUSH DWORD PTR SS:[EBP-10] ; hThread = 0xC0
00403B41 |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403B46 |. C785 8CFCFFFF>MOV DWORD PTR SS:[EBP-374],usps.00402EF5 ; lpContext->Eip = 0x00402EF5 ( OEP )
00403B50 |. FF90 80080000 CALL DWORD PTR DS:[EAX+880] ; kernel32.SetThreadContext
00403B56 |. FF75 F0 PUSH DWORD PTR SS:[EBP-10] ; hThread = PROCESS_INFORMATION->hThread
00403B59 |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403B5E |. FF90 7C080000 CALL DWORD PTR DS:[EAX+87C] ; kernel32.ResumeThread
La méthode est toute simple :
- Création d'un processus (ici en l'occurence svchost.exe)
- Appel à VirtualAllocEx, pour allouer de la mémoire dans un processus cible
- Appel à WriteProcessMemory, on écrit le code qui va étre éxecuté
- Appel à GetThreadContext, on récupère le context du thread du processus cible.
- On set eip sur le code que l'on vient d'écrire
- Appel à SetThreadContext, on met à jour le context du thread du processus cible.
- Appel à ResumeThread, on relance l'éxécution du thread du processus cible.
A partir de là, j'étais en mesure de pouvoir réecrire la chose, seul soucis lorsque je vais injecter du code
dans le processus cible, il va falloir que je resolv à la main les adresses des API dont j'ai besoin. (reconstruction d'IAT
à l'éxécution).
Éxecutable injecté
Le code injecté est assez fun, je vais résoudre l'adresse de kernel32.dll, chercher l'adresse de la table de fonction,
puis me balader sur la table de noms de fonctions, et calculer un hash pour chaque nom, que je comparerais avec un hash précalculé
pour l'API que je désire appeler.
Dans l'exemple ici, j'affiche juste un message "It Works", donc je load User32.dll et fait un appel
à MessageBoxA.
Bref le code parlera mieux de lui même :
.386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib assume fs:nothing .code start: cld xor edx, edx ;; Get address of kernel32.dll mov edx, fs:[edx + 30h] mov edx, [edx + 0Ch] mov edx, [edx + 14h] next_mod: mov esi, [edx + 028h] mov ecx, 24 xor edi, edi loop_modname: xor eax, eax lodsb cmp al, 'a' jl not_lowercase sub al, 020h not_lowercase: ror edi, 13 add edi, eax loop loop_modname cmp edi, 6A4ABC5Bh mov ebx, [edx + 10h] mov edx, [edx] jne next_mod mov eax, ebx push ebx push offset Addrkernel32 push offset Afonction push offset Namefunc push offset Aordinal call filladdr lea edi, Nuser call load_dll push eax push offset Addruser32 push offset Afunuser32 push offset Nfunuser32 push offset Aorduser32 call filladdr push 30h push offset Message push offset Message push 0 push 4 push [Addruser32] push [Nfunuser32] push [Aorduser32] push [Afunuser32] push [HMessageBox] call callapi jmp exit ;; ------------------------- ;; Function : load_dll ;; Description : Load the desired dll ;; Parameters : EDI = Crypted name of the dll ;; Output : Eax = Adress of the loaded dll ;; ------------------------- load_dll: push ebp mov ebp, esp push edi push 1 push [Addrkernel32] push [Namefunc] push [Aordinal] push [Afonction] push [HLoadLibrary] call callapi leave ret ;; ------------------------- ;; Function : strlen ;; Description : Compute the length of a string ;; ------------------------------- strlen: push edi mov edi, dword ptr[esp + 08] push edi xor eax, eax symbol: scas byte ptr[edi] jne symbol xchg eax, edi dec eax pop edi sub eax, edi pop edi ret ;; ------------------------------- ;; Function : xCRC32 ;; Description : Calcul CRC32 for the desired string ;; Parameters : ;; - String ;; - Length of the string ;; -------------------------------- xCRC32: pushad mov ebp,esp xor eax,eax mov edx,dword ptr [ebp+24h] mov ecx,dword ptr [ebp+28h] jecxz @4 dec eax @1: xor al, byte ptr [edx] inc edx push 08 pop ebx @2: shr eax,1 jnc @3 push edx push ecx mov ecx, 0EDB8h mov edx, eax shr eax, 16 xor eax, ecx mov ecx, 08320h xor edx, ecx and edx, 0ffffh shl eax, 16 or eax, edx pop ecx pop edx @3: dec ebx jnz @2 loop @1 not eax @4: mov dword ptr [ebp + 1Ch], eax popad ret ;; ------------------------------- ;; Function : callapi ;; Description : Function for calling the desired api with all her arguments ;; Parameters : ;; - Push all the arguments for the further called api ;; - Number of arguments for the further called api ;; - Adress of the desired dll ;; - Adress of name table of the desired dll ;; - Adress of ordinal table of the desired dll ;; - Adress of function table of the desired dll ;; - Hash (CRC32) of the desired api to call ;; ;; ------------------------------- callapi: push ebp mov ebp,esp mov ecx,dword ptr [ebp + 01Ch] ; Number of args add ecx, 7 load: mov edx,dword ptr [ebp + 4 * ecx] push edx dec ecx cmp ecx, 7 jne load mov edx, dword ptr [ebp + 018h] ; Addr dll mov esi, dword ptr [ebp + 014h] ; Addr Name func xor ecx, ecx gob: lodsd add eax, edx mov edi, eax comb: push edx lea edi, [edi] push edi call strlen pop edi mov edx, eax push edx push edi call xCRC32 pop edi pop edx cmp eax, dword ptr [ebp + 08h] ; HashApi pop edx je goob jmp nexte nexte: inc ecx jmp gob goob: shl ecx, 1 add ecx, dword ptr [ebp + 010h] ; Addrordinal xor eax, eax mov ax, word ptr [ecx] shl eax, 2 add eax, dword ptr [ebp + 0Ch] ; Addrfonction mov eax, [eax] add eax, edx call eax pop ebp ret ;; ------------------------------- ;; Function : filladdr ;; Description : usefull function for filling all information about a dll ;; Parameters : ;; - Addr of "ms-dos" header of the desired dll ;; - Ptr to addr of name table ;; - Ptr to addr of ordinal table ;; - Ptr to addr of function table ;; -------------------------------- filladdr: push ebp mov ebp,esp mov ecx, dword ptr [ebp + 018h] mov ecx, [ecx + 03Ch] add ecx, dword ptr [ebp + 018h] cmp word ptr [ecx], 'EP' jnz exit ;; Save Header addr mov edi, dword ptr[ebp + 014h] mov [edi], eax mov edx, [ecx + 78h] add edx, eax mov ebx, [edx + 1ch] add ebx, eax ;; Save fun addr mov edi, dword ptr[ebp + 010h] mov [edi], ebx mov ebx, [edx + 20h] add ebx, eax ;; Save Name fun addr mov edi, dword ptr[ebp + 0Ch] mov [edi], ebx mov ebx, [edx + 24h] add ebx, eax ;; Save ordinal addr mov edi, dword ptr[ebp + 08h] mov [edi], ebx pop ebp ret exit: ;; Exit(0) push 0 push 1 push [Addrkernel32] push [Namefunc] push [Aordinal] push [Afonction] push [HExitProcess] call callapi ;; Address of kernel32.dll Addrkernel32 dd 0 ;; Adress of function table in kernel32.dll Afonction dd 0 ;; Adress of name function table in kernel32.dll Namefunc dd 0 ;; Adress of ordinal's table in kernel32.dll Aordinal dd 0 ;; Address of user32.dll Addruser32 dd 0 ;; Adress of function table in user32.dll Afunuser32 dd 0 ;; Adress of name function table in user32.dll Nfunuser32 dd 0 ;; Adress of ordinal's table in user32.dll Aorduser32 dd 0 Nuser db "user32.dll", 0 ;; Hash for kernel32.dll HLoadLibrary dd 03FC1BD8Dh HExitProcess dd 0251097CCh ;; Hash for user32.dll HMessageBox dd 0572D5D8Eh Message db "It Works !", 0 end start
Le makefile qui va avec :
@echo off
if exist "creepy.obj" del "creepy.obj"
if exist "creepy.exe" del "creepy.exe"
\masm32\bin\ml /c /coff "creepy.asm"
if errorlevel 1 goto end
\masm32\bin\Link /SUBSYSTEM:WINDOWS /SECTION:.text,erw /BASE:0x400000 "creepy.obj"
if errorlevel 1 goto end
:end
pause
Nous avons donc notre programme qui va être injecté, il nous faut le programme pour l'injecter mais avant j'ai écrit un script python pour transformer le binaire injecté en variable pour masm, afin de pouvoir l'inclure dans mon programme :
out = open("creepy_exe.asm", "w") i = 0 count = 0 f = open ("creepy.exe", "rb" ) out.write("creepy db ") while (1): tampon = f.read(1) if tampon == "": break i = i + 1 if i % 8 == 0: s = "0%02Xh\n" % ord(tampon) else: s = "0%02Xh, " % ord(tampon) out.write(s) count = count + 1 if i % 8 == 0: out.write("\tdb\t") i = 0 out.write("000h") print "COUNT =", count
Voilà il ne reste plus qu'à coder l'injecteur :
.386 .model flat, stdcall assume fs:nothing option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib .data include creepy_exe.asm scr db "\svchost.exe", 0 len_code dd 1536 oep_foffset dd 200h oep dd 00401000h .data? SystemDir db 256 dup(?) Bwritten dd ? Ctx CONTEXT <?> SuInfo STARTUPINFO <?> PrcInfo PROCESS_INFORMATION <?> .code start: invoke GetSystemDirectory, addr SystemDir, sizeof SystemDir invoke lstrcat, addr SystemDir, addr scr invoke CreateProcess, NULL, addr SystemDir, NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, addr SuInfo,addr PrcInfo invoke VirtualAllocEx, PrcInfo.hProcess, oep, len_code, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE mov eax, len_code sub eax, oep_foffset mov ebx, offset creepy add ebx, oep_foffset invoke WriteProcessMemory, PrcInfo.hProcess, oep, ebx, eax, Bwritten mov Ctx.ContextFlags, CONTEXT_FULL invoke GetThreadContext, PrcInfo.hThread, addr Ctx push oep pop Ctx.regEax invoke SetThreadContext, PrcInfo.hThread, addr Ctx invoke ResumeThread, PrcInfo.hThread invoke ExitProcess, 0 end start
Voilà, la bêtise que j'ai ecrite récemment et qui m'a bien amusé.
Bonus
Just for the lulz, en reversant le malware il communiquait avec un serveur distant à l'aide du format XML. J'ai écrit un petit script python pour voir les communications :
import base64 def decrypt(s): res = "" for c in s: res += chr(ord(c) ^ 0x53) print res ch1 = "HBhvITw8J21jbycgKj0wczo3bnRiYGJiYmFjY2RqdHxtb3whPDwnbQ==" ch2 = """byE8PCdtbzE6PTU8czo3bnRgYGVqZGFgYGpidHM9J250Y3RzMSVudGd 9ZXRzPydudB8SHXRzPCBudAQ6PTc8JCBzCwNzAyE8NTYgIDo8PTI/c3 Rtc298MTo9NTxtb3whPDwnbQ==""" ch3 = "byE8PCdtbyM6PTRzOjdudGBgZWpkYWBgamJ0fG1vfCE8PCdt" f = open('527069638.dat', 'r') s = f.read() decrypt(s) decrypt(base64.b64decode(ch1)) decrypt(base64.b64decode(ch2)) decrypt(base64.b64decode(ch3))
Output :
<?xml version="1.0"?>
<root/>
OK<root>0<tsync id='3369723391'/></root>
<root><binfo id='3369723391' nt='0' bv='4.6' lt='LAN' os='Windows XP Professional '> </binfo></root>
<root><ping id='3369723391'/></root>
Dungeon Siege for the lulz
Introduction
Il y a bien longtemps avec un ami nous jouions à Dungeon Siege, un jeu de role/action développé par Gas Powered Games sorti le 7 mai 2002. Dans cette article je me suis penché sur le fait de me faire un no-cd et etre en mesure de parser leurs fichiers de sauvegarde multijoueur.
No-Cd
Première chose à faire, c'est de regarder si le binaire est packé :
Ici nous avons affaire avec un packer dont je ne connaissais pas l'existance, et en googelant, on tombe sur aucun tutorial pour pouvoir l'unpacker mais on va voir quand fait il n'y a rien de compliqué.
Quand on ouvre le binaire sous OllyDbg, on tombe sur la chose suivante :
007C0000 > /EB 10 JMP SHORT DSLOA.007C0012
007C0002 |0300 ADD EAX,DWORD PTR DS:[EAX]
007C0004 |0000 ADD BYTE PTR DS:[EAX],AL
007C0006 |0200 ADD AL,BYTE PTR DS:[EAX]
007C0008 |0000 ADD BYTE PTR DS:[EAX],AL
007C000A |0000 ADD BYTE PTR DS:[EAX],AL
007C000C |0000 ADD BYTE PTR DS:[EAX],AL
007C000E |0000 ADD BYTE PTR DS:[EAX],AL
007C0010 |0000 ADD BYTE PTR DS:[EAX],AL
007C0012 \55 PUSH EBP
007C0013 E8 00000000 CALL DSLOA.007C0018
007C0018 5D POP EBP
007C0019 81ED 18000000 SUB EBP,18
En voyant la chose suivante, on peut émettre l'hypothèse suivante :
Le packer fait un call à une routine qui vient s'occuper de decompresser l'éxecutable, puis jump ou ret sur le real entry point. Il nous suffit donc de step jusqu'au CALL, placer un hardware breakpoint sur l'acces à la valeur de ESP au moment de cette appel, et nous devrions arriver sur l'entry point.
On run le programme, et après s'etre mangé quelques exceptions dans la tronche que l'on repasse au programme, on arrive ici :
0040A39B 55 DB 55 ; CHAR 'U'
0040A39C 8B DB 8B
0040A39D EC DB EC
0040A39E 6A DB 6A ; CHAR 'j'
0040A39F FF DB FF
0040A3A0 68 DB 68 ; CHAR 'h'
0040A3A1 E8 DB E8
0040A3A2 96 DB 96
0040A3A3 72 DB 72 ; CHAR 'r'
0040A3A4 00 DB 00
0040A3A5 68 DB 68 ; CHAR 'h'
On analyse le code, et on voit la mise en place d'une SEH, un appel à kernel32!GetVersion, GetCommandLineA, ca a l'air de ressembler à notre real OEP. On dump donc notre processus, et on reconstruit l'IAT avec ImportRec :
En fait à partir de là le no-cd est deja fait, car ces boulets cré un processus qui va etre chargé de vérifier si le cd est bien dans le lecteur, si oui on continue la décompression et on lance le jeu, sinon on ExitProcess.
En fait on bypass cette vérification en unpackant à la main en ayant bien mis le cd original dans le lecteur cd et le tour est joué étant donné que la vérification n'aura plus lieu.
I want to cheat
J'étais un peu sur ma faim après cette protection lame.
Je me suis donc mis en tete d'écrire un lecteur / unpacker pour les sauvegardes de types multijoueur (fichier .dsparty). Un logiciel fait deja bien le travail "TankViewer", mais malheuresement, source closed c'est moche.
Je décide donc de regarder d'un peu plus près avec un éditeur hexa :
La première image ressemble typiquement à un header de sauvegarde pour Dungeon Siege, j'ai meme entouré des offset plutot intéressants qu'on retrouve sur la deuxieme image, ou on peut voir le nom de 3 fichiers :
- infos.gas : Informations a propos de la partie en cours ( nom personnage, zone de jeu, etc ... )
- player.gas : Informations relatives aux personnages ( level, or, item dans l'inventaire, etc ... )
- portraite-0.bmp : L'avatar de notre personnage
C'est en googelant des betises que j'ai réussi à tomber sur ce site, qui grace auquel j'ai tout dessuite compris comment était formé les fichiers de sauvegarde.
A partir de là j'étais en mesure de pouvoir ecrire un décodeur fonctionnel.
Il faut savoir que chacun des 3 fichiers est compressé avec zlib. D'ailleurs ca m'a fait pas mal chié de faire marcher cette daube avec MinGW.
Je vais pas éterniser la chose et realease les sources : Reader.c, Reader.h.
Une fois les 3 fichiers récupérés, ils sont tres facile à comprendre ansi qu'à parser, voici un exemple.
On peut trés bien imaginer la suite, créer un éditeur complet de sauvegarde ( cheateur ? ), mais lister tous les objets ca relève de beaucoup de travail et je n'y vois pas vraiment d'amusement.
Voici quand meme un apercu de l'output de mon outil :
Un OEP Finder pour escargot en C
Introduction
Je voulais m'amuser avec l'API de debug sous windows ( voir autre chose que ptrace() ).
J'ai donc décidé d'écrire un OEP finder pour le packer escargot.
Voici le cheminement de mon programme :
- Bypass IsDebuggerPresent()
- Hadware Breakpoint à l'éxécution sur l'OEP
- Step Over de 3. L'état de tous les registres seront sauvés sur la pile (PUSHAD)
- Hardware Breakpoint à l'accés de l'un de ses états
- Step Over de 3. On arrive à l'OEP original
- Il suffit de fix le dump crée avec ImportRec à partir de l'OEP trouvé par le programme et le tour est joué
Source
Voici les sources : main.c, main.h
Conclusion
Ce programme peut vous apprendre les subtilités des debug register, comment utiliser l'API de debug de Windows, et jouer avec le format PE.
Have fun.
PECompact2 unpacking
Introduction
Dans la même lignée, il y avait du PECompact pendant la defcon.
Alors j'enchaine les vidéos :
Vidéo
Binaire
Voici le binaire en question pour pouvoir vous entrainer : calender.exe.
tElock v0.98 unpacking
Introduction
C'est aprè la defcon 19 que je suis retombé, sur le packer tElock.
Mais cette fois j'ai décidé d'unpack la bête ( qui est toute bidon ).
Donc voici une vidéo que j'ai faite exprès pour vous présenter la chose :
Vidéo
Binaire
Voici le binaire en question pour pouvoir vous entrainer : calender.exe.
Unpacking or not unpacking that is the question
Introduction
C'est en regardant l'analyse faite par Nicolas Brulez du Kaspersky Lab de gpcode, que je vois une technique de détéction d'unpacking d'UPX. Je décide de regarder de plus près et recréer le meme comportement ainsi qu'ajouter une autre technique en utilisant les TLS callback.
UPX
Avant de sauter sur le réel OEP, upx fait la chose suivante :
00418DBD . 61 POPAD
00418DBE . 8D4424 80 LEA EAX,DWORD PTR SS:[ESP-80]
00418DC2 > 6A 00 PUSH 0
00418DC4 . 39C4 CMP ESP,EAX
00418DC6 .^ 75 FA JNZ SHORT test.00418DC2
00418DC8 . 83EC 80 SUB ESP,-80
00418DCB .- E9 3082FEFF JMP test.00401000
- Restauration des registres
- Met dans eax l'adresse de esp - 80h
- Push 0 sur la stack
- Compare eax et esp
- Saut conditionnel tant que eax != esp
- Soustrait 80h à esp
- Saute sur le real entry point
Du fait que eax soit égale à une valeur proche de la stack, on peut écrire le bout de code suivant pour détecter l'unpacking UPX :
.code
start:
call DetectUnpack
...
DetectUnpack:
push ebp
mov ebp, esp
mov ecx, esp
sub ecx, 078h ; 80h - saved ebp (4 octets) - saved eip (4 octets)
jnz quit
leave
ret
A partir de ce snippet de code, la détection de ce check d'anti unpack est trè facilement bypassable, en changeant simplement le saut conditionnel.
Avec un peu d'obfuscation, on peut arriver à quelque chose de plus funky. Mais ce n'est pas le but de cette article.
Ce que font majoritairement le genre de personnes qui unpack du UPX, ils dumpent et reconstruisent l'IAT avec ImportRec ( ou à la main ).
C'est en jouant avec les TLS Callback que je me suis dit y'a moyen de faire retarder ce processus en venant écraser le PeHeader, ProcDump ( un plugin d'ollydbg ) se sert des sections tables pour fonctionner correctement. En lui enlevant ces informations il perd la boule.
TLS
Quand on lit la doc de Microsft, le Thread Local Storage (TLS) est un mécanisme qui permet à Windows de défininir des objets qui ne sont pas des objets sur la stack, des variables propres à chaque thread qui éxécute le code. Chaque thread pourra tenir à jour une variable qui lui sera propre. Ces informations sont dans le PEHeader.
Un développeur, si il le souhaite peut définir des TLS Callback fonctions, qui seront désignés à initialiser / nettoyer les objets.
En gros ces fonctions seront appelés avant l'entry point du programme.
Voici la structure :
;0x00 Raw Data Start VA Starting address of the TLS template
;0x04 Raw Data End VA Address of last byte of TLS template
;0x08 Address of Index Location to receive the TLS index
;0x0C Address of Callbacks Pointer to array of TLS callbacks
;0x10 Size of Zero Fill Size of unused data in TLS template
;0x14 Characteristics (reserved but not checked)
A partir de là on peut commencer à coder un exemple de code masm :
TLS_CALLBACK PROTO :PVOID,:DWORD, :PVOID
TLS_DIRECTORY STRUCT
lpTLS_Template_RDS LPVOID ?
lpTLS_Template_RDE LPVOID ?
lpTLS_Index LPVOID ?
lpTLS_Callbacks_Array LPVOID ?
dwTLS_Filler_Size DWORD ?
dwCharacteristics DWORD ?
TLS_DIRECTORY ENDS
.data
TLS_Directory TLS_DIRECTORY <NULL,NULL,OFFSET TLSIndex,OFFSET CallBacksArray, NULL,NULL>
CallBacksArray DD TLS_CALLBACK, NULL
TLSIndex DWORD NULL
MsgBoxCaption db "...", 0
GoodText db "MAIN", 0
TLSText db "CALLBACK", 0
.code
start:
invoke MessageBox, NULL, addr GoodText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
start:
;call DetectUnpack
invoke MessageBox, NULL, addr GoodText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
TLS_CALLBACK PROC DllHandle :PVOID, Reason :DWORD, dwReserved :PVOID
invoke MessageBox, NULL, addr TLSText, addr MsgBoxCaption, MB_OK
RET
TLS_CALLBACK ENDP
end start
Et le make.bat qui va avec :
@echo off
if exist "first.obj" del "test.obj"
if exist "first.exe" del "test.exe"
\masm32\bin\ml /c /coff "test.asm"
if errorlevel 1 goto end
\masm32\bin\Link /SUBSYSTEM:WINDOWS "test.obj"
if errorlevel 1 goto end
:end
pause
Le problème que j'ai rencontré c'est que je ne savais pas quoi comment option passer au linker pour mettre en place dans le peheader la TLSTable.
De ce fait j'y suis allé à la main avec un éditeur Hexadécimal.
Ce qui est facile c'est qu'avec ce code la TLS Table se trouve juste à l'entrée de la section data.
On teste et ca marche, ca nous affiche bien 2 messages box d'affilé.
On va faire évoluer notre code pour eraser le pe header au moment de la TLS :
TLS_CALLBACK PROC DllHandle :PVOID, Reason :DWORD, dwReserved :PVOID
push 0
call GetModuleHandleA
push eax
push esp
push 4
push 1
push eax
xchg edi, eax
call VirtualProtect
xor ecx, ecx
mov ch, 10h
rep stosb
leave
ret
TLS_CALLBACK ENDP
On reéedite à la main notre binaire avec notre éditeur Héxadécimal. Et en breakant sur l'entry point, on va regarder que notre PeHeader s'est bien fait écraser :
Ca a l'air de plutot bien marcher. Bon il faut le savoir que si on conf bien ollydbg, on peut arriver à breaker dessus :
Ou dans IDA, une fois le binaire chargé, pressez CTRL + E, en voyant l'adresse de l'entry point, les tls callback devrait etre juste avant :
Bon maitenant la tache qui reste c'est essayer de refaire passer UPX par dessus le tout, quand on lit les changelog :
Changes in 3.06 (04 Sep 2010):
* win32/pe: TLS callback support contributed by Stefan Widmann. Thanks!
On décide donc de tester, mais :( WTF, on se mange une vieille erreur :
A partir de là j'étaits bloqué, je me demandais si j'essayais de patcher upx ou trouver un autre moyen. C'est en continuant de regarder mon binaire avec WinHex que me vient une idée, à cause du FileAlignement ( champ dans le PeHeader ), il y a un peu de place entre les sections et la premiere section du binaire ( dans notre exemple section .text ). Malheuresement il faudrait pour cela faire du call à LoadLibrary, GetProcAdress. Pour ca faudrait résousdre à la main toutes les entrées mais je ne l'ai pas encore fait pour l'instant par manque de temps : saison des partiels.
Toute facon prochainement, je vais me lancer dans l'écriture d'un packer je reprendrais surement de là pour expliquer ma démarche.
Sur ceux have fun.
Vous reprendrez bien un peu de RE ?
Introduction
Après la defcon, j'avais de grosse envie de continuer à reverse des choses (en l'occurence des binaires win32), j'ai donc voulu alller sur le site crackmes.de, mais malheuresement le site est indisponible actuellement.
Je décide donc d'aller plus loin et me pencher sur un malware, chose que j'ai déjà faite mais sur un blog non-officiel.
Je vais donc comme à mes habitudes sur le site malwaredomainlist.com et visite un domaine foireux pour récupérer un sample.
Informations
Name : pusk.exe
MD5 (pusk.exe) = 76e6e91590b47eba7f13ed164c867999
SHA1 (pusk.exe) = 833312a8b90851e68b75dbb6f1532b64df1e15fa
SHA256 (pusk.exe) = b05df025e7833f99691b019f8ee4656858cc721daa052ba26085be0d0b49d7ef
Break it
Première chose à faire c'est regarder si l'éxécutable est packé, pour ca on sort Peid :
Peid ne détecte rien, de plus la table d'IAT (Import Adress Table) bronche, tout du moins pas d'entrées vers GetProcAdress, LoadLibrary ...
Mais en regardant les strings présentes dans le binaire, on voit des "kernel32.dll", "ntdll.dll".
Je décide donc d'étudier le code de plu près avec le disas.
Je me rends compte qu'il decrypt un nouveau PE dans un zone alloué au préalable :
On sort lordPe et on va dump cette zone, et essayer de reconstruire le pe :
Si on avait voulu travailler avec ce dump, il aurait fallu éditer pas mal de choses(PeHEader, Sections...), mais j'ai juste dump pour le faire manger à Peid et voir si il me détectait quelque chose : Non.
En regardant le code de plus près, on peut voir ceci : ( image avec commentaire )
On regarde le memory map :
Il a effectivement changee l'accés de pas mal de zone mémoire qui lui sont propres.
On peut émettre l'hypothèse qu'il va re-unpack quelque chose dans cette zone étant donné qu'il a memset tout à 0x00.
On regarde le code plus loin et on voit un jump :
00A4137C 83C4 28 ADD ESP,28
00A4137F 83C4 08 ADD ESP,8
00A41382 - FF25 1C309700 JMP DWORD PTR DS:[40301C] ; pusk.006631D0
00A41388 33D2 XOR EDX,EDX
00A4138A ^ 0F85 B7FEFFFF JNZ 00A41247
Qui nous fait tomber sur :
Hmm ca sauve tous nos registres (UPX Like ?), bah on descend dans le code et chercher un POPAD et un jmp :
Donc le real oep serait en "2634", de plus on voit des strings ASCII plutot intéressante :
"adw: start"
"adw: license self delete"
"adw: lang self delete"
"adw: install"
On va deja dump le process, fix l'IAT avec ImportRec et travailler sur ca ( je vais pas expliquer la tache, un paquet de tuto traite de ce sujet ).
Au tout dÉ le binaire va créer un thread pour la routine en 00602612 :
00602612 68 84966000 PUSH dump_.00609684 ; ASCII "adw: start def block thread"
00602617 E8 CA2B0000 CALL dump_.006051E6
0060261C 59 POP ECX
0060261D 68 6C966000 PUSH dump_.0060966C ; UNICODE "MSASCui.exe"
00602622 E8 B1200000 CALL dump_.006046D8
00602627 68 D0070000 PUSH 7D0
0060262C FF15 E0606000 CALL DWORD PTR DS:[<&kernel32.Sleep>] ; kernel32.Sleep
00602632 ^ EB E9 JMP SHORT dump_.0060261D
Ce pauvre thread va juste checker si le programme "MSACui.exe" fait partie de la liste des process (routine 6046d8), et sleep pendant 2000 millisecondes, puis reboucle, ansi de suite.
Pour l'instant je vais partir sur la routine adw: install, ou l'on peut remarquer des choses fun :
00601FDC |. 50 PUSH EAX
00601FDD |. 68 80886000 PUSH dump_.00608880 ; UNICODE "ElCo]N}BMG"
00601FE2 |. E8 621E0000 CALL dump_.00603E49
00601FE7 |. 8D85 54FFFFFF LEA EAX,DWORD PTR SS:[EBP-AC]
00601FED |. 68 18956000 PUSH dump_.00609518 ; /S2 = ".exe"
00601FF2 |. 50 PUSH EAX ; |S1
00601FF3 |. FF15 A4616000 CALL DWORD PTR DS:[<&shlwapi.StrCatW>] ; \StrCatW
La routine 00603e49 est une simple routine pour decrypt les chaines (XOR 4), jai donc récupéré toutes les chaines du binaires et écrit un script python :
dump_8080 = "ElCo]N}BMG" unknow = ["WkbpsevaXImgvkwkbpXSmj`kswXGqvvajpRavwmkjXTkhmgmawXW}wpai", "*a|a", "lppt>++!w+!w+!w)`mvagp", "E`kfa[Bhewl[The}av*a|a","T5oEhImC6Of3B~T1pI5UFM2@WW=6g75Etcno=hRO", "ghmgokh`*kvc?waevglfvae`*kvc", "Wkbpseva", "<7e1b<7f)1ee3)0be3)ffb1)27<6=e``6=2a", ".A@W*p|p", "Vjcqrdw`YHlfwjvjcqYLkq`wk`q%@}uijw`wYAjrkijda", "Vjcqrdw`YHlfwjvjcqYRlkajrvYFpww`kqS`wvljkYUjilfl`vYDqqdfmh`kqv", "Vjcqrdw`YHlfwjvjcqYRlkajrvYFpww`kqS`wvljkYUjilfl`vYDvvjfldqljkv", "*~mt?*vev?*jbk?*p|p?*a|a?*fep?*gki?*gi`?*vac?*iwm?*lpi?*lpih?*cmb?*fit?*ntc?*erm?*itc?*itac?*ikr?*it", "Fm`fn@}`Vlbkdqpw`v", "Vds`_jk`Lkcjwhdqljk", "IjrWlvnCli`Q|u`v", "lppt>++!w+040*tlt;p}ta9wpepw", "ebbm`9!w""wqfm`9!w""esko", "waevglehmoa*kvc?ghmgonegowkjrmhha*kvc?waevglepikwtlava*kvc?waevglfacej*kvc?waevglejp*kvc", "E`kfa[Bhewl[The}av*a|a", "<2a<e0=1)713g)073g)f2a=)57a313fbefef", "31be7<f3)<f=0)0==1)e`76)16a=7<<23=10", "`g52af7g)a4b2)002b)<`63)=56615b<a2`e"] def decrypt(ch): name = "" for c in ch: name += chr(ord(c) ^ 4) print "Name = " + name decrypt(dump_8080) for val in unknow: decrypt(val)
Voici l'output :
Name = AhGkYJyFIC
Name = Software\Microsoft\Windows\CurrentVersion\Policies\System
Name = .exe
Name = http://%s/%s/%s-direct
Name = Adobe_Flash_Player.exe
Name = P1kAlMiG2Kb7FzP5tM1QBI6DSS92c31Apgjk9lVK
Name = clickold.org;searchbread.org
Name = Software
Name = 83a5f83b-5aa7-4fa7-bbf5-63829add296e
Name = *EDS.txt
Name = Rnguv`sd]Lhbsnrngu]Houdsodu!Dyqmnsds]Envomn`e
Name = Rnguv`sd]Lhbsnrngu]Vhoenvr]BtssdouWdsrhno]Qnmhbhdr]@uu`bildour
Name = Rnguv`sd]Lhbsnrngu]Vhoenvr]BtssdouWdsrhno]Qnmhbhdr]@rrnbh`uhnor
Name = .zip;.rar;.nfo;.txt;.exe;.bat;.com;.cmd;.reg;.msi;.htm;.html;.gif;.bmp;.jpg;.avi;.mpg;.mpeg;.mov;.mp
Name = BidbjDydRhfo`utsdr
Name = R`wd[nodHognsl`uhno
Name = MnvShrjGhmdUxqdr
Name = http://%s/404.php?type=stats
Name = affid=%ssubid=%sawok
Name = searchalike.org;clickjacksonville.org;searchatmosphere.org;searchbegan.org;searchant.org
Name = Adobe_Flash_Player.exe
Name = 86e8a495-357c-437c-b6e9-13e757bfabab
Name = 75fa38b7-8b94-4995-ad32-52e938867954
Name = dc16eb3c-e0f6-446f-8d27-912251f8e6da
On peut y voir en premier le nom de l'éxecutable qui va étre crée, des noms de domaines, une clef de registre, des urls ... etc.
Next :
La routine en 0060485C va simplement disable le task_manager, stack call de advapi32.RegSetValueExW
On regarde aussi la routine de crétion ( plutot recopie ) car c'est exactement le meme binaire qui est copiée : ( disas commenté)
00602157 |. FF15 84616000 CALL DWORD PTR DS:[<&shell32.SHGetSpecialFol>; shell32.SHGetSpecialFolderPathW
0060215D |. 8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]
00602163 |. 50 PUSH EAX ; /\Path = "C:\Documents and Settings\All Users\Application Data"
00602164 |. FF15 A8616000 CALL DWORD PTR DS:[<&shlwapi.PathAddBackslas>; \PathAddBackslashW
0060216A |. 8D85 54FFFFFF LEA EAX,DWORD PTR SS:[EBP-AC]
00602170 |. 50 PUSH EAX ; /S2 = "AhGkYJyFIC.exe"
00602171 |. 8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328] ; |
00602177 |. 50 PUSH EAX ; |S1 = "C:\Documents and Settings\All Users\Application Data\"
00602178 |. FF15 A4616000 CALL DWORD PTR DS:[<&shlwapi.StrCatW>] ; \StrCatW
0060217E |. 8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]
00602184 |. 56 PUSH ESI ; /FailIfExists = FALSE
00602185 |. 50 PUSH EAX ; |NewFileName = "C:\Documents and Settings\All Users\Application Data\AhGkYJyFIC.exe"
00602186 |. 8D85 D0FAFFFF LEA EAX,DWORD PTR SS:[EBP-530] ; |
0060218C |. 50 PUSH EAX ; |ExistingFileName = "C:\Documents and Settings\analys\My Documents\T",E9,"l",E9,"chargements\pusk\dump_.exe"
0060218D |. FF15 E8606000 CALL DWORD PTR DS:[<&kernel32.CopyFileW>] ; \CopyFileW
00602193 |. 6A 05 PUSH 5 ; /IsShown = 5
00602195 |. 56 PUSH ESI ; |DefDir = NULL
00602196 |. 8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328] ; |
0060219C |. 56 PUSH ESI ; |Parameters = NULL
0060219D |. 50 PUSH EAX ; |FileName = "C:\Documents and Settings\All Users\Application Data\AhGkYJyFIC.exe"
0060219E |. 68 78946000 PUSH dump_.00609478 ; |Operation = "open"
006021A3 |. 56 PUSH ESI ; |hWnd = NULL
006021A4 90 NOP ; \ShellExecuteW
J'ai NOP le call à ShellExecute afin de pouvoir continuer à analyser le binaire.
Car plus loin il va ajouter une valeur dans la RegKey : Software\Microsoft\Windows\CurrentVersion\Run :
0012C6E4 80000001 |hKey = HKEY_CURRENT_USER
0012C6E8 0012C718 |Subkey = "Software\Microsoft\Windows\CurrentVersion\Run"
0012C6EC 0012C6FC \pHandle = 0012C6FC
0012C6D8 00000060 |hKey = 60
0012C6DC 0012CF74 |ValueName = "AhGkYJyFIC"
0012C6E0 00000000 |Reserved = 0
0012C6E4 00000001 |ValueType = REG_SZ
0012C6E8 0012E22C |Buffer = 0012E22C
0012C6EC 00000086 \BufSize = 86 (134.)
Ce qui m'interessait de voir surtout après c'était la routine "adw download rootkit", mais malheuresement elle failed :(.
L'URL :
http://searchbread.org/pica1/531-direct
Les domaines ne répondent pas c'est dommage. Pareil pour la routine "adw download exe"...
On continue et on aboutit donc sur la routine "self delete" :
00602707 |> \68 08986000 PUSH dump_.00609808 ; ASCII "adw: self delete"
0060270C |. E8 D52A0000 CALL dump_.006051E6
00602711 |. 59 POP ECX
00602712 |. E8 7F2B0000 CALL dump_.00605296
00602717 |. E9 D7010000 JMP dump_.006028F3
La routine 605296 delete le fichier et ensuite ExitProcess tout le code qui suit est obsolète.
Bon maitenant on peut bosser sur l'autre binaire.
AhGkYJyFIC.exe
MD5 (AhGkYJyFIC.exe) = 865ea9c1a3fbd1f089749951004b39aa
SHA1 (AhGkYJyFIC.exe) = 9162235f005d7b256d9034201799cdeb9731c14f
SHA256 (AhGkYJyFIC.exe) = 2c7cf2452d1a3f37a9985e7dfdbe2b349e2d5ee4755f5f14460279855c6d882e
On peut remarquer que nous n'avons pas la meme empreinte qu'au départ avec "pusk.exe".
L'unpacking se passe exactement de la meme maniere que pour le précédent binaire.
J'ai omis une chose lors de ma première analyse, une routine qui teste si son nom de process est "AhGkYJyFIC.exe" si c'est le cas on part sur la routine "adw: work"
006026AF |> \E8 07FBFFFF CALL dump_.006021BB
006026B4 |. 85C0 TEST EAX,EAX
006026B6 |. 75 64 JNZ SHORT dump_.0060271C
.....
0060271C |> \68 FC976000 PUSH dump_.006097FC ; ASCII "adw: work"
Dans le disas on voit plusieurs call à CreateThread, 2 routines sont importantes à voir :
006022AF
Crétion d'un fichier temporaire extrait des ressources :
On étudiera ce fichier aprés, c'est le fakeAV :]
00603785
Utilisation de l'éxecutable attrib.exe (man page) sur chacun des fichiers du disque dur, afin de les rendre tous "caché".
Et le pourquoi c'est que le programme en lui meme va modifier des clefs de registre qui changeront la visibilité de chacun des fichiers :
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL" -> "CheckedValue" -> "0".
14081828.exe
Voici le fakeAV que nous attendions tant :
En fait à partir de là, la partie est gagné en faisant un simple strings sur le binaire on trouvait la clef pour se register.
Une fois le logiciel register, ca va restaurer comme il faut les clefs de registre, le wallpaper, et rapeller attribute.exe sur tous les fichiers du disque.
Et ouvrir notepad avec le message suivant :
Thank you for your purchase, Windows XP Recovery!
Your activation code: 8475082234984902023718742058948
EDS URL: http://www.edsawake.org/customers/dl/Recovery.exe
Contact us through Help&Support section in the Windows XP Recovery menu or by phone +1.877.2820139
Voilà c'est la fin de cette article sur une occupation lorsque crackmes.de n'est pas disponible.
Defcon 19 b400 writeup
Hint
Retrive the key
Résolution
Pour commencer on va ouvrir le fichier avec PEiD, qui nous détecte :
tElock 0.98b1 -> tE!
tElock
Pour ce type de packer, il va falloir configurer correctement notre Ollydbg, aucune exception ne doit etre passé au programme :
Aprè avoir laissé olly chargé le processus, nous sommes actuellemt à l'entry point :
00438BD6 >^E9 25E4FFFF JMP b400_e32.00437000
Lancez le programme avec F9. Puis enchainez les excpetions avec SHIT + F9.
On va compter combien de fois olly break ( ici 20 fois ), apres qu'une exception est était levé, voir le listing ci - dessous :
- 00438A12 | DIV EDI
- 00438A48 | JNB SHORT b400_e32.00438A26
- 0043708D | NOP
- 00437090 | STC
- 00437099 | CLC
- 0043709E | CLD
- 004370A3 | NOP
- 004370A7 | DIV EBX
- 004376A8 | LEA EAX,EAX ; Illegal use of register
- 00437AA1 | DIV BX
- 00437AE4 | CLC
- 00437B28 | NOP
- 00437B67 | DIV EBX
- 00437BA6 | INT 68
- 00437BF1 | NOP
- 00437DE4 | DIV EBX
- 00437E1E | JNB SHORT b400_e32.00437DFC
- 004386F1 | LEA EAX,EAX ; Illegal use of register
- 004387D7 | DIV EDI
- 00438827 | JNB SHORT b400_e32.00438805
Afin de trouver l'OEP, on va breaker sur le dernier JNB, 19 ième combinaison de SHITF + F9.
A partir de là, on appuie sur ALT + M ( on ouvre le memory map ) et on va set un breakpoint d'accés mémoire sur la section de code :
Une fois le breakpoint set, SHIT + F9 et on tombe ici :
0042A000 B8 DB B8
0042A001 30 DB 30 ; CHAR '0'
0042A002 5C DB 5C ; CHAR '\'
0042A003 43 DB 43 ; CHAR 'C'
0042A004 00 DB 00
0042A005 50 DB 50 ; CHAR 'P'
0042A006 64 DB 64 ; CHAR 'd'
0042A007 FF DB FF
0042A008 35 DB 35 ; CHAR '5'
0042A009 00 DB 00
0042A00A 00 DB 00
0042A00B 00 DB 00
0042A00C 00 DB 00
0042A00D 64 DB 64 ; CHAR 'd'
0042A00E 89 DB 89
0042A00F 25 DB 25 ; CHAR '%'
0042A010 00 DB 00
0042A011 00 DB 00
0042A012 00 DB 00
0042A013 00 DB 00
0042A014 33 DB 33 ; CHAR '3'
0042A015 C0 DB C0
0042A016 89 DB 89
0042A017 08 DB 08
0042A018 50 DB 50 ; CHAR 'P'
0042A019 45 DB 45 ; CHAR 'E'
0042A01A 43 DB 43 ; CHAR 'C'
0042A01B 6F DB 6F ; CHAR 'o'
0042A01C 6D DB 6D ; CHAR 'm'
0042A01D 70 DB 70 ; CHAR 'p'
0042A01E 61 DB 61 ; CHAR 'a'
0042A01F 63 DB 63 ; CHAR 'c'
0042A020 74 DB 74 ; CHAR 't'
0042A021 32 DB 32 ; CHAR '2'
On tombe ici sur le réel OEP du packer tElock : "0042A000".
Je n'ai pour l'instant pas analysé le code car on voit quelque chose d'intéressant, un nouveau packer apparament "PECOMPACT2".
Donc rien ne sert de se précipiter à dump le process et reconsruire l'IAT.
Continuons sur notre lancé et jouons avec PeCompact.
PeCompact
J'ai dumpé le process à partir de ce moment et vérifié avec PEiD :
PECompact V2.X-> Bitsum Technologies * Sign.By.fly *
Si on analyse (CTRL + A) le code vu au dessus, on voit une anti-debug tricks :
0042A000 B8 305C4300 MOV EAX,b400_e32.00435C30 ; Mise en place
0042A005 50 PUSH EAX ; SEH Handler
0042A006 64:FF35 00000000 PUSH DWORD PTR FS:[0]
0042A00D 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
0042A014 33C0 XOR EAX,EAX ; Creation d'une
0042A016 8908 MOV DWORD PTR DS:[EAX],ECX ; PageFault
Un apercu de la fenetre de l'état de la stack, pour confirmer :
Pour bypass cette tricks, il suffit d'aller poser un breakpoint en 00435C30.
Run le programme jusqu'à l'access violation.
Pressez SHIFT + F9 pour passer l'exception au programme.
En "00426016" la meme tricks est présente, on regarde le seh handler : "00430C30", et on va poser notre breakpoint, et SHIFT + F9.
A partir de là, on step over...step over...step over... jusqu'à :
00430CF2 FFE0 JMP EAX
Qui va nous faire arriver sur un PUSHAD :
0042E1E0 . 60 PUSHAD
Ici apparament c'est le réel entry point du packer PeCompact.
Mais comme précedement, continuons l'analyse.
A partir de ce moment, je me dis tiens peut etre de l'UPX derriere, je descends dans le disas :
0042E35D . 61 POPAD
0042E35E . 8D4424 80 LEA EAX,DWORD PTR SS:[ESP-80]
0042E362 > 6A 00 PUSH 0
0042E364 . 39C4 CMP ESP,EAX
0042E366 .^75 FA JNZ SHORT b400_e32.0042E362
0042E368 . 83EC 80 SUB ESP,-80
0042E36B .-E9 F031FDFF JMP b400_e32.00401560
Je pose un breakpoint sur le JMP.
Apres tout ca j'etais perdu, je savais plus trop ou j'allais, le programme m'affichait "hello world".
Et il avait l'air de decrypt encore pas mal de merde dans la section code et data.
Je décide donc de regarder de plus près tout ce qu'il a pu decrypt, je voyais des chaines PE passer, ca m'avait l'air d'etre un semblant de pe header mais malheuresement non.
Pas du tout ou alors je m'y prenais mal car je recuperais le ms dos header du bin orignal afin de reconstruire un nouveau pe.
Bref a force de tourner en rond, j'ai grep sur tout le dump et là la clef est sortie :
> strings dump3.exe | grep PE
HowCanThisPossiblyBeAValidPEFile?
PEC2^O
gEPEI
Key
HowCanThisPossiblyBeAValidPEFile?
Pages : 1 2