Les Anti Soft-Ice

         
       

By PuLsar

Depuis quelques temps, vous avez du voir apparaître de plus de Sharewares équipés d'anti SoftIce qui viennent renforcer les protections habituelles…

Cet essai n'a pas pour objectif de vous présentez toutes les techniques pour détecter softice, car il y en a vraiment beaucoup, mais d'essayer de décortiquer certaines d'entres-elles.
(Ce texte est normalement accompagné d'un
fichier "Anti-Sice.exe" et de ses sources. Pour en comprendre le fonctionnement, n'hésitez pas à les regarder).


I.Les codes habituels

I.1.Les errors Handlers

Lorsque vous programmez, il peut vous arriver d'avoir besoin de catcher des erreurs pour rediriger votre programme vers une zone mémoire plus sure ou vers un code qui évitera le plantage...
Pour cela vous utilisez les Exception Handler:
Lorsque qu'une erreur est catchée l'erreur est envoyée au débuggeur en premier. Si celui-ci la traite, rien d'autre ne se passe, dans le cas contraire l'erreur est envoyée à l'exception handler, puis une dernière fois au débugger...
Vous avez sûrement déjà vu des exceptions handler à l'œuvre (la boite de dialogue qui arrive quand un programme plante et qui affiche les registres. Et bien cette boite est celle par défaut :)
C'est à ce niveau que les premières protections anti-sice interviennent car si le débugger catche l'erreur et la traite, vous n'irez jamais dans le execpt handler.

I.1.a. La methode BCHK

S_BCHK proc

push offset ExceptHandler2
PUSH FS:[0] 
MOV FS:[0],ESP              ;on installe l'execpt 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 lance 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!
Pourquoi? Simplement parce qu'elle a été spécialement crée par les programmeurs de Softice pour permettre la communication avec d'autres produits Numega Boundschecker...

Un des aspects les plus embarrassant de cette technique, les premières fois qu'on la rencontre, c'est que rien ne semble anormal...on vient de mettre 0 dans le Flag un peut avant, et c'est logique qu'il soit resté a zéro...
Par contre avec un peu d'expérience, on remarque facilement les codes types utilisés pour générer des erreurs et pour enclencher les Execpt Handlers...

Par exemple:

xor eax,eax
mov eax,[eax]       ;page Fault


I.1.b. Les magics values

S_Int_Handler proc

push offset ExceptHandler     ;idem que tout a l'heure, on met un offset handler
PUSH FS:[0] 
MOV FS:[0],ESP

mov Flag,0

MOV ESI,04647h                ;on met les mots magiques dans esi et edi
MOV EDI,04A4Dh
INT 3                         ;on appelle l'int 03h

POP FS:[0]
ADD ESP,4h                    ;on enlève l'exception handler
mov eax,Flag
ret

ExceptHandler:

xor eax,eax
mov Flag,01          ;si softice n'est pas lancé, on arrive là et on met le flag a 1 
;->pas de debugger

ret

S_Int_Handler endp


Ici c'est identique à tout à l'heure, sauf que cette fois ci ce n'est plus ebp qui contient le mot magique, mais esi et edi

Pour détecter si ces méthodes sont utilisées, le plus facile est de regarder si il n'y as pas un push fs:[0] aux alentours... (assez voyant lorsque vous tracez :)
Mais attention, les programmes utilisent souvent un vrai except handler et il n'y aura aucune routine anti-sice dedans.


I.2. Meltice trick

Lorsqu'un vxd est présent en mémoire, vous pouvez obtenir un handler de ce dernier avec la fonction createfilea...
Si softice est présent, le vxd correspondant sera chargé en mémoire et ainsi vous pourrez checker sa présence. Cette méthode est sans doute la plus connues de toutes et très utilisée par les programmeurs qui veulent avoir un anti-softice sans s'embarrasser à coder quoi que ce soit par eux même :)

S_Meltice proc
push  0
push  FILE_ATTRIBUTE_NORMAL
push  OPEN_EXISTING
push  0
push  FILE_SHARE_READ+FILE_SHARE_WRITE
push  GENERIC_READ+GENERIC_WRITE
push  offset Sice                          ;Sice db \\.\SICE
                                           ;le nom du vxd qu'on veut ouvrir
call CreateFileA
cmp  eax,-001                              ;eax=-1 si sice n'est pas loadé
je   @END                                  ;good guy
xor  eax,eax                               ;bad guy
@END:
ret
S_Meltice endp

Rien de bien extraordinaire avec cette méthode

I.3. int 068h

S_int68 proc
mov ah,43h         ;fonction 43h
int 68h
cmp ax,0F386h      ;sice est la?
jnz @END
xor eax,eax
@END:
ret
S_int68 endp

Softice possède un handler pour l'interrupt 068h et il met F386h lorsque la fonction 43h est appelée.

I.4. Détection par la fonction 1 de la dll Kernel32

S_Kernel01 proc

push 0000004fh             ;fonction 4f 
push 002a002ah 
mov ebx,0bff713D4h         ;Kernel32!ORD_001
call ebx
cmp ax, 0f386h             ;retournée par les debuggers system
jnz @END
mov eax,Flag
@END:

ret

S_Kernel01 endp

II.Les méthodes un peu moins connues

II.1.a Checker le dr7

Pour pouvoir lire les debugs register (au nombre de 6) vous devez être en ring0. Pour ce faire, il existe 3 ou 4 méthodes pour passer du ring3 (niveau de privilège le plus bas, là où tournent les programmes normaux) au ring 0 (niveau de privilège le plus élevé ->Vxd...)
Etant donné que ce changement de ring pourrait dérouter certains d'entre vous, j'ai un peu masqué la méthode utilisée... Vous pourrez la voir un peu plus bas si cela vous intéresse :)
En ring 0 le programme peut lire les debugs register. A titre d'exemple le dr7 vaut 400h normalement, mais si un debugger est présent il a de fortes chances pour qu'il change :)

