Question:
Comment récupérer des variables à partir d'un code d'assemblage?
perror
2013-03-26 21:31:58 UTC
view on stackexchange narkive permalink

En supposant que nous ayons un code d'assemblage, quelles sont les techniques connues qui pourraient être utilisées pour récupérer les variables utilisées dans le code de haut niveau d'origine?

Modifier : par récupérer des variables , je ne veux pas dire récupérer des noms de variables , mais essayer d'identifier les emplacements mémoire qui sont utilisés pour stocker des résultats temporaires qui pourraient être remplacés par une variable dans le code de haut niveau . De plus, je ne parle pas de bytecodes, mais d'un vrai code binaire sans informations de type, ni de noms complets intégrés.

Quatre réponses:
#1
+15
Igor Skochinsky
2013-03-27 05:49:06 UTC
view on stackexchange narkive permalink

(J'avais l'intention d'en faire un commentaire mais il s'est avéré assez long et il fait une réponse tout seul)

Certains des commentaires mentionnaient le décompilateur Hex-Rays. Ses idées de base ne sont pas un secret commercial et sont en fait décrites dans le livre blanc d'Ilfak Guilfanov qui accompagne la présentation qu'il a faite en 2008.

Je vais coller la partie pertinente ici:

Allocation de variables locales

Cette phase utilise l'analyse de flux de données pour connecter des registres de différents blocs de base afin de les convertir en local variables. Si un registre est défini par un bloc et utilisé par un autre, alors nous créerons une variable locale couvrant à la fois la définition et l'utilisation. En d'autres termes, une variable locale comprend toutes les définitions et tous les usages qui peuvent être connectés ensemble. Bien que l'idée de base soit simple, les choses se compliquent à cause des registres byte / word / dword.

C'est simple en surface mais bien sûr l'implémentation doit prendre en compte de nombreux détails. Et il y a toujours place à l'amélioration. Il y a ce passage:

Pour le moment, nous n'analysons pas les plages en direct de variables de pile (cela nécessite d'abord une bonne analyse d'alias: il faut être capable de prouver qu'une variable de pile n'est pas modifié entre deux emplacements). Je doute qu'une analyse complète de la plage en direct soit disponible pour les variables de pile dans un proche avenir.

Donc, pour les variables de pile, l'approche est actuellement simple: chaque emplacement de pile est considéré comme un seul variable pour l'ensemble de la fonction (à quelques exceptions près). Le décompilateur s'appuie ici sur le travail effectué par IDA lors du démontage, où un emplacement de pile est créé pour chaque accès par une instruction.

Un problème actuel concerne plusieurs noms pour la même variable. Par exemple, le compilateur peut mettre en cache la pile var dans un registre, la transmettre à une fonction, puis la recharger plus tard dans un autre registre. Le décompilateur doit être pessimiste ici. Si nous ne pouvons pas prouver que le même emplacement contient la même valeur à deux moments dans le temps, nous ne pouvons pas fusionner les variables. Par exemple, chaque fois que le code passe une adresse d'une variable à un appel, le décompilateur doit supposer que l'appel peut gâcher quoi que ce soit après cette adresse. Ainsi, même si le registre contient toujours la même valeur que la variable de pile, nous ne pouvons pas être sûrs à 100%. Ainsi l'excès de noms de variables. Cependant, l'utilisateur peut le remplacer par un mappage manuel.

Il existe quelques idées sur l'introduction d'annotations de fonction qui spécifieraient exactement comment une fonction utilise et / ou modifie ses arguments (similaire au SAL de Microsoft), ce qui atténuerait ce problème , mais il y a là des problèmes techniques de mise en œuvre.

Exactement le type de réponse que je recherchais, merci!
Les commentaires ne sont pas destinés à une discussion approfondie; cette conversation a été [déplacée vers le chat] (https://chat.stackexchange.com/rooms/93469/discussion-on-answer-by-igor-skochinsky-how-to-recover-variables-from-an-assembl) .
#2
+9
Rolf Rolles
2013-03-27 04:33:39 UTC
view on stackexchange narkive permalink

Ce que vous décrivez est exactement le problème qui a été abordé par Gogul Balakrishnan dans son travail de doctorat sur l'analyse des ensembles de valeurs [1]. En particulier, il définit un modèle de mémoire pour x86 en termes de concepts tels que les «emplacements abstraits». Voici sa description de ce concept:

Comme indiqué précédemment, les exécutables n'ont pas d'entités intrinsèques comme les variables de code source qui peuvent être utilisées pour l'analyse; par conséquent, l'étape suivante consiste à récupérer des entités de type variable à partir de l'exécutable. Nous nous référons à de telles entités de type variable comme a-locs (pour «emplacements abstraits»).

Cela vous semble familier vis-à-vis de votre question? Vous devriez lire cette thèse, mais sachez que - comme la plupart des documents sur l'interprétation abstraite - c'est une lecture laconique et peu conviviale.

[1] http: //pages.cs.wisc. edu / ~ bgogul / Research / Thesis / thesis.html

#3
+8
endeavor
2013-03-26 22:00:06 UTC
view on stackexchange narkive permalink

Soo ..... c'est l'une des raisons pour lesquelles l'analyse binaire est difficile , la perte d'informations sémantiques. Une variable n'est pas un concept connu dans l'architecture informatique, cela rappelle un niveau de compréhension plus élevé.

La meilleure réponse que je puisse vous donner est, si vous faites une Analyse de sortie du compilateur (ce que vous êtes), vous pouvez rechercher les conventions utilisées par ce compilateur pour stocker des variables, probablement comme une combinaison de registres et de variables "spillage" dans des emplacements sur le cadre de la pile.

La mauvaise nouvelle est cela dépend du compilateur. La bonne nouvelle est que la plupart des compilateurs sont plus ou moins similaires.

Vous pouvez essayer de déterminer la signature en observant les opérations conditionnelles qui fonctionnent sur une valeur (en supposant que le développeur n'a pas fait une telle erreur comme comparant une valeur signée et non signée).

Vous donnez de jolis chemins d'investigation, mais il doit y avoir des techniques «ad hoc» existantes. Par exemple, quelles sont les techniques utilisées dans [hex-radius Decompiler] (https://www.hex-rays.com/products/decompiler/) ou [boomerang] (http://boomerang.sourceforge.net/) pour identifier les variables dans un cadre de pile?
Le décompilateur Hex-Rays est en fait assez pauvre pour comprendre les limites des variables. Il semble simplement supposer que tout ce qui peut être une variable l'est. Cela peut conduire à une surestimation brute du nombre de variables. Vous devez généralement mapper un grand nombre de variables en tant qu'alias afin d'obtenir une décompilation propre. C'est toujours un produit génial. Igor en sait probablement beaucoup plus, mais cela peut être à la limite des secrets commerciaux ou autre.
effort: pas seulement dépendant du compilateur, cependant. Considérez les conventions d'appel, elles sont dictées par l'architecture ou la plate-forme.
@PeterAndersson:, ce ne serait probablement pas quelque chose qu'ils divulgueraient. Je suppose qu'en plus de ce que chacun de nous pourrait proposer, Hex-Rays (la société) a probablement mis au point un tas d'heuristiques pour identifier les choses comme ceci ou cela. Et je suis d'accord, après avoir testé la version bêta du plugin décompilateur, je n'étais pas du tout convaincu. Cela me trompait beaucoup là où l'IDA ne l'a jamais été. Pourtant, cela fait quelques années, mais en tant que personne privée, je ne veux pas me le permettre pour le moment;)
@0xC0000022L, le décompilateur est génial. Cela nous fait gagner beaucoup de temps. Vous devez juste être assez minutieux dans la saisie et la cartographie de tout. Il fait encore des erreurs et parfois induit en erreur mais c'est un net positif.
#4
-2
samuirai
2013-03-26 22:02:40 UTC
view on stackexchange narkive permalink

Une astuce intéressante concernant les chaînes à l'intérieur d'un binaire, est l'outil de ligne de commande strings . Il peut être important de mentionner qu'il ne recherche pas de «variables». Il recherche simplement les caractères valides continus et les imprime. Cela est donc également utile pour extraire des chaînes de tout type de fichier (lorsqu'il est stocké en texte clair).

Exemple de programme:

  int main (int argc, char * argv [ ]) {char pw [] = "SecretPW"; if (! strcmp (pw, argv [1])) {printf ("Correct! \ n"); } else {printf ("Faux ... \ n"); } return 0;}  

Utilisation de la chaîne pour extraire les chaînes:

  $ ./test FalsePWFalse ... $ strings testSecretPWCorrect! False ... $ ./test SecretPW 139 ↵Correct!  


Ce Q&R a été automatiquement traduit de la langue anglaise.Le contenu original est disponible sur stackexchange, que nous remercions pour la licence cc by-sa 3.0 sous laquelle il est distribué.
Loading...