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!