S_DRx proc
push offset RingZeroCode           ;la routine qu'on veut exécuter en ring0
call RingZero                      ;fonction qui permet de passer en ring0 :)

cmp eax,0400h                      ;le dr7 etait egal a 400h?
je @END                            ;oui->good guy
xor eax,eax                        ;non->Debugger detected
@END:
ret

RingZeroCode:

mov eax,dr7
iretd
S_DRx endp


Cette méthode est assez déroutante pour le "newbie" car le passage en ring0 n'est pas de toute simpliste, mais une fois que vous aurez compris le principe, cela devient enfantin!
L'important pour voir ce style de méthodes est de repérer les SIDT fword ptr [XXXXXXXX] et les iretd qui vont être les indicateurs permettant da savoir qu'il va se produire quelque chose en ring0 :)

II.1.b Checker le dr7 en se protegeant :)

Certains tools comme frogsice tentent de catcher les accès au drx en enclenchant un bit du Dr6 qui déclenche une int01h si jamais vous essayez de toucher à un debug register :)
Pour contrer ce genre de protections, il exister une méthode, assez simple en soit, qui consiste à hooker nous même l'int 01h, et tant qu'a faire l'int 03h, pour éviter les breakpoints (0CCh)..

S_DRxP proc

sidt fword ptr IDT                    ;on obtient le registre IDT
mov ebx, dword ptr [IDT+2]            ;on met dans ebx l'adresse de la base de l'IDT
add ebx, 8*03h                        ;on se place au niveau de l'entrée de l'int 03h 
cli 
mov dx, word ptr [ebx+6]              ;on sauve le high word de l'ancienne routine 
shl edx, 16d
mov dx, word ptr [ebx]                ;le low word 
mov [lpOldGate], edx                  ;on sauvegarde
mov eax, offset RingZeroCodeProtected ;on installe notre routine 
mov word ptr [ebx], ax ;low word
shr eax, 16d
mov word ptr [ebx+6], ax ;high word

