Vous êtes sur la page 1sur 11

Construction d’un désassembleur

de taille orienté Hooking


Rubén Santamarta

Article publié dans le numéro 4/2006 du magazine hakin9


Tout droits réservés. La copie et la diffusion d’article sont admises a condition de garder sa forme et son contenu actuels.
Magazine hakin9, Software – Wydawnictwo, ul. Piaskowa 3, 01-067 Varsovie, Pologne, fr@hakin9.org
Construction d'un
désassembleur de taille
Pratique
orienté Hooking
Rubén Santamarta

Degré de difficulté

Jour après jour, les chercheurs des programmes malveillants, les


analystes juridiques ou les administrateurs doivent faire face aux
dangers menaçant la sécurité des systèmes informatiques. Leur
objectif est d'expliquer les intrusions non autorisées, de protéger
les utilisateurs contre les virus ou de protéger les systèmes
contre différents dangers.

P
our atteindre ces buts, une analyse très De cela, nous prendre des mesures suffi-
détaillée du fonctionnement des logiciels santes pour résoudre chaque type de problè-
malveillants est nécessaire ; c'est l'ingé- mes auxquels nous pouvons nous heurter.
nierie inverse qui entre ici en jeu.Les concepteurs
des programmes malveillants (virus, troyens, Hooking
rootkits) essaient de rendre cette analyse extrê- Comme on peut remarquer, il existe beaucoup
mement difficile, par exemple à l'aide des techni- d'astuces qui ont pour but de rendre difficile
ques empêchant le déboguage, des techniques l'utilisation du débogueur (aussi bien au niveau
polymorphiques, des techniques stealth ou des Ring0 que Ring3) qui est un principal outil dans
programmes pour la compression des fichiers ; l'ingénierie inverse. De cela, il est nécessaire
ces derniers non seulement réduisent la taille des
fichiers exécutables, mais aussi ajoute une cou-
che protectrice plus ou moins complexe. Cet article explique...
Dans ces situations, c'est le temps qui
• Comment utiliser hooking pour l'analyse des
compte avant tout : grâce à une analyse longue
programmes malveillants (malware).
et précise, tôt ou tard, nous atteindrons notre
• Comment exploiter Structure Exception Han-
but et pourrons connaître tous les détails du dling pour la création d'un désassembleur de
danger. Hélas, il arrive que nous n'avons pas taille.
assez de temps et dans ce cas il faut optimiser
les opérations liées aux procédures de l'ana-
Ce qu'il faut savoir...
lyse. Imaginez un ver exploitant une erreur
inconnue d'un programme qui lui permet de • Connaître Assembleur x86 y C.
se répandre via Internet. Le temps investi dans • Connaître Win32 Api et Structure Exception
l'analyse et la compréhension du fonctionne- Handling.
ment de ce ver marque la limite entre la vrai • Avoir les notions de base des techniques utili-
catastrophe des utilisateurs et le danger réduit sées par les logiciels malveillants et les virus.
et neutralisé.

52 hakin9 Nº 4/2006 www.hakin9.org


Reconnaître l'adversaire

d'élaborer une méthode qui, dans


