Vous êtes ici

The Unparsable

Portrait de ivan
Je fouinais dans ma réserve d'échantillons de virus liés au groupe APT1 quand j'en ai découvert un qui sortait du lot. Il s'agit de e480c8839e819eaa9b19d53acfa95052, et sa particularité est qu'il a mis en échec l'intégralité des analyseurs que je lui ai jetés au visage. Pourtant, il s'agit bien d'un exécutable Windows valide, et l'OS de Microsoft ne semble pas avoir de difficultés à le lancer.
Remontons les manches et jetons un coup d’œil sous le capot.

Si on en croit les plaintes des outils testés, il y a un problème au niveau des imports de ce fichier et effectivement, quand je regarde ce qui se trouve dans la structure   lors de la lecture des données de la section relative aux imports, je constate que les valeurs sont complètement dans les choux. Que se passe-t-il ? Où sont ces fichus imports, pour commencer ? J'ouvre le binaire dans un éditeur hexadécimal pour tirer les choses au clair, et tombe nez à nez avec cette horreur :

 

Quiconque est habitué au format verra immédiatement que le header PE a été lourdement modifié, et la présence de chaînes comme   ou   laisse penser que les imports que nous recherchons sont justement ici (là où lls n'ont rien à faire). Une seule explication : cet exécutable a été modifié (voire entièrement conçu) à la main, dans le but de faire planter les outils d'analyse. Car il faut savoir que malgré une jolie norme d'environ 70 pages, le loader de Windows fait preuve d'un laxisme outrancier et tolère des déformations du format PE qui rendent très complexe le décorticage des binaires... Car chez les créateurs de virus, l'implémentation de référence, c'est évidemment celle de l'OS et pas ce qui est marqué sur le papier. Malheureusement pour ceux qui écrivent les outils d'analyse, il faut donc s'aligner à l'aveuglette sur cette manière de faire, pour laquelle il n'existe naturellement aucune documentation officielle. Même les plus grands (IDA, OllyDbg) s'y cassent parfois les dents.
Mais revenons à nos moutons. Si on regarde la table des sections de ce binaire, on peut remarquer quelque chose d'intéressant pour celle qui contient les imports :

 

La valeur qui surprend, c'est   ( ). En théorie, on doit trouver ici un multiple de   (traditionnellement,  ). Si ce n'est pas le cas, il faut arrondir en-dessous pour s'aligner sur le dernier multiple. Une subtilité qui rappelée par ReversingLabs dans leur conférence sur le sujet à la Black Hat 2011.
En l'espèce, la section recherchée ne commence donc pas à l'offset   dans le fichier, mais à l'offset   : les outils qui omettraient ce détail liraient alors les données recherchées au mauvais endroit.

Mais les ennuis ne s'arrêtent pas là. Voici à quoi ressemble la structure que l'on vient de réussir à lire.

 

Là encore, c'est très piégeux.
  est laissé à zéro ; dans ce cas-là, on peut utiliser   à la place. Le champ   est censé contenir une Adresse Relative Virtuelle (RVA) - celle-ci est ensuite traduite pour retrouver la position de la donnée pointée dans le fichier sur le disque. Que se passe-t-il lorsque la traduction échoue ? Windows utilise l'adresse telle quelle, au cas où. Évidemment, c'est le cas ici. Pour finir, le dernier   ( ) se trouve à mi-chemin entre deux sections. Voyez plutôt :

Si vous vous remémorez la table donnée plus haut, la section commençait à l'offset   et avait une taille de  ... Je ne sais pas exactement comment le loader Windows gère le ré-alignement de la section, mais il me semblerait logique que sa limite haute soit maintenue à  . Or ici, les deux derniers octets lus ( ) sont en-dehors des limites. Que se passe-t-il ? Il ne s'agit que de spéculation, mais je pense que la représentation mémoire qu'utilise le le système d'exploitation fait qu'il n'y a aucun débordement possible. Les octets "en trop" sont laissés à zéro, ce qui nous donnerait  ... Pile poil la valeur qui nous fait tomber au bon endroit.
...Après quoi on tombe encore sur des adresses qui se traduisent mal et qu'il faut ré-interpréter, etc. etc.

Voila pour la lecture des imports ! En vrac, il y a aussi des petits pièges ailleurs, comme le   relatif aux informations de Debug qui a une taille nulle, ou d'autres qui n'existent tout simplement pas.
Et vu l'échec de IDA pour obtenir le code désassemblé, je pense qu'il y a encore des astuces intéressantes qui se cachent dans ce PE.
Si vous poussez l'analyse plus loin, faîtes-moi signe !

Source de l'image d'en-tête : http://wondermark.com/new-shirt-unparsable-symbols/