Assembleur moderne. Plongez dans l'assembleur. Un cours complet sur la programmation dans Asma de ][. Qu'est-ce que l'assembleur

Pour qu'une machine exécute des commandes humaines niveau matériel, il est nécessaire de définir une certaine séquence d'actions dans le langage des « zéros et uns ». L'assembleur deviendra un assistant en la matière. Il s'agit d'un utilitaire qui fonctionne avec la traduction des commandes en langage machine. Cependant, l’écriture d’un programme est un processus très laborieux et complexe. Ce langage n'est pas destiné à créer des poumons et gestes simples. Sur à l'heure actuelle tout langage de programmation utilisé (Assembler fonctionne très bien) vous permet d'écrire des tâches spéciales et efficaces qui influencent grandement le fonctionnement du matériel. Le but principal est de créer des micro-instructions et des petits codes. Ce langage offre plus de possibilités que, par exemple, Pascal ou C.

Une brève description des langages d'assemblage

Tous les langages de programmation sont divisés en niveaux : bas et haut. N'importe lequel de système syntaxique La "famille" des assembleurs se distingue par le fait qu'elle combine à la fois certains des avantages des produits les plus courants et langues vivantes. Ce qu'ils ont en commun avec les autres, c'est que au maximum vous pouvez utiliser un système informatique.

Une caractéristique distinctive du compilateur est sa facilité d'utilisation. Cela le distingue de ceux qui fonctionnent uniquement avec des niveaux élevés. Si vous considérez un tel langage de programmation, Assembler fonctionne deux fois plus vite et mieux. Pour y écrire programme facile, cela ne prendra pas trop de temps.

En bref sur la structure de la langue

Si nous parlons en général du travail et de la structure du langage, nous pouvons affirmer avec certitude que ses commandes correspondent pleinement aux commandes du processeur. Autrement dit, l'assembleur utilise des codes mnémoniques, la plupart confortable pour une personne pour l'enregistrement.

Contrairement à d'autres langages de programmation, l'assembleur utilise des emplacements mémoire au lieu d'adresses pour écrire certaines marques. Avec le processus d'exécution du code, ils sont traduits en directives. Ce sont des adresses relatives qui n'affectent pas le fonctionnement du processeur (ne sont pas traduites en langage machine), mais sont nécessaires à la reconnaissance par l'environnement de programmation lui-même.

Chaque ligne de processeur a le sien. Dans cette situation, tout processus, y compris celui traduit, sera correct.

Le langage assembleur a plusieurs syntaxes qui seront abordées dans l'article.

Avantages de la langue

L'avantage le plus important et le plus pratique du langage Assembly est que vous pouvez y écrire n'importe quel programme pour le processeur, qui sera très compact. Si le code s'avère volumineux, certains processus sont redirigés vers la RAM. De plus, ils font tout assez rapidement et sans échec, à moins bien sûr qu'ils ne soient gérés par un programmeur qualifié.

Les pilotes, systèmes d'exploitation, BIOS, compilateurs, interprètes, etc. sont tous des programmes en langage Assembly.

Lorsque vous utilisez un désassembleur qui effectue une traduction de machine à machine, vous pouvez facilement comprendre comment fonctionne telle ou telle tâche système, même s'il n'y a aucune explication à cela. Cependant, cela n’est possible que si les programmes sont simples. Malheureusement, les codes non triviaux sont assez difficiles à comprendre.

Inconvénients de la langue

Malheureusement, les programmeurs débutants (et souvent professionnels) ont du mal à comprendre le langage. L'assembleur nécessite description détaillée la commande requise. En raison de la nécessité d'utiliser des commandes machine, la probabilité d'actions erronées et la complexité d'exécution augmentent.

Afin d'écrire même le plus un programme simple, le programmeur doit être qualifié et son niveau de connaissances est suffisamment élevé. Malheureusement, le spécialiste moyen écrit souvent du mauvais code.

Si la plate-forme pour laquelle le programme est créé est mise à jour, toutes les commandes doivent être réécrites manuellement - cela est requis par le langage lui-même. L'assembleur ne prend pas en charge la fonction d'ajustement automatique des performances des processus et de remplacement d'éléments.

Commandes de langage

Comme mentionné ci-dessus, chaque processeur possède son propre ensemble de commandes. Les éléments les plus simples reconnus par n'importe quel type sont les codes suivants :


Utiliser des directives

Programmation des microcontrôleurs dans le langage lui-même (Assembler le permet et gère bien le fonctionnement) niveau bas dans la plupart des cas, cela se termine avec succès. Il est préférable d'utiliser des processeurs aux ressources limitées. Pour la technologie 32 bits langue donnée s'adapte parfaitement. Vous pouvez souvent remarquer des directives dans les codes. Qu'est-ce que c'est? Et à quoi sert-il ?

Tout d’abord, il faut souligner que les directives ne sont pas traduites en langage machine. Ils régulent la manière dont le compilateur effectue son travail. Contrairement aux commandes, ces paramètres, ayant diverses fonctions, ne diffèrent pas grâce à différents processeurs, mais aux frais d'un autre traducteur. Parmi les principales directives figurent les suivantes :


Origine du nom

