jeudi 22 octobre 2009

INT 0x2E a la trick

Je m'excuse de la présentation hasardeuse de ce post (notamment la page vide), blogger c'est de la bouse.


Afin de prouver au monde entier que je ne suis pas complètement mort, je me permet de mettre ici une petite analyse d'un trick anti-unpacker qui n'a pas reçu l'attention qu'il mérite à mon humble avis. Il n'est pas compliqué et pas très méchant mais il peut casser los boulos quand on le découvre.


En effet, il n'est pas présent dans la Holy Bible de l'unpacking de notre ami Peter et à ma connaissance la seule référence dans la littérature se trouve dans les slides d'une présentation faite récemment lors de la dernière conférence VB par Kurt Baumgartner. Mais cette analyse, qui décrit le trick comme "Int 0x2e 0xc0000005 location generation trick" est un peu trop rapide, donc j'en profite pour réveiller mon blog.

Comme tu l'a déjà deviné cher lecteur, ce trick utilise l'interruption 0x2E qui permettait de lancer dans « l’ancien temps » les appels systèmes Windows : on plaçait le numéro de l’appel dans le registre EAX, ses arguments dans EDX et hop ! Depuis, l’instruction SYSENTER à remplacé ce bouzin, car plus rapide. Mais bien sur INT 0x2E traine toujours sa ganache dans le coin pour des questions de compatibilité. Finalement, l'instruction n'est pas documentée par Microsoft, ce qui impose une approche "black box" si, comme moi, on est faignant et peu porté dans l'architecture Windows.

I- Utilisation "basique"

Le trick utilisé de la façon la plus simple possible donne :

B8 FFFFFFFF____MOV EAX,-1
CD 2E__________INT 2E
66:817A FE CD2_CMP WORD PTR DS:[EDX-2],2ECD

On appelle donc l’interruption avec comme numéro de syscall "-1" (ce qui est invalide).

Le comportement « normal » dans ce cas là est de faire échouer le syscall en plaçant un code d’erreur dans EAX (0xC000001C) et en faisant pointer EDX vers l’instruction suivant l'interruption. Donc dans notre exemple, EDX-2 pointe sur le début de l'instruction INT 2E dont les opcodes sont bien 2ECD en little-endian.

Par contre sous contrôle d’OllyDbg (v1 ou v2), le registre EDX va être mis à 0xFFFFFFFF si on passe l’interruption en « single-step ». Dans ce cas là un accès mémoire invalide sera réalisé lors de la comparaison, ce qui fera planter le programme.

Remarquons que ce trick devient aussi « anti-VM » dans le cas de VirtualBox de Sun (EDIT: Voir commentaires), puisque même sans être sous le contrôle du débuggeur le registre EDX est mis à 0xFFFFFFFF à tous les coups, sûrement une mauvaise implémentation des interruptions... Une raison de plus (avec la non-implémentation des hardwares breakpoints lorsqu’on n’utilise pas les techniques de virtualization matérielle) pour rester sur VMWare pour l’analyse de malwares.

Finalement, il est utile contre tout émulateur qui n'implémentent pas les interruption de façon rigoureuse.

II- Utilisation "avancée"


Ce qui n'est pas dit dans la littérature et qui est déjà connu des méchants (par exemple ceux qui font le packer de Waledac), c'est que :

  • d'une part, en plaçant autre chose dans EAX que "-1" la valeur de retour va varier et donner divers code d'erreur, tout ceci de façon "stable" (on nike pas tout dude). Pour vérifier cela on se fait un petit programme qui appelle l'interruption sur divers paramètres, voici les premières valeurs qu'on obtient :






















































INT 2E return values
Parameter Return value
0xFFFFFFFF 0xC000001C
0x0-0x1 0xC0000005
0x2 0xC000005C
0x3 0xC0000005
0x4-0x8 0xC000000D
0x9 0xC0000002
0xA-0xC 0xC0000005
0xD 0xC0000008
0xE-0x10 0xC0000005
0x11 0xC00000F1
0x12 0xC0000141
... ...


Il y a donc certaines valeurs particulières, ce qui fait un levier de plus pour détecter un émulateur|débuggeur|VM-pourrie-de-chez-Sun.

  • d'autre part, ECX doit normalement toujours être positionné sur ESP après la levée de l'interruption.

Tout ca peut donner lieu à des utilisations plus rigolote du trick, comme celle-ci vu récemment dans mon malware préféré :

Le code démarre sur ça :

