Les Anti Soft-Ice

         
       

By PuLsar

Softice 4.01 Un petit travail de reverse (première partie...)


Suite à mon dernier essai sur le tricks anti-sice il m'a semblé intéressant de proposer un tut sur la façon de parer ces techniques. J'ai donc désassemblé SoftICE pour tenter de le reverser et essayer d'en comprendre le fonctionnement :)

IDA ayant finit son travail, voici ce que j'ai pu découvrir :)

premier constat: Ce programme a sûrement été code en asm, les codeurs de Numega sont vraiment des Gourous de la programmation low-level, leur travail est vraiment sensationnel

Comme je cherchais à enlever la méthode BCHK, il fallait trouver la routine qui manageait l'int 03h

Donc petit tour sous softice:
On tape IDT et on regarde un peu ce qu'il nous donne l'animal, et on dirait qu'il n'aime pas trop qu'on regarde ses fonctions:))

IDTbase=800A0000 Limit=02FF
0000 IntG32 0028:C0001350 DPL=0 P VMM(01)+0350
0001 IntG32 0028:C0001360 DPL=3 P VMM(01)+0360 <-ici c'est l'int 01h
0002 IntG32 0028:C00046E0 DPL=0 P Simulate_IO+02A0
0003 IntG32 0028:C0001370 DPL=3 P VMM(01)+0370 <-ici la 03h
--------
|____________________ addresse de la routine qui contrôle l'int 03h

0004 IntG32 0028:00000004 DPL=3 P
0005 IntG32 0028:00000005 DPL=3 P
0006 IntG32 0028:C00013A0 DPL=0 P VMM(01)+03A0
0007 IntG32 0028:C00013B0 DPL=0 P VMM(01)+03B0
0008 TaskG 0068:00000000 DPL=0 P
0009 IntG32 0028:C00013C0 DPL=0 P VMM(01)+03C0
000A IntG32 0028:C00013E0 DPL=0 P VMM(01)+03E0

