--------------------------------------------------------------- [003] Winasm, the beginning [_rix] --------------------------------------------------------------- Hello ! Voilà, je vais vous parler un petit peu de la programmation assembleur sous Windows... A QUOI CA SERT ? ================ Tout d'abord, avant de commencer, vous allez me dire: - a quoi ca sert ? - y a tellement de bons languages sous windows (visual c++,visual basic, delphi,...) - déjà l'assembleur sous dos, c'est vachement compliqué, alors l'assembleur windows ? Et là je vous répond: - l'assembleur windows est selon moi plus facile que l'assembleur sous dos (jvai vous montrer ;) - l'assembleur windows permet d'accéder de facon très puissante à toute une série de fonctions particulières. Ca va nous permettre de programmer directemment le système (programmer des drivers par exemple), de faire des trojans, de programmer des dlls appelables par un autre language... - l'assembleur windows crée du code ultra-ultra compact ! On peut par exemple avoir un programme .exe de 10K ! Oui, pour un programme windows ! On croit rêver :) - l'assembleur windows permet de créer des programmes ultra rapides ! - l'assembleur windows peut servir à patcher ou à cracker des programmes ou des jeux windows. - ...pas convaincus? SKIL FAUT POUR PROGRAMMER EN ASSEMBLEUR WINDOWS =============================================== Qu'est-ce qu'il faut ? Ben là, y a pas mal de choses à aller chercher avant de s'y mettre, hehe. Tout d'abord, il faut aller chercher les trucs suivants: *** MASM32 : macro-assembleur de microsoft 32bits -> les librairies de déclaration des APIS : pour pouvoir faire appel à toutes les APIs (fonctions) windows -> L2INC : pour créer les fichiers d'include (déclarations) de ces librairies -> WINDOWS.INC : un fichier de définitions de pleins de constantes WINDOWS -> et puis éventuellement (c'est quand même vachement conseillé ;) WIN32.HLP (fichier de référence de toutes les APIs windows) -> OPCODES.HLP (fichier de toutes les instructions asm 16bits et 32bits) -> MASM32.HLP (fichier de toutes les nouvelles macros et instructions définies dans MASM32) -> Ceci est facilement trouvable sur le net. INSTALLATION ============ Maintenant que vous avez été chargé tout ces beaux zips, va s'agir de les installer convenablement :)))) D'abord, tu vas créer un beau répertoire MASM, par exemple C:\MASM32 :))) Là dedans, tu dézip le fichier zip de masm32. Il va créer toute une série de répertoires, comme par exemple C:\MASM32\BIN (le compilateur, etc...), C:\MASM32\LIB, C:\MASM32\INCLUDE ,etc.. Une fois que c'est fait, tu vas dans le répertoire C:\MASM32\LIB, et tu dézip le fichier zip des librairies. Ca va créer tout pleins de fichiers .LIB dans ce répertoire. Après ca, tu copies le pti programme L2INC.EXE dans le répertoire C:\MASM32\BIN, puis tu vas dans le répertoire C:\MASM32\INCLUDE et tu lances ceci: C:\MASM32\BIN\L2INC /M C:\MASM32\LIB\*.LIB Ca va créer toute une série de fichiers .inc dans le répertoire C:\MASM32\INCLUDE. Puis enfin, tu peux copier le fichier WINDOWS.INC dans le répertoire C:\MASM32\INCLUDE aussi. LES BASES ========= Qu'est-ce que l'assembleur windows a de différent par rapport a l'assembleur dos ? Ben déjà, il est en 32bits, alors que l'assembleur DOS est en 16bits ! Ca veut dire quoi ? Ben ca veut dire en gros que tout les nombres avec lequels ont va chipoter vont plus être limité de 0 à 65535, mais de 0 à 65535*65535 (jmen fout du résultat exact du calcul hehe, mais je sais que ca fait beaucoup ;) Bon vous allé me dire "nous on veut pas faire de calcul" ! Ok ok ! hehe :) Mais C'est pas parce que vous voulez pas en faire que votre chti processeur en fait pas quand il exécute vos programmes ;) Par exemple, si vous connaissez déjà un ti peu l'assembleur sous DOS, vous savez qu'il faut souvent chipoter avec les "segments". Ben sous Windows, c'est terminé. En fait c'est pas vraiment terminé, mais c'est le système qui le fait pour nous automatiquement ;) Les APIs... Qu'est-ske c'est encore que ce mot tordu ? :) Ben les APIs, c'est toutes les fonctions que Microsoft a codé dans Windows, pour faciliter la vie aux programmeurs. Ca fait quoi ? Ben quasi tout ske vous voyez quand vous lancer des programmes: l'affichage des fenêtres, la saisie de données, l'affichage de belles images et de belles couleurs, mais aussi l'ouverture de connections vers Internet, l'écriture sur votre disque dur, jouer des fichiers sur votre carte sons, sortir un beau listing sur votre imprimante, ... :)) Et c'est ptêt pour ca que l'assembleur Windows est plus facile que l'assembleur Dos. Parce que justement y a des kgs de fonctions toutes prêtes à l'emploi, et le programme en assembleur n'a pas tellement à faire de plus que de les appeler dans le bon ordre :) Pour savoir quelles APIs utiliser, ben il faut souvent aller zieuter dans le fichier WIN32.HLP que je vous ai conseillé ! Alors maintenant on va parler un ti peux plus en détail de la manière de programmer en assembleur windows. Tout d'abord, il faut retenir ceci: L'assembleur (Windows ou Dos), travaille avec les instructions et les registres du processeur. C'est quoi un registre ? C'est une petite case dans le processeur, ou on peut mettre une valeur (une variable "physique" quoi :))) En assembleur windows, on va principalement utiliser les registres suivants: EAX,EBX,ECX,EDX,ESI,EDI (c'est en fait les principaux registres 16bits du DOS (AX,BX,CX,DX,SI,DI) mais avec un E devant). Si vous connaissez déjà un petit peur l'assembleur DOS, vous savez que les registres ressemblent à ceci: Par exemple, le registre AX(16bits), qui se compose de 2 parties (AH(8bits), et AL(8bits)) <------- AX ------> +--------+--------+ | AH | AL | +--------+--------+ Ben en assembleur 32bits (Windows), il y a encore une 3ème partie, et ca donne ceci: <--------------- EAX ---------------> <------- AX ------> +-----------------+--------+--------+ | | AH | AL | +-----------------+--------+--------+ Chacun de ces registres a une fonction particulière, pour lequel il est "optimisé": EAX sert comme valeur de retour des APIs, et comme registre pour les multiplications EBX sert à recevoir des adresses de variables ECX sert le plus souvent de compteur EDX sert à un peut tout le reste ;) ESI sert à recevoir des adresses sources (d'ou on va aller chercher des données) EDI sert à recevoir des adresses cibles (ou on veut copier, déplacer, des données) Tout ces registres peuvent être utilisé dans les instructions assembleur, qui sont décrites en détails dans le fichier OPCODES.HLP. Maintenant parlons un petit peu de MASM32. Ben ouaip. C'est un programme de Microsoft, et pour une fois il est rudement bien fait ;) MASM32 permet en effet de faciliter la programmation, grâce à toutes une série de macro-commandes Par exemple, MASM32 permet maintenant d'utiliser des instructions comme ceci .if EAX=0 ;exécute les instructions si EAX=0 .else ;exécute les instructions si EAX<>0 .endif C'est quand même vachement proche d'un language comme le Basic ou le C hein ;))) Pour le détail de toutes ces nouvelles instructions, je vous conseille de looker un ti peu le fichier MASM32.HLP ;) 1ER PROGRAMME ============= On va maintenant faire un beau pti programme tout con, pour afficher une petite boîte de dialogue à l'écran hehe :) Voici l'explication de toutes les commandes, puis le listing par après:) .386 ;à mettre chaque fois, vous connaissez qqun qui a un 286 ET windows 95/98 dessus ? :) ;Ca permet au compilateur de savoir qu'il peut utiliser les instructions des 386; ;C'est principalement parce qu'on utilise les registres 32bits. .model flat, stdcall ;"flat", c'est une manière d'organiser la mémoire pour ne plus qu'il y aie de segments, et juste ;des adresses mémoire 32bits. ;"stdcall", c'est pour annoncer au compilateur la manière dont on doit passer les paramètres à ;des fonctions: sous windows, c'est toujours cette valeur, car les APIs utilisent ce format. includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\user32.lib ;on déclare dans includelib toutes les librairies qui contiennent les APIs windows qu'on va ;utiliser. Kernel y en a besoin chaque fois (fonctions de bases dans tout les programmes Windows) ;et User, c'est toutes les fonctions qui concernet les APIs d'interfaces (affichage de fenêtres, ;boites de dialogues, etc...). Le compilateur va utiliser ces fichiers .lib pour créer notre ;programme assembleur. include c:\masm32\include\windows.inc include c:\masm32\include\kernel32.inc include c:\masm32\include\user32.inc ;on déclare ici tout les fichiers d'ou on va aller chercher les syntaxes d'appels au APIs. ;Ce n'est pas nécessaire, mais c'est vachement plus pratique et ca ne coute rien. ;Si tu vas voir dans le répertoire c:\masm32\include, et que tu édites un des fichiers, tu verras ;la syntaxe de chaque API avec ses paramètres. .const NULL equ 0 ;la section .const permet de définir des constantes. ;par exemple, ici, MASM32 saura que partout dans le programme ou il trouvera le mot "NULL", il ;devra le remplacer par le caractère 0. .data TITRE db "Titre de ma fenêtre",0 H HINSTANCE ? COMMANDLINE LPSTR ? ;la section .data permet de définir les variables globales (connues partout dans le programme) ;Par exemple, je définit TITRE comme étant une suite la bytes (caractères) "Titre de ma fenêtre" ;ATTENTION, il faut toujours mettre un ",0" à la fin des chaînes de caractères, car c'est le ;caractère 0 qui indique la fin de la chaîne aux APIS, à Windows, etc... ;ensuite, je définit HI comme étant une variable de type HINSTANCE (on va s'en servir plus loin) ;et dont je ne connait pas le contenu ("?"), et je définit COMMANDLINE comme étant un pointeur ;sur une chaîne de caractères (on va aussi s'en servir plu loin ;) .code ;maintenant voilà les instructions proprement-dites du programme Main: ;Main est une étiquette, et ici c'est l'endroit ou notre programme va commencer. ;A la fin de notre programme, il faut qu'on ajoute une commande "end Main", qui indique la fin ;de notre programme au compilateur. invoke GetModuleHandle,NULL ;Voici ton premier appel à une API Windows ! hehe :) ;Donc, pour appeler une API, on met une commande invoke, puis on met derrière le nom de l'API, ;et tout ses paramètres avec une ",". Notre programme va donc appeler l'API GetModuleHandle, que ;Microsoft conseille toujours d'appeler au début d'un programme Windows par compatibilité avec ;les anciens programmes Windows 16bits (Windows 3.1 ;) ;Cette API, comme toutes les APIs windows, renvoie une valeur dans le registre EAX (on stape de ;de la valeur c'est juste par compatibilité ;) mov H,eax ;Si on regarde dans la documentation des APIs Windows, on voit que l'API GetModuleHandle renvoie ;une valeur de type HMODULE. Microsoft Windows a maintenant défini un type HINSTANCE équivalent, ; c'est donc pour ca que j'ai déclaré la variable H de type HINSTANCE ;) ;Donc ici, je prend le contenu du registre eax, et je l'envoie dans ma variable H (c'est pas ;nécessaire, c'est juste pour montrer comment ca fonctionne en 32bits ;) ;Donc l'instruction mov recoit toujours 2 paramètres, le 1er c'est la destination, le 2ème c'est ;la source de la copie. Il faut souvent employer cette instruction après une API, car il nous ;faut souvent stocker le résultat de l'API quelque part (dans une variable) hehe ;))) invoke GetCommandLine mov COMMANDLINE,eax ;Ici, j'appelle maintenant l'API GetCommandLine, qui me renvoie l'adresse ou se trouve la ligne ;de commande de mon beau pti programme. Sous windows, toutes les adresses sont d'office 32bits. ;Puis je sauve cette adresse (retournée par l'API dans EAX) dans la variable COMMANDLINE, qui a ;été définie du type LPTSTR puisque c'est mentionné comme ca dans WIN32.HLP ! :)))) invoke MessageBox,NULL,COMMANDLINE,addr TITRE,MB_OK ;Maintenant, j'appelle l'API MessageBox, qui ouvre une belle petite fenêtre, avec un titre, et ;belle phrase dedans, et un beau bouton "OK" :)))) ;Le paramètre NULL est ici utilisé, parce que dans WIN32.HLP, on mentionne que le 1er paramètre ;doit être NULL s'il n'y a pas de fenêtre dans laquelle doit apparaître notre boite de dialogue, ;ce qui est le cas ici puisqu'on a pas créé de fenêtre ! ;Ensuite l'API doit recevoir une adresse d'une chaîne qui contient la phrase à afficher. ;Nous on va afficher ici la ligne de commande de notre programme, donc on donne comme paramètre ;la variable COMMANDLINE. ;Puis le paramètre suivant doit être l'adresse d'une chaîne qui contient le titre de la boîte de ;dialogue. ;ATTENTION ! L'API nécessite une adresse. Or nous avons définit une variable TITRE. Pour fournir ;son adresse, on doit mettre l'identificateur "addr" devant. ;Enfin, le dernier paramètre est une constante qui indique le type du bouton de la boite de ;dialogue. Ici on veut seulement un bouton "OK", donc on utilise la constante MB_OK définie dans ;le fichier WINDOWS.INC. invoke ExitProcess,NULL ;Enfin, pour terminer notre programme, on doit TOUJOURS appelé cette fonction (un peu comme sous ;DOS, on doit appeler une fonction de l'interruption 21h). On doit lui donner comme paramètre un ;code de retour, ici on donne un code de retour = à 0. end Main ; indique au compilateur qu'il n'y a plus rien après ! ;ATTENTION: même s'il y a des instructions après, le compilateur ne les lira pas ! Voici le listing du programme sans les commentaires: .386 .model flat, stdcall includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\user32.lib include c:\masm32\include\windows.inc include c:\masm32\include\kernel32.inc include c:\masm32\include\user32.inc .const NULL equ 0 .data TITRE db "Titre de ma fenêtre",0 H HINSTANCE ? COMMANDLINE LPSTR ? .code Main: invoke GetModuleHandle,NULL mov H,eax invoke GetCommandLine mov COMMANDLINE,eax invoke MessageBox,NULL,COMMANDLINE,addr TITRE,MB_OK invoke ExitProcess,NULL end Main Pour compiler ce petit programme et créer l'exécutable, il ne nous reste plus qu'a lancer les commandes suivantes: C:\MASM32\BIN\ML /c /coff /Cp exemple1.asm (pour assembler notre programme et créer un .obj) C:\MASM32\BIN\LINK /SUBSYSTEM:WINDOWS exemple1.obj (pour créer l'exécutable exemple1.exe) Regardons tout d'abord la taille de notre exécutable: 2560 octets ! Qu'est-ce que je vous disais que c'était compact ;) Vous pouvez toujours essayer de créer le même programme avec Visual C++ en 2,5K ;) Nous pouvons lancer ce petit programme, par exemple en tapent 'EXEMPLE1 paramètre' Et tu devrais avoir une petite boite de dialogue qui s'ouvre, avec comme titre 'Titre de ma fenêtre', et comme message '"répertoire\exemple1.exe" paramètre', et un petit bouton OK. Voici maintenant quelques conseils, pour programmer en assembleur. L'assembleur permet facilement de créer de petits programmes, par contre, si vous voulez créer un super éditeur ou un client IRC, je ne vous conseille pas de le faire en assembleur ;) En fait, tout ce qui concerne l'interface est assez difficile à réaliser en assembleur. C'est beaucoup plus facile de réaliser cela avec des clicks de souris dans Delphi,Visual Basic ou Visual C++. Par contre, si tu désires coder un petit programme ne nécessitant pas beaucoup d'interface, mais faisant appel au fonctions systèmes de Windows, il devient très avantageux d'utiliserl'assembleur Windows. Je te conseille aussi de faire un petit fichier .bat permettant d'assembler et de compiler en même temps, car ca permet de gagner pas mal de temps et de faciliter la mise au point. Pour debugger (trouver les erreurs dans le programme), je te conseille de charger W32DASM, un excellent desassembleur-debugger qui permet tout ou kasi ;) Quelles APIs utiliser ? Il faut d'abord savoir que Windows contient une infinité de fonctions ! :)))) Le meilleur moyen pour se renseigner est d'avoir le fichier WIN32.HLP, qui fournit les paramètres, les valeurs de retour, la syntaxe, et les explications pour toutes les APIs documentées de Windows, et ca fait déjà pas mal :) Par exemple, windows contient des APIs pour gérer des chaines (lstrcpy, lstrcmp,...), donc pas besoin de reprogrammer ca en assembleur, des APIs pour gérer des sockets (programmation Winsock, Internet,...) des APIs systèmes (pour lancer d'autres exécutables, pour créer des Services, pour accéder aux données d'autres programmes, accéder au fichiers,...), des APIs GUI (pour créer des fenêtres, des boîtes de dialogues,etc...) Pour ceux qui connaissent déjà un peu d'assembleur sous DOS, il faut savoir que toutes les instructions assembleur 16bits, fonctionnent parfaitement, mais que vous pouvez aussi utiliser toutes ces instructions avec les registres 32bits, ce qui fortement conseillé. De plus, toutes les instructions faisant appel aux segments sont quasiment inutiles sous Windows. Par exemple, la séquence suivante sous DOS: mov cx,65000 mov ds,segment SOURCE mov es,segment CIBLE mov si,offset SOURCE mov di,offset CIBLE rep movsb ,qui copie 65000 octets de l'adresse SOURCE vers l'adresse CIBLE, peut être remplacée par la séquence suivante sous WINDOWS: mov ecx,65000*65000 ;taille de 32 bits ;)))) mov esi,offset SOURCE mov edi,offset CIBLE rep movsb qui copie quelques millions d'octet de l'adresse SOURCE vers l'adresse CIBLE, sans chipoter avec les registres de segments ;) FONCTIONNEMENT INTERNE DE WINDOWS ================================= Nous avons maintenant créer un petit programme, mais c'est vrai qu'il ne fait pas grand chose d'utile ;)))) Je vais maintenant vous expliquer les grands principes de bases de la programmation sous Windows. En fait, Windows et DOS sont très différents, parce que sous DOS, un programme tourne seul en mémoire, et il n'a pas besoin de se préoccuper de l'exécution d'autres programmes éventuels en même temps. Donc le programme fait ce qu'il veut, quand il veut, ou il veut ! :))) Sous Windows, les choses sont moins simples. Un programme Windows recoit lors de son exécution une zone mémoire dans lequel il doit travailler , et doit respecter un certain nombre de règles pour avoir une exécution "convenable". Tout d'abord, Windows est un interface graphique, et donc, le système gère beaucoup de choses au niveau de l'interface. Le programme Windows habituel se contente de collecter les informations renvoyées par des boîtes de dialogues ou des fenêtres, et de les traiter.Pou cela, Windows utilise un principe de "messages". Un programme Windows bien concu doit donc s'initialiser, déclarer ses variables etc, puis le plus souvent créer une fenêtre, et enfin rendre la main à Windows. C'est ensuite Windows qui gère cette fenêtre, et qui exécute une procédure spéciale de gestion d'évènement (messages), associée à la fenêtre du programme, selon que l'utilisateur a appuyé sur une touche, déplacé la souris, ou encore fermé la fenêtre. Le programme récupère alors la main, exécute au plus vite l'action nécessaire, puis rend de nouveau la main à Windows. Ensuite, windows est un système multitâche,ce qui signifie que non seulement plusieurs programmes peuvent tourner simultanément sur la machine (un programme est appelé Process), mais que un programme lui même peut faire plusieurs actions simultanément (une tâche, appelée Thread). Voilà, j'espère que cette toute petite introduction vous a donné l'eau à la bouche, et que vous allez tous vous mettre à programmer de bons chtis programmes sous Windows en assembleur.Je vous conseille une dernière adresse, http://win32asm.cjb.net, ou j'ai moi-même été cherché la plupart des informations qui m'ont apprit à programmer sous Windows !!! C'est en anglais, mais c'est très très bien écrit et vous avez toute une série de tutorials sur les concepts fondamentaux et puissants de la programmation Windows. Bye bye, et a+ ptêt pour un autre programme un peu plus consistant ;) Rix--Agressor-Shogun