68 CD2EEB08___PUSH 8EB2ECD________________; INT 2E camouflé
58____________POP EAX
2D C42EEB08___SUB EAX,8EB2EC4_____________; EAX = 9
EB F4_________JMP SHORT Waledac_.00401153 ; JMP sur le
INT 2E

Donc on saute sur le INT 2E camouflé au milieu du premier PUSH, ce qui donne à ce moment là le code suivant (les commentaires donnent les valeurs "attendues") :

CD 2E_______INT 2E
EB 08_______JMP SHORT Waledac_.0040115F
58__________POP EAX
2D C42EEB08_SUB EAX,8EB2EC4
EB F4_______JMP SHORT Waledac_.00401153
52__________PUSH EDX____________________; adresse qui suit INT2E (0x401155)
91__________XCHG EAX,ECX________________; EAX=ESP, ECX=0xC0000002
0FC9________BSWAP ECX___________________; ECX=0x020000C0
C1E1 04_____SHL ECX,4___________________; ECX=0x20000C00
8B00________MOV EAX,DWORD PTR DS:[EAX]__; adresse de retour initiale
2B4424 04___SUB EAX,DWORD PTR SS:[ESP+4]; EAX = 0
03C8________ADD ECX,EAX_________________; ECX=
0x20000C00
81E9 FAE5FF_SUB ECX,1FFFE5FA____________; ECX=0x00002606
03CA________ADD ECX,EDX_________________; ECX =
0x401155 + 0x00002606 = 0x40375B
870C24______XCHG DWORD PTR SS:[ESP],ECX
FF1424______CALL DWORD PTR SS:[ESP]_____; CALL
0x40375B -> HERE WE GO 3v1L!


On voit dans ce petit exemple comment sont utilisés les trois leviers possibles de ce trick :
  • la valeur de retour qui doit être le code d'erreur associé à l'appel système (comme on passe pas d'arguments dans EDX, y a peu de risque que l'appel système fonctionne).
  • l'adresse mise dans EDX qui doit normalement être celle de l'instruction qui suit la levée de l'interruption.
  • ECX doit être égal à ESP après la levée de l'interruption.
En jouant avec les valeurs de retours on peut faire beaucoup de tricks différents pour tester l'implémentation de cette interruption, that's not so bad.

Bon allez je retourne dans ma grotte!

7 commentaires:

Anonyme a dit…

Merci pour ton article.
J'étais tout content de découvrir enfin un trick anti VirtualBox mais il ne marche pas :/

J'ai testé :

mov eax, -1
int 2Eh
mov ax, word ptr [edx-2]

AX = 2ECD sur windows xp SP2 virtualisé par VirtualBox 3.0.8

EDX n'est pas mis à 0xFFFFFFFF.

JoE a dit…

Oups, merci de la remarque ! J'ai bitché VirtualBox mais j'avais pas la dernière version (encore en v2 pour tout dire...), so mea culpa :)

JoE a dit…

Hmmm, en fait j'obtiens exactement le même comportement avec la dernière version.
J'utilise le bout de code suivant :

mov eax, -1
int 2Eh
jmp edx

Si le test est passé correctement ca lance une boucle infinie (EDX pointe sur le JMP EDX), sinon ca crash (JMP 0xFFFFFFFF).
Pourrais tu essayer de compiler ce code et voir ce que ça fait ? (Sinon je l'ai mit ici : http://vxhell.org/~joe/poc_INT2E.exe, mais pour peu que tu est un AV tatillons il va raler, et je pourrai comprendre que tu n'ai pas confiance :P )

Anonyme a dit…

J'ai testé ton exe et le bout de code compilé, c'est pareil sur mon xp normal et sur mon xp sur VirtualBox.. (boucle infinie)

Vraiment bisard.
J'ai un Core2Duo T7200.

Anonyme a dit…

Je présise aussi qu'avec OllyDebug, edx est mis à 0xFFFFFFFF en single step.
Donc ça marche juste comme antiDebug chez moi..

JoE a dit…

I got it !
Pour que le trick fonctionne il faut que la VM ait les extenstions de virtualization hardware désactivée (décochée dans System-> Acceleration -> Enable VT-x/AMD-V), mais comme c'est coché par défaut à priori ça a peu d'impact...

Par contre je vois pas vraiment le rapport avec l'implémentation des interruptions!

Merci de m'avoir fait remarqué ça :)

JoE a dit…

A noter que Peter en parle en fait brièvement dans la version VB de son article, désolé d'avoir douter de ta puissance Pitou :
http://pferrie.tripod.com/papers/unpackers21.pdf