Techniques utilisées contre désassembleurs les conditions appropriées, permet-
et débogueurs trait d'influencer le comportement
Pendant des années, les concepteurs des programmes malveillants, les auteurs du fichier exécutable analysé et le
des virus ou même les développeurs des programmes commerciaux, dotaient modifier.
leur programmes des méthodes anti-debug et anti-disasm. La plupart d'elles sont L'une des techniques les plus
destinées à détecter si un programme donné est suivi par un débogueur. Si c'est utilisées servant à atteindre ce but
le cas, le programme peut entreprendre différentes actions, en commençant par est hooking.
l'arrêt immédiat du démarrage et en terminant par le redémarrage de l'ordinateur On peut distinguer différentes
ou même par des opérations plus agressive, ce qui, heureusement, est peu po- techniques d'accrochage en fonction
pulaire.
de l'endroit où il a lieu. Chaque type
• Une très vielle astuce utilisée pour détecter la présence de SoftIce, le débogueur est destiné à d'autres applications.
Ring0 le plus connu, employé dans le monde entier dans l'ingénierie inverse, Ainsi, on obtient les types suivants :
était une tentative d'accéder aux mécanismes créés par l'un de ses pilotes -
NtIce. • Inline Hooking,
• L'instruction RDTSC dans l'assembleur x86 : un mnémonique de Read • Import Address Table hooking,
Time−Stamp Counter. Cette instruction stocke dans EDX:EAX (64 bits) la valeur • System Service Table hooking
timestamp du processeur. Imaginons que RDTSC est lancée au début du bloc (Ring0),
du code, et la valeur retournée est stockée. À la fin de ce bloc du code, nous • Interrupt Descriptor Table hoo-
lançons encore une fois RDTSC et soustrayons la valeur obtenue de la valeur
king (Ring0),
stockée. Quand le programme est démarré de façon ordinaire, le résultat de
• IRP hooking (Ring0),
cette opération aura les valeurs rationnelles, en tenant bien sûr compte de la
vitesse et de l'utilisation du processeur. Mais si nous nettoyons ce bloc du code,
• Filter drivers (NDIS,IFS...Ring0).
l'incrément de timestamp entre deux lecture sera très élevé par rapport à celui
trouvé dans le débogueur. Dans notre cas, nous allons utiliser
• La manipulation des interruptions pour modifier le flux du code. Une possibilité la méthode Inline Hooking. Nous
très puissante de l'architecture Win32 est la fonction Structure Exception Handling l'employons parce qu'elle permet
(SEH) qui permet de déterminer les schémas de la fonction callback pour contrôler Nous utilisons cette technique parce
les exceptions. Vu que les débogueurs s'occupent de toutes les exceptions ayant qu'elle permet de retoucher la fonc-
lieu lors du fonctionnement du programme, les procédures définies par le pro- tion à intercepter quand elle est char-
grammeur pour la gestion des exceptions ne seront jamais utilisées. Admettons gée dans la mémoire. Ainsi, nous ne
que nous avons basé le flux de notre programme sur la procédure de ce type.
devons pas nous soucier de quel en-
Si après une génération expresse de l'exception (par exemple, au moyen de xor
droit proviennent les appels ou com-
eax,eax, et ensuite mov [eax],eax ), nous ne parvenons pas à l'espace du code
admis, nous sommes probablement sous la surveillance d'un débogueur.
bien de fois ils ont eu lieu, mais nous
• Autres astuces, moins élaborées, basés sur les propriétés spécifiques de chaque attaquons directement le noyau. Un
débogueur : les tentatives de retrouver certains types ou titres de fenêtres enregis- appel quelconque de la fonction sera
trés par le programme ou bien des clés dans le registre de Windows qui pourraient intercepté par notre crochet.
le dévoiler.
Interception et modification
du flux de code
Admettons que nous voulons inter-
cepter tous les appels API Close-
Handle qui se produisent lors du
démarrage du programme. L'API se
trouve dans kernel32.dll, donnons
un coup d'œil sur ses premières
instructions :

01 8BFF mov edi,edi


02 55 push ebp
03 8BEC mov ebp,esp
04 64A118000000 mov eax,fs:[00000018]
05 8B4830 mov ecx,[eax][30]
06 8B4508 mov eax,[ebp][08]

Ce bloc du code présente les pre-


miers octets du point d'entrée Clo-
Figure 1. Le schéma de base d'Inline Hooking seHandle, ce qui signifie que chaque

www.hakin9.org hakin9 Nº 4/2006 53


Pratique

Tableau 1. Les données disponibles pour la gestion des exceptions, si celle- C'est pourquoi, il faut développer
ci est active une technique le moins agressive
T Données envers le code source qui permet-
trait à la fonction accrochée de fonc-
ESP+4 Pointeur à la structure
tionner normalement tout en restant
EXCEPTION_RECORD
constamment sous contrôle. Cette
ESP+8 Pointeur à la structure ERR technique est appelée Detour (elle
ESP+C Pointeur à la structure a été présenté par Galen Hunt et
CONTEXT_RECORD Doug Brubacher des laboratoires
Microsoft)
appel de la fonction démarrera sans à détecter parce qu'il est suspect si le
doute le code précédent. En nous point d'entrée d'une fonction système Detour
servant du schéma d'Inline Hooking, contient le saut inconditionnel vers une Par rapport à Inline hooking, la
nous remplacerons ces premières autre adresse dans la mémoire. Nous technique Detour implémente deux
instructions par notre propre crochet pouvons utiliser une autre option étant nouvelles conceptions : la fonction
qui modifiera le flux normal de la la combinaison de PUSH + RET . Detour et la fonction Trampoline.
fonction dans la direction du filtre.
01 68EFBEADDE push 0DEADBEEF • Fonction Detour doit se compo-
Différentes possibilités 02 C3 retn ser d'une partie dans laquelle
pour le même objectif 03 A118000000 mov eax,[00000018] sont réalisées les premières
Il existe différentes méthodes de chan- opérations sur les données ob-
gement du flux dans la direction du Dans ce cas, nous remplaçons les tenues, ensuite vient l'appel à la
notre code. La plus simple consiste à 6 premiers octets. Si nous regar- fonction Trampoline et à la fin, se
changer les premiers octets de Close- dons ce fragment avec attention, trouve la partie du code qui sera
Handle par un saut inconditionnel. nous constatons que le code origi- lancé après la terminaison de la
nal CloseHandle est effectivement fonction Trampoline.
01 E9732FADDE jmp 0DEADBEEF changé, mais non du point de vue • Fonction Trampoline contient aussi
02 64A118000000 mov eax,fs:[00000018] des instructions ajoutées, mais aussi bien les instructions de la fonction
certaines d'entre elles sont perdues cible, remplacées complètement
Nous avons remplacé 5 premiers et d'autres ont changé. C'est une par le saut inconditionnel, que
octets par un saut vers l'adresse question à discuter. Bien que nous celles qui ont été changées par-
0xDEADBEEF. Bien sûr, cette avions atteint notre objectif et inter- tiellement. Ensuite, il y a un saut
adresse n'est pas valide car nous cepté tous les appels de la fonction, à l'instruction suivante correspon-
travaillons en mode utilisateur. la modification étaient si importante dant à la fonction cible.
À cette adresse, il y aurait le code qu'elle a entraîné les anomalies du
que nous avons introduit dans l'espa- comportement, ce qui en résultat Ainsi, nous pouvons résoudre le
ce adressable du fichier exécutable. pourrait provoquer la fin inattendue problème lié aux données perdues
Vu que c'est la méthode la plus du programme pendant le premier ou aux instructions modifiées qui
simple, elle est aussi la plus facile appel CloseHandle. se produisait dans Inline Hooking.
Il s'agit de sauver ces instructions
dans la fonction Trampoline pour
qu'elles puissent être exécutées. En-
suite, nous sautons à une instruction
suivante où la fonction cible fonction-
nera normalement. Une fois la fonc-
tion cible terminée, nous reprenons
le contrôle sur la fin de la fonction
Detour. Elle est capable de récupérer
le chemin de démarrage en restau-
rant le contrôle de la fonction source
ou la possibilité de réaliser un autre
type d'opération.
Mais comment savoir combien
d'instructions faut-il copier dans la
fonction Trampoline à partir de la
fonction cible ? Chaque fonction
Figure 2. Le schéma de base de la technique Detour cible sera différente, et de cela il

