BlindRead
By Christal avec un Addentum de TeeJI
|
BlindRead est un petit programme (moins de 400ko) qui présente l'intérêt
d'être truffé d'AntiSice. UPX0 000A3000 00001000 00000000 00000400 E0000080 UPX1 0004C000 000A4000 0004C000 00000400 E0000040 .rsrc 00002000 000F0000 00001E00 0004C400 C0000040 Après modifications des caractéristiques avec un outil comme ProcInfo
(by TeeJi), celles ci vont prendre la valeur E0000020, et permettre ainsi un break sur l'EntryPoint, avec un débuggeur
comme TRW2000 (pour changer…) ou SoftIce : :004EFD30 60 PUSHAD > sauvegarde des registres :004EFD31 BE00404A00 MOV ESI,004A4000 :004EFD36 8DBE00D0F5FF LEA EDI,[ESI+FFF5D000] :004EFD3C C787D0A40A001D6F831EMOV DWORD PTR [EDI+000AA4D0],1E836F1D :004EFD46 57 PUSH EDI :004EFD47 83CDFF OR EBP,-01 :004EFD4A EB0E JMP 004EFD5A UPX est un compresseur, pas un système de protection comme ASProtect par
exemple. Il est juste composé d'un loader qui va se charger de décompresser l'exécutable.
La fin du loader est "lisible" dès l'Entry Point, non compressé et donc accessible dès
le break sur l'Entry Point: :004EFE9A 61 POPAD > restitution des registres :004EFE9B E98493FAFF JMP 00499224 > jump to OEP :004EFEA0 B8FE4E00C8 MOV EAX,C8004EFE :004EFEA5 FE4E00 DEC BYTE PTR [ESI+00] :004EFEA8 D0B44A INVALID :004EFEAB 0000 ADD [EAX],AL :004EFEAD 0000 ADD [EAX],AL Un jmp EIP à la place du jmp 00499224 va permettre d'obtenir un Dump de l'exécutable d'origine, sans pour autant supprimer ce que le passage dans UPX aura rajouté et /ou modifié (loader, nom des sections, caractéristiques...) UPX0 000A3000 00001000 000A2FD0 00000600 E0000020 UPX1 0004C000 000A4000 0004BEAC 000A3600 E0000020 .rsrc 00002000 000F0000 00001D40 000EF600 E0000020 Seuls les raw Size et Raw Offset auront changé…
- et les commandes sont presque toutes les mêmes que son jumeau. Pas de dépaysement,
et surtout une prise en main des plus rapide pour peu que vous connaissiez son " concurrent "… VirtualSize RVA PhysicalSize PhysicalOffset
----------
a3000 1000 0 400
4c000 a4000 4c000 400
2000 f0000 1e00 4c400
Writing DOS head [UPX] L1=OBJR L2=LOOK 61,E9 > la signature d'UPX L3=BP > pose d'un breakpoint avec sauvegarde de l'EIP L4=STEP > effectue le dump à partir de l'OEP La signature trouvée correspond au POPAD, et à la première instruction du JUMP : :004EFE9A 61 POPAD > restitution des registres :004EFE9B E98493FAFF JMP 00499224 > jump to OEP Place aux nouveaux:
Ultimate Packer for eXecutables
Copyright (C) 1996, 1997, 1998, 1999, 2000
UPX v1.01 Markus F.X.J. Oberhumer & Laszlo Molnar Apr 9th 2000
Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file..
Commands:
-1 compress faster -9 compress better
-d decompress -l list compressed file
-t test compressed file -V display version number
-h give more help -L display software license
Options:
-q be quiet -v be verbose
-oFILE write output to `FILE'
-f force compression of suspicious files
-k keep backup files
file.. executables to (de)compress
L'option -d va permettre de retrouver un exécutable totalement identique à l'original,
avec toutes les sections, ressources et StringData possibles et imaginables: Ultimate Packer for eXecutables
Copyright (C) 1996, 1997, 1998, 1999, 2000
UPX v1.01 Markus F.X.J. Oberhumer & Laszlo Molnar Apr 9th 2000
File size Ratio Format Name
------------------- ------ ----------- -----------
944640 -> 320000 33.88% win32/pe c:\temp\blindread.exe
Unpacked 1 file. CODE 00098408 00001000 00098600 00000400 60000020 DATA 00010DA4 0009A000 00010E00 00098A00 C0000040 BSS 00000BF1 000AB000 00000000 000A9800 C0000000 .idata 00002568 000AC000 00002600 000A9800 C0000040 .tls 00000010 000AF000 00000000 000ABE00 C0000000 .rdata 00000018 000B0000 00000200 000ABE00 50000040 .reloc 0000A5AC 000B1000 0000A600 000AC000 50000040 .rsrc 00030400 000BC000 00030400 000B6600 50000040 Lancement d'un des Dump ou de la version Unpacked, et : " This software is not designed to operate under a debugger "
Un vrai festival :du meltIce, des INT 68, des INT 03,et de l'Except Handler. Au
finish, la présence de l'execpt handler est probablement la cause de la fermeture sauvage du programme,
tant que Sice est actif, et FrogsIce n'y peut rien… :00492B88 6A00 PUSH 00 > hTemplateFile :00492B8A 6A00 PUSH 00 > file attributes :00492B8C 6A03 PUSH 03 > dwCreationDistribution :00492B8E 6A00 PUSH 00 :00492B90 6A02 PUSH 02 > dwShareMode :00492B92 6800000080 PUSH 80000000 > accès (read-write) mode :00492B97 683C2C4900 PUSH 00492C3C > / //NTICE :00492B9C E84B39F7FF CALL KERNEL32!CreateFileA :00492BA1 83F8FF CMP EAX,-01 > si <> FFFFFFFF :00492BA4 741D JZ 00492BC3 > bad Boy ! Un ch'tit coup d'śil sur ce qui se trouve autour de l'adresse 00492C3C va être intéressant : \\.\NTICE...Erro r...This softwar e is not designe d to operate und er a debugger. T he application w ill stop....\\.\ Pour le moment, rien de particulier... :00492BE1 6A10 PUSH 10 > bouton de la MessageBox :00492BE3 68482C4900 PUSH 00492C48 > titre (error...) :00492BE8 68502C4900 PUSH 00492C50 > the software... :00492BED 6A00 PUSH 00 > pas de fenêtre proprio :00492BEF E83041F7FF CALL USER32!MessageBoxA :00492BF4 B801000000 MOV EAX,00000001 > flag :00492BF9 E8F60FF7FF CALL 00403BF4 > vers ExitProcess Le call 00403BF4 va être une des sorties du schéma de protection anti SoftIce et va mener vers un Exitprocess : 017F:00403BA9 833B00 CMP DWORD PTR [EBX],00 017F:00403BAC 7508 JNZ 00403BB6 017F:00403BAE 8B06 MOV EAX,[ESI] 017F:00403BB0 50 PUSH EAX 017F:00403BB1 E8EAD6FFFF CALL KERNEL32!ExitProcess > bad boy! Mais aucune inversion des sauts précédents le call ExitProcess n'en
viendront à bout…
Tiens, tiens, tiens... :00492BD2 68A82C4900 PUSH 00492CA8 > \\.\SIWVID :00492BD7 E81039F7FF CALL KERNEL32!CreateFileA :00492BDC 83F8FF CMP EAX,-01 :00492BDF 741D JZ 00492BFE avec la MessageBox qui suit, et le troisième MeltIce se trouve ici : :00492C0D 68B42C4900 push 00492CB4 > \\.\SICE :00492C12 E8D538F7FF CALL KERNEL32!CreateFileA :00492C17 83F8FF CMP EAX,-01 :00492C1A 741D JZ 00492C39 Mais INIC, E.BR et KHCB vont être très intéressant à
suivre... Blindrea Attributes C:\NTICE\NMTRANS.DLL NOTFOUND GetAttributes Et aucune tentative pour son équivalent Sice, qui utilise pourtant le même
fichier ! :00492DDA B804000000 mov eax, 00000004 :00492DDF BD4B484342 mov ebp, 4243484B > BCHK :00492DE4 CC int 03 :00492DE5 5A pop edx :00492DE6 646789160000 mov fs:[0000], edx :00492DEC 5A pop edx :00492DED B801000000 mov eax, 00000001 :00492DF2 C3 ret et :004644C2 B804000000 mov eax, 00000004 :004644C7 BD4B484342 mov ebp, 4243484B > BCHK :004644CC CC int 03 :004644CD 5A pop edx :004644CE 646789160000 mov fs:[0000], edx :004644D4 5A pop edx :004644D5 B801000000 mov eax, 00000001 :004644DA C3 ret Oups ! :00492D32 B057 mov al, 57 > " W " :00492D34 BF00000100 mov edi, 00010000 > à partir de l'@ 10000 :00492D39 B900003F00 mov ecx, 003F0000 > et jusqu'à l'@ 3F0000 et dans les lignes qui suivent, tout ce qu'il faut pour initier une except handler... :00492D3E 33D2 xor edx, edx :00492D40 685C2D4900 push 00492D5C :00492D45 64FF32 push dword ptr fs:[edx] :00492D48 8925D0A84A00 mov dword ptr [004AA8D0], esp :00492D4E 892DD4A84A00 mov dword ptr [004AA8D4], ebp :00492D54 648922 mov dword ptr fs:[edx], esp avant un saut :00492D57 EB1A jmp 00492D73 qui nous mènera droit vers un Scan de la mémoire : :00492D73 F2 repnz
:00492D74 AE scasb
:00492D75 E325 jcxz 00492D9C > branchement si CX est à 0\par
:00492D7E 813F494E4943 cmp dword ptr [edi], 43494E49 > INIC
:00492D84 7406 je 00492D8C > bad Boy ?
:00492D8A EBE7 jmp 00492D73 > loop
:00492D8C 83C704 add edi, 00000004
:00492D8F 813F452E4252 cmp dword ptr [edi], 52422E45 > on compare la fin
> soit WINICE.BR->E.BR
:00492D95 75DC jne 00492D73 > continue la recherche
:00492D97 B801000000 mov eax, 00000001 > Flag Bad Boy
:00492D9C 5A pop edx
:00492D9D 646789160000 mov fs:[0000], edx > et revoilà notre Except handler !
:00492DA3 5A pop edx
:00492DA4 C3 ret
Une nouvelle recherche dans le Dead Listing va nous redonner le même, le pareil (vive le copié/Collé, n'est ce pas ByteCop...) :00464477 813F452E4252 cmp dword ptr [edi], 52422E45 :0046447D 75DC jne 0046445B :0046445B F2 repnz :0046445C AE scasb :0046445D E325 jcxz 00464484 :00464466 813F494E4943 cmp dword ptr [edi], 43494E49 :0046446C 7406 je 00464474 :00464472 EBE7 jmp 0046445B Bon, deux détections supplémentaires de la présence de SICE... BPM 492E2A X :00492E28 B443 MOV AH,43 > voir le Code.txt de FrogsIce :00492E2A CD68 INT 68 :00492E2C 5A POP EDX :00492E2D 646789160000 MOV FS:[0000],EDX > encore de l'except handler... :00492E33 5A POP EDX :00492E34 663D86F3 CMP AX,F386 > debuggeur detected ? :00492E38 7407 JZ 00492E41 :00492E3E 33C0 XOR EAX,EAX > Good Boy :00492E40 C3 RET :00492E41 B801000000 MOV EAX,00000001 > Bad Boy :00492E46 C3 RET mais comme on ne sait jamais, une petite recherche dans le Dead Listing sur " INT 68 " va nous en donner une de plus : :00464510 B443 mov ah, 43 :00464512 CD68 int 68 :00464514 5A pop edx :00464515 646789160000 mov fs:[0000], edx :0046451B 5A pop edx :0046451C 663D86F3 cmp ax, F386 :00464520 7407 je 00464529 Je ne jurerai pas que j'ai choppé tous les anti XXX, mais la petite modification
suivante va suffire: T=50000: > temps de recherche alloué F=blindread.exe: > nom de la cible O=crk_blindread.exe: > nom du crack.exe P=492ba4/74/eb: > 1er meltice P=492bdf/74/eb: > 2ème meltice P=492c1a/74/eb: > 3ème meltice P=464512/cd,68/90,90: > 1er Int 680 P=492e2a/cd,68/90,90: > 2ème INT 68 P=492de4/cc/C3: > 1er INT 03 P=4644CC/cc/c3: > 2ème INT 03 P=492d84/74,06/90,90: > 1er INIC P=46446C/74,06/90,90: > 2ème INIC $ Reboot de Windows pour réimplanter SICE (opération inutile quand
vous utilisez TRW2000 !) C605A42B4900EB MOV BYTE PTR [00492BA4],EB C605DF2B4900EB MOV BYTE PTR [00492BDF],EB C6051A2C4900EB MOV BYTE PTR [00492C1A],EB 66C705124646009090 MOV WORD PTR [00464612],9090 66C7052A2E49009090 MOV WORD PTR [00492E2A],9090 C605E42D4900C3 MOV BYTE PTR [00492DE4],C3 C605CC444600C3 MOV BYTE PTR [004644CC],C3 66C705842D49009090 MOV WORD PTR [00492D84],9090 E94693FAFF JMP 00499224 > et jump vers l'OEP... Glop ! Glop ! :004CFB14 8965FC MOV [EBP-04],ESP :004CFB17 683AFB4C00 PUSH 004CFB3A > pousse l'offset du Handler :004CFB1C E80363F3FF CALL KERNEL32!SetUnhandledExceptionFilter :004CFB21 BD4B484342 MOV EBP,4243484B >BCHK :004CFB26 B804000000 MOV EAX,00000004 :004CFB2B CC INT 3 :004CFB2C E8DB68F3FF CALL USER32!MessageBoxA :004CFB31 E8E660F3FF CALL KERNEL32!ExitProcess :004CFB36 66FA CLI :004CFB38 FB STI :004CFB39 A7 CMPSD :004CFB3A 8B65FC MOV ESP,[EBP-04] :004CFB3D 6843FB4C00 PUSH 004CFB43 :004CFB42 C3 RET :004CFB43 C3 RET Si c'est la fonction en 004CFB3A qui est exécutée, le programme ne
retournera pas en 004CFB2C (après l'INT 03) mais en 004CFB43. Pour neutraliser cette protection, il faudra
remplacer l'INT 03 par ADD ESP,04 ; RET.
Vous voyez qu'il n'est pas difficile d'installer une " per-thread exception handler " : PUSH OFFSET HANDLER PUSH FS:[0] > adresse de la structure ERR suivante MOV FS:[0],ESP > place l'adresse de la structure ERR dans FS :[0000] ... ... > le code protégé par le handler vient ici... POP FS:[0] > restore la structure ERR suivante dans FS :[0000] ADD ESP,4h RET ;*********************** HANDLER: ... ... > l'exception handler vient ici ... MOV EAX,1 > EAX=1 -> continue vers l'Handler suivant RET ; > EAX=0 -> continue l'exécution Dans le code ci dessus, vous pouvez voir que le 2ème Dword de la structure
ERR, où se trouve l'adresse de notre handler, est placé sur la pile en premier, puis le 1er Dword
de la structure ERR suivante est placé sur la pile par l'instruction PUSH FS :[0000]. :00492D95 75DC jne 00492D73 > Good Boy :00492D97 B801000000 mov eax, 00000001 > Flag Bad Boy Un Mov eax,00 à la place du mov eax,01 donnera le même résultat
que le Ret, et plus " proprement " à mon avis… Handler 1
Ici, l'adresse de chaque fonction est poussée sur la pile. PUSH Return value PUSH pExceptionRecord PUSH OFFSET CodeLabel PUSH LastStackFrame CALL RtlUnwind Bien que l'on trouve la présence de ces deux API dans les codes de la cible, seul RtlUnWind semble être sollicité... * Referenced by a CALL at Addresses: |:004034C2 , :OO40364A , :O0403880 | * Reference To: KERNEL32.RtlUnwind, Ord:OOOOh | :OO401268 FF25COC14AOO Jmp dword ptr [O04AC1CO] Voici l'un des call appelant : :00403641 6AOO push OOOOOOOO > Return value :00403643 50 push eax > pExceptionRecord -> @71F9FC :O0403644 684F364000 push OO40364F > CodeLabel -> good boy :O0403649 52 push edx > LastStackFrame -> @ 71FBC4 Bad Boy :OO40364A E819DCFFFF Call KERNEL32.RtlUnwind :O040364F 5B pop ebx On trouvera donc dans EDX l'adresse de la routine " bad boy " S_BCHK proc
Push offset ExceptHandler2
PUSH FS:[0]
MOV FS:[0],ESP ;on installe l'except Handler qui sera ici notre ExceptHandler2
mov Flag,0
mov ebp,"BCHK" ;on met le mot magique dans ebp
mov ax, 04h
int 3 ;on crée une erreur
;à ce moment si sice est lancé on ne va rien voir et
;tout va se passer normalement,
;mais on n'atterrira pas dans l'except Handler...
mov eax,Flag ;si sice est lancé, il est tjrs a Zero
mov Flag,0
POP FS:[0]
ADD ESP,4h ;on enlève l'except handler
ret
ExceptHandler2:
mov Flag,01 ;Si on arrive ici c'est que sice n'est pas lancé
;on met 1 dans le Flag -> pas de debugger
xor eax,eax
ret
S_BCHK endp
Même si vous avez désactivé l'utilisation de l'int 03h dans
SoftIce (i3here off; faults off), vous ne pourrez pas passer cette routine! xor eax,eax mov eax,[eax] ;page Fault Dans le cas de BlindRead, l'équivalent de cette routine est écrit ainsi : :00492DA5 33D2 xor edx, edx :00492DA7 68C32D4900 push 00492DC3 On pousse sur la pile l'adresse de la routine (l'Except handler 2) : :00492DC3 8B25D0A84A00 mov esp, dword ptr [004AA8D0] on place l'adresse contenue dans la mémoire réservée [004AA8D0] :00492DC9 8B2DD4A84A00 mov ebp, dword ptr [004AA8D4] idem pour ebp avec [004AA8D4] :00492DCF 5A pop edx > edx récupéré sur la pile :00492DD0 646789160000 mov fs:[0000], edx création d'une Except handler :00492DD6 58 pop eax :00492DD7 33C0 xor eax, eax :00492DD9 C3 ret :00492DAC 64FF32 push dword ptr fs:[edx] :00492DAF 8925D0A84A00 mov dword ptr [004AA8D0], esp :00492DB5 892DD4A84A00 mov dword ptr [004AA8D4], ebp :00492DBB 648922 mov dword ptr fs:[edx], esp > installation d'une except handler (edx=0) :00492DDA B804000000 mov eax, 00000004 :00492DDF BD4B484342 mov ebp, 4243484B > mot magique dans EBP :00492DE4 CC int 03 > création de l'erreur :00492DE5 5A pop edx > on récupère EDX sur la pile :00492DE6 646789160000 mov fs:[0000], edx > on installe l'Except Handler Le fait d'avoir shunté les deux INT 03 en les remplaçant par des RET (ou en mettant le mov eax à 00) ne va plus permettre au programme de passer par ici : * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00464548(U) | :00464564 0F010D2CAF4900 sidt [0049AF2C] :0046456B A12EAF4900 mov eax, dword ptr [0049AF2E] :00464570 83C008 add eax, 00000008 :00464573 8B18 mov ebx, dword ptr [eax] :00464575 83C010 add eax, 00000010 :00464578 8B00 mov eax, dword ptr [eax] :0046457A 25FFFF0000 and eax, 0000FFFF :0046457F 81E3FFFF0000 and ebx, 0000FFFF :00464585 2BC3 sub eax, ebx :00464587 83F81E cmp eax, 0000001E :0046458A 7406 je 00464592 :00464590 33C0 xor eax, eax :00464592 5A pop edx :00464593 646789160000 mov fs:[0000], edx :00464599 5A pop edx :0046459A C3 ret Et comme il y a deux INT 03, on trouve de la même façon une seconde
SIDT en 00492E7C, à l'identique de la première sidt fword ptr IDT ;on obtient l'IDT
mov ebx, dword ptr [IDT+2] ;ebx=IDT base
add ebx, 8*ExceptionUsed ;ebx=selector pour l'int recherchee
cli ;clear interupts
;On sauvegarde l'ancienne routine
mov dx, word ptr [ebx+6] ;le highword
shl edx, 16d
mov dx, word ptr [ebx] ;le lowword
mov [OldGate], edx
mov eax, Fct_Address ;on installe notre routine
mov word ptr [ebx], ax ;le lowword
shr eax, 16d
mov word ptr [ebx+6], ax ;le highword
int ExceptionUsed ;on passe en ring 0 :)))
mov ebx, dword ptr [IDT+2] ;on restore la vielle routine
add ebx, 8*ExceptionUsed
mov edx, [OldGate]
mov word ptr [ebx], dx
shr edx, 16d
mov word ptr [ebx+6], dx
ret
On a donc une fonction parfaitement opérationnelle prenant comme paramètre
d'entrée l'offset de la routine que nous voulons utiliser en ring0. push offset Ring0Proc call RingZero Ring0Proc: mov eax,dr7 ;par exemple :) bx=1350 Pour autant le RET qui remplace l'INT 03 est un morceau de chance. Suite à la lecture de ce texte, TeeJi m'a approté ce complément indispensable à la compréhension de la façon dont l'exception fonctionne: |
Bonne Journée