LA RESSURECTION DE LA PROGRAMMATION EN ASSEMBLEUR - PART 1
(voir le cours précédent : winasm_0.htm)
-
-----=========-----
------ -====================
-------- -===Cours de _Masta_ sur l'asm Win95 part 1===----------
------ -====================
 
 
--==INTR0==--
 
-[Hi!]-
Après la première partie j'ai reçu un certain nombre de mails disant que c'était une bonne idée de faire un renouveau de "l'art ancien" du langage assembleur basé sur windows. Nous savont tous que le DOS est près mourir (peu nombreux (si il y'en a) parmis nous sont heureux de cela), mais malencontreusement nous ne pourrons rien changer de cet état de fait.
 
--==QU'EST-CE QUI EST REQUIS?==--
 

                1. Un éditeur de texte
                2. TASM 5.0, complet avec libs, etc.
                3. Une référence API-Win32 (Win32.HLP)

Je suppose que avez une connaissance fondamentale de l'assembleur, sinon la plupart des
outils sont facile à obtenir.

--==OBJECTIF DE CE PROJET==--

Nous voulons coder un patcher generique aussi connu comme un "Search-and-Destroy Patcher".
Beaucoup de gens ne savent pas quoi attendre d'un  Patcher Générique, je vais l'expliquer brièvement. C'est un patcher, qui est non seulement capable de patcher une certaine version d'un programme, mais les versions futures aussi, s'ils sont presque identiques. Cela se fait en cherchant un certain  modèle d'octet (et en écrivant un nouveau) et non en patchant une adresse précise, ce qui fait cette sorte de patcher plus universel à employer.
Comme généralement le schéma de protection n'a pas été changé par le coder, les octets de cette routine peuvent avoir une autre adresse dans la nouvelle (ou ancienne) version, mais les octets restent les mêmes.
Voila l'astuce =).

--==LET'S GO!==--
 

OK, premierement nous penserons à la structure principale de notre programme, puis nous
penseront aux fonctions que nous emploierons, et "last but not least"  nous écrirons le programme.

1. Intro                - Petite intro, présentée dans un MessageBox
2. Open File            - Assigner un handle pour le fichier.
                          Si fichier n'existe pas -> MessageBox
3. Get Filesize         - Obtenir la taille du fichier
4. Allocate Memory      - Allouer une zone mémoire égale a la taille du fichier.
                          Si erreur -> MessageBox
5. Read File            - copier le fichier complet dans la mémoire allouée
6. Search Byte          - Détermination de l'adresse (NdT : offset) du modèle d'octets.
                          Si erreurs -> MessageBox
7. Set File Pointer to offset
8. Overwrite File       - Patch du fichier. SUCCES -> MessageBox
   with new bytes
9. Close File           - Nettoyage!
   Deallocate Mem
   Quit

--==FONCTIONS APIs== --
 

- Tous les messages seront présentés par un messagebox, ex: 'MessageBoxA'.

- Pour ouvrir le fichier nous emploierons la fonction 'CreateFileA', qui est plus complexe que 'OpenFile', mais qui est plus flexible à employer.

- Pour fermer le fichier nous emploierons 'CloseHandle'.

- La taille du fichier (NdT : filesize) sera obtenue par 'GetFileSize'

- Nous affecterons la mémoire à l'aide de 'GlobalAlloc'; nous la libèrerons avec 'GlobalFre'

- Logiquement nous lirons le fichier avec 'ReadFile' et l'écrirons avec 'WriteFile'

- Le pointeur de fichier (NdT : Filepointer) peut être établi avec 'SetFilePointer'

- Pour quitter nous emploierons 'ExitProcess'
 

--==LA RECHERCHE DES OCTETS==--
 

C'est le coeur de notre patcher. A l'aide de cette petite routine, un modèle d'octet est cherché dans le fichier cible, qui sera changé ultérieurement. Je vous l'expliquerai brièvement, parce que la plus grande partie que vous pourrez obtenir est dans le code.
OK, en premier lieu nous chargeons la taille du fichier (pour l'allocation mémoire) en ECX afin d'établir une valeur pour la commande REPNZ; le premier octet du modèle de la recherche est écrit en AL et ESI est placè à l'adresse des valeurs originales.
Avec 'REPNZ SCASB' la valeur de AL est comparé avec la valeur de l'adresse mémoire pointée par EDI (EDI est incrementé de 1à chaque fois). La commande 'REPNZ' répète le 'SCASB' jusqu'a ce que ECX=0 ou que les valeurs comparées soient égales (FLZ=1).
Si les valeurs sont égales, ECX est chargé avec la longueur du patch, EDI est décrementé de 1, car 'SCASB' compte déjà un octet de plus.
la commande 'REPZ CMPSB' répète 'CMPSB' (comparaison de l'adresse [ESI] avec [EDI]) jusqu'a ce que ECX=0 ou que la valeur diffère.
 

--==LE PATCHEUR EN LUI-MEME==--
 

Maintenant, jetons rapidement un coup d'oeil sur la routine de patch en elle-même.
Premièrement, l'offset est calculé en incrementant ECX (le compteur) par 1, et en soustrayant cette valeur à la taille du fichier (NdT : Filesize) :

- - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - -
(FILESIZE) - (OCTETS JUSQU'A LA FIN DU FICHIER) = OFFSET REEL
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Cette valeur est mise sur la pile, ainsi que l'handle du fichier pour ultérieurement
APPELER la fonction 'SetFilePointer' afin d'établir le pointeur du fichier (NdT : Filepointer) à notre décalage.
Ensuite, le buffer des octets à écrire (bwrite), la longueur du patch, le décalage des nouveaux octets et l'handle du fichier sont POUSSES sur la pile et la fonction API 'WriteFile' est APPELEE.

--==LE SOURCE==--
 

Peut-être un peu complexe, mais je pense encore facilement comprendre =) ...
(NdT : la traduction du code n'a pas été faite dans un soucis de compréhension, honnêtement le code est très facile à comprendre !!!)

;------------------- ----------- -===START===-------- -------------------
                ; établir un couple d'options pour l'assembleur
.386P
Locals
jmps

.Model Flat ,StdCall
mb_ok                     equ 0
hWnd                      equ 0
FILE_ATTRIBUTE_NORMAL     equ 080h
OPEN_EXISTING             equ 3
GENERIC_READ              equ 80000000h
GENERIC_WRITE             equ 40000000h
 

; --==declaration de toutes les fonctions à employer==--

extrn     ExitProcess      : PROC ;la procédure pour terminer le programme
extrn     MessageBoxA      : PROC ;la procédure pour afficher un MessageBox
extrn     CreateFileA      : PROC ;   ...       pour ouvrir un fichier
extrn     ReadFile         : PROC ;lire un bloc dans un fichier
extrn     WriteFile        : PROC ;écrire un bloc dans un fichier
extrn     CloseHandle      : PROC ;fermer le fichier
extrn     GetFileSize      : PROC ;obtenir la taille du fichier
extrn     GlobalAlloc      : PROC ;allouer la mémoire
extrn     GlobalFree       : PROC ;libérer la mémoire
extrn     SetFilePointer   : PROC ;déplacer le pointeur de fichier

; --==Ici commence nos données==--

.Data

caption  db "_masta_'s essayer sur Win32-ASM-Coding, partie 1,0"
 
                                   ;captionstring, 0-terminated

text     db "Hi, nice to CU again",13,10 
         db "This tut will describe you how to make",13,10
         db "Win32-ASM Search and Destroy patchers",0
 

                                   ;introtext, 0-terminated

err_cap  db "ERREUR",0              ;caption for errormessage

openerr  db "Erreur d'ouverture de fichier",0       ;errortext opening file
memerr   db "Erreur d'allocation mémoire",0         ;errortext alloc. memory
byterr   db "Le fichier est présent, mais pas les octets originaux",0  ;error while bytesearch

readycap db "Ready",0                           ;caption for 'done'

readytxt db "Ok, le fichier est patché",0       ;text for 'done'

file     db "make.old",0        ;what file we want to patch?
org_val  db "Xmas'97"           ;original values
new_val  db "_masta_"           ;new values
len      equ $-new_val          ;how many values (length)
                                ;org_val and new_val must be equal

fhandle  dd  ?                  ;variable for the filehandle
fsize    dd  ?                  ;variable for the filesize
memptr   dd  ?                  ;pointer to allocated memory
bread    dd  ?                  ;number of read bytes
bwrite   dd  ?                  ;number of written bytes

;--==et ici nous commencons notre code==--

.Code
Main:
        push mb_ok              ;PUSH value for 'uType'
        push offset caption     ;PUSH pointer to caption
        push offset text        ;PUSH pointer to Text
        push hWnd               ;PUSH Masterhandle
        call MessageBoxA        ;CALL MessageBoxA

        push 0                                  ;for Win95 always 0
        push FILE_ATTRIBUTE_NORMAL              ;standard Fileattributes
        push OPEN_EXISTING                      ;open existing file
        push 0                                  ;no Security-attributes
        push 0                                  ;disable Share-Mode
        push GENERIC_READ + GENERIC_WRITE       ;read- and writeaccess
        push offset file                        ;offset of the filename
        Call CreateFileA                        ;open file
        mov  fhandle,eax                        ;save filehandle
        cmp  eax,0FFFFFFFFh                     ;if eax=FFFFFFFF then
                                                 error
        jnz  file_is_here

        push mb_ok
        push offset err_cap
        push offset openerr
        push hWnd
        call MessageBoxA        ; showerrormessage
        jmp  end_               ; jump to end

file_is_here:                   ;file is there, so go on

        push 0                  ;can be 0, if the filesize is less then 4,3 GB :)
        push fhandle            ;PUSH filehandle
        Call GetFileSize        ;get the filesize
        mov  fsize,eax          ;save the filesize

        push fsize              ;PUSH filesize=size of the buffer
        push 0                  ;0=GMEM_FIXED -> fixed memory-area
        Call GlobalAlloc        ;allocate as much as memory as filesize
        mov  memptr,eax         ;save pointer to memory-area

        cmp  eax,0              ;if eax=0, then there were errors
        jnz  mem_ok

        push mb_ok
        push offset err_cap
        push offset memerr
        push hWnd
        call MessageBoxA
        jmp  end_kill_handle    ;end program, close file b4

mem_ok:                         ;memory is allocated -> next step

        push 0                  ;set to 0 in most cases
        push offset bread       ;pointer to number of read bytes
        push fsize              ;read how many bytes?, fsize=whole file
        push memptr             ;save where? ->allocated memory
        push fhandle            ;filehandle
        Call ReadFile           ;read file!

read_ok:

        mov  edi,memptr         ;set EDI to memory-area
        mov  ecx,fsize          ;set ECX (for repnz) to filesize
        mov  esi,offset org_val ;set ESI to the string to find
        mov  al, byte ptr [esi] ;load AL with the first byte

loop_:
        repnz scasb             ;repeat until ECX=0 or AL equals
                                ;the value of the byte [EDI], EDI is
                                ;incremented by 1 every run
        cmp  ecx,0              ;If ECX=0, nothing is found
        jz   not_found
 

here_is_something:              ;found matching byte

        push ecx                ;save register
        push edi
        push esi
        dec  edi                ;EDI-1, cos REPNZ SCASB is one step too far
        mov  ecx,len            ;ECX=length of the patch
        repz cmpsb              ;repeat until the values in the memory of
                                ;[EDI] and [ESI] are different,
                                ;or ecx=0
        cmp  ecx,0              ;If ecx=0, then the org_val is in memory
        jz   patch              ;->jump to patcher

not_that:                       ;it is not yet here

        pop  esi                ;POP ESI
        pop  edi
        pop  ecx
        jmp  loop_              ;search next byte

patch:                          ;start of the patcher
        pop  esi                ;POP registers
        pop  edi
        pop  ecx
        dec  edi                ;EDI-1
        inc  ecx                ;ECX+1
        mov  eax,fsize
        sub  eax,ecx            ;compute Offset
        push 0                  ;offset from the beginning of the file
        push 0                  ;is 0, if file < 4,3GB
        push eax                ;offset
        push fhandle            ;filehandle
        call SetFilePointer     ;set FilePointer

        push 0                  ;normally 0
        push offset bwrite      ;how many bytes where written?
        push len                ;length of the bytes to write
        push offset new_val     ;offset to new values
        push fhandle            ;filehandle
        Call WriteFile          ;write block to file

        push mb_ok
        push offset readycap
        push offset readytxt
        push hwnd
        call MessageBoxA        ;OK, patch is done!

        jmp  end_kill_all       ;END! Cleanup!

not_found:

        push mb_ok
        push offset err_cap
        push offset byterr
        push hWnd
        call MessageBoxA        ;the bytes where not in the file

end_kill_all:

        push memptr             ;pointer to Memoryarea
        call GlobalFree         ;enable (free) memory

end_kill_handle:

        push fhandle            ;PUSH filehandle
        call CloseHandle        ;CloseHandle
 

end_:

        CALL    ExitProcess     ;Quit program
End Main                        ;end of code, JUMP-spot (main)

;------------------- ----==END OF SOURCE==----------- -----------------

OK, c'est fini !
Pour le compiler, lancer simplement 'MAKE.BAT'

--==UNE PETITE CHOSE==--
 
--==THE END==--

OK, je pense que cette fois nous avons fait quelque chose de réellement utile, pas seulement un MessageBox comme dans mon premier cours, mais un véritable outil-de-tous-les-jours d'un cracker.
Le source peut être employé librement et peut-être qu'il y a certaines choses que vous
voudriez optimiser, surtout concernant la routine de recherche (Hi Fongus ;)), mais
Je pense que pour un rôle d'apprentissage il est OK.

Voici un petit défi:

--> Vous pourriez chercher les 4 premiers octets du début
 

OK, j'espère que ma boîte aux lettres ( >masta_t@USA.NET ) explosera bientôt
(LES CRITIQUES SONT BIENVENUES) et que je vous verrai tout prochainement ... =)
Sur cette lancée, j'essaie d'établir un channel IRC sur le sujet ...

--> #win32asm

J'espère juste qu'il y a assez de gens intéressés à ce travail, qu'ils donnent leur connaissance à d'autres.

--==GREETINX==--
VucoeT (Translator and Designer(:])), scut (Idea is from your 
DSP), |caligo| (bad news about you :(), fravia+ (best on the 
web), +Aescalapius (nice Bytepatcher) not4you (we Ossis must 
stick together ;)), fungus (something to optimze), Quest, 
Silvio, TheDoctor, everyone on #LAC and #cracking4newbies 
and to every cracker around the world.
 
--==WISE WORDS==--
-------====================--          --====================--------
------======Nous étions tous des lamers avant de devenir l'ELITE======-------
-------====================--          --====================-------
-----==========-----
 


here follows tut.asm

 
                ; set a couple of options for the assembler
.386P
Locals
jumps

.Model Flat ,StdCall
mb_ok                   equ 0
hWnd                    equ 0
FILE_ATTRIBUTE_NORMAL   equ 080h
OPEN_EXISTING           equ 3
GENERIC_READ            equ 80000000h
GENERIC_WRITE           equ 40000000h


; --==declaration of all used API-functions==--

extrn     ExitProcess      : PROC ;procedure to end the program
extrn     MessageBoxA      : PROC ;procedure to show a MessageBox
extrn     CreateFileA      : PROC ;   " ...  to open a file
extrn     ReadFile         : PROC ;read a block of a file
extrn     WriteFile        : PROC ;write a block into a file 
extrn     CloseHandle      : PROC ;close file
extrn     GetFileSize      : PROC ;get the filesize
extrn     GlobalAlloc      : PROC ;allocate memory
extrn     GlobalFree       : PROC ;set (free) memory
extrn     SetFilePointer   : PROC ;set the filepointer

; --==here begins our Data==--

.Data

caption  db "_masta_'s essay on Win32-ASM-Coding, part 1",0 
 
                                   ;captionstring, 0-terminated

text     db "Hi, nice to CU again",13,10 
         db "This tut will describe you how to make",13,10
         db "Win32-ASM Search and Destroy patchers",0
 
                                   ;introtext, 0-terminated

err_cap  db "ERROR",0              ;caption for errormessage

openerr  db "Error on opening File",0       ;errortext opening file
memerr   db "Error on allocating memory",0  ;errortext alloc. memory
byterr   db "File is here, but i can't find the original bytes",0
 
                                            ;error while bytesearch

readycap db "Ready",0                           ;caption for 'done'

readytxt db "Ok, file is patched",0             ;text for 'done'

file     db "make.old",0        ;what file we want to patch?
org_val  db "Xmas'97"           ;original values
new_val  db "_masta_"           ;new values
len      equ $-new_val          ;how many values (length)
                                ;org_val and new_val must be equal

fhandle  dd  ?                  ;variable for the filehandle
fsize    dd  ?                  ;variable for the filesize
memptr   dd  ?                  ;pointer to allocated memory
bread    dd  ?                  ;number of read bytes
bwrite   dd  ?                  ;number of written bytes

;--==and here we start with our code==--

.Code
Main:
        push mb_ok              ;PUSH value for 'uType'
        push offset caption     ;PUSH pointer to caption 
        push offset text        ;PUSH pointer to Text
        push hWnd               ;PUSH Masterhandle
        call MessageBoxA        ;CALL MessageBoxA

        push 0                                  ;for Win95 always 0
        push FILE_ATTRIBUTE_NORMAL              ;standard Fileattributes
        push OPEN_EXISTING                      ;open existing file
        push 0                                  ;no Security-attributes
        push 0                                  ;disable Share-Mode
        push GENERIC_READ + GENERIC_WRITE       ;read- and writeaccess
        push offset file                        ;offset of the filename
        Call CreateFileA                        ;open file
        mov  fhandle,eax                        ;save filehandle
        cmp  eax,0FFFFFFFFh                     ;if eax=FFFFFFFF then 
                                                 error
        jnz  file_is_here

        push mb_ok 
        push offset err_cap 
        push offset openerr 
        push hWnd 
        call MessageBoxA        ; showerrormessage
        jmp  end_               ; jump to end

file_is_here:                   ;file is there, so go on

        push 0                  ;can be 0, if the filesize is less 
                                 then 4,3 GB :)
        push fhandle            ;PUSH filehandle
        Call GetFileSize        ;get the filesize
        mov  fsize,eax          ;save the filesize

        push fsize              ;PUSH filesize=size of the buffer
        push 0                  ;0=GMEM_FIXED -> fixed memory-area
        Call GlobalAlloc        ;allocate as much as memory as filesize 
        mov  memptr,eax         ;save pointer to memory-area

        cmp  eax,0              ;if eax=0, then there were errors
        jnz  mem_ok

        push mb_ok 
        push offset err_cap 
        push offset memerr 
        push hWnd 
        call MessageBoxA
        jmp  end_kill_handle    ;end program, close file b4

mem_ok:                         ;memory is allocated -> next step

        push 0                  ;set to 0 in most cases
        push offset bread       ;pointer to number of read bytes
        push fsize              ;read how many bytes?, 
                                 fsize=whole file
        push memptr             ;save where? ->allocated memory
        push fhandle            ;filehandle
        Call ReadFile           ;read file!

read_ok:

        mov  edi,memptr         ;set EDI to memory-area
        mov  ecx,fsize          ;set ECX (for repnz) to filesize
        mov  esi,offset org_val ;set ESI to the string to find
        mov  al, byte ptr [esi] ;load AL with the first byte

loop_:
        repnz scasb             ;repeat until ECX=0 or AL equals
                                ;the value of the byte [EDI], EDI is 
                                ;incremented by 1 every run
        cmp  ecx,0              ;If ECX=0, nothing is found
        jz   not_found
 

here_is_something:              ;found matching byte

        push ecx                ;save register
        push edi
        push esi
        dec  edi                ;EDI-1, cos REPNZ SCASB is one step too far
        mov  ecx,len            ;ECX=length of the patch
        repz cmpsb              ;repeat until the values in the memory of 
                                ;[EDI] and [ESI] are different,
                                ;or ecx=0
        cmp  ecx,0              ;If ecx=0, then the org_val is in memory
        jz   patch              ;->jump to patcher

not_that:                       ;it is not yet here

        pop  esi                ;POP ESI
        pop  edi
        pop  ecx
        jmp  loop_              ;search next byte

patch:                          ;start of the patcher
        pop  esi                ;POP registers
        pop  edi
        pop  ecx
        dec  edi                ;EDI-1
        inc  ecx                ;ECX+1
        mov  eax,fsize
        sub  eax,ecx            ;compute Offset
        push 0                  ;offset from the beginning of the file
        push 0                  ;is 0, if file < 4,3GB
        push eax                ;offset
        push fhandle            ;filehandle
        call SetFilePointer     ;set FilePointer

        push 0                  ;normally 0
        push offset bwrite      ;how many bytes where written?
        push len                ;length of the bytes to write
        push offset new_val     ;offset to new values
        push fhandle            ;filehandle
        Call WriteFile          ;write block to file

        push mb_ok 
        push offset readycap 
        push offset readytxt 
        push hwnd
        call MessageBoxA        ;OK, patch is done!

        jmp  end_kill_all       ;END! Cleanup!

not_found:

        push mb_ok 
        push offset err_cap 
        push offset byterr 
        push hWnd 
        call MessageBoxA        ;the bytes where not in the file

end_kill_all:

        push memptr             ;pointer to Memoryarea
        call GlobalFree         ;enable (free) memory

end_kill_handle:

        push fhandle            ;PUSH filehandle
        call CloseHandle        ;CloseHandle


end_:

        CALL    ExitProcess     ;Quit program
End Main                        ;end of code, JUMP-spot (main)


Traduit de l'anglais par CyberBobJr
CyberBobJr@yahoo.com