[ - Intro - Explications - L'outil expliqué - Liens - Notes de fin - ] |
[
- Tutoriaux :
Bases |
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.
Nous lançons le crackme,
vous pourrez voir un boite de message windows, puis une autre après
avoir cliqué sur Ok.
![]() |
![]() |
|
|
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. )
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 !
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