mov ebx, dword ptr [IDT+2] 
add ebx, 8*01h                       ;on se place au niveau de l'entree de l'int 01h
cli 
mov dx, word ptr [ebx+6]             ;on sauvegarde le high word
shl edx, 16d
mov dx, word ptr [ebx]               ;le low word 
mov [lpOldGate2], edx                ;on sauvegarde
mov eax, offset HookInt0             ;on installe notre routine pour l'int 01h
mov word ptr [ebx], ax               ;low word
shr eax, 16d
mov word ptr [ebx+6], ax             ;high word 

int 03h ;on crée une int qui va nous envoyer dans notre routine de l'int 03h 

mov ebx, dword ptr [IDT+2]          ;on fait tout dans l'autre sens en restaurant 
add ebx, 8*01h
mov edx, [lpOldGate2]
mov word ptr [ebx], dx
shr edx, 16d
mov word ptr [ebx+6], dx            ;l'int 01h

mov ebx, dword ptr [IDT+2]
add ebx, 8*ExceptionUsed
mov edx, [lpOldGate]
mov word ptr [ebx], dx
shr edx, 16d
mov word ptr [ebx+6], dx            ;l'int 03h

cmp eax,0400h                       ;on reteste notre dr7
je @END
xor eax,eax
@END:
ret

RingZeroCodeProtected:

mov eax,dr7 ;les outils qui hookent l'int 01h ne verront rien 
;car leur routine n'est plus active

iretd

HookInt0:
iretd

S_DRxP endp

(pour le passage en ring0 si ca vous intéresses, demandez-moi des URLs)

II.2 Les strings que softice laisse traîner en mémoire :)

S_Winice_brk proc

mov al,"W"            ;le W de WINICE :)
mov edi,10000h        ;on scanne la mémoire en commençant a 10000h
mov ecx,0FFFFFh       ;on va scanner sur 0FFFFFh bytes

@Loop:
repnz scasb
jecxz @END            ; scannage terminé sans rien avoir trouvé -> pas de softice en vue

cmp dword ptr [edi], "CINI"    ;on compare le INIC de WINICE 
                               ;(ne pas oublier qu'il faut inverser les caractères)

jz @Suite

jmp @Loop
@Suite: 
add edi, 4
cmp dword ptr [edi], "RB.E"   ;on compare la fin soit WINICE.BR->E.BR (qu'on pense a inverser)
jnz @Loop
xor eax,eax                   ;softice est détecté :)


@END:

ret
S_Winice_brk endp


II.3 L'IDT

S_SIDT proc

SIDT FWORD PTR IDT
mov edi,dword ptr [IDT+2]
mov bx,word ptr [edi]
mov ax,word ptr [edi+8]
sub ax,bx
sub ax,010h

@END:
ret
S_SIDT endp

Si vous vous en rappelez, nous avions lu les adresses des fonctions qui contrôlaient les int lorsque nous passions en ring0. Cette fois ci, nous allons nous servir du fait que softice détourne les int 01h et int 03h (ne vous fiez pas a ce qu'il dit quand vous faites IDT dans softice.... il ment :)
En prenant le low word de l'adresse de la routine pour l'int 00h et celle pour l'int 01h, normalement vous devriez avoir:

bx=1350
ax=1360

Faites bx-ax-10 et si le résultat est égal à zéro sice n'as pas hooké les int, et donc il est absent :)
La fonction renvoie ax=0 si sice est absent et une autre valeur s'il est la :)

Voici un premier tout d'horizon des méthodes classiques et de celles qui le sont un peu moins.
Il y a bien sur d'autres méthodes comme vous pouvez le voir dans le fichier code.txt livre avec Frogsice :)
Si vous venez à en découvrir de nouvelles, merci de bien vouloir me les faire parvenir :)

Pulsar
Décembre 1999