54 hakin9 Nº 4/2006 www.hakin9.org


Reconnaître l'adversaire

tion SEH définie pour le thread, la


Codes des exceptions gestion définie dispose des données
Une exception due à un accès à la zone de la mémoire invalide et une exception pro- suivantes.
duite à la suite de l'opération de division par 0 ne peuvent pas être traitées de la même Si une exception se produit, le
façon. Le système identifie chaque situation pour faciliter la tâche de la gestion des système démarre la gestion des
exceptions. Les codes des exceptions les plus répandues sont : exceptions pour qu'elle décide des
actions ultérieures. À ce moment esp
• C0000005h – Violation des droits d'accès aux opérations de lecture ou d'écriture.
pointe vers différentes structures.
• C0000017h – Pas de mémoire libre.
Dans la structure EXCEPTION_
• C00000FDh – Stack Overflow.
RECORD, l'attention est attirée sur
Les codes suivants sont les plus importants pour notre projet : les champs ExceptionCode et Ex-
ceptionAddress :
• 80000003h – Breakpoint généré par l'instruction int 3.
• 80000004h – Single step généré par l'activation de Trap Flag dans le registre
• ExceptionCode identifie le type
EFLAGS.
de l'exception créée. Dans le
système, différents codes sont
sera impossible de copier le nombre le problème dont nous avons parlé définis pour chaque type ; de
d'octets déterminé car les instruc- auparavant. plus, il est possible de définir
tions pourraient être coupées. Ce À partir du potentiel de Structure nos propres codes pour person-
problème est résolu à l'aide du dé- Exception Handling, nous explique- naliser les exceptions via API
sassembleur de taille. rons la technique innovatrice de la RaiseException.
création des désassembleurs dyna- • ExceptionAddress est une adresse
Désassembleurs de taille miques de taille. Au boulot. de la mémoire appartenant à l'ins-
Les désassembleurs de taille diffè- truction créée par une exception ;
rent des désassembleurs ordinai- elle correspondrait au registre eip
res par leur fonction qui consiste
Utilisation de Structure au moment où une exception s'est
à obtenir la taille des instruction et Exception Handling produit.
pas leur présentation. Ce type de (SEH)
désassembleurs était utilisé par les Premièrement, nous devons nous Une autre structure de base qu'il
virus cavités, polymorphiques, etc. concentrer sur les caractéristiques faut connaître est CONTEXT. Cette
C'est pourquoi, les deux des désas- du problème auquel nous voulons structure contiendra toutes les va-
sembleurs de taille (les vraie perles trouver la solution. leurs des registres au moment où
de l'optimisation) les plus utilisés ont l'exception se produit.
été programmés par les concepteurs • Les premières instructions des Il ne faut pas oublier que c'est la
des virus connus : Zombie et RGB. fonctions cibles ne diffèrent pas possibilité de contrôler chaque instruc-
Ils sont basés sur un désassem- trop, mais les différences sont tion démarrée est la plus importante,
balge statique des instructions. Pour aussi importantes qu'il est né- comme cela se fait dans le débogueur.
cela, ils utilisent les tableaux des co- cessaire d'envisager chaque cas En fait, notre désassembleur aura tou-
des des opérations de l'architecture séparément. tes les fonctionnalités du débogueur.
sur laquelle ils fonctionnent, dans ce • Le saut inconditionnel (jmp) ou
cas x86. Push + ret n'occupent pas plus
Outre le fait qu'ils sont utilisés de 6 octets. Il faudra analyser de Programmation
pour la création des virus comple- 4 à 5 instructions au maximum. du désassembleur de
xes, on l'emploie aussi pour le hoo- • Les premières instructions exécu- taille
king car ils permettent de résoudre tent généralement les opérations Premièrement, il faut définir l'en-
liées à l'ajustement de la pile. vironnement dans lequel nous dé-
Tableau 2. Champs de la structure
• L'idée consiste à pouvoir lancer marrerons les fonctions surveillées ;
EXCEPTION_RECORD
ces premières instructions dans ainsi, nous appellerons les instruc-
Offset Données l'environnement contrôlé, ce qui tions appartenant à la fonction cible
nous permettra de calculer leur dont la longueur nous voudrons
+0 ExceptionCode
longueur. connaître, pour pouvoir ensuite les
+4 ExceptionFlag copier entièrement dans la fonction
+8 NestedExceptionRecord Pour savoir comment construire cet Trampoline.
+C ExceptionAddress environnement, il faut saisir les infor- Pour cela, premièrement il faut
+ 10 NumberParameters mations apportées par SEH. déterminer l'étendue de SEH, SEH_
Pour chaque exception que a eu SEHUK étant une routine gérant les
+ 14 AdditionalData
lieu dans le code protégé par la fonc- exceptions qui se produisent.

www.hakin9.org hakin9 Nº 4/2006 55


Pratique

01 push dword SEH_SEHUK interruption, après le démarrage Tableau 3. Le champ CONTEXT


02 push dword [fs:0] de l'instruction suivante, l'excep- appartenant aux registres généraux
03 mov [fs:0], esp tion Single Step sera automatique- et de contrôle
ment générée. Ainsi, le contrôle Offset Registre
Dès ce moment, tout le code dé- est restauré. Nous avons obtenu la
+ 9C EDI
marré après ces instructions sera même information qu'à la suite des
protégé. L'étape suivante consiste opérations effectuées pas à pas. + A0 ESI
à copier un nombre déterminé d'oc- + A4 EBX
tets qui contiendront les instructions Consultons ces points créés dans le + A8 EDX
surveillées, à partir de la fonction code de l'assembleur : + AC ECX
cible vers la zone réservée à l'inté-
+ B0 EAX
rieur de notre code. Sa taille peut 12 SEH_SEHUK:
changer. Dans notre cas, nous avons 13 mov esi, [esp + 4] + B4 EBP
choisi 010h car elle est assez large ; EXCEPTION_RECORD + B8 EIP
pour contenir en entier les premières 14 mov eax, [esi] + BC CS
instructions. ; ExceptionCode
+ C0 EFLAGS
15 test al, 03h
+ C4 ESP
04..mov esi,TargetFunction ; Int3 Exception Code
05 mov..edi,Code 16 mov eax, [esi + 0Ch] + C8 SS
06..push 010h ; Eip Exception
07 pop ecx 17 mov esi, [esp + 0Ch] (code de l'exception 80000003h), ou
08..rep movsb ; CONTEXT record au contraire, il s'agit d'une exception
18 mov edx, [esi + 0C4h] provoquée par Trap Flag ou un autre
Quand nous sommes arrivés à ce ; Esp Exception évènement. S'il s'agit de l'exception
point, il ne nous reste qu'une seule 19 jz Int1h BreakPoint, nous effectuerons les
étape avant de démarrer les instruc- 20 mov eax, [esi + 0B4h] opérations expliquées auparavant
tions surveillées. Voyons : ; Ebp Exception (lignes 20, 21, 22).
21 mov [OrigEbp], eax Il est nécessaire de modifier EIP
09 int 3 22 mov [OrigEsp], edx du contexte pour qu'au moment de la
10 Code: 2 inc dword [esi + 0B8h] restauration du contrôle dans le sys-
11 ModCode times 12h db (90h) ; Eip++ (Int3->Instruction suivante) tème, il pointe vers l'instruction qui
24 mov eax,Code vient après int 3, autrement, nous
ModCode est la zone dans laquelle 25 mov [PrevEip],eax serions dans une situations sans is-
nous avons copié les instructions 26 jmp RetSEH sue. Pour éviter cela, comme on voit
surveillées ; avant d'arriver à ce dans la ligne 23, nous incrémentons
point, nous avons mis la constante On voit que dans la ligne 15, nous la valeur d'une unité. Cela est dû au
int 3. Pourquoi ? Il y en plusieurs comparons al avec 03 pour savoir si fait que le code de l'opération pour
raisons : l'exception a été produite par int 3 int 3 a 1 octet.

• Comme nous l'avons déjà men-


tionné, les premières instructions
d'une fonction cible quelconque
réalisent d'habitude les opérations
de modification de la pile. De cela,
il faut s'assurer que notre pile ne
sera pas détruite par ces actions.
Après l'appel d'int 3, une excep-
tion qui nous permettra d'accéder
à SEH_SEHUK (notre gestion),
est générée. Ainsi, en accédant
à la structure CONTEXT, nous
sauvons les registres ESP et EBP
pour restaurer l'état de notre pile
aux valeurs précédentes après la
terminaison de l'analyse.
• L'activation de Trap Flag dans le
registre EFLAGS. Grâce à cette Figure 3. Le schéma du fonctionnement de notre désassembleur de taille

56 hakin9 Nº 4/2006 www.hakin9.org


12 Nos
OFFRES DE COUPLAGE
6 Nos 12 Nos 6 Nos

+ +

147 €
12 Nos 6 Nos

99 € 12 Nos 6 Nos

+ +

OUI, je m’abonne et désire profiter des offres spéciales de couplage


Abonnez – vous !!!
Référence de l’offre : Prix : Qté : À partir du
numéro : l par courrier en nous
renvoyant le bon ci-dessous
12 Nos Linux+ DVD + 6 Nos PHP 99 € l par le web, sur notre site :
http://buyitpress.com/fr
12 N Linux+ DVD + 6 N Hakin9 comment se défendre ?
os os
99 €
l par téléphone entre 10-17
12 Nos Linux+ DVD + 6 Nos SDJ Extra 99 € au 00 48 22 887 13 44
l par fax au 00 48 22 887 10 11
12 Nos Linux+ DVD + 6 Nos Programmation sous Linux 99 €

Commande
Merci de remplir ce bon de commande et de nous le retourner par fax : 0048 22 887 10 10 ou par courrier :
Software-Wydawnictwo Sp. z o. o., Piaskowa 3, 01-067 Varsovie, Pologne ; E-mail : abonnement@software.com.pl

Prénom Nom ..................................................................................... Entreprise .........................................................................................

Adresse ...........................................................................................................................................................................................................

Code postal ...................................................................................... Ville ...................................................................................................

Téléphone ......................................................................................... Fax ....................................................................................................

E-mail ................................................................................................

Je règle par :

¨ Carte bancaire n° CB expire le date et signature obligatoires


type de carte .......................................................................... code CVV/CVC
¨ Virement bancaire :
Nom banque : Société Générale Chasse/Rhône
banque guichet numéro de compte clé Rib
30003 01353 00028010183 90

IBAN : FR76 30003 01353 00028010183 90


Adresse Swift (Code BIC) : SOGEFRPP
Pratique

Dans la ligne 25, nous enregis- Objectif Taille 2 octets +3 octets des précé-
trons l'adresse de la mémoire où Le but de cette partie du code est dents = 5 octets. On continue
commencent nos instructions sur- l'analyse de la longueur des instruc-
veillées, ce qui nous permettra au tions surveillées jusqu'à trouver la 04 64A118000000 mov eax,fs:[00000018]
moment de l'émulation des appels valeur supérieure ou égale à celle
et des sauts conditionnels et incon- qui occuperait notre crochet, indé- Taille 6 octets +5 octets des précé-
ditionnels. pendamment du fait, si ce serait un dents = 11 octets. C'est déjà fait !
saut inconditionnel (jmp 5 octets) ou Notre désassembleur de taille
push+ ret (6 octets). Imaginons par retournera 11. Que cela signifie-t-il ?
Analyse exemple que le type de notre cro- Cela veut dire que pour un crochet
des instructions chet est push+ret et nous essayons composé de six octets, pour ne pas
surveillées d'accrocher CloseHandle. Nous perdre aucune instruction, il faut co-
Jusqu'alors, tout ce que nous avons indiquons au désassembleur que pier onze octets de la fonction cible
présenté, pourrait être contenu dans notre crochet occupe 6 octets (Hoo- dans la fonction Trampoline.
le bloc préparation de l'environne- kLength = 6). Il commencera alors
ment. Nous allons commencer à exa- à compter la longueur de la première Analyse des données
miner le code appartenant à l'analyse instruction Nous avons ici un bloc initial de l'ana-
des instructions surveillées. lyse. Jusqu'à la ligne 46, nous avons
01 8BFF mov edi,edi l'algorithme permettant d'émuler les
Listing 1. Observation du code appels et les sauts. Cet algorithme
Taille 2 octets. Vu qu'il est inférieure consiste à vérifier le décalage entre
27 Int1h: à 6, il continue comme suit EIP dans lequel l'exception s'est pro-
28 mov ecx, eax duite et la valeur précédente du re-
29 sub eax, [PrevEip]
02 55 push ebp gistre. Si ce décalage est important,
30 cmp ax, 10h
31 jb NoCall nous avons à faire avec un appel ou
32 mov ebx, dword [edx] Taille 1 octet +2 octets de l'instruc- un saut, c'est pourquoi nous passe-
33 mov edx,ebx tion précédente = 3 octets. Toujours rons à la récupération du contexte
34 sub ebx, [PrevEip] inférieur à 6. pour qu'il pointe vers une instruction
35 cmp bl, 7
surveillée suivante au lieu d'exécu-
36 jbe HabemusCall
37 mov edi,[PrevEip] 03 8BEC mov ebp,esp ter le processus jusqu'à l'adresse 4
38 inc edi
39 inc edi
40 mov dword [esi + 0B8h], edi
41 mov ecx,edi
42 jmp NoCall
43 HabemusCall:
44 mov dword [esi + 0B8h], edx
45 mov ecx,edx
46 NoCall:
47 mov [PrevEip], ecx
48 sub ecx, Code
49 cmp ecx, [HookLength]
50 jge Success
51 RetSEH:
52 or word [esi + 0C0h], 0100h
; Activation de Trap Flag
53 xor eax,eax
54 ret
55 Success:
56 mov [LenDasm], ecx
;Nous restaurons la longueur
de l'instruction
57 mov esp, [OrigEsp]
;Nous récupérons Esp
58 mov ebp, [OrigEbp]
; Nous récupérons Ebp
59 pop dword [fs:0]
;Nous nettoyons l'étendue SEH
60 add esp, 4
;Nous ajustons la pile

Figure 4. Le registre EFLAGS

58 hakin9 Nº 4/2006 www.hakin9.org


Reconnaître l'adversaire

laquelle pointait l'appel ou le saut. ver. Imaginons aussi que ce petit empêche notre analyse et l'utilisation
Nous compterons aussi avec notre code était compacté avec le pro- du débogueur. Il ne reste pas aussi
compteur combien d'octets occupe gramme contenant plusieurs types longtemps dans la mémoire qu'il soit
notre instruction. de protections anti-déboguage et possible de le décompresser sur le
Dans les lignes 47, 48, 49, nous anti-désassemblage. Nous devons disque dur à l'aide d'un outil de dé-
vérifions s'il y a suffisamment d'ins- vite savoir comment il s'infiltre dans compactage (ProcDump...). En fait,
tructions pour mettre notre crocher. un autre fichier exécutable et quel le fichier exécutable ne reste dans la
La partie la plus principale du dé- code il introduit. Pour tout cela, il mémoire que pendant des dizaines
sassembleur sont les lignes 52, 53 et serait nécessaire d'effectuer sur- de secondes empêchant également
54. Là, nous activons Trap Flag en le-champ le désassemblage du de capturer son image. Que pou-
configurant à 1 le bit approprié dans fichier exécutable, mais comme vons-nous faire ?
le registre EFLAGS. C'est la base nous l'avions dit, il contient un pro- Pour résoudre ce problème, on
pour le nettoyage pas à pas. gramme de compactage (packer) qui pourrait accrocher ExitProcess, la
À partir de moins de 256 octets,
nous avons construit un désassem- Listing 2. ACPInject
bleur de taille tout à fait opérationnel.
Maintenant nous pouvons l'appliquer #include <stdio.h>
#include <windows.h>
dans la pratique.
typedef BOOL (WINAPI *PQUEUEAPC)(FARPROC,HANDLE,LPDWORD);
int main(int argc, char *argv[])
Application pratique {
PROCESS_INFORMATION strProces;
pour l'analyse d'un STARTUPINFOA strStartupProces;
programme malveillant PQUEUEAPC QueueUserApc;
DWORD MessageAddr,Ret1,Ret2,Longueur;
On utilise les techniques differentes : char *szExecutableName;
unsigned char Snippet[]=
• Pour nos besoins, nous allons "\x90" /* nop */
créer un programme qui intro- "\x6A\x00" /* push NULL */
"\x6A\x00" /* push NULL */
duira et démarrera du code dans
"\x6A\x00" /* push NULL */
le fichier exécutable qui lui sera "\x6A\x00" /* push NULL */
passé en paramètre. Les techni- "\xB9\x00\x00\x00\x00" /* mov ecx,MessageBox*/
ques d'insertion du code dans les "\xFF\xD1" ; /* Call ecx */
processus sont bien connues. Il Longeur = (DWORD) strlen(
"c:\\windows\\system32\\calc.exe") + 1;
en existe différents types, mais
ZeroMemory( &strStartupProces, sizeof( strStartupProces) );
toutes sont basées pratiquement strStartupProces.cb = sizeof( strStartupProces );
sur les mêmes API. ZeroMemory( &strProces, sizeof( strProces) );
• VirtualAllocEx sert à allouer de la szExecutableName = (char*) malloc( sizeof(char) * Długość);
mémoire dans le processus cible. if( szExecutableName ) strncpy(szExecutableName,
"c:\\windows\\system32\\calc.exe",Longueur);
Le code sera introduit dans cet
else exit(0);
espace mémoire. Pour cela, nous _QueueUserApc = (PQUEUEAPC)GetProcAddress( GetModuleHandle (
utiliserons WriteProcessMemory. "kernel32.dll" ), "QueueUserAPC");
• Au moment du démarrage du MessageAddr = (DWORD) GetProcAddress ( LoadLibraryA(
code, nous pouvons choisir "user32.dll") , "MessageBoxA" );
// U32!MessageBoxA
parmi CreateRemoteThread ou
*( DWORD* )( Snippet + 10 ) = MessageAddr;
SetThreadContext. Ret1 = CreateProcessA( szExecutableName, NULL, NULL, NULL,
0, CREATE_SUSPENDED,
Mais nous nous servirons d'une NULL,NULL,
autre fonction : QueueUserAPC. &strStartupProces,&strProces);
Ret2 = (DWORD) VirtualAllocEx( strProces.hProcess,NULL,sizeof(Snippet),
MEM_COMMIT,
Domaines d'application PAGE_EXECUTE_READWRITE);
Ce programme introduit dans la
calculette Windows un petit code WriteProcessMemory(strProces.hProcess,(LPVOID)Ret2, Snippet,
qui entraîne l'affichage d'une boîte sizeof(Snippet), NULL);
_QueueUserApc((FARPROC)Ret2,strProces.hThread,NULL);
de message (Message Box). La
ResumeThread(strProces.hThread);
calculette ne s'affichera pas après return 0;
le démarrage de ce code. Imaginons }
qu'au lieu d'un code innocent, nous
introduisons un code malicieux d'un

www.hakin9.org hakin9 Nº 4/2006 59


Pratique

Listing 3. Outils API

unsigned char HookCode[]=


"\x90"
/* nop */
"\x68\x38\x03\x00\x00"
/* push 3E8h */
"\x6A\x6E"
/* push 6Eh */
"\xB9\x00\x00\x00\x00"
/* mov ecx,00h */
"\xFF\xD1"
/* Call K32!Beep */
"\x68\x00\x00\x00\x00"
/* push dword 00h */
"\xB9\x00\x00\x00\x00"
/* mov ecx,00h */
"\xFF\xD1"
/* Call K32!Sleep */
"\x90\x90\x90\x90"
/* Espace pour les instructions
surveillées */
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x68\x00\x00\x00\x00"
Figure 5. Le schéma d'accrochage d'ExitProcess
/* push dword 00h */
"\xC3" figer dans la mémoire du processus En tant que technique de hoo-
/* ret */ (à l'aide de Sleep) et le maintenir king, nous utiliserons Inline Hooking
"\x90";
dans cet état aussi longtemps qu'il avec certains éléments de la techni-
/* nop */
unsigned char ExitHook[]=
soit possible de la capturer et ensui- que Detour
"\x68\x00\x00\x00\x00" te reconstruire cette capture binaire HookCode contient les éléments
/* push dword 00h */ pour le désassembler et restaurer étant le prologue de la fonction
"\xC3"; à l'état initial. Pourquoi accrocher Detour. La tâche de ce prologue
/* ret */
ExitProcess ? 90% de programme consiste à nous informer que le
malveillant, après avoir arrivé programme malveillant a atteint
à ExitProcess est décompressé dans ExitProcess par un signal sonore
Listing 4. Nous construisons la mémoire. Il peut arriver que seule en appelant API Beep. Ensuite,
notre HookCode à l'aide des une partie du fichier exécutable se comme nous avions mentionné,
adresses obtenues décompacte, mais cela n'est pas trop nous appellerons Sleep au moyen
fréquent parce que c'est trop difficile du paramètre envoyé via la ligne
printf("[+]Rebuilding à concevoir. Quant à nous, nous de commande. Ce paramètre sera
HookCode...");
nous concentrerons à construire un assez fort pour nous permettre les
// K32!Beep
outil qui nous permettrait de s'accro- opérations de capture, ainsi que
*( DWORD* )( HookCode + 9 ) = cher rapidement à une API d'une dll d'autres que nous voudrions exécu-
BeepAddr; quelconque utilisée par un program- ter. Une fois le prologue terminé, les
me malveillant. Pour ce faire, nous premières instructions d'ExitProcess
// Sleep param
allons utiliser bien sûr nous nouveau seront démarrées (les instructions
Parameter = atoi( argv[2] );
*( DWORD* )( HookCode + 16 ) =
désassembleur de taille. contrôlés par le désassembleur
Parameter;

// K32!Sleep Sur Internet


*( DWORD* )( HookCode + 21 ) = • http://www.reversemode.com/index.php?option=com_remository&Itemid=2&fun
SleepAddr; c=select&id=8 – Le code source complet de toutes les applications mentionnées
dans l'article. Le désassembleur de taille. L'exemple d'un programme malveillant et
*( DWORD* )( HookCode + 48) = d'une application pour hooking,
HookAddr + LenDasm; • http://research.microsoft.com/~galenh/dfPublications/HuntUsenixNt99.pdf
– Detours: Binary Interception of Win32 Functions,
printf("[OK]\n");
• http://msdn2.microsoft.com/en-us/library/ms253960(VS.80).aspx
– Structure Exception Handling in x86.

60 hakin9 Nº 4/2006 www.hakin9.org


Reconnaître l'adversaire

valeurs obtenues à partir du désas-


Listing 5. ProcessusA sembleur de taille.
Ret1 = CreateProcessA( szExecutableName, NULL, NULL, NULL,0,
Ensuite, nous créons un proces-
CREATE_SUSPENDED,NULL,NULL, sus en mode suspendu et nous al-
&strStartupProceso,&strProceso); louons de la mémoire dans l'espace
adressable.
if( !Ret1 ) ShowError();
À la fin, nous restaurons Exi-
printf("[OK]\n");
tHook à l'aide de l'adresse obtenue
via VirtuaAllocEx, nous retouchons
printf("[+]Allocating remote memory..."); Entry Point de la fonction cible (dans
Ret2 = (DWORD) VirtualAllocEx( strProceso.hProcess,NULL,sizeof(HookCode), ce cas ExitProcess) au moyen d'Exit-
MEM_COMMIT,
Hook.
PAGE_EXECUTE_READWRITE);
La façon d'appeler le programme
était le suivant :

Listing 6. Nous restaurons ExitHook congrio c:\acpinject.exe 10000


Kernel32.dll ExitProcess
*( DWORD* )( ExitHook + 1 ) = Ret2;

• en premier argument, nous avons


printf("[OK]->Address : 0x%x",Ret2);
printf("\n[+]Hooking %s...",argv[4]);
path malware,
printf("\n\t[-]Reading %d bytes from %s Entry Point ...", LenDasm, argv[4]); • en deuxième argument, l'interval-
Ret1 =(DWORD) memcpy( (LPVOID)( HookCode + 27 ),(LPVOID)HookAddr, LenDasm); le en millisecondes du passage
if( !Ret1 ) ShowError(); à Sleep,
printf("[OK]\n");
• le troisième argument est DLL qui
printf( "\t[-]Hooking %s...", argv[4] );
exporte la fonction cible,
Ret1=0; • la fonction cible est ici ExitPro-
while( !Ret1 ) cess.
{
ResumeThread(strProceso.hThread);
Sleep(1);
Conclusion
SuspendThread(strProceso.hThread);
Comme on a pu voir dans l'article,
Ret1 = WriteProcessMemory(strProceso.hProcess, (LPVOID)HookAddr, les différents domaines de l'ingénie-
/ * Nous retouchons la fonction cible */ rie inverse se convergent dans un
ExitHook, HookLength, NULL); point. Nous avons couplé différentes
/* dans la mémoire */
techniques, comme par exemple
}
les désassembleurs de taille, le et
printf("[OK]\n"); l'introduction des processus pour fa-
ciliter notre analyse des programmes
printf("\t[-]Injecting Hook..."); malveillants.
Ret1 = WriteProcessMemory(strProceso.hProcess,(LPVOID)Ret2,
Paradoxalement, ces mêmes
/* Nous copions le code dans l'espace adressable*/
HookCode, sizeof(HookCode), NULL);
techniques sont exploitées par les
programmes malicieux, ce qui aide
/* du processus récemment créé */ aussi le développement de l'ingé-
/* Nous permettons au processus de se dérouler */ nierie inverse. Plus les rootkits, les
ResumeThread(strProceso.hThread);
virus, etc. sont complexes, plus l'ana-
lyse des techniques utilisées et des
de taille), et ensuite, le contrôle de Ensuite, il faut reconstruire Hoo- façons dont elles sont appliquées est
l'instruction convenable suivante kCode et ExitHook au moyen des détaillée. Ainsi, nous avons une sor-
(ExitProcess+7) sera restauré. adresses de la mémoire API et des te d'une course à laquelle participent
les chercheurs, les programmeurs
des programmes malveillants ou les
À propos de l'auteur auteurs des virus, mais les effets en
L'auteur s'intéressent depuis 16 ans de l'environnement de l'ingénierie inverse et en seront ressentis par millions d'utilisa-
général de la sécurité informatique. À l'âge de 19 ans, en parfait autodidacte, il a com- teurs. Pourtant, on ne peut pas nier
mencé à travailler comme programmeur. Ensuite, il se développé dans les domaines le fait que les recherches et les in-
liés au bas niveau, aux programmes anti-virus et à la vulnérabilité aux attaques. Ac- novations proviennent de deux côtés
tuellement, il se concentre sur ce dernier domaine.
de la barricade. l

www.hakin9.org hakin9 Nº 4/2006 61

Vous aimerez peut-être aussi