-
Quel processeur bizarre - faire des misères au x86... et un peu de PE [Portable Executable]
-
Bienvenue... et surtout, dites-moi si je vais trop vite
-
Je vais parler des opcodes, et un peu du format PE et de leurs bizarreries
-
Je fais du reverse depuis quelque temps. J'ai créé un projet appelé Corkami.
-
Dans le passé, j'ai participé à Mame, l'émulateur Arcade, et professionnellement, analyste de virus,
-
mais je suis ici à titre personnel, ce sont mes propres expériences à la maison.
-
Donc, je viens de mentionner Corkami. C'est le nom du projet de mon projet de reverse [rétro-ingénierie]
-
Je n'y parle que de choses techniques, pas de pubs, pas de login requis
-
Directement le pur jus.
-
J'essaie de le maintenir à jour, et util. J'ai créé des fiches pense-bêtes et d'autre documents
-
que j'utilise moi-même au boulot, au quotidien
-
mais c'est juste un loisir. je commence quand les enfants sont couchés
-
tard dans la nuit, donc ça n'a probablement pas le polish professionnel
-
que j'aimerais qu'il ait.
-
Actuellement, Corkami est un wiki et des pense-bêtes
-
et je me focalise sur la création de preuves de concepts [démonstration d'hypothèse, "proof of concept] [Tiens, salut Bob!]
-
donc les binaires sont fait-main, d'habitude je n'utilise pas de compilateurs, je créé la structure du PE à la main
-
pour que ce soit focalisé sur le point pertinent
-
et qu'on est pas de parasites. on n'a probablement pas besoin d'IDA
-
pour comprendre ce qui se passe
-
car je me concentre uniquement sur ce qui est important.
-
Les binaires sont directement téléchargeables pour que vous puissiez
-
tester avec votre débogueur, vos outils, vos connaissances,
-
directement.
-
pour l'instant, je me suis concentré sur le PDF, l'assembleur et le format PE
-
quelques autres trucs, mais ce sont les sujets les plus approfondis
-
du site, et je partage tout ça avec une licence
-
très permissive, BSD, donc vous pouvez réutiliser commercialement
-
Même les images sont en format ouvert.
-
Donc, la raison de cette présentation... il y a quelque temps,
-
J'étais jeune et innocent, et je pensais que les CPUs, fait de transistors,
-
était parfaitement logique
-
et ensuite, un virus m'a piégé... simplement,
-
IDA était rendu inutile. Donc j'ai décidé de repartir à zéro
-
et d'étudier l'assembleur et le PE du début.
-
j'ai écrit en chemin des documents, partagés sur Corkami
-
et maintenant je vous présente le résultat plus ou moins final,
-
ou du moins quelques buts atteints. Si j'étais juste un gars qui a étudié l'assembleur
-
je ne serais probablement pas en train de présenter ici,
-
à moins d'avoir eu quelques résultats avec certains outils.
-
Donc, pour résumer, j'ai trouvés des bogues dans tous les désassembleurs que j'ai essayé
-
et j'ai aussi obtenus quelques plantages. J'insiste que tous les auteurs ont été contactés
-
et que la plupart des problèmes ont déjà été corrigé,
-
mais en tout cas, en version 6.1, il plante directement, mais dans la version 6.2
-
c'est corrigé.
-
et la dernière version de Hiew [Hacker's view] (enfin, pas la dernière publique)
-
corrige ça aussi.
-
Donc, pour cette présentation, je commencerais facile,
-
je pars du principe que vous êtes tous habitués à l'assembleur ?
-
Oui. et vous êtes tous habitués, ou
-
vous avez déjà rencontré des cas d'opcodes non documentés?
-
Genre vous faites confiance à IDA, point barre.
-
Est-ce habituel de voir quelque chose non géré par IDA ?
-
Levez les bras... ok, pas tant que ça.
-
Bon, après l'introduction en accéléré,
-
Je parlerais de quelques techniques, puis présenterai CoST, mon programme
-
et je parlerais aussi un peu du format PE
-
Bon, vous connaissez tous l'assembleur, on va survoler ...
-
Donc, on compile un binaire, il y a de l'assembleur, il y a
-
un rapport, des points communs entre le code source et l'assembleur généré...
-
et bien sûr, une relation entre langage machine et les opcodes
-
ce qui est important est que l'assembleur est généré par le compilateur, mais ce qui reste
-
dans le binaire, sont uniquement les opcodes, qui sont interprétés
-
directement par le processeur, ce qui implique qu'il sache quoi faire avec,
-
il s'en fiche si vous ou vos outils
-
ne savent pas ce qui va arriver. il le fait, tout simplement.
-
Et le problème, c'est que d'habitude, on ne lit pas les opcodes directement, mais le désassemblage
-
donc si le désassemblage échoue, on est bloqué
-
on est aveugle, on ne sait pas ce qui va s'exécuter
-
l'autre problème est comme les instructions sont de longueurs variables
-
on ne sait pas ou la suivante commence
-
donc même la suite est inconnue.
-
Donc, on créé une seule instruction non documentée dans un programme simple
-
on utilise le mot-clef 'emit' -- c'est Visual Studio 2010 ultimate --
-
et on obtient un octet non identifié au désassemblage
-
on a des juste points d'interrogations.
-
et donc, même si Visual Studio coûte plusieurs milliers d'euros
-
il ne sait pas ce qui va se produire....
-
et si on regarde les documentations Intel
-
circulez, il y a rien a voir... l'opcode D6... inconnu au bataillon
-
Microsoft n'a rien à dire, Intel non plus,
-
donc d'habitude, si vous essayez des choses comme ça, attendez-vous au pire.
-
Donc, aucune information... en général, ça annonce un plantage...
-
mais dans ce cas particulier, aucun problème...
-
nous ne savons pas ce qui se passe, si on regarde les docs Intel et Microsoft, on n'en sait pas plus.
-
Mais le CPU a juste fait son boulot dans son coin. Ce qui s'est passé est qu'en fait
-
D6 est un opcode très simple, qui ne fait pas grand chose, mais pas documenté par Intel,
-
par contre, il l'est par AMD, et la plupart le sont aussi également, par AMD mais pas Intel
-
aucune idée pourquoi. si quelqu'un le sait...
-
c'est un opcode pourtant trivial, pourtant... 'y a rien à voir, continuez votre chemin'
-
Il est utilisé souvent... l'utilisation habituelle de tels opcodes sont les virus et les packeurs
-
pour éviter les analyses automatisées où trop facile.
-
Ce qui est ironique, c'est que si vous regardez la doc, elle est pleine de trous. mais le désassembleur d'Intel,
-
Xed, qui est gratuit mais pas ouvert, gère tous ces opcodes sans problème...
-
Alors que Microsoft, Visual Studio, et WinDBG, suivent la documentation aveuglément
-
Donc vous obtiendrez des points d'interrogation alors qu'Intel sait très bien ce qui se passe
-
'faites ce que je fais, pas ce que je dit'
-
donc, bien sûr, vous me direz que WinDbg est fait uniquement ce qui est généré
-
par les compilateurs Microsoft, mais ça exclut WinDBG comme outil d'analyse de virus:
-
vous rajoutez D6, trivial, et WinDbg est muet.
-
pas génial
-
un autre problème est que toutes ses choses non documentées
-
sont peut-être présentes, l'une dans un virus,
-
l'autre dans un packeur, etc... donc il n'est pas facile
-
de trouver un bon regroupement de tels fichiers pour rassembler toutes ces informations
-
donc par exemple,
-
quelqu'un vous parle d'une astuce
-
et vous dit qu'elle est enfouit dans MebRoot (un virus costaud)
-
donc vous êtes obligé de creuser pour voir juste la partie qui vous intéresse
-
et en plus, on sait que c'est un virus, donc ca ne circule pas facilement,
-
et il y a plein de trucs dedans qui n'ont rien à voir dedans,
-
avant ou après ce qui vous intéresse. Donc c'est le fossé que je veux remplir en fournissant
-
un groupe de fichier à la fois simple et complet, et focalisé.
-
Donc, on commence. enfin, du vrai, quelques opcodes non documentés.
-
Mais avant d'avoir commencé à étudier, je me suis demandé quelles étaient les vrais capacités
-
des processeurs, quelles instructions existent vraiment.
-
C'est un peu comme l'anglais: la plupart de la population mondiale comprend ces mots, et
-
si vous avez déjà désassemblé quelque chose, vous êtes habitués à ces instructions
-
tous les compilateurs les utilisent. elles sont tellement répandues que si elles sont absentes,
-
on se sent pris au dépourvu.
-
mais les processeurs Intels datent des années 70. Donc,
-
comme l'anglais de Shakespeare, vous voyez que c'est de l'anglais... mais, ça veut dire quoi en fait ?
-
d'ailleurs, j'ai déjà oublié
-
ces instructions sont toutes gérées par tous nos processeurs, pourtant, on n'a plus l'habitude de les lire
-
c'est plus que génant
-
En fait, j'ai écrit un exemple qui n'utilisent que ces vieilles instructions, qui font vraiment quelque chose
-
donc si vous vous sentez ici comme chez vous, je voudrais vous demander 'quel âge avez-vous ?'
-
parce que même moi, j'ai l'habitude de PUSH/JUMP/CALLs, mais ça, euhh...
-
pourtant, ça marche même sur un i7, et c'est utilisable par les virus,
-
les packeurs, etc... pourtant, la plupart est complètement inutilisée de nos jours, bien que parfaitement
-
gérées par nos processeurs modernes.
-
Et, tel l'anglais, c'est une langue qui évolue, et les générations précédentes
-
n'ont pas l'habitude des derniers mots à la mode.
-
Ces instructions sont parfois présentes sur les processeurs les plus récent. en une seule instruction, on fait
-
un CRC32, on décrypte l'AES, on compare des chaînes, ou d'autres opérations complexes.
-
Donc, c'est possible sur un processeur moderne. pas tous, bien sûr.
-
une que j'aime bien est MOVBE -- move big endian -- parce que c'est le vilain petit canard du lot...
-
uniquement implémentée sur les processeurs Atom, donc comme ce netbook, qui la gère...
-
alors qu'un i7 64-bits ne l'a pas, même si lui aura CRC32, peut-être AES, etc...
-
donc, tant pis pour la retro-compatibilité
-
à ma connaissance, il n'y existe aucun processeur qui gère à la fois CRC32 et MOVBE.
-
De plus, MOVBE est un peu inutile car il fait la même chose que MOV et BSWAP réunit...
-
donc, bizarre. en tout cas, ce mini-PC a une instruction que la plupart des PC n'ont pas
-
Pourquoi ? quelqu'un sait?
-
[Auditeur:] "cette instruction est documentée ?"
-
oui
-
totalement documentée officiellement
-
[Auditeur:] "Mais, c'est un drapeau du processeur juste pour cette instruction, ou implicitement lié au
-
processeurs Atom ?
-
mmm, je ne sais pas. je vérifie la valeur par CPUID, mais j'ai oublié la définition exacte.
-
il y a tant d'informations données par CPUID que j'ai vite oublié.
-
Autre chose, spécifique à Windows, car je m'intéresse aux virus...
-
avant d'exécuter quoi que ce soit, je me demandais quelle était la valeur initiale de chaque registre
-
quand le programme démarre.
-
et ça vous donne en fait de l'information fiable, qu'on retrouve utilisée par les virus
-
donc par exemple, au démarrage, EAX vous dit si vous êtes sur un OS ancien (XP ou avant)
-
ou plus récent, Vista ou 7.
-
de même, ce n'est pas utilisé par les virus à mon souvenir, mais si GS est nul, on est dans un OS 32 bits
-
sinon, sur un 64b.
-
J'utilise ça régulièrement, on le verra plus tard.
-
De même, la relation entre les registres n'est pas forcément évidente. il y en a beaucoup,
-
et j'ai été surpris qu'une instruction à virgule flottante (FPU) change le statut (FST), les registres
-
eux-mêmes (STx), les registres MMx aussi, mais en plus, toutes les documentations en ligne associent
-
ST0 et MM0, ce qui semble logique, alors qu'en fait, une seule instruction modifie
-
non pas MM0, mais MM7, dans l'autre sens.
-
donc après une instruction comme "load PI" [FLDPI] on regarde la valeur de MM7,
-
ça peut-être utilisé comme astuce simple.
-
alors que toutes les documentations, Wikipedia ou autre, se trompent.
-
quelque chose d'autre qui peut être utilisé comme anti-débogueur sous XP: le FPU modifie aussi CR0
-
donc ça fait une astuce anti-émulateur plutôt inattendue rien qu'avec le FPU.
-
'store machine status word' [SMSW] est une instruction qui date du 286, quand le mode protégé était
-
récent et incomplet, qui permet de lire les valeurs de CR0, même depuis le mode utilisateur.
-
alors que 'MOV CR0' est privilégiée
-
pour une raison inconnue, le mot de poid fort est officiellement non défini.
-
donc à priori, on ne sait pas ce qu'il va contenir,
-
mais en fait il s'agit bien du même mot de poid fort que CR0.
-
et, sous XP, après une instruction FPU, la valeur de CR0 est modifiée, mais reprend sa valeur initiale
-
toute seule par la suite. donc juste avec SMSW, verifier le résultat, puis faire une opération FPU,
-
alors SMSW donnera un résultat différent, mais ensuite, le résultat redeviendra comme à l'origine.
-
un moyen élégant de faire des boucles en apparence infinies. une astuce complexe anti-émulateurs.
-
Une astuce similaire sous Windows 32b, où GS n'est pas stocké dans le CONTEXT, donc quand l'OS passe d'un
-
fil d'exécution à l'autre (thread-switch), GS est remis à zero. Donc, si on attend, quoi qu'on fasse,
-
GS change comme par enchantement. Mais si vous faites du pas à pas, c'est lent, donc l'OS change de fil,
-
et GS est perdu dès l'instruction suivante.
-
De même, si on attend que GS devienne nul, on finira par sortir de la boucle.
-
En tout cas, la première fois, ça défiait mon entendement... sans autres process ou autre, je ne comprenais
-
pas d'où ça venait. Au moins, mon exemple commence dès le début avec ça
-
c'est plus pratique
-
une autre particularité, c'est que ça met du temps pour être remis à zéro. en faisant 2 boucles d'attentes,
-
et mesurant le temps entre les 2 (changement de fil), ça prend somme toute assez longtemps, comparé à une
-
exécution standard. ça fait un anti-émulation solide.
-
Je pensais tout naturellement que NOP était parfait! NOP, c'est NOP, ça ne fait rien, donc, aucun problème !
-
Mais à l'origine, NOP est 'échange ?AX avec lui-même' (xchg ?ax, ?ax) mais c'est le cas pour le NOP encodé 0x90
-
mais il y a un autre encodage de XCHG EAX, EAX, qui ne fait rien non plus en 32b
-
mais, à l'instar de toutes les instructions sur 32b en mode 64b
-
cette instruction remet à zéro le double mot de poid fort
-
donc on a un XCHG EAX [,EAX] qui fait quelque chose.
-
bien qu'initialement il semblerait ne rien faire, de sa relation avec NOP
-
mais heureusement, le NOP 0x90, lui, ne fait toujours rien, un vrai fainéant ;)
-
Celui-ci est maintenant bien répandu,
-
le HINT NOP est un NOP sur plusieurs octets
-
qui indique au processeur ce qui va être exécuté ou accédé ensuite
-
quelque soit l'adresse dans un HINT NOP en mémoire,
-
aucune exception n'est déclenchée
-
mais ...
-
autre chose, il est partiellement non documenté par Intel
-
(comme d'habitude, ce n'est pas le cas avec AMD)
-
en outre, comme c'est une instruction sur plusieurs octets, avec des opérandes immédiates
-
si on met ces octets à la fin d'une page mémoire
-
alors le processeur va vouloir lire l'encodage des opérandes,
-
et ça va déclencher une exception
-
donc c'est un NOP qui peut déclencher une exception en bas de page
-
Merci, Intel
-
MOV également, je croyais...
-
qu'il devrait être parfaitement logique
-
mais, même s'il est documenté, il y a des cas compliqués,
-
qui n'étaient pas parfaitement gérés dans tous les assembleurs que j'ai essayé
-
sauf peut-être Xed
-
on ne peut pas faire un MOV de ou vers CR0 en mémoire,
-
la documentation dit que le Mod/RM est ignoré
-
ce qui ne veut pas dire que l'instruction est invalide
-
simplement, considéré comme n'utilisant pas la mémoire
-
sinon, ça ferait un plantage
-
donc, voici l'équivalent
-
ce qui mettait en tord tous les désassembleurs
-
jusque récemment
-
MOVSXD est une instruction 64b, qui étend un registre
-
donc, d'un registre vers un plus gros
-
mais sans préfixe REX - ce qui n'est pas encouragé,
-
on peut l'utiliser comme un MOV standard, de 32b vers 32b
-
et dans l'autre sens,
-
MOV d'un selecteur à un registre 32b marche sans problème,
-
alors que beaucoup de désassembleurs afficheraient MOV AX, CS, ce qui semble logique
-
niveau taille
-
mais en fait le mot de poid fort du registre cible est 'non défini'
-
mais ici, pas de surprise amusante,
-
ce sont juste des zéros
-
donc c'est équivalent à MOV EAX, CS
-
BSWAP est un de mes favoris
-
parce que je le compare à une administration:
-
il est supposé passer d'un endianisme [endianness] à l' autre
-
mais pour diverses raisons,
-
il ne peut jamais faire son travail correctement
-
donc, c'est juste en 64b que tout se passe
-
comme prévu
-
en 32b, comme toutes les autres instruction 32b
-
le double mot de poid fort est mis à zéro...
-
et sur un mot, il est non défini officiellement,
-
mais il est pourtant utilisé couramment dans les virus et les packeurs
-
car il remet simplement à zéro le registre,
-
comme un simple XOR AX, AX
-
donc, devant un résultat si inexplicable, je comprends
-
qu'Intel ne veuille pas en parler
-
ça serait sûrement gênant de devoir expliquer
-
ce résultat plutôt comique.
-
BSWAP AX est aussi mal géré par WinDbg et les autres
-
on lira BSWAP EAX
-
alors que le registre est remis à zéro... génant
-
tout le monde comprend ce code?
-
quelqu'un voit un piège possible?
-
l'adresse de est empilée
-
puis dépilée par RETN
-
donc, on saute vers une adresse immédiates
-
l'ordre d'exécution ?
-
oui, l'exécution démarre ici
-
non, rien à voir ici
-
voilà OllyDbg 1 - c'est corrigé dans le 2
-
Olly essaie même d'être sympa et de vous dire
-
via un commentaire automatique,
-
que RET sera utilisé comme saut vers
-
et, voyez le résultat, plutôt différent
-
donc, que c'est-il passé ?
-
quelqu'un voit ?
-
donc, RETN a un prefix 66
-
donc, il retourne vers IP (16b), pas EIP
-
donc on ne saute pas vers 401008, mais 00001008
-
et dans mon exemple, la page nulle a été allouée
-
et du code a été placé à 1008
-
donc, ça ne retourne pas vers []
-
mais l'autre problème est que c'est aussi appelé un RETN
-
bien que ce soit différent. les désassembleurs ont leur propre façon de l'afficher
-
tel que 'small retn', 'ret.16', ou autre
-
mais officiellement, c'est la même instruction qu'un RETN standard.
-
donc, le dernier Hiew, et OllyDbg 1
-
peut-être pas le 2
-
mais on peut se faire avoir
-
et le préfixe 66 a le même role avec CALLs, RETs, LOOPs, JMPs
-
toutes les instructions contrôlant le flux d'exécution
-
je ne vais pas tout énumérer
-
car sinon vous allez mourir d'ennui
-
pour en savoir plus, j'ai créé une page sur Corkami [x86.corkami.com],
-
avec quelques graphiques et penses-bêtes
-
pour faciliter le boulot
-
bon, trop de théorie
-
je n'aime pas lire sans avoir quelque chose à me mettre sous le débogueur,
-
donc j'ai créé CoST
-
ce qui signifie Corkami Standard Test
-
CoST est un unique binaire, sans option de ligne de commande
-
on l'exécute, il fait plein de tests
-
le tout dans un PE compliqué
-
pour aussi tester vos outils PE
-
ou vos connaissances
-
mais, dans ce PE compliqué, il est casse-pied à déboguer
-
donc j'ai fait aussi une version 'PE standard'
-
si on ne veut étudier que l'assembleur
-
sans difficultés
-
donc, CoST contient de nombreux tests
-
les classiques, triviaux,
-
un peu plus compliqués, JMP to IP, IRET...
-
les non documentés
-
les spécifiques aux processeurs, comme MOVBE, POPCNT, CRC32
-
les détections d'OS et VM
-
comme le fameux 'red pill'... juste une instruction SLDT, on compare le résultat,
-
et on baptise ça 'red pill'... sans commentaire...
-
et aussi, des bogues des SE, parce que Windows XP
-
se trompe en essayant de vous dire
-
quelle exception vient de se produire,
-
ce qui permettrait de différencier un SE réel d'un émulateur.
-
CoST est écrit en assembleur, donc rien de superflu
-
pas compilé, ni généré
-
mais pour le documenter un peu plus, j'ai ajouté des exports internes
-
donc on peut aller facilement d'une section à l'autre
-
donc on se retrouve facilement à la partie qui nous intéresse
-
via les exports,
-
et je voulais pouvoir afficher un message de manière pratique
-
sans que ça allonge trop le listing
-
je veux dire, devoir faire défiler l'écran parce qu'il est couvert d'appel à printf
-
donc j'au utilisé un Vectored Exception Handler, et une instruction-marqueur
-
donc plein de commentaires sont affichés,
-
directement dans le code
-
donc ça fait un code binaire documenté sans fichier de symboles de débogage
-
et vous avez vu, il n'y a pas beaucoup d'affichage
-
mais en fait, il y a beaucoup plus d'affichage de déboguage
-
une centaine au moins, qui indiquent même ce qui va se produire, etc
-
donc on est pas pris au dépourvu
-
on est guidé lors de l'analyse
-
quelqu'un comprend de quoi il s'agit?
-
c'est un de mes préférés
-
on ne peut pas voir les opcodes
-
ah, il n'y a pas d'astuce à ce niveau là cette fois-ci ;)
-
donc, on empile des valeurs
-
on saute ici
-
et avec le RETF, j'ai empilé l'adresse de 'push_eip'
-
et un mot, 0x33
-
donc on va revenir de loin ici
-
donc on retourne à cette adresse avec un sélecteur 33
-
qui est réservé au mode 64b, même pour un programme en 32b
-
donc on va revenir ici, en mode 64b
-
car 33 est le sélecteur pour le mode 64b
-
qu'on peut atteindre depuis un programme 32b
-
donc le code sera d'abord exécuté en mode 32b, avec le sélecteur standard,
-
puis à nouveau, avec le sélecteur 33
-
en mode 64b
-
donc on a la même adresse, les mêmes opcodes,
-
mais le désassemblage sera différent
-
et j'ai choisi des opcodes qui génèrent des instructions
-
spécifique à chaque mode
-
donc, c'est déjà une belle s*loperie à désassembler,
-
car avec la même EIP, si on ne fait pas attention au sélecteur,
-
on est bloqué
-
On peut déboguer un tel code - voir ma présentation à BerlinSides, transparent de démonstration 58
-
Si on l'exécute en passant par dessus, on retourne au sélecteur d'origine
-
qui était sauvé par le PUSH CS
-
donc on retourne à avec le sélecteur initial
-
l'exécution est rapide,
-
mais difficile à déboguer (uniquement avec WinDbg+wow64exts)
-
ça rend inutile les désassembleurs, et la plupart des débogueurs
-
pourtant, c'est si simple.
-
Voilà ce que donne CoST
-
sous la dernière version de Hiew
-
je pense que ce sera bientôt corrigé
-
c'est un HINT NOP non documenté par Intel
-
et oublié par la plupart des désassembleurs
-
donc WinDbg et Hiew
-
vous donnent des points d'interrogations
-
au début, je pensais m'arrêter là pour Hashdays
-
mais j'ai décidé de rajouter quelques astuces PE à CoST
-
donc, en voici l'en-tête... MZ, puis du texte
-
donc on peut taper 'type cost.exe', comme ce bon vieux Budokan...
-
et ensuite,
-
l'en-tête 'NT headers' - appelé 'PE' car il commence par ces lettres-là,
-
et en fait à la fin du fichier
-
le 'pied-de-page PE'
-
et les valeurs de l'en-tête sont un peu extrême
-
donc, pour le moins inattendu
-
donc voici ce qu'IDA 6.1 en pensait
-
...plantage directe...
-
ça lui apprendra à se fier aux valeurs
-
mais, aussi bien dans CoST, on peut changer un registre, faire une comparaison, tester,
-
et enchaîner ainsi plein de tests,
-
aussi bien, côté PE, on n'a qu'un seul binaire, donc
-
un seul essai
-
donc même si CoST n'a pas de section, des TLS bizarres,....
-
il ne peut pas tout tester
-
donc, j'ai créé pour ça une autre page sur Corkami
-
avec comme d'habitudes, des exemples, des graphiques,
-
elle n'est pas finie, mais déjà largement suffisante pour tester ou faire planter
-
n'importe quel outil
-
j'ai déjà une centaine d'exemples,
-
qui mettent l'accent sur de nombreux aspects, avec parfois des résultats inattendus
-
donc, voici une table de section virtuelle, et Hiew
-
quand les alignements sont bas, on n'a pas besoin de section
-
ou la table peut être vide
-
donc, j'ai modifié SizeOfOptionalHeader pour qu'il pointe en espace mémoire virtuel
-
donc la table des sections est en dehors du PE, pleines de zéros
-
et Hiew n'aime pas ça. En conséquence, il ne pense même pas que c'est un PE
-
alors que ça marche parfaitement, du moins sous XP
-
car Windows 7 est plus capricieux concernant les valeurs de la table des section
-
ensuite...
-
si on fait de l'art dans les Data Directories
-
vous pouvez commencer à vous inquiéter
-
si vous avez un dessin plus joli, je suis preneur
-
donc, il s'agit du 'Dual PE header', présenté
-
par Reversing Labs à la BlackHat
-
quelqu'un connaît ?
-
donc, on augmente SizeOfHeader pour que
-
les en-têtes NT soient en fait assez loin,
-
pour qu'il soit aligné avec les sections
-
quand il se charge en mémoire
-
la première section sera chargée par dessus
-
la première partie du OPTIONAL_HEADER est lue dans le fichier
-
donc, prise en compte pour charger le fichier
-
mais les Data Directories sont lus en mémoire
-
donc, d'abord l'OPTIONAL_HEADER est analysé, chargé en mémoire,
-
puis la section est dépliée sur la partie basse de l'en-tête
-
et les vrais Data directories qui étaient initialement au début de la section
-
vont être pris en compte,
-
donc tout ceci est du pipeau présent dans le fichier, à la suite de SizeOfOptionalHeader
-
mais en mémoire, ce sera écrasé et ignoré
-
une autre bizarrerie est que les noms d'exports peuvent avoir n'importe quelle valeur,
-
jusqu'au caractère zéro
-
donc, absolument n'importe quoi,
-
et de plus,
-
Hiew les affichent directement
-
donc on peut insérer ses propres messages
-
ce sont juste des noms d'exports, d'ailleurs
-
l'un d'entre eux est excessivement long,
-
idéal pour tester les dépassement de tampon [buffer overflow]
-
dans votre outil favori
-
et il est également possible d'avoir un export avec un nom nul,
-
on peut donc importer l'api nulle
-
sans problème
-
j'ai aussi essayé les différentes possibilités,
-
avec des fichiers contenant un maximum de section,
-
la limite est 96 sous XP, et 64K sous Vista et Windows 7
-
ce qui donne
-
avec le dernier OllyDbg 2 ce message surprenant
-
mais le fichier charge quand même
-
OllyDbg 1, lui, plante directement
-
il reste du temps ?
-
un dernier... pas très visuel
-
l'adresse AddressOfIndex du TLS est mise à zéro au chargement
-
et le terminus [terminator] des imports n'a pas besoin d'être composé de 5 doubles mots nuls
-
mais juste d'un seul, pour son AddressOfName
-
pour être considéré comme le terminus
-
donc, si on fait pointer AddressOfIndex vers l'AddressOfName d'un descripteur d'imports
-
s'il est mis à zéro,
-
les imports seront tronqués
-
et en fait, le comportement est différent sous XP ou 7
-
donc, sous XP, il est écrasé après que les imports soient chargés,
-
donc la table n'est pas tronquée
-
alors que sous 7, il est écrasé avant les imports
-
donc, pour le même PE, on a 2 comportements différents
-
sous deux versions de Windows différentes,
-
alors que le fichier marche sous les 2 versions.
-
ah, attendez, on a encore le temps?
-
15 minutes ? ok
-
je vais commencer la démonstration
-
pour vous montrer...
-
le style de PE que je créé d'habitude
-
avec le minimum d'éléments définis
-
c'est en fait un pilotes [driver]
-
même si j'ai utilisé des opcodes non documentés,
-
ce pilote marche, sans le vrac
-
habituel rajouté par le compilateurs
-
donc, un exemple clair, simple
-
sans rien pour vous géner la vue
-
ou votre débogueur
-
donc, cet exemple regarde les valeurs de CR0
-
via SMSW, officiellement non défini sur un double mot
-
mais en fait donne la même valeur
-
que le MOV EAX, CR0 standard
-
ensuite, MOV EAX, CR0 avec un 'mauvais' Mod/RM
-
sous le dernier Hiew, ce n'est en fait même pas désassemblé
-
on espère que ça ne plante pas...
-
donc, vous voyez, on obtient les mêmes valeurs
-
via le CR0 standard, l'invalide, et le non défini
-
dont la partie de poids fort est non définie
-
d'habitude, en langage Intel, non défini signifie 'mis à zéro',
-
mais ici, on a bien CR0 entier
-
et ma machine n'a même pas plantée
-
ce qui signifie que le pilote fonctionne correctement
-
donc vous pouvez étudier des petits pilotes
-
le premier exemple présenté aujourd'hui
-
était celui avec l'assembleur ancien
-
quelqu'un connaît le résultat final ?
-
certaines instructions sont inutiles,
-
juste pour vérifier que le processeur les gère
-
mais d'autres modifient les registres
-
et ces instructions des années 70-80
-
sont toujours gérées par les processeurs modernes
-
un des exemples que j'ai crée teste
-
les valeurs initiales de chaque registre
-
donc on peut voir les valeurs possibles sous XP ou W7
-
à chaque fois [TLS, EntryPoint, DllMain], je sauve tous les registres
-
et je compare à diverses valeurs possibles
-
successivement
-
en fait, au TLS, on a beaucoup de contrôles de ces valeurs,
-
car ces valeurs proviennent du Data Directory
-
en particulier, son adresse relative, sa taille, les callbacks...
-
pour plus de détail, voir le source...
-
ça vous permet d'imiter mieux un SE dans votre émulateur,
-
si ça vous intéresse
-
on utilise SMSW, on compare la valeur
-
ensuite, après une opération FPU, on regarde si la valeur a changé
-
et si elle revient à sa valeur initiale.
-
une autre bizarrerie, si quelqu'un a l'explication
-
est qu'en fait
-
ça se comporte différemment si on exécute le fichier normalement
-
ou avec une redirection
-
si on redirige, 'échec'
-
sinon, ça marche normalement.
-
pour vous montrer... on exécute, et on lance TYPE
-
normal : OK
-
redirection: ECHEC
-
si vous avez une explication, je suis preneur
-
tu as essayé de rediriger vers autre chose ?
-
non, je n'ai pas essayé
-
donc, rediriger vers un autre périphérique?
-
mais, comment on récupère le résultat ?
-
imprimante ?
-
je n'ai pas de périphérique COM
-
non, je ne sais pas
-
mais c'était surprenant, car je lançais tous mes tests...
-
et d'un coup, 'ECHEC'...
-
alors qu'à la main, aucun problème.
-
l'astuce avec GS
-
très simple
-
et quelques affichages
-
je modifie GS, qui se remet à zéro
-
puis j'attends le résultat,
-
ensuite je fais 2 tests et je compare le temps entre
-
car ça ne doit pas arriver trop vite
-
NOPs
-
je teste les NOPs non documentés
-
celui sur une page invalide
-
NOP standard
-
32 bits
-
tous mes tests 64b sont fait dans des programmes 32b, car on peut les exécuter sur un OS normal
-
et ensuite je regarde GS pour voir si le mode 64b est disponible
-
dans ce cas, on obtiendrait un résultat différent
-
donc, en 64b, que je n'ai aps ici, on obtiendrait
-
les tests en 64b
-
et ces results.
-
mais, pas si facile à déboguer
-
mais ici, pas de piège, donc on peut revenir facilement en 32b
-
on saute le code 64b et revient en 32b
-
PUSH/RET
-
on affiche le message et ensuite...
-
Olly vous dit qu'on va sauter vers [4010]08
-
mais en fait - ici, c'est correct
-
et le TLS a alloué la page NULLE
-
qui affiche 'ECHEC'
-
donc, comme mentionné auparavant, pas de façon standard de désassembler ça correctement
-
je ne peux pas exécuter les 64K sections
-
et en fait, cet exemple exécute tout le code (l'espace virtuel complet des 65535 sections)
-
elles sont grosses,
-
et je modifie EAX pour que tous les 00 00 soient exécutés
-
juste pour faire un printf à la fin
-
ça prend plusieurs secondes sur un i7
-
c'est assez amusant: on le lance, et même avec le cache,
-
et que le SE n'est pas occupé, ça prend un temps visible, juste pour un tas de 00
-
les sections virtuelles... celui que le dernier Hiew ne voit pas comme un PE
-
enfin, bientôt corrigé
-
ah, je ne peux pas l'analyser car il ne pense même pas que c'est un PE
-
mais, pour simplifier, OPTIONAL_HEADER pointe au delà
-
du fichier
-
l'en-tête plié...
-
quelques messages d'erreurs,
-
à cause des faux Data Directories
-
et les DD réels sont au début
-
de la première section
-
ceci deviendra les imports, et le vrai Data Directory
-
et pour finir, celui avec TLS AddressOfIndex qui pointe...
-
...dans les descripteurs d'imports, sur AddressOfName
-
donc, il sera écrasé au chargement
-
et quand on le charge, il reconnait XP
-
à la façon dont les imports ont été chargés
-
et sous 7, on obtiendra un autre résultat.
-
pour finir, les exports
-
certains ont des noms très longs
-
en fait, on remarque que j'écrase le désassemblage même
-
donc je répète les faux opcodes et adresses
-
donc le désassembleur est perturbé
-
mais c'est juste visuel, pas vraiment grave
-
bien que ce soit un problème récent dans IDA
-
ou si on met un export au milieu d'une instruction
-
l'export aura priorité sur le désassemblage,
-
et casse l'instruction en deux
-
il y a bien sûr un exemple pour ça sur Corkami
-
donc, voilà pour les démonstrations
-
Je voulais donc en savoir plus sur le x86 et le PE
-
qui sont loin d'être correctement documentés
-
et qui ne le sont toujours pas,
-
mais j'en ai couvert un peu
-
il y a encore des flous
-
mais de moins en moins, j'y travaille
-
en publiant mes recherches librement
-
tel WinDbg, si vous suivez les documentations officielles à la lettre,
-
vous êtes voués à l'échec, surtout avec tous les virus et packeurs existants
-
si vous êtes intéressés, ou que vous programmez un outil, un émulateur, un moteur...
-
vous savez que vous pouvez aller sur Corkami, lire les pages
-
télécharger les exemples, qui sont disponibles librement, à tout point de vue
-
et si vous trouvez des bugs, ce qui pourrait arriver
-
envoyez-moi une carte postale, ou un t-shirt croix rouge ;)
-
Merci à Peter Ferrie, et tous mes relecteurs et contributeurs
-
vous avez des questions ?
-
tu les as testés sur des anti-virus ? tu devrais trouver une ch*ée de 0days
-
mouai, non, je ne saurais pas en faire des exploits...
-
tenir en échec les désassembleurs me suffit
-
j'ai trouvé un crash dans Xed d'Intel, ça me suffit
-
une autre question ? tout le monde a survécu ?
-
c'était une super présentation, mec
-
merci!
-
MERCI!