Comment la langue tire-t-elle son nom - « Assembleur » ? Nous parlons d'un traducteur et d'un compilateur qui cryptent les données. En anglais, Assembler ne signifie rien de plus qu'un assembleur. Le programme n'a pas été assemblé manuellement ; une structure automatique a été utilisée. De plus, pour le moment, les utilisateurs et les spécialistes ont déjà perdu la différence entre les termes. Les langages de programmation sont souvent appelés assembleur, bien qu'il ne s'agisse que d'un utilitaire.

En raison du nom collectif commun, certaines personnes croient à tort qu'il existe un seul langage de bas niveau (ou des normes standard pour celui-ci). Pour que le programmeur comprenne de quelle structure nous parlons, il est nécessaire de clarifier pour quelle plate-forme un langage Assembly particulier est utilisé.

Macro signifie

Les langages d'assemblage, qui sont relativement nouveaux, possèdent des fonctionnalités de macro. Ils facilitent à la fois l’écriture et l’exécution d’un programme. Grâce à leur présence, le traducteur exécute le code écrit plusieurs fois plus rapidement. Lors de la création d’une sélection conditionnelle, vous pouvez écrire un énorme bloc de commandes, mais il est plus facile d’utiliser des outils macro. Ils vous permettront de basculer rapidement entre les actions si une condition est remplie ou non.

Lors de l'utilisation de directives en langage macro, le programmeur reçoit des macros Assembler. Parfois, il peut être largement utilisé, et parfois il caractéristiques fonctionnelles réduit à une seule équipe. Leur présence dans le code facilite le travail, le rendant plus compréhensible et visuel. Cependant, il faut quand même être prudent : dans certains cas, les macros, au contraire, aggravent la situation.

Programmation en langage assembleur

Cette partie du cours couvre les bases de la programmation en langage assembleur pour l'architecture Win32.

Tous les processus de la machine au niveau matériel le plus bas sont pilotés uniquement par des commandes (instructions) langage machine. Le langage assembleur est une représentation symbolique du langage machine. Le langage assembleur vous permet d'écrire de manière courte et programmes rapides. Cependant, ce processus demande énormément de main d’œuvre. Pour écrire le plus possible programme efficace Vous avez besoin d’une bonne connaissance des fonctionnalités des commandes du langage assembleur, d’attention et de précision. Par conséquent, en réalité, la plupart des programmes sont écrits en langage assembleur qui devrait fournir travail efficace avec du matériel. De plus, les sections du programme critiques en termes de temps d'exécution ou de consommation de mémoire sont écrites en langage assembleur. Par la suite, ils sont formalisés sous forme de sous-programmes et combinés avec du code dans le langage haut niveau.

1. Registres

Les registres sont des cellules mémoire spéciales situées directement dans le processeur. Travailler avec des registres est beaucoup plus rapide que travailler avec des cellules BÉLIER, par conséquent, les registres sont activement utilisés à la fois dans les programmes en langage assembleur et dans les compilateurs de langage de haut niveau.

Les registres peuvent être divisés en registres usage général ,index des commandes,registre de drapeaux et registres de segments.

1.1. Registres à usage général

À Les registres à usage général sont un groupe de 8 registres qui peuvent être utilisés dans un programme en langage assembleur. Tous les registres ont une taille de 32 bits et peuvent être divisés en 2 parties ou plus.

Comme le montre la figure, les registres ESI, EDI, ESP et EBP permettent d'accéder aux 16 bits inférieurs portant respectivement les noms SI, DI, SP et BP, et les registres EAX, EBX, ECX et EDX permettent de accéder à la fois aux 16 bits inférieurs (nommés AX, BX, CX et DX) et aux deux octets faibles séparément (nommés AH/AL, BH/BL, CH/CL et

Les noms des registres proviennent de leur finalité :

EAX/AX/AH/AL (registre d’accumulateur) – batterie ;

EBX/BX/BH/BL (registre de base) – registre de base ;

ECX/CX/CH/CL (registre de compteur) – compteur ;

EDX/DX/DH/DL (registre de données) – registre de données ;