Regardez attentivement les offsets qu'il donne pour les int 01h et int 03h VMM(01)+360h et VMM(01)+370
Ca ne nous arrange pas trop parce que ces offsets tombent dans le Vmm et non dans softice comme ce serait souhaitable.
:(( or on sait que c'est softice qui manage ces routines pour les pbx (int 03h) et les bpm , le trace...(int 01h)
Donc il va falloir trouver nous même ces routines

On retape IDT et on regarde l'IDTbase=800A0000 (ces valeurs peuvent changer d'un Pc à l'autre), on fait dd puis on tape d 800A0000.
(pour plus d'explication, referez vous à mon texte sur les intérruptions)

Voila mon IDT

0030:C000D474 00281350 C0008E00 0028AFDA C00EEE00 P.(.......(.....
0030:C000D484 00288BF6 C0038E00 0028AFE0 C00EEE00 ..(.......(.....
0030:C000D494 0028AFE7 C00EEE00 0028AFEF C00EEE00 ..(.......(.....
0030:C000D4A4 00288C14 C0038E00 002813B0 C0008E00 ..(.......(.....
0030:C000D4B4 00680000 00008500 00288CAA C0038E00 ..h.......(.....
0030:C000D4C4 002813E0 C0008E00 00288C23 C0038E00 ..(.....#.(.....
0030:C000D4D4 0028E7C4 C0028E00 00288C41 C0038E00 ..(.....A.(.....
0030:C000D4E4 00288C50 C0038E00 00288CB9 C0038E00 P.(.......(.....
0030:C000D4F4 00284720 C000EE00 00284728 C000EE00 G(.....(G(.....
0030:C000D504 00284730 C000EE00 00284738 C000EE00 0G(.....8G(.....
0030:C000D514 00284740 C000EE00 00284748 C000EE00 @G(.....HG(.....
0030:C000D524 00284750 C000EE00 00284758 C000EE00 PG(.....XG(.....
0030:C000D534 00284760 C000EE00 00284768 C000EE00 `G(.....hG(.....
0030:C000D544 00284770 C000EE00 00284778 C000EE00 pG(.....xG(.....

On a comme offset pour notre int03h C00EAFE0.
En faisant u C00EAFE0 pour voir ce qu'il y a, on trouve un JMP C0038C05
Puis u C0038C05 et cette fois ci on obtient PUSH XXXXXX
JMP C0031798
En faisant un u C0031798 on trouve une partie de code intéressant :)))
En effet on reconnaît vite des codes qui pourraient être à l'origine des méthodes 'BCHK' et des magics word dans esi et edi..

0028:C0031798 FC                    CLD
0028:C0031799 E8AAD50400            CALL C007ED48
0028:C003179E 2E803DCD3F08C001      CMP  BYTE PTR CS:[C0083FCD],01
0028:C00317A6 7501                  JNZ  C00317A9
0028:C00317A8 C3                    RET
0028:C00317A9 E8BC590000            CALL C003716A
0028:C00317AE 8D642404              LEA  ESP,[ESP+04]

0028:C00317B2 81FD4B484342          CMP  EBP,4243484B  ;CMP EBP,'BCHK'
0028:C00317B8 0F84C7250700          JZ   C00A3D85      ;JZ BCHK_ROUTINE

0028:C00317BE 6681FE4746            CMP  SI,4647       ;CMP SI,'FG'
0028:C00317C3 752A                  JNZ  C00317EF
0028:C00317C5 6681FF4D4A            CMP  DI,4A4D       ;CMP DI,'JM'
0028:C00317CA 7523                  JNZ  C00317EF      ;JNZ Fonction_Normale

0028:C00317CC E857060000            CALL C0031E28      ;Magic_Value_Fonction
0028:C00317D1 7316                  JAE  C00317E9 
0028:C00317D3 C7052EE805C004000100  MOV  DWORD PTR [C005E82E],00010004
0028:C00317DD C605B85A03C000        MOV  BYTE PTR [C0035AB8],00
0028:C00317E4 E8EB060000            CALL C0031ED4
0028:C00317E9 E87B590000            CALL C0037169
0028:C00317EE CF                    IRETD
.........

Comme on connaît les bytes de la routine qui autorise les back-doors dues a l'int 03h, dans IDA il n'y a plus qu'à chercher ces codes que l'on trouve a l'offset C00024B4

Int03h:                  ; DATA XREF: HookInt+56 o
cld 
call sub_C004FA64
cmp cs:byte_C0054CE9, 1
jnz short loc_C00024C5
retn 

Woo une Cross reference :) allons voir :)
Les noms sont quelques peu modifiés.... pour permettre une meilleure compréhension :))

HookInt proc near ; CODE XREF: LCOD:C000026C 
p

var_8		= qword	ptr -8
pusha	
mov	   ebp, esp
sub	   esp, 8
mov	   ds:word_C000AD1C, fs
sidt	   [ebp+var_8]
mov	   esi, dword ptr [ebp+var_8+2]
mov	   ds:dword_C00066AF, esi
call   sub_C0007D04
mov	   ecx, 0FFh

loc_C000798E:				                ; CODE XREF: HookInt+2B
j
mov	   ds:dword_C00062AF[ecx*4], ecx
loop	   loc_C000798E
mov	   eax, 1                        ;01->int 01h? :)) oui c'est ca
mov	   dl, 60h                       ;dl=60 ->on hooke
mov	   edi, offset Int01h            ;<- bizzare on appelle la meme fonction 
call    sub_C0007B97                     ;pour notre int 03h
mov	   eax, 2
xor	   dl, dl
mov	   edi, offset loc_C0002A51
call   sub_C0007B97
mov	   eax, 3                     ;03=int 03h surement :)
mov	   dl, 60h
mov	   edi, offset Int03h         ;<- là on trouve notre reference
call	   sub_C0007B97               ;on appelle Une Fonction qui hooke l'int 03h
mov	   eax, 6
xor	   dl, dl
mov	   edi, offset loc_C0001270
call	   sub_C0007B97
mov	   eax, 0Bh
xor	   dl, dl
mov	   edi, offset loc_C000134C
call	   sub_C0007B97
mov	   eax, 0Ch
xor	   dl, dl
mov	   edi, offset loc_C00014BA
call	   sub_C0007B97
mov	   eax, 0Dh
xor	   dl, dl
mov	   edi, offset loc_C0001517
call	   sub_C0007B97
mov	   eax, 0Eh
xor	   dl, dl
mov	   edi, offset loc_C000197B
call	   sub_C0007B97
mov	   eax, ds:dword_C00098FA
dec	   eax
imul	   eax, 0Fh
add	   eax, offset dword_C00098FE
lea	   eax, [eax+5]
mov	   ds:dword_C000ACC0, eax
mov	   eax, 41h                       ;on va hooker l'int 041h
mov	   dl, 60h                        ;héhé on retrouve le dl=60
mov	   edi, offset Int41h             ;offset de notre int41h routine
call	   sub_C0007B97                   ;Tjrs la meme fonction
call	   sub_C0007AAD
mov	   esp, ebp
popa	
retn	
HookInt		endp                              ; sp =  20h
Et qu'y a t il dans l'int41?? 

Regardez les différents codes de détection de Sice:

Int   41h proc near          ; DATA XREF: HookInt+D2 o
add   esp, 4
cmp   cs:byte_C0054CE9, 1
jz    loc_C005401B
cmp   cs:byte_C00067E8, 1
jnz   short loc_C00539FA
cmp   ax, 70h
jz    short loc_C0053A0E
cmp   ax, 4Fh                ;ici si on appelle la fonction 4F de l'int41h
jnz   short loc_C00539F9     ;les débuggers systemes renvoient 0F386 dans eax :)
mov   eax, 0F386h            ;la valeur magique:)

loc_C00539F9:                ; CODE XREF: Int41h+25 j
iret 

Je vous laisse le soin de patcher votre softice en exercice pour éviter tout les zolis programmes comme Vbox qui utilisent un exception handler et l'int 03h comme protection :))

Voila pour le premier point... Vous êtes encore avec moi?
Alors c'est parfait on va pouvoir passer au second!!
Je vous propose de passer au gros morceau en attaquant ce qui me parait être le point principal de la fonction qui traite ce qui est entré au clavier et qui redirige vers les différentes fonctions....
Une bonne chose à faire est d'aller chercher une string assez spécifique comme "Invalid Command" que vous trouverez à l'offset C00135B5

Invalid_command:                 ; CODE XREF: sub_C0011B3D+1EA j
                                 ; LCOD:C007500F j LCOD:C0075179 j
                                 ; LCOD:C0075225 j
                                 ; DATA XREF: LCOD:C0016D47 o
mov esi, offset aInvalidCommand  ; "Invalid command"
call DisplayString2
retn

Pour le moment on sait pas encore que le call suivant est celui qui permet d'afficher une chaîne de caractère mais on peut déjà s'en douter car elle prend comme argument l'offset d'une chaîne de caractères terminée par un 0
On entre dans le call en regardant toutes les cross references faisant appels a cette fonction. A chaque fois esi reçoit l'offset d'une chaîne de caractères, puis appel la fonction. On fera plus tard un petit programme qui montrera que ca marche bien.

Maintenant qu'on a trouvé l'invalid command, il suffit de remonter les cross references.
Sur la XREF: sub_C0011B3D+1EA , on trouve quelque chose d'intéressant....

loc_C0011D1A:                ; CODE XREF: sub_C0011B3D+1AE j
call sub_C000FD7F
call sub_C000FE15
shl eax, 2
call ds:FonctionAppellee[eax]
call sub_C000FE2D

En regardant au niveau de l'offset de FonctionAppellee, on trouve les lignes suivantes que je commenterai ensuite:

FonctionAppellee dd offset Invalid_command ; DATA XREF:	sub_C0011B3D+1EA
r
                                      ; LCOD:C007500F
r LCOD:C0075179
r
                                      ; LCOD:C0075225
r
		dd offset AltScreen
off_C0016D4F	dd offset BC_BD_BE    ; DATA XREF: LCOD:C00751D2
r
		dd offset BC_BD_BE
		dd offset BC_BD_BE
		dd offset BPINT
		dd offset BPIO
		dd offset BrkList
		...........
		...........            ;Que des "dd offset XXXXXXXXXXX"
		...........
		dd offset Not_Done
		dd offset Not_Done
		dd offset Not_Done
		dd offset Not_Done
off_C0016FF3	dd offset Not_Done    ; DATA XREF: StatusBar+A6
o

CommandesPossibles db  2Eh ; .        ; DATA XREF: GetFonctionCalled+5
o
					; LCOD:C0017307
o
		dd 3F3800h, 5E004130h
byte_C0017000	db 61h			; DATA XREF: sub_C0011AD0+7
w

aDdr		db 'DDR',0
		db  80h	; €
aAltkey		db 'ALTKEY',0
		db  36h	; 6
aAltscr_1	db 'ALTSCR',0
		db    1	;  
aAnswer_0	db 'ANSWER',0
.......
.......
aD_3		db 'D',0
		db  10h	;  
aData_0		db 'DATA',0
		db  3Ch	; <

.....
.....			;Ici il n'y a que des commandes suivie d'un byte special

aXrset		db 'XRSET',0
		db  47h	; G
aXt		db 'XT',0
		db  49h	; I
aZap		db 'ZAP',0
		db  52h	; R		

Au début on ne trouve que des dd offset loc_C0XXXXXX, ce qui signifie que ce call est basé par l'index ecx
(on le voyait par le code call ds:FonctionAppellee[eax]), et juste en dessous on trouve des strings terminées par un 0 puis par un mystérieux byte, comme

aXrset		db 'XRSET',0
		db  47h ; G ;<- le mysterieux byte pour la string XRSET par exemple
aXt		db 'XT',0
		db  49h ; I ;<- le mysterieux byte pour la string XT
aZap		db 'ZAP',0
		db  52h ; R ;<- le mysterieux byte pour la string ZAP

C'est le premier volet de reverse sur softice, il faut un peu en sentir le code :)
Ce byte mystérieux va correspondre a la fonction qu'on va appeler ensuite, mais le problème est qu'il n'y a pas que des multiples de 4 dans ces bytes, ce qui signifie que on ne tomberait pas sur des pointeurs valides lors du "call ds:FonctionAppellee[eax]". Pourtant en regardant juste un peu plus haut on trouve:

call sub_C000FE15
shl eax, 2
call ds:FonctionAppellee[eax]

A à quoi peut bien correspondre ce shl eax,02 ?
C'est une multiplication par 4! Voilà qui fait notre affaire.
On va tester cette hypothèse. Imaginons qu'on tape ZAP la fonction sub_C000FE15 semblerait renvoyer la valeur magique. Ca voudrait dire qu'on nous renverrait 52h, puis on multiplie le résultat par 4 et on l'ajoute a l'offset de FonctionAppellee qui nous donne
C0016D47+52h*04=C0016E8F.
Puis en allant à l'offset donné par le dword en C0016E8F, on arrive à ceci:

ZAP:                         ; CODE XREF: LCOD:C007500F j
                             ; DATA XREF: LCOD:C0016E8F o
mov  edi, ds:dword_C00104BD
dec  edi
dec  edi
add  edi, ds:dword_C00067E0
call sub_C004D7FC
call sub_C000B738
mov  bx, 9090h
cmp  ax, 1CDh
jz   short loc_C001D946
mov  bl, al
cmp  ah, 0CCh
jnz  short loc_C001D954

loc_C001D946:               ; CODE XREF: LCOD:C001D93D j
mov  ax, bx
call sub_C000B8F6
call sub_C000CBCD
retn 
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

loc_C001D954:                   ; CODE XREF: LCOD:C001D944 j
mov esi, offset aNoEmbeddedInt1 ; "No embedded INT 1 or INT 3"
call DisplayString2
retn

Héhé, on dirait que l'hypothèse n'atait pas si mauvaise que cela, car on voit le "No embedded INT 1 or INT 3" suivit de notre fonction DisplayString2 :))

J'espère que vous avez été suffisamment intéressé pour avoir envie de lire la prochaine édition :)

(Les contributions personnelles sur le reverse de SoftICE sont les bienvenues évidemment)

Greetz to:
Christal,Alsindor,Frog's Print,Spath,Acid_Burn,Stone,G-ROM,Elraizer,DXP,Fravia,ShroOm
Iczelion,ArseniK,Lucifer48,DXP,Nody,TeeJi,TaiefOon...and the others i've forgotten :)

Vous pouvez me joindre si vous avez des questions (ou si vous avez des sources asm a faire partager :))

pulsar_c@geocities.com UIN:13411849

Pulsar Decembre 1999

(Ah j'allais oublier, j'ai fait un petit programme asm pour modifier une commande (ZAP en l'occurrence) et la rediriger vers une routine de votre choix.
Ce programme est joint avec le
zip et les sources sont inclues.
Si vous souhaitez des commentaires, n'hésitez pas à me le demander, il y a certaines parties du code qui sont assez obscures:))