104 votes

Assemblée d'apprentissage

J'ai décidé d'apprendre le langage assembleur. La raison principale est d'être capable de comprendre du code désassemblé et peut-être d'être capable d'écrire des parties de code plus efficaces (par exemple, avec c++), de faire des choses comme des grottes de code, etc. J'ai vu qu'il y a des milliards de variantes de l'assemblage, donc, pour les objectifs que je mentionne, comment dois-je commencer ? Quel type d'assemblage dois-je apprendre ? Je veux apprendre en faisant d'abord quelques programmes faciles (par exemple une calculatrice), mais le but lui-même sera de m'accoutumer à l'assembleur pour pouvoir comprendre le code affiché, par exemple, par IDA Pro.

J'utilise Windows (si cela fait une différence).

edit : Donc, il semble que tout le monde se dirige vers MASM. Bien que je comprenne qu'il possède des capacités de haut niveau, ce qui est très bien pour le programmeur de code assembleur, ce n'est pas ce que je recherche. Il semble avoir des instructions if, invoke, etc. qui n'apparaissent pas dans les désassembleurs populaires (comme IDA). Donc, ce que j'aimerais entendre, si possible, c'est l'opinion de quelqu'un qui utilise ASM dans le but que je demande (lire le code de l'exe désassemblé dans IDA), et pas seulement les programmeurs assembleurs "généraux".

edit : OK. Je suis déjà en train d'apprendre l'assemblage. J'apprends MASM, sans utiliser les trucs de haut niveau qui n'ont pas d'importance pour moi. Ce que je suis en train de faire, c'est d'essayer mon code sur les directives __asm en c++, ce qui me permet d'essayer des choses bien plus rapidement que si je devais tout faire à partir de zéro avec MASM.

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 !

47voto

dwelch Points 27195

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

Merci pour la réponse. Mais je ne sais même pas comment écrire un désassembleur.

8 votes

"Ecrivez votre propre désassembleur" - Je suis d'accord, c'est la façon dont j'ai le mieux appris. (Qu'est-ce qui se passe avec "Mais je ne sais même pas comment écrire un désassembleur" ?) LOL.

0 votes

Je vais avec toi ! Je viens d'acheter un MSP430 et un livre sur le sujet... :)

40voto

Noon Silk Points 30396

Commencez par MASM32 et de là, regardez FASM . Mais vous vous amuserez avec MASM.

1 votes

J'ai entendu parler de MASM. Si je ne me trompe pas, il a beaucoup de fonctionnalités de "haut niveau", que je ne vois pas quand je regarde du code dissamilé. J'aimerais avoir à programmer dans quelque chose qui soit exactement comme le code de sortie de la plupart des désassembleurs, si cela a un sens.

1 votes

Cela reviendrait à écrire des op-codes, ce qui n'a pas vraiment de sens. Apprendre MASM32 vous aidera à comprendre comment le code se présente dans un débogueur. Vous pouvez également consulter OllyDbg : ollydbg.de

0 votes

Beaucoup de ces fonctionnalités de "haut niveau" sont là pour une raison. Il n'est pas facile d'écrire en langage d'assemblage, alors chaque petit bout de papier aide. Vous pourriez aussi apprendre à écrire en code machine, en émettant des ints 32 bits dans un fichier EXE au lieu d'utiliser le langage d'assemblage et un compilateur/lien, mais il n'y a vraiment aucune raison de le faire. MASM (ou TASM, si vous pouvez en trouver une copie) sont de bons endroits pour commencer.

33voto

kquinn Points 5788

L'assemblage que vous écrivez à la main et l'assemblage généré par un compilateur sont souvent très différents lorsqu'ils sont vus de haut niveau. Bien sûr, l'intérieur du programme sera très similaire (il n'y a qu'un nombre limité de façons différentes d'encoder le code d'un programme). a = b + c ), mais ils ne posent pas de problème lorsque l'on essaie de faire de la rétro-ingénierie. Le compilateur ajoutera un tonne de code passe-partout dans des exécutables même simples : la dernière fois que j'ai comparé, "Hello World" compilé par GCC faisait environ 4 ko, alors que s'il était écrit à la main en assembleur, il faisait environ 100 octets. C'est pire sur Windows : la dernière fois que j'ai comparé (il est vrai que c'était la dernière fois que j'ai comparé siècle ), le plus petit "Hello World" que j'ai pu faire générer par le compilateur Windows de mon choix à l'époque faisait 52 Ko ! En général, ce passe-partout n'est exécuté qu'une seule fois, voire pas du tout, et n'affecte donc pas beaucoup la vitesse du programme - comme je l'ai dit plus haut, le cœur du programme, la partie où la plupart du temps d'exécution est passé, est généralement assez similaire, qu'il soit compilé ou écrit à la main.

En fin de compte, cela signifie qu'un montage expert programmeur et un expert désassembleur sont deux spécialités différentes. On les retrouve souvent chez la même personne, mais elles sont vraiment distinctes, et apprendre à devenir un excellent codeur assembleur ne vous aidera pas beaucoup à apprendre l'ingénierie inverse.

Ce que vous voulez faire, c'est prendre les manuels d'architecture IA-32 et AMD64 (les deux sont couverts ensemble) à partir de Intel y AMD et parcourez les premières sections sur les instructions et les opcodes. Lisez peut-être un ou deux tutoriels sur le langage d'assemblage, juste pour vous familiariser avec les bases du langage d'assemblage. Ensuite, prenez un petit Un exemple de programme qui vous intéresse et désassemblez-le : parcourez son flux de contrôle et essayez de comprendre ce qu'il fait. Voyez si vous pouvez le modifier pour qu'il fasse autre chose. Puis essayez à nouveau avec un autre programme, et répétez jusqu'à ce que vous soyez suffisamment à l'aise pour essayer d'atteindre un objectif plus utile. Vous pourriez être intéressé par des choses comme les "crackmes", produits par la communauté de la rétro-ingénierie, qui sont des défis pour les personnes intéressées par la rétro-ingénierie pour essayer leur main, et si possible apprendre quelque chose en cours de route. Leur niveau de difficulté varie de basique (commencez ici !) à impossible.

Avant tout, vous devez juste pratique . Comme dans de nombreuses autres disciplines, en matière de rétro-ingénierie, c'est en pratiquant que l'on devient parfait... ou du moins... meilleur .

0 votes

Je sais que lorsque vous compilez quelque chose avec un langage de haut niveau, vous obtenez beaucoup de code "poubelle" qui ne serait pas nécessaire s'il était codé directement en assembleur. Je comprends aussi qu'il y a une différence entre un programmeur assembleur expert et un désassembleur expert. Mais on pourrait dire la même chose de presque tout le reste.

3 votes

Ce qui me préoccupe, c'est qu'en théorie, je pourrais lire les articles et comprendre ce qu'ils signifient, mais tant que je n'aurai pas commencé à écrire des choses moi-même, je ne pense pas que je comprendrai vraiment. Vous dites que je peux commencer par modifier de petites parties de code, mais pour ce faire, je dois d'abord savoir quelle "saveur" d'assemblage IDA pro, par exemple, utilise.

0 votes

De plus, qu'utilise MSVC++ pour le code d'assemblage en ligne ? MASM ?

15voto

Alex Martelli Points 330805

Je vais aller à l'encontre de la plupart des réponses et recommander le livre de Knuth. MMIX variante de l'architecture RISC MIPS. Il ne sera pas aussi utile en pratique que les langages d'assemblage x86 ou ARM (même s'ils ne sont pas si importants que ça dans la plupart des emplois réels de nos jours...;-), mais il débloquera pour vous la magie de la dernière version du plus grand chef-d'œuvre de Knuth sur la compréhension profonde de bas niveau des algorithmes et des structures de données... TAOCP The Art of Computer Programming". Les liens des deux URL que j'ai cités sont un excellent moyen de commencer à explorer cette possibilité !

12voto

Nick Dandoulakis Points 26809

(Je ne sais pas pour vous mais moi j'étais excité par l'assemblage)

Un outil simple pour expérimenter l'assemblage est déjà installé dans votre pc.

Allez dans le menu Démarrer->Exécuter, et tapez debug

debug (commande)

déboguer est une commande dans DOS, MS-DOS, OS/2 et Microsoft Windows (uniquement les versions x86, pas x64) qui exécute le programme debug.exe (ou DEBUG.COM dans les anciennes versions de DOS). DOS). Debug peut agir en tant qu'assembleur, désassembleur ou programme de vidage hexadécimal. permettant aux utilisateurs d'examiner de manière interactive le contenu de la mémoire (en langage d'assemblage, hexadécimal ou ASCII), d'apporter des modifications et d'exécuter de manière sélective des fichiers COM, EXE et autres. Il possède également plusieurs sous-commandes qui sont utilisées pour accéder à des secteurs spécifiques du disque, des ports d'E/S et des adresses mémoire. spécifiques, aux ports d'E/S et aux adresses mémoire. MS-DOS Debug fonctionne à une vitesse de le niveau de processus 16 bits et, par conséquent, il est limité aux programmes informatiques 16 bits . FreeDOS Debug possède une version "DEBUGX" supportant également les programmes DPMI 32 bits.

Tutoriels :

Si vous voulez comprendre le code que vous voyez dans IDA Pro (ou OllyDbg ), vous devrez apprendre comment le code compilé est structuré. Je vous recommande le livre Inverser : Les secrets de la rétroconception

J'ai expérimenté pendant quelques semaines debug lorsque j'ai commencé à apprendre l'assemblage (il y a 15 ans).
Notez que debug travaille au niveau de la machine de base, il n'y a pas de commandes d'assemblage de haut niveau.

Et maintenant, un exemple simple :

Donnez a pour commencer à écrire le code d'assemblage - tapez le programme ci-dessous - et enfin donnez g pour l'exécuter.

alt text

( INT 21 affiche à l'écran le caractère ASCII stocké dans la mémoire de l'ordinateur. DL si le AH est réglé sur 2 -- INT 20 termine le programme)

0 votes

J'ai dû appuyer sur ctrl-c, avant de pouvoir entrer "g".

2 votes

@ericp, vous n'avez pas besoin d'appuyer sur ctrl-c. Par exemple, vous tapez a et [enter] pour commencer à écrire le code d'assemblage. Si vous appuyez deux fois sur [enter], vous quittez le mode assembleur. g & [enter] pour l'exécuter (offset 100 par défaut).

0 votes

Est-ce que cela provoque réellement un débordement de pile ou est-ce que cela l'écrit simplement à l'écran ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X