ESI/SI (source registre d'indexation) – index des sources ;

EDI/DI (registre d'index de destination) – index du destinataire (destinataire) ;

ESP/SP (registre de pointeur de pile) – registre de pointeur de pile ;

EBP/BP (registre de pointeur de base) – registre de pointeur de base de trame de pile.

Malgré la spécialisation existante, tous les registres peuvent être utilisés dans n'importe quelle opération machine. Il faut cependant tenir compte du fait que certaines instructions ne fonctionnent qu’avec certains registres. Par exemple, les instructions de multiplication et de division utilisent les registres EAX et EDX pour stocker les données source et le résultat de l'opération. Les instructions de contrôle de boucle utilisent le registre ECX comme compteur de boucle.

Une autre nuance est d'utiliser des registres comme base, c'est-à-dire Stockage des adresses RAM. Tous les registres peuvent être utilisés comme registres de base, mais il est conseillé d'utiliser des registres EBX, ESI, EDI ou EBP. Dans ce cas, la taille des commandes machine est généralement plus petite.

Malheureusement, le nombre de registres est catastrophiquement faible et il est souvent difficile de déterminer comment les utiliser de manière optimale.

1.2. Index des commandes

Registre EIP ( index des commandes) contient le décalage de la prochaine commande à exécuter. Ce registre n'est pas directement accessible au programmeur, mais sa valeur peut être chargée et modifiée différentes équipes contrôles, qui comprennent des contrôles conditionnels et sauts inconditionnels, appeler des procédures et revenir des procédures.

1.3. Registre des drapeaux

Un indicateur est un bit qui prend la valeur 1 (« indicateur défini ») si une condition est remplie, et la valeur 0 (« indicateur effacé ») dans le cas contraire. Le processeur dispose d'un registre d'indicateurs contenant un ensemble d'indicateurs reflétant état actuel processeur.

Désignation

Nom

Drapeau déplacé

Réservé

Drapeau de parité

Réservé

Drapeau de transport auxiliaire

Auxiliaire

Réservé

Drapeau zéro

Signe de drapeau

Traceur de drapeau

Indicateur d'activation d'interruption

Indicateur d'autorisation

Indications du drapeau

Indicateur de pension

Niveau de privilège E/S

Niveau à

Drapeau imbriqué

Réservé

Le drapeau a repris

Mode virtuel 8086

Mode virtuel

Je vous vérifie

Indicateur d'interruption virtuelle

Virtuel

Interruption virtuelle en attente

En attendant

Vérifiez

Réservé

La valeur des flags CF, DF et IF peut être modifiée directement dans le registre des flags en utilisant instructions spéciales(par exemple CLD pour réinitialiser le drapeau de direction), mais il n'y a aucune instruction permettant d'accéder au registre des drapeaux comme s'il s'agissait d'un registre normal. Cependant, vous pouvez enregistrer

le registre de drapeaux sur la pile ou le registre AH et restaure le registre de drapeaux à partir d'eux en utilisant les instructions LAHF , SAHF , PUSHF , PUSHFD , POPF et POPFD .

1.3.1. Indicateurs d'état

Les indicateurs d'état (bits 0, 2, 4, 6, 7 et 11) reflètent le résultat d'instructions arithmétiques telles que ADD,SUB,MUL,DIV.

Le drapeau de transport CF est placé lors du transport d'un senior peu significatif/emprunter au bit de poids fort et indique la présence d'un débordement en arithmétique entière non signée. Également utilisé en arithmétique longue.

L'indicateur de parité PF est activé si l'octet de poids faible du résultat contient un nombre pair de bits un. Initialement, ce drapeau était destiné à être utilisé dans les programmes de communication : lors de la transmission de données sur des lignes de communication, un bit de parité pouvait également être transmis pour le contrôle, et les instructions de vérification du drapeau de parité facilitaient le contrôle de l'intégrité des données.

Drapeau de transport auxiliaire AF est défini lors du transport depuis le bit 3ème résultat/prêt en 3ème bit de résultat. Ce drapeau est destiné à être utilisé dans BCD (décimal codé binaire, BCD) arithmétique.

L'indicateur zéro ZF est activé si le résultat égal à zéro.

Le drapeau de signe SF est égal à la valeur du bit de poids fort du résultat, qui est le bit de signe en arithmétique signée.

Indicateur de débordement OF est défini si le résultat entier est trop long pour tenir dans l'opérande cible (registre ou emplacement mémoire). Cet indicateur indique la présence d'un débordement dans l'arithmétique des nombres entiers signés.

Parmi ces drapeaux, seul le drapeau CF peut être modifié directement à l'aide des instructions STC, CLC et CMC.

Les indicateurs d'état permettent d'émettre la même instruction arithmétique résultat de trois différents types: Entier décimal (BCD) non signé, signé et codé binaire. Si le résultat est considéré comme un nombre non signé, alors l'indicateur CF indique la condition de débordement (carry ou emprunt), pour un résultat signé, un report ou un emprunt affiche l'indicateur OF, et pour un résultat BCD, un report/emprunt affiche l'AF. drapeau. Le drapeau SF reflète le signe d'un résultat signé, le drapeau ZF reflète à la fois un résultat nul non signé et signé.

En arithmétique des nombres entiers longs, l'indicateur CF est utilisé conjointement avec les instructions d'ajout avec report (ADC) et de soustraction avec emprunt (SBB) pour propager un report ou un emprunt à partir d'un seul bit calculé. numéro longà un autre.

Instructions

conditionnel

transition Jcc (transition

condition cc ), SETcc (ensemble

signification

octet de résultat

dépendances

conditions cc ),LOOPcc (organisation

et CMOVcc (conditionnel

copier)

utiliser

un ou plusieurs

drapeaux d'état pour vérifier la condition. Par exemple, l'instruction de saut JLE (saut si inférieur ou égal) vérifie la condition « ZF = 1 ou SF ≠ OF ».

Le drapeau PF a été introduit pour des raisons de compatibilité avec d'autres architectures de microprocesseurs et est rarement utilisé aux fins prévues. Son utilisation la plus courante est en conjonction avec d'autres indicateurs d'état en arithmétique à virgule flottante : les instructions de comparaison (FCOM, FCOMP, etc.) dans le coprocesseur mathématique y définissent les indicateurs de condition C0, C1, C2 et C3, et ces indicateurs peuvent être copiés. pour signaler le registre. Pour ce faire, il est recommandé d'utiliser l'instruction FSTSW AX pour stocker le mot d'état du coprocesseur dans le registre AX et l'instruction SAHF pour copier ensuite le contenu du registre AH dans les 8 bits de poids faible du registre flag, C0 se terminant par dans le drapeau CF, C2 dans le drapeau PF et C3 dans le drapeau ZF. Le drapeau C2 est activé, par exemple, en cas d'arguments incomparables (NaN ou format non pris en charge) dans l'instruction de comparaison FUCOM.

1.3.2. Drapeau de contrôle

Drapeau de direction DF (bit 10 dans le registre des indicateurs) contrôle les instructions de chaîne (MOVS, CMPS, SCAS, LODS et STOS) - la définition de l'indicateur entraîne la réduction des adresses (traitement des lignes des adresses hautes aux adresses basses), la mise à zéro entraîne l'augmentation des adresses. Les instructions STD et CLD activent et effacent respectivement l'indicateur DF.

1.3.3. Indicateurs système et champ IOPL

Indicateurs système et contrôle de champ IOPL environnement opérationnel et ne sont pas destinés à être utilisés dans des programmes d'application.

Indicateur d'activation d'interruption IF : la suppression de cet indicateur interdit de répondre aux demandes d'interruption masquées.

Indicateur de trace TF – la définition de cet indicateur permet mode pas à pas débogage, quand après chaque terminé

instructions, le programme est interrompu et un gestionnaire d'interruption spécial est appelé.

Le champ IOPL affiche le niveau de priorité des E/S programme exécutable ou tâches : pour qu'un programme ou une tâche exécute des instructions d'E/S ou modifie un indicateur IF, son niveau de priorité actuel (CPL) doit être ≤ IOPL.

Indicateur d'imbrication de tâches NT – cet indicateur est défini lorsque la tâche en cours est « imbriquée » dans une autre tâche interrompue et que le segment d’état TSS tâche actuelle fournit retour avec le TSS de la tâche précédente. L'indicateur NT est vérifié par l'instruction IRET pour déterminer si le retour est intertâche ou intratâche.

Reprendre le drapeau RF est utilisé pour masquer les erreurs de débogage.

VM - La définition de cet indicateur en mode protégé entraîne un passage en mode virtuel 8086.

Indicateur de contrôle d'alignement AC – la définition de cet indicateur avec le bit AM dans le registre CR0 permet le contrôle de l'alignement des opérandes lors de l'accès à la mémoire : l'accès à un opérande non aligné provoque une exception.

VIF copie virtuelle SI drapeau ; utilisé en conjonction avec le drapeau VIP.

VIP – défini pour indiquer la présence d’une interruption en attente.

ID - La possibilité de modifier par programme cet indicateur dans le registre des indicateurs indique la prise en charge de l'instruction CPUID.

1.4. Registres de segments

Le processeur dispose de 6 registres dits de segments : CS, DS, SS, ES, FS et GS. Leur existence est due aux spécificités de l’organisation et de l’utilisation de la RAM.

Les registres 16 bits ne pouvaient adresser que 64 Ko de RAM, ce qui n'est clairement pas suffisant pour un programme plus ou moins décent. Par conséquent, la mémoire a été allouée au programme sous la forme de plusieurs segments d’une taille de 64 Ko. Dans le même temps, les adresses absolues étaient de 20 bits, ce qui permettait d'adresser 1 Mo de RAM. La question se pose : comment stocker des adresses 20 bits avec des registres 16 bits ? Pour résoudre ce problème, l'adresse a été divisée en décalage. Base est l'adresse du début du segment et offset est le numéro d'octet dans le segment. Une restriction a été imposée sur l'adresse du début du segment - elle devait être un multiple de 16. Dans ce cas, les 4 derniers bits étaient égaux à 0 et n'étaient pas stockés, mais implicites. Ainsi, deux parties de 16 bits de l'adresse ont été obtenues. Pour recevoir

de l'adresse absolue, quatre bits zéro ont été ajoutés à la base et la valeur résultante a été ajoutée avec un décalage.

Les registres de segments ont été utilisés pour stocker l'adresse du début du segment de code (CS - segment de code), du segment de données (DS - segment de données) et du segment de pile (SS - segment de pile). Les registres ES, FS et GS ont été ajoutés ultérieurement. Il existait plusieurs modèles de mémoire, chacun impliquait l'allocation d'un ou plusieurs segments de code et d'un ou plusieurs segments de données à un programme : minuscule, petit, moyen, compact, grand et énorme. Il existait certaines conventions pour les instructions en langage assembleur : les adresses de saut étaient segmentées par le registre CS, les accès aux données étaient segmentés par le registre DS et les accès à la pile étaient segmentés par le registre SS. Si un programme se voyait attribuer plusieurs segments pour le code ou les données, alors les valeurs des registres CS et DS devaient être modifiées pour accéder à un autre segment. Il y a eu des transitions dites « proches » et « lointaines ». Si la commande à laquelle il fallait sauter se trouvait dans le même segment, alors pour y accéder, il suffisait de modifier uniquement la valeur du registre IP. Une telle transition s'appelait proche. Si la commande vers laquelle la transition doit être effectuée se trouvait dans un segment différent, alors pour effectuer la transition, il était nécessaire de modifier à la fois la valeur du registre CS et la valeur du registre IP. Une telle transition était appelée longue distance et prenait plus de temps.

Les registres 32 bits permettent d'adresser 4 Go de mémoire, ce qui est déjà suffisant pour n'importe quel programme. Windows exécute chaque programme Win32 dans un espace virtuel. Cela signifie que chaque programme Win32 aura un espace d'adressage de 4 Go, mais cela ne signifie pas que chaque programme disposera de 4 Go. mémoire physique, mais seulement que le programme peut accéder à n'importe quelle adresse dans ces limites. Et Windows fera tout le nécessaire pour garantir que la mémoire à laquelle le programme accède « existe ». Bien entendu, le programme doit respecter les règles établies

Windows, sinon une erreur d'erreur de protection générale se produit.

Sous l'architecture Win32, il n'était pas nécessaire de séparer l'adresse en base et offset, ni de modèles de mémoire. A 32-

sont utilisés différemment1. Auparavant, il était nécessaire d'associer des parties individuelles du programme à l'un ou l'autre registre de segments et de sauvegarder/restaurer le registre DS lors du passage à un autre segment de données ou de segmenter explicitement les données dans un autre registre. Avec une architecture 32 bits, cela n'est plus nécessaire et, dans le cas le plus simple, vous pouvez oublier les registres de segments.

1.5. Utiliser une pile

Chaque programme possède une zone de mémoire appelée pile. La pile est utilisée pour transmettre des paramètres aux procédures et pour stocker les données locales des procédures. Comme vous le savez, la pile est une zone de mémoire, lorsqu'on travaille avec laquelle il faut observer certaines règles, à savoir : les données qui atteignent la pile en premier sont les dernières à être récupérées. D'un autre côté, si le programme dispose d'une certaine mémoire, il n'y a aucune restriction physique en lecture et en écriture. Comment concilier ces deux principes contradictoires ?

Disons une fonction f1 qui appelle la fonction f2, et la fonction f2, à son tour, appelle la fonction f3. Lors de l'appel de la fonction f1, elle est allouée lieu précis sur la pile pour les données locales. Cet espace est alloué en soustrayant du registre ESP une valeur égale à la taille de la mémoire requise. La taille minimale de la mémoire allouée est de 4 octets, soit même si une procédure nécessite 1 octet, elle devrait prendre 4 octets.

La fonction f1 effectue certaines actions puis appelle

la fonction f2 appelle la fonction f3, qui alloue également de l'espace sur la pile. La fonction f3 n'appelle pas d'autres fonctions et, une fois terminée, doit libérer de l'espace sur la pile en ajoutant au registre ESP la valeur qui a été soustraite lors de l'appel de la fonction. Si la fonction f3 ne restaure pas la valeur du registre ESP, alors la fonction f2, poursuivant son travail, n'accédera pas à ses propres données, car elle cherche

ce qui était avant son appel.

Ainsi, au niveau de la procédure, il est nécessaire de suivre les règles de travail avec la pile - la procédure qui a pris de la place sur la pile en dernier doit d'abord la libérer. Si cette règle n'est pas respectée, le programme ne fonctionnera pas correctement. Mais chaque procédure peut accéder à sa zone de pile de n'importe quelle manière. Si nous étions obligés de suivre les règles de travail avec la pile dans chaque procédure, nous devrions déplacer les données de la pile vers une autre zone de mémoire, ce qui serait extrêmement gênant et ralentirait considérablement l'exécution. du programme.

Chaque programme possède une zone de données où se trouvent les variables globales. Pourquoi les données locales sont-elles stockées sur la pile ? Ceci est fait pour réduire la quantité de mémoire occupée par le programme. Si le programme est séquentiellement appeler plusieurs procédures, alors à chaque instant il y aura de l'espace alloué uniquement pour les données d'une procédure, car La pile est occupée et libérée. La zone de données existe tant que le programme est en cours d'exécution. Si les données locales se trouvaient dans la zone de données, un espace devrait être alloué aux données locales pour toutes les procédures du programme.

Les données locales ne sont pas automatiquement initialisées. Si dans l'exemple ci-dessus, la fonction f2 après que la fonction f3 appelle la fonction f4, alors la fonction f4 prendra la place sur la pile qui était précédemment occupée par la fonction f3, ainsi la fonction f4 « héritera » des données de la fonction f3. Chaque procédure doit donc se charger d’initialiser ses données locales.

2. Concepts de base du langage assembleur

2.1. Identifiants

Le concept d'identifiant en langage assembleur n'est pas différent du concept d'identifiant dans d'autres langages. Peut être utilisé lettres latines, chiffres et signes _. ? @ $ , et le point ne peut être que le premier caractère de l'identifiant. Les majuscules et les minuscules sont considérées comme équivalentes.

2.2. Entiers

DANS Dans un programme en langage assembleur, les entiers peuvent être écrits en binaire, octal, décimal et systèmes hexadécimaux Compte. Pour spécifier le système numérique à la fin d'un numéro

Aujourd'hui, il y a quantité énorme langages de programmation de haut niveau. Dans ce contexte, la programmation dans un langage de bas niveau - l'assembleur - peut à première vue sembler dépassée et irrationnelle. Cependant, il semble que ce soit le cas. Il faut reconnaître que l'assembleur est en fait un langage de processeur, ce qui signifie qu'on ne peut s'en passer tant que les processeurs existent. Les principaux avantages de la programmation en langage assembleur sont performances maximales et la taille minimale des programmes reçus.

Les inconvénients sont souvent dus uniquement à l'inclinaison marché moderne privilégier la quantité à la qualité. Ordinateurs modernes sont capables de gérer facilement un tas de commandes de fonctions de haut niveau, et si ce n'est pas facile, veuillez mettre à jour le matériel de votre machine ! C'est la loi de la programmation commerciale. Si nous parlons de sur la programmation pour l'âme, alors un programme compact et agile écrit en langage assembleur laissera une impression bien plus agréable qu'un hulk de haut niveau chargé d'un tas d'opérations inutiles. Il existe une opinion selon laquelle seuls quelques privilégiés peuvent programmer en assembleur. Ce n'est pas vrai. Bien sûr, les programmeurs assembleurs talentueux peuvent être comptés sur une main, mais c'est le cas dans presque tous les domaines de l'activité humaine. Il n’y a pas beaucoup d’as-drivers, mais n’importe qui peut apprendre à conduire une voiture s’il en a l’envie. Après avoir lu cette série d’articles, vous ne deviendrez pas un hacker cool. Cependant vous recevrez informations générales et apprendre des moyens simples Programmation en langage assembleur pour Windows, à l'aide de ses fonctions intégrées et des instructions de macro du compilateur. Naturellement, pour maîtriser la programmation Windows, vous devez posséder des compétences et une expérience Windows. Au début, vous ne comprendrez pas grand-chose, mais ne vous inquiétez pas et continuez à lire : avec le temps, tout se mettra en place.

Donc, pour commencer à programmer, nous avons au moins besoin d’un compilateur. Un compilateur est un programme qui traduit le code source écrit par un programmeur en quelque chose d'exécutable par le processeur. code machine. La plupart des manuels d'assembleur se concentrent sur l'utilisation du package MASM32 (Microsoft Macro Assembler). Mais pour varier et pour bien d'autres raisons, je vais vous présenter le jeune compilateur FASM (Flat Assembler), qui gagne rapidement en popularité. Ce compilateur est assez facile à installer et à utiliser, il est compact et rapide, possède une syntaxe de macro riche et volumineuse qui vous permet d'automatiser de nombreux tâches courantes. Son dernière version vous pouvez le télécharger sur : site Web en sélectionnant l'assembleur plat pour Windows. Pour installer FASM, créez un dossier, par exemple "D:\FASM" et extrayez-y le contenu de l'archive zip téléchargée. Exécutez FASMW.EXE et fermez sans rien changer. D'ailleurs, si vous utilisez conducteur standard, et que l'extension du fichier (par exemple, .EXE) ne s'affiche pas, je recommande d'exécuter Outils -> Options des dossiers -> Afficher et de décocher Masquer les extensions pour les types de fichiers enregistrés. Après le premier lancement du compilateur, un fichier de configuration devrait apparaître dans notre dossier - FASMW.INI. Ouvrez-le avec bloc-notes standard et ajoutez 3 lignes tout en bas :

Fasminc=D:\FASM\INCLUDE
Inclure=D:\FASM\INCLUDE

Si vous avez décompressé FASM vers un autre emplacement, remplacez "D:\FASM\" par votre chemin. Enregistrez et fermez FASMW.INI. Pour l’avenir, je vais expliquer brièvement comment nous utiliserons le compilateur :
1. Nous écrivons le texte du programme, ou ouvrons le texte précédemment écrit enregistré dans le fichier .asm, ou collons le texte du programme depuis le presse-papiers à l'aide d'une combinaison.
2. Appuyez sur F9 pour compiler et exécuter le programme, ou sur Ctrl+F9 pour simplement compiler. Si le texte du programme n'a pas encore été enregistré, le compilateur vous demandera de le sauvegarder avant la compilation.
3. Si le programme démarre, nous testons son bon fonctionnement, sinon nous recherchons des erreurs dont le compilateur nous signalera les plus graves ou nous fera subtilement allusion.
Eh bien, nous pouvons maintenant commencer la pratique tant attendue. Nous lançons notre FASMW.EXE et y tapons le code de notre premier programme :

Inclure "%fasminc%/win32ax.inc"

Données
Légende db "Mon premier programme.",0
Texte db "Bonjour à tous!",0

Code
commencer:

invoquer ExitProcess,0

Cliquez sur Exécuter -> Exécuter ou sur F9 sur le clavier. Dans la fenêtre de sauvegarde, spécifiez le nom du fichier et le dossier à enregistrer. Il est conseillé de prendre l'habitude de sauvegarder chaque programme dans dossier séparé, pour ne pas se tromper à l'avenir, lorsque chaque programme pourra avoir un tas de fichiers : images, icônes, musique, etc. Si le compilateur génère une erreur, vérifiez soigneusement la ligne qu'il a spécifiée - vous avez peut-être manqué une virgule ou un espace. Vous devez également savoir que le compilateur est sensible à la casse, donc .data et .Data sont traités comme deux instructions différentes. Si vous avez tout fait correctement, le résultat sera une simple MessageBox (Fig. 1). Voyons maintenant ce que nous avons écrit dans le texte du programme. En première ligne avec la directive include que nous avons incluse dans notre programme gros texteà partir de plusieurs fichiers. Rappelez-vous, lors de l'installation, nous avons écrit 3 lignes dans le fichier ini FASMO ? Maintenant, %fasminc% dans le texte du programme signifie D:\FASM\INCLUDE ou le chemin que vous avez spécifié. La directive include semble s'insérer dans lieu spécifié texte d'un autre fichier. Ouvrez le fichier WIN32AX.INC dans le dossier include à l'aide du bloc-notes ou dans FASMA lui-même et assurez-vous que nous avons automatiquement inclus (joint) à notre programme le texte de win32a.inc, macro/if.inc, un tas d'incompréhensibles (pour l'instant ) instructions de macro et ensemble commun de bibliothèques Fonctions Windows. À son tour, chacun des fichiers inclus peut contenir plusieurs autres fichiers inclus, et cette chaîne peut aller au-delà de l'horizon. À l'aide des fichiers inclus, nous organisons une sorte de langage de haut niveau : afin d'éviter la routine de description manuelle de chaque fonction, nous incluons des bibliothèques de description entières. fonctionnalités standards Fenêtres. Est-ce que tout cela est vraiment nécessaire pour un si petit programme ? Non, c'est quelque chose comme " ensemble de gentleman pour toutes les occasions." Les vrais hackers, bien sûr, ne connectent pas tout, mais nous ne faisons qu'apprendre, donc c'est pardonnable pour nous pour la première fois.

Ensuite, nous avons la section de données désignée - .data. Dans cette section, nous déclarons deux variables : Légende et Texte. Ce n'est pas équipes spéciales, donc leurs noms peuvent être modifiés à votre guise, même a et b, tant qu'il n'y a pas d'espaces et pas en russe. Eh bien, vous ne pouvez pas nommer les variables mots réservés, par exemple, code ou data, mais vous pouvez utiliser code_ ou data1. La commande db signifie "définir l'octet". Bien entendu, tout ce texte ne tiendra pas dans un octet, car chaque caractère individuel occupe un octet entier. Mais dans dans ce cas Avec cette commande, nous définissons uniquement une variable de pointeur. Il contiendra l'adresse où est stocké le premier caractère de la chaîne. Le texte de la ligne est indiqué entre guillemets, et vous pouvez éventuellement mettre « tel » et « tel » entre guillemets - à condition que le guillemet de début soit le même que celui de fin. Le zéro après la virgule ajoute un octet zéro à la fin de la chaîne, ce qui indique la fin de la chaîne (terminateur nul). Essayez de supprimer ce zéro ainsi que la virgule dans la première ligne et voyez ce que vous obtenez. Dans la deuxième ligne de ce exemple spécifique vous pouvez vous passer du zéro (supprimez-le avec la virgule - sinon le compilateur indiquera une erreur), mais cela ne fonctionnera que car dans notre exemple, la section suivante commence immédiatement après la deuxième ligne, et avant de démarrer, le compilateur entrera automatiquement un tas de zéros alignant la section précédente. DANS cas généraux Les zéros à la fin des lignes de texte sont obligatoires ! La section suivante est la section du code exécutable du programme - .code. Au début de la section il y a un start: label. Cela signifie que c'est à partir de ce point que notre programme commencera à s'exécuter. La première commande est la macro-instruction d'invocation. Il appelle la fonction API Windows intégrée MessageBox. Les fonctions API (interface de programmation d'applications) simplifient considérablement le travail dans le système d'exploitation. C'est comme si nous demandions au système d'exploitation de faire quelque chose. action standard, et il exécute et, une fois terminé, nous renvoie le résultat du travail effectué. Le nom de la fonction est suivi de ses paramètres, séparés par des virgules. La fonction MessageBox a les paramètres suivants :

Le 1er paramètre doit contenir le handle de la fenêtre propriétaire. La poignée ressemble à quelque chose numéro personnel, qui est délivré système opérateur chaque objet (processus, fenêtre, etc.). 0 dans notre exemple signifie que la fenêtre n'a pas de propriétaire, qu'elle est autonome et ne dépend d'aucune autre fenêtre.
Le 2ème paramètre est un pointeur vers l'adresse de la première lettre du texte du message se terminant par le terminateur nul mentionné ci-dessus. Pour bien comprendre qu'il ne s'agit que d'une adresse, décalons cette adresse de 2 octets directement dans l'appel de fonction : invoquons MessageBox,0,Text+2,Caption,MB_OK et assurons-nous que désormais le texte sera affiché sans les deux premières lettres .
3ème - indicateur d'adresse de la première lettre de l'en-tête du message.
4ème - style de message. Vous pouvez trouver une liste de ces styles, par exemple, dans INCLUDE\EQUATES\USER32.INC. Pour ce faire, il serait préférable que vous utilisiez la recherche dans le Bloc-notes pour retrouver rapidement MB_OK et le reste. Malheureusement, il n'y a pas de description ici, mais à partir du nom du style, vous pouvez généralement deviner son objectif. D'ailleurs, tous ces styles peuvent être remplacés par un nombre signifiant un style ou une combinaison de ceux-ci, par exemple : MB_OK + MB_ICONEXCLAMATION. USER32.INC contient valeurs hexadécimales. Vous pouvez les utiliser tels quels ou les convertir en système décimal V mode ingénierie standard Calculatrice Windows. Si vous n'êtes pas familier avec les systèmes numériques et ne savez pas en quoi le décimal diffère de l'hexadécimal, alors vous avez 2 options : soit lire ce sujet vous-même sur Internet/un manuel/demander à un ami, soit laisser cette idée jusqu'à des temps meilleurs et essayer de le faire sans ces informations. Ici, je ne donnerai même pas brève information sur les systèmes numériques, car même sans moi, un grand nombre d'articles et de pages de tout niveau imaginable ont été écrits à leur sujet.

Revenons à nos moutons. Certains styles ne peuvent pas être utilisés en même temps, par exemple MB_OKCANCEL et MB_YESNO. La raison est que leur somme valeurs numériques(1+4=5) correspondra à la valeur d'un autre style - MB_RETRYCANCEL. Expérimentez maintenant avec les paramètres de fonction pour vous entraîner à sécuriser le matériel, et nous passerons à autre chose. La fonction MessageBox suspend l'exécution du programme et attend l'action de l'utilisateur. Une fois terminée, la fonction renvoie le résultat de l'action de l'utilisateur au programme et le programme continue de s'exécuter. L'appel de la fonction ExitProcess termine le processus de notre programme. Cette fonction n'a qu'un seul paramètre : le code de sortie. Généralement, si le programme se termine normalement, ce code est zéro. Pour mieux comprendre la dernière ligne de notre code - .end start - regardez de plus près le code équivalent : format PE GUI 4.0

inclure "%fasminc%/win32a.inc"

section ".data" données lisibles inscriptibles

Caption db "Notre premier programme.",0
Texte db "L'assembleur FASM est facile!",0

section ".code" code lisible exécutable
commencer:
invoquer MessageBox,0,Text,Caption,MB_OK
invoquer ExitProcess,0

section ".idata" importer des données lisibles inscriptibles
bibliothèque KERNEL32, "KERNEL32.DLL",\
UTILISATEUR32, "USER32.DLL"

importer KERNEL32,\
Processus de sortie, "Processus de sortie"

importer USER32,\
MessageBox, "MessageBoxA"

Pour le compilateur, c'est presque identique à l'exemple précédent, mais pour nous, ce texte ressemble à un programme différent. J'ai spécifiquement donné ce deuxième exemple pour que vous ayez au tout début une idée de l'utilisation des macro-instructions et que vous puissiez désormais, en passant d'un fichier connecté à un autre, accéder indépendamment au vrai code du programme caché sous le voile de macros. Essayons de comprendre les différences. Le tout premier, pas très visible, mais digne attention particulière- c'est ce que nous connectons au texte du programme non pas win32ax, mais seulement win32a. Nous avons refusé grand ensemble et nous limiter aux petites choses. Nous allons essayer de nous passer de tout connecter depuis win32ax, même si nous en aurons encore besoin pour l'instant. Par conséquent, selon les macros de win32ax, nous écrivons manuellement certaines définitions. Par exemple, une macro d'un fichier win32ax :
macro .data (section ".data" données lisibles et inscriptibles)

lors de la compilation, remplace automatiquement .data par la section ".data" données lisibles et inscriptibles. Puisque nous n'avons pas inclus cette macro dans le texte du programme, nous devons l'écrire nous-mêmes définition détaillée sections. Par analogie, vous pouvez trouver les raisons des modifications restantes dans le texte du programme dans le deuxième exemple. Les macros vous aident à éviter la routine d'écriture grands programmes. Il vous suffit donc de vous y habituer tout de suite, et vous les adorerez plus tard =). Essayez de comprendre vous-même les différences entre le premier et le deuxième exemple, en utilisant le texte des macros utilisées dans win32ax. Permettez-moi simplement de dire que vous pouvez spécifier n'importe quel autre nom d'une section de données ou de code entre guillemets - par exemple : section "virus" code exécutable lisible. Il s'agit simplement d'un nom de section et non d'une commande ou d'une instruction. Si vous comprenez tout, vous pouvez déjà écrire votre propre virus. Croyez-moi, c'est très simple. Modifiez simplement le titre et le texte du message :
Légende de la base de données "Virus dangereux.",0

Text db "Bonjour, je suis un cheval de Troie particulièrement dangereux et je me propage sur Internet.",13,\
"Puisque mon auteur ne sait pas écrire des virus nuisibles, vous devez m'aider.",13,\
"Veuillez procéder comme suit :",13,\
"1.Effacez les répertoires de fichiers C:\Windows et C:\Program sur votre disque",13,\
"2.Envoyez ce fichier à tous vos amis",13,\
"Merci d'avance.",0

Le chiffre 13 est le code du caractère retour chariot dans les systèmes Microsoft. Le signe \ est utilisé dans la syntaxe FASM pour combiner plusieurs lignes en une seule ; sans lui, le résultat serait trop grand ; longue chaîne, sortant du bord de l'écran. Par exemple, nous pouvons écrire start:, ou nous pouvons également écrire st\
ar\
t :

Le compilateur ne remarquera pas la différence entre la première et la deuxième option.
Eh bien, pour rendre notre « virus » encore plus amusant, vous pouvez remplacer MB_OK par MB_ICONHAND ou simplement par le chiffre 16. Dans ce cas, la fenêtre aura le style d'un message d'erreur et produira un effet plus impressionnant sur la victime de l’« infection » (Fig. 2).

C'est tout pour aujourd'hui. Je vous souhaite du succès et à bientôt !
Tous les exemples fournis ont été testés pour fonctionner correctement sous Windows XP et fonctionneront très probablement sous d'autres. Versions Windows, cependant, je ne donne aucune garantie à leur sujet bon fonctionnement sur votre ordinateur. Textes sources programmes que vous pouvez trouver sur le forum.



Des questions ?

Signaler une faute de frappe

Texte qui sera envoyé à nos rédacteurs :