|
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 Rien de bien extraordinaire avec cette méthode Softice possède un handler pour l'interrupt 068h et il met F386h lorsque
la fonction 43h est appelée. S_Kernel01 endp (pour le passage en ring0 si ca vous intéresses, demandez-moi des URLs)
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 BCHKS_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 valuesS_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
I.3. int 068hS_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
I.4. Détection par la fonction 1 de la dll Kernel32S_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
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
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