[ - Intro - Explications - L'outil expliqué - Liens - Notes de fin - ]
 
[ - Tutoriaux : Bases ~ Jnz / Jz ~ Jmp ~ La fonction d'enregistrement ~ Crack GifMovieGear 3.0 - ]

Ce premier cours porte sur le renversement de saut( s ) portant sur une condition.
Nous allons voir aussi plusieurs solutions.

Problème : J'ai un message Windows "Cours n°1 -1..." qui doit apparaitre mais un Nag-screen que je dois cliquer, se montre juste avant.
Emplacement : dans le package fourni
Outils : W32Dasm8.93, Hexworkshop 2.54

Pour plus d'informations sur l'utilisation de W32dasm, reportez-vous sur la section l'outil expliqué.
Dans ce premier tut, je vais expliquer montrer comment s'en servir.
J'ai prévu une section Plus d'informations pour des questions éventuelles que vous auriez.
 

Le Crack

Nous lançons le crackme, vous pourrez voir un boite de message windows, puis une autre après avoir cliqué sur Ok.
 

Nag-screen
Programme

Jusqu'ici, il vous est facile de deviner qu'il faut enlever le nag-screen pour avoir directement la deuxième boite de message.
Commençons notre travail en ouvrant notre programme sous W32Dasm ( bouton  ).
Une fois désassemblée, notre programme apparaît sous forme plus clair en assembleur.
Toutefois, si notre programme n'est pas lourd ( 2,50 Ko ), on va procéder comme s'il était plus gros.
Donc, recherchons les chaines de caractéres dont le programme se sert ( bouton  ). Les String Data References. ( ou SDR )
Plusieurs textes apparaîssent. Cliquons sur "VILAIN NAG-SCREEN", le titre de notre boite de message ( ou caption ).
Vous devez tomber sur le texte suivant :

//******************** Program Entry Point ********
:00401000 33C0                    xor eax, eax
:00401002 85C0                    test eax, eax
:00401004 7513                    jne 00401019 
:00401006 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"VILAIN NAG-SCREEN"
                                  |
:00401008 6804304000              push 00403004

* Possible StringData Ref from Data Obj ->"ENREGISTRE-MOI SINON JE ME FACHE "
                                        ->"!!!"
                                  |
:0040100D 6816304000              push 00403016
:00401012 6A00                    push 00000000

* Reference To: USER32.MessageBoxA, Ord:01BBh
                                  |
:00401014 E829000000              Call 00401042

Ceci représente une partie du code en assembleur de notre programme et on voit clairement notre nag-screen.
Toutes ces lignes de code ne nous intéressent pas puisqu'on ne veut pas qu'elles soient exécutées par notre programme.
Si nous remontons avant notre Nag-screen, on a la chose suivante :

//******************** Program Entry Point ********
:00401000 33C0                    xor eax, eax  <=== ici
:00401002 85C0                    test eax, eax  <=== ici
:00401004 7513                    jne 00401019  <=== ici
:00401006 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"VILAIN NAG-SCREEN"
                                  |
:00401008 6804304000              push 00403004

Donc si nous lisons les instructions pas-à-pas, on a :

xor eax, eax ( remet le registre de travail eax à 0 )
test eax, eax ( teste si eax = 0 )
jne 00401019 ( si non, on va à la ligne 00401019, si oui on ignore le saut et on continue pour tomber sur le Nag-screen )

Comme eax a été mis à 0 avant avec le "xor", on ne fera jamais ce saut.
Mais qu'est ce qu'il peut bien y avoir en 00401019 ?
Mettons notre curseur sur la ligne en 401004 et normalement, celle-ci devrait se mettre en vert et un bouton nouveau  devrait être apparu. Cliquons dessus.
Et hop, on a atteri sur notre ligne en 00401019.

* Reference To: USER32.MessageBoxA, Ord:01BBh
                                  |
:00401014 E829000000              Call 00401042

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401004(C)
|
:00401019 6A00                    push 00000000 <=== on atteri ici !

* Possible StringData Ref from Data Obj ->"Cour n"
                                  |
:0040101B 683B304000              push 0040303B

* Possible StringData Ref from Data Obj ->"Mon premier Crack : Le saut invers"
                                  |
:00401020 6854304000              push 00403054
:00401025 6A00                    push 00000000

* Reference To: USER32.MessageBoxA, Ord:01BBh
                                  |
:00401027 E816000000              Call 00401042

Voilà, voilà.
La phrase juste au-desus de 401019 nous montre qu'il y a un saut ( U ) nconditional ou ( C ) onditional qui nous envoit à cet endroit. L'information est précisée juste en dessous :  |:00401004(C) ( donc c'est en 401004 et c'est un saut ( C ) onditionnel. )
Et en 401004, c'est le saut que l'on vient de faire.
Pour le vérifiez, cliquons sur  qui nous permet de revenir à la ligne où se trouve le saut que l'on a fait.
Donc pour récapituler :

401000 eax = 0
401002 test si eax = 0
401004 si non, va en 401019
401008 début du passage des arguments de notre Nag-screen
....
....
401014 apparition du Nag-screen
401019 début du passage des arguments de notre programme "Cours n° 1 - 1 ...."

Vous avez une idée ?
Bien, en voici 4 !

1 - renverser la condition original "si test eax = 0" par "si test eax = 1".
2 - faire que le saut ne soit pas basé sur une condition.
3 - mettre eax = 1, comme ça notre saut se fait.
4 - détruire toutes instructions du Nag-screen !

La solution doit prévoir 2 choses :
- ne pas faire planter le programme. ( c'est assez logique )
- le crack doit pouvoir être fait sans écraser les instructions qui doivent être exécutées. ( ce qui rejoint la ligne d'avant. )
 

Renversement de condition

Le contraire de jne ( jump if not equal ) est je ( jump if equal ).

:00401000 33C0                    xor eax, eax
:00401002 85C0                    test eax, eax
:00401004 7513                    jne 00401019  ===> je 00401019 

On se retrouve avec si eax = 0, on saute vers le programme et on évite le Nag-screen : impeccable !

Mais comment changer cette instructions dans le programme ?
En fait, on change l'opcode de l'instruction.
Si nous nous reportons sur le fichier hlp que vous trouverez dans le package fourni avec ces tuts, nous voyons pour "jxx" ( dans l'index ) une série d'instructions de sauts et plus bas les mêmes, précédées par des chiffres en rouge.
Ce sont les opcodes correspondant aux instructions !
Donc pour jne ( Jump short if not equal ) = 75
et pour je ( Jump short if equal ) = 74
NB : vous remarquerez que l'opcode de jne = jnz et je = jz. C'est normal, ce sont des instructions portant sur les mêmes conditons.

Donc notre code devient :
( original )
:00401004 7513                    jne 00401019

( cracké )
:00401004 7413                    je 00401019

Le "13" correspond au déplacement à faire pour se retrouver sur la ligne où on va atterir. Donc on ne le change pas !

Mettons en oeuvre notre crack, faites un click droit sur le programme ( qui ne doit plus tourner ! ) et validons "Hex Edit"
Si ça ne marche pas, il faut lancer hexworkshop et l'ouvrir de là.
Maintenant que c'est fait, il reste à trouver où se trouve notre opcode à changer !
Hexworkshop et les autres éditeurs hexa vous permettent de faire une recherche sur une signature hexadécimale ; c'est-à-dire de trouver une succession de codes hexadécimaux...
Si nous reprennons, notre code, on a :

:00401000 33C0                    xor eax, eax
:00401002 85C0                    test eax, eax
:00401004 7513                    jne 00401019

En rouge, c'est la signature que nous voulons trouver : 33C085C07513

Donc sous hexworkshop, cliquons sur  ( search ) et une fenêtre apparait. Validons "Hex" dans "Values" et entrons notre signature 33C085C07513 puis "Find Next". Hexworkshop vous indique un endroit. Cliquons sur  ( search next ) afin de voir s'il n'y a pas d'autres signatures identiques.
NB : mon crackme est simple et contient peu de code, donc ici ça va, mais de manière générale, prévoyez une signature plus grande car sinon, vous risquez de tomber sur la même mais qui n'a rien à voir avec notre protection !
Changons le 75 par 74 . Sauvegardons et relançons. 

Ca marche, on n'obtient plus que la deuxième fenêtre !
 

Faire que le saut ne soit pas basé sur une condition.

Nous avons renversé la condition avant mais pourquoi ne pas faire un saut sans conditions : jmp
Je n'épilogue pas car cela reprend le même principe que juste avant.

:00401000 33C0                    xor eax, eax 
:00401002 85C0                    test eax, eax
:00401004 7513                    jne 00401019 ===> jmp 00401019

L'opcode de jmp est EB dans ce cas présent.
:00401004 EB13                    jmp 00401019
 

Mettre eax = 1 pour que le saut se fasse

Ce coup-ci, on ne touche pas au saut mais à l'argument de la condition : eax.
Il y a ici plus qu' un byte à changer mais deux.
Le problème est comment le changer.
On peut faire :

:00401000 33C0                    xor eax, eax + inc eax ( incrémente eax d'1 )
:00401002 85C0                    test eax, eax
:00401004 7513                    jne 00401019

inc eax est équivalent à eax = eax + 1, donc ici eax = 0 + 1 = 1, son opcode est 40.
Mais on ne peut pas l'injecter dans le code car sinon on écrase 33C0 ( xor eax,eax ) ou 85C0 ( test eax,eax ).
Donc c'est pas bon.
De même, mov eax, 00000001 car son opcode est B801000000.
Voici l' "astuce".

:00401000 33C0                    xor eax, eax ===> mov al, 01
:00401002 85C0                    test eax, eax
:00401004 7513                    jne 00401019

test eax, eax marche sur 0 et tout chiffre différent de 0.
Donc si eax = 0001 ou eax = 5201 ou autre chose de tel sorte que eax différent de 0, alors le jnz sera fait !

eax = 0000 ( Nag-screen )
eax = 0001 à FFFF ( pas de Nag-screen )

L'opcode pour mov al, 01 est B001.

:00401000 33C0                 xor eax, eax
devient
:00401000 B001                    mov al, 01
 

Détruire toutes instructions du Nag-screen

Voici une solution, introduisant une nouvelle instruction : nop. Son opcode est 90.
NOP ne fait rien. Il servait avant à ralentir l'éxécution d'un programme par la lecture d'une instruction ne fesant rien !
Dans cette partie, nous suivons le programme sauf que quand il tombera sur le code du Nag-screen, il n'y trouvera rien !
Et on se retrouvera sur notre programme. 

:00401000 33C0                    xor eax, eax
:00401002 85C0                    test eax, eax
:00401004 7513                    jne 00401019 
:00401006 6A00                    push 00000000 ===> début du Nag-screen
:00401008 6804304000              push 00403004 ===>"VILAIN NAG-SCREEN"
:0040100D 6816304000              push 00403016 ===>"ENREGISTRE-MOI SINON JE..."
:00401012 6A00                    push 00000000
:00401014 E829000000              Call 00401042 ===> USER32.MessageBoxA( Fin du Nag )

On change en :

:00401000 33C0                    xor eax, eax
:00401002 85C0                    test eax, eax
:00401004 7513                    jne 00401019 
:00401006 9090                    nop ( 2 x )
:00401008 9090909090              nop ( 5 x )
:0040100D 9090909090              nop ( 5 x )
:00401012 9090                    nop ( 2 x )
:00401014 9090909090              nop ( 5 x )

On n'a plus le nag, mais la solution peut sembler lourde car on change 19 bytes alors que dans les autres solutions, on a changé 1 byte ou 2 maximum.
Toutefois, nop est intéressant quand vous voulez "effacer" quelques bytes.

Voici un exemple sur un autre programme :
Je veux mettre eax = 1.

:004011F0 B8D0070000              mov eax, 000007D0
:004011F5 4A                      dec edx

Ici , on peut mettre mov eax, 00000001 dont l'opcode est B801000000. Ca tombe juste pour les bytes à remplacer.
Mais admettons que nous devions mettre mov al,01 dont l'opcode estB001.

B8D0070000 ===> B001070000
Problème car en B001 = mov al,01 mais que veut dire 070000 ?!
Donc le programme va sûrement planter car 070000 correspond à des choses que le programme ne va pas comprendre ou des instructions que l'on ne veut pas !

Avec nop, ça marche !

:004011F0 B8D0070000              mov eax, 000007D0
:004011F5 4A                      dec edx

devient
:004011F0 B001                    mov al, 01
:004011F2 909090                  nop ( 3 x )
:004011F5 4A                      dec edx

On a ce que l'on veut et le prog tourne !
 

Plus d'informations

Générer un crack

Pour cela, vous avez plusieurs solutions ( comme avant ; ) :
- compiler ou assembler votre crack.
- utiliser un crack maker. ( vous en trouverez un dans le package )
 

Boite de message

Une boite de message comme nous l'avons vu, se code de la façon suivante en assembleur 32 bits.

push #######  ===> Les push servent à passer les arguments de notre MessageBoxA 
push #######  ===> ( handle, adresse du titre, adresse du texte, style )
push #######  ===>
push #######  ===>
call MessageBoxA ===> On fait appel à la fonction MessageBoxA pour faire apparaître notre Nag-screen

En fonction de ça, notre boite de message avec titre + texte + bouton Ok apparait et ne passera à l'instruction qui suit le Call que si le bouton est cliqué.
 

Test eax, eax

Vous retrouverez souvent cette instruction en assembleur, mais dans le cas qui nous intéresse, on peut facilement voir sa traduction dans un langage de plus haut niveau, grâçe aux booléens.
Il y a 2 valeurs possibles : VRAI ou FAUX.
Et c'est aussi notre cas.
L'utilisateur est-il enregistré : VRAI ou FAUX. ( que 2 valeurs possibles )
 

Test eax, eax + jnz / jz

Attention, la grande erreur serait de penser que l'on peut trouver une protection en cherchant ces instructions. En effet, comme je l'ai écrit juste en haut, le test eax, eax conditionne 2 états possibles. Et cela peut aussi vouloir dire dans un programme :
- l'utilisateur a-t-il cliqué sur la fenêtre ?
- est-ce qu'il y a un modem ?
...
 

Saut short et long

Dans nos exemples, on des sauts courts ( short ) :
Instruction de saut ( jmp, jne, jnz, je, jz, jle... ) + nombre de bytes à sauter
Ici : 75 ( saut ) 13 ( la distance )
Il y a un problème, c'est que la distance que l'on peut avoir, va de 0 à FF donc de 0 à 255 bytes de distance !
C'est peu ! Mais l'assembleur a prévu le coup et donc on a les saut longs.
L'opcode évidemment change !
Exemple :

:004012D4 0F85D3050000            jne 004018AD

Mais pas de problème, le fichier help vous indique cela aussi.
jne long = 0F85 / je long = 0F84
 

Trouver l'opcode d'une instruction

Vous verrez qu'avec Softice, ce genre de problème ne se posera plus mais pour l'instant, quand vous ne trouvez pas l'opcode correspondant à une instruction, vous pouvez faire une recherche sur W32Dasm avec "Search" dans le menu. Entrez l'instruction en respectant bien le formatage de W32Dasm et vous aurez peut-être une chance de trouver un exemple dans le programme.
Formatage:

mov( espace )eax,( espace )ebx
 

Lise Grim 2002 / WiA