Je l'ai fait de nombreuses fois et je continue à le faire. Dans ce cas, où votre objectif principal est de lire et non d'écrire en assembleur, je pense que cela s'applique.
Ecrivez votre propre désassembleur. Ce n'est pas dans le but de créer le prochain grand désassembleur, celui-ci est strictement pour vous. Le but est d'apprendre le jeu d'instructions. Qu'il s'agisse d'apprendre l'assembleur sur une nouvelle plate-forme ou de se souvenir de l'assembleur d'une plate-forme que j'ai connue. Commencez avec seulement quelques lignes de code, en ajoutant des registres par exemple, et en jouant au ping-pong entre le désassemblage de la sortie binaire et l'ajout d'instructions de plus en plus compliquées du côté de l'entrée :
1) apprendre le jeu d'instructions pour le processeur spécifique
2) apprendre les nuances de l'écriture du code en assembleur pour ledit processeur de manière à pouvoir manipuler chaque bit d'opcode dans chaque instruction.
3) Vous apprenez le jeu d'instructions mieux que la plupart des ingénieurs qui utilisent ce jeu d'instructions pour gagner leur vie.
Dans votre cas, il y a plusieurs problèmes. Je recommande normalement le jeu d'instructions ARM pour commencer, il y a plus de produits basés sur ARM livrés aujourd'hui que tout autre (ordinateurs x86 inclus). Mais la probabilité que vous utilisiez ARM maintenant et que vous ne connaissiez pas assez l'assembleur pour écrire le code de démarrage ou d'autres routines connaissant ARM peut ou ne peut pas aider ce que vous essayez de faire. La seconde et plus importante raison pour laquelle ARM est le premier est que les longueurs d'instructions sont de taille fixe et alignées. Désassembler des instructions de longueur variable comme le x86 peut être un cauchemar pour votre premier projet, et le but ici est d'apprendre le jeu d'instructions et non de créer un projet de recherche. Troisièmement, ARM est un jeu d'instructions bien fait, les registres sont créés égaux et n'ont pas de nuances individuelles spéciales.
Vous devrez donc déterminer avec quel processeur vous voulez commencer. Je suggère le msp430 ou ARM en premier, puis ARM en premier ou en second puis le chaos de x86. Quelle que soit la plate-forme, toute plate-forme digne d'être utilisée possède des fiches techniques ou des manuels de référence pour les programmeurs, disponibles gratuitement auprès du fournisseur, qui comprennent le jeu d'instructions ainsi que l'encodage des opcodes (les bits et les octets du langage machine). Pour apprendre ce que fait le compilateur et comment écrire du code avec lequel le compilateur n'a pas à se battre, il est bon de connaître quelques jeux d'instructions et de voir comment le même code de haut niveau est implémenté sur chaque jeu d'instructions avec chaque compilateur et chaque paramètre d'optimisation. Vous ne voulez pas vous lancer dans l'optimisation de votre code pour vous rendre compte que vous l'avez amélioré pour un compilateur/plateforme mais beaucoup moins pour tous les autres.
Oh pour désassembler des jeux d'instructions de longueur variable, au lieu de simplement commencer au début et de désassembler chaque mot de quatre octets linéairement à travers la mémoire comme vous le feriez avec l'ARM ou tous les deux octets comme le msp430 (Le msp430 a des instructions de longueur variable mais vous pouvez toujours vous en sortir en allant linéairement à travers la mémoire si vous commencez aux points d'entrée de la table vectorielle d'interruption). Pour les instructions de longueur variable, vous devez trouver un point d'entrée basé sur une table de vecteurs ou savoir comment le processeur démarre et suivre le code dans l'ordre d'exécution. Il faut décoder complètement chaque instruction pour savoir combien d'octets sont utilisés puis, si l'instruction n'est pas un branchement inconditionnel, supposer que l'octet suivant cette instruction est une autre instruction. Vous devez également stocker toutes les adresses de branchement possibles et supposer qu'il s'agit des adresses d'octets de départ pour d'autres instructions. La seule fois où j'ai réussi, j'ai effectué plusieurs passages dans le binaire. En commençant par le point d'entrée, j'ai marqué cet octet comme étant le début d'une instruction, puis j'ai décodé linéairement dans la mémoire jusqu'à ce que je tombe sur un branchement inconditionnel. Toutes les cibles de branchement étaient marquées comme des adresses de départ d'une instruction. J'ai effectué plusieurs passages dans le binaire jusqu'à ce que je ne trouve plus de nouvelles cibles de branchement. Si à un moment donné vous trouvez une instruction de 3 octets mais que pour une raison quelconque vous avez marqué le deuxième octet comme étant le début d'une instruction, vous avez un problème. Si le code a été généré par un compilateur de haut niveau, cela ne devrait pas se produire, à moins que le compilateur ne fasse quelque chose de mal, si le code a été écrit à la main en assembleur (comme un vieux jeu d'arcade), il est tout à fait possible qu'il y ait des branchements conditionnels qui ne peuvent jamais se produire, comme r0=0 suivi d'un saut si ce n'est pas zéro. Vous devrez peut-être les éditer manuellement pour continuer. Pour vos objectifs immédiats qui, je suppose, seront sur x86, je ne pense pas que vous aurez un problème.
Je recommande les outils gcc, mingw32 est un moyen facile d'utiliser les outils gcc sous Windows si x86 est votre cible. Sinon, mingw32 plus msys est une excellente plateforme pour générer un compilateur croisé à partir des sources de binutils et de gcc (généralement assez facile). mingw32 a quelques avantages sur cygwin, comme des programmes significativement plus rapides et vous évitez l'enfer des dlls de cygwin. gcc et binutils vous permettront d'écrire en C ou en assembleur et de désassembler votre code et il y a plus de pages web que vous ne pouvez en lire vous montrant comment faire l'un ou les trois. Si vous devez faire cela avec un jeu d'instructions de longueur variable, je vous recommande fortement d'utiliser un jeu d'outils qui inclut un désassembleur. Un désassembleur tiers pour x86, par exemple, sera difficile à utiliser car vous ne saurez jamais vraiment s'il a été désassemblé correctement. Le but est de compiler les modules dans un format binaire qui contient des informations séparant les instructions des données afin que le désassembleur puisse faire un travail plus précis. Votre autre choix pour cet objectif primaire est d'avoir un outil qui peut compiler directement en assembleur pour votre inspection, puis espérer que lorsqu'il compile dans un format binaire, il crée les mêmes instructions.
La réponse courte (ou légèrement plus courte) à votre question. Ecrivez un désassembleur pour apprendre un jeu d'instructions. Je commencerais par quelque chose de RISCy et facile à apprendre comme ARM. Une fois que vous connaissez un jeu d'instructions, les autres deviennent beaucoup plus faciles à apprendre, souvent en quelques heures, et au troisième jeu d'instructions, vous pouvez commencer à écrire du code presque immédiatement en utilisant la fiche technique/le manuel de référence pour la syntaxe. Tous les processeurs qui valent la peine d'être utilisés ont une fiche technique ou un manuel de référence qui décrit les instructions jusqu'aux bits et aux octets des opcodes. Apprenez suffisamment un processeur RISC comme ARM et un CISC comme x86 pour avoir une idée des différences, des choses comme le fait de devoir passer par des registres pour tout ou d'être capable d'effectuer des opérations directement sur la mémoire avec moins ou pas de registres. Les instructions à trois opérandes contre deux, etc. Lorsque vous mettez au point votre code de haut niveau, compilez pour plusieurs processeurs et comparez les résultats. La chose la plus importante que vous apprendrez est que, quelle que soit la qualité du code de haut niveau écrit, la qualité du compilateur et les choix d'optimisation effectués font une énorme différence dans les instructions réelles. Je recommande llvm et gcc (avec binutils), aucun ne produit grand mais ils sont multiplateformes et multicibles et disposent tous deux d'optimiseurs. Tous deux sont gratuits et vous pouvez facilement construire des compilateurs croisés à partir des sources pour différents processeurs cibles.
0 votes
Question similaire à stackoverflow.com/questions/1355524/
0 votes
Oui, je lisais aussi celui-là. Mais ma question est un peu plus "ciblée", je dirais.
0 votes
Si vous êtes sous Windows, la cible (c'est-à-dire le processeur, et donc le jeu d'instructions) est x86 ou x86-64. A moins que vous ne vous procuriez une autre machine ou une carte MCU ou que vous utilisiez un émulateur. Donc, la question est de savoir quel assembleur devrais-je utiliser ? Ou demandez-vous vraiment quelle architecture cibler ? Personnellement, j'adore le jeu d'instructions orthogonales des puces de la série m68k, mais hélas, hélas !
2 votes
"Il semble avoir des instructions if, invoke, etc." - Ce sont des macros (le 'M' de "MASM") et vous n'êtes pas obligé de les utiliser même si l'assembleur les supporte.
3 votes
Ce fut une décision difficile de donner à la question son 65ème vote positif, 64 est un si beau chiffre. . .
0 votes
Si vous utilisez l'assembleur de Microsoft, il a été appelé ML au lieu de MASM depuis qu'il est passé de la version 5.x à la version 6.x (à l'époque de MSDOS). La version 6.x est celle où les fonctions de plus haut niveau comme .if ... ont été ajoutées. Microsoft Visual C / C++ et Visual Studio incluent ML.EXE et ML64.EXE (version 64 bits). Si vous créez un projet et ajoutez un fichier .asm au projet, Visual Studio devrait créer une étape de construction personnalisée pour invoquer ML ou ML64 (il vous demandera si vous êtes d'accord pour créer l'étape de construction personnalisée).