Random IRC quote :      <@Javuto> se ha picado una movida en php, q hace llorar al niño jesus

Solución al crackme de erg0t.

Hombre que tal !
frapereaHace ya mucho mucho tiempo, ese lejano ente (dentro de poco será cercano) que se esconde detrás del nick «erg0t» planteó un reto a la distinguida audiencia de 48bits: un crackme. Pero había una «pega», era para linux!! ai dió mío!
He de reconocer que ahora uso muchísimo menos Linux que Windows, pero hace algunos años era al revés, así que bueno siempre está bien recordar cosas que si no luego se olvidan… y que mejor excusa que un crackme para repasarlas.

Desconecten los móviles, amordacen a los niños, asegúrense que han cerrado el gas, apuren las botellas de Vodka y no se muevan de sus asientos porque… ¡ empezamos !
(Tarda un poco en cargar…¡¡es que tiene colorines!!)

Cuando se trata de crackmes cualquier pista o insinuación equivale a algo, en el post original vemos esto «no os fieís de erg0t … probadlo en más de una máquina y descargad el fichero más de una vez ) .

Para ello necesitaís un kernel 2.6, tráfico UDP permitido hacia internet y libz.». Además vemos como el enlace al crackme es un .php por lo que «descargad el fichero más de una vez» debe
significar algo importante.

Obedecemos y los descargamos 5 veces. Usando el comparador de ficheros del Ultraedit vemos que cada archivo es diferente de otro en unos offsets determinados y comparte el mismo número de bytes diferentes.

Raw Offset:   Bytes diferentes
0xA65      ->            0xB8
0x13c0    ->             0x4
0x2C52    ->             0x4
 

Este último son los 4 últimos bytes del fichero al final de la sección .strtab por lo que a primera vista es un overlay con algun valor generado al vuelo.

El siguiente paso es sacar el IDA a pasear a ver que nos encontramos. Todo se desensambla bien, no está empaquetado, no hay nada raro (por ahora). Vamos a ver paso a paso entonces cómo funciona internamente el crackme.

Lo primero interesante :

.text:08048D26                 add     eax, 1000h
.text:08048D2B                 mov     [esp+288h+var_27C], 0
.text:08048D33                 mov     [esp+288h+var_280], 2810900h
.text:08048D3B                 mov     [esp+288h+var_284], eax
.text:08048D3F                 mov     [esp+288h+var_288], offset check
.text:08048D46                 call    _clone
 

Esto crea un thread cuya rutina es «check», con los siguientes flags CLONE_SIGHAND | CLONE_UNTRACED | CLONE_STOPPED | CLONE_VM | CLONE_THREAD. De momento nos quedamos con la copla de los flags, más tarde veremos porqué los usa.

Posteriormente crea un socket DGRAM sin mayor novedad

.text:08048D4B                 mov     [ebp+var_250], eax
.text:08048D51                 mov     [esp+288h+var_280], 0
.text:08048D59                 mov     [esp+288h+var_284], 2
.text:08048D61                 mov     [esp+288h+var_288], 2
.text:08048D68                 call    _socket
 

A continuación lee el link al directorio /proc/self/exe que es ni más ni menos que el path y el nombre del ejecutable. Además comprueba que estemos corriendo un kernel 2.6 mediante uname.

.text:08048D6D                 mov     [ebp+var_254], eax
.text:08048D73                 lea     eax, [ebp+var_198]
.text:08048D79                 mov     [esp+288h+var_288], eax
.text:08048D7C                 call    _uname
.text:08048D81                 mov     [esp+288h+var_280], 1000h
.text:08048D89                 mov     [esp+288h+var_284], offset crackme
.text:08048D91                 mov     [esp+288h+var_288], offset aProcSelfExe ; "/proc/self/exe"
.text:08048D98                 call    _readlink
.text:08048D9D                 mov     ds:crackme[eax], 0
.text:08048DA4                 lea     eax, [ebp+var_198]
.text:08048DAA                 add     eax, 82h
.text:08048DAF                 mov     [esp+288h+var_280], 3
.text:08048DB7                 mov     [esp+288h+var_284], offset a2_6 ; "2.6"
.text:08048DBF                 mov     [esp+288h+var_288], eax
.text:08048DC2                 call    _strncmp

 

Intenta resolver 48bits.com,
– «¿qué sabes de 48bits.com?»
– «Pues que es un país precioso con una gente estupenda»

.text:08048DD7                 mov     [esp+288h+var_288], offset a48bits_com ; "48bits.com"
.text:08048DDE                 call    _gethostbyname
.text:08048DE3                 mov     [ebp+var_1AC], eax
.text:08048DE9                 cmp     [ebp+var_1AC], 0
.text:08048DF0                 jnz     short loc_8048DFE
.text:08048DF2                 mov     [esp+288h+var_288], 0FFFFFFFFh
 

Ahora empieza ya el mondongo

.text:08048DFE                 mov     [ebp+var_248], offset segfault
.text:08048E08                 lea     eax, [ebp+var_248]
.text:08048E0E                 add     eax, 4
.text:08048E11                 mov     [esp+288h+var_288], eax
.text:08048E14                 call    _sigemptyset
.text:08048E19                 mov     [ebp+var_1C4], 4
.text:08048E23                 mov     [esp+288h+var_280], 0
.text:08048E2B                 lea     eax, [ebp+var_248]
.text:08048E31                 mov     [esp+288h+var_284], eax
.text:08048E35                 mov     [esp+288h+var_288], 0Bh
.text:08048E3C                 call    _sigaction
.text:08048E41                 mov     [esp+288h+var_280], 0
.text:08048E49                 lea     eax, [ebp+var_248]
.text:08048E4F                 mov     [esp+288h+var_284], eax
.text:08048E53                 mov     [esp+288h+var_288], 4
.text:08048E5A                 call    _sigaction
 

Lo que hace aquí es asignar un manejador de señales tanto para SIGILL (instrucción ilegal) como para SIGSEGV (violacion de segmento). Quedaros con esto para después.
Luego establece una conexión con ese bonito país al puerto 0xCAFE

.text:08048E84                 mov     word ptr [ebp+var_1A8], 2
.text:08048E8D                 mov     [esp+288h+var_288], 0CAFEh
.text:08048E94                 call    _htons
.text:08048E99                 mov     word ptr [ebp+var_1A8+2], ax
.text:08048EA0                 mov     eax, [ebp+var_24C]
.text:08048EA6                 mov     [ebp+var_1A4], eax
.text:08048EAC                 mov     [esp+288h+var_280], 10h
.text:08048EB4                 lea     eax, [ebp+var_1A8]
.text:08048EBA                 mov     [esp+288h+var_284], eax
.text:08048EBE                 mov     eax, [ebp+var_254]
.text:08048EC4                 mov     [esp+288h+var_288], eax
.text:08048EC7                 call    _connect
 

Y aquí llega la primera protección, en este caso anti-debug:

.text:08048EDD loc_8048EDD:                            ; CODE XREF: main+1FCj
.text:08048EDD                 mov     [ebp+var_248], offset th
.text:08048EE7                 mov     [esp+288h+var_280], 0
.text:08048EEF                 lea     eax, [ebp+var_248]
.text:08048EF5                 mov     [esp+288h+var_284], eax
.text:08048EF9                 mov     [esp+288h+var_288], 5
.text:08048F00                 call    _sigaction
 

Primero establece un manejador de señal para SIGTRAP, posteriormente obtiene el pid del proceso padre e intentas attachearse a él usando ptrace. En el caso que estemos corriendo el programa en un debugger o usando strace o , pues esto equivale a que todo se bloquee. Trick antidebug de toda la vida 🙂 . Hay que contar con que la thread ha sido creada con CLONE_UNTRACED lo que hace la vida dificil para depurarla con el GDB. La solución, ejecutarlo y cuando empieze a pedir el «Name», attachearte y directamente usar » (gdb) jump funcion » para verificar movidas. Recomiendo echar un ojo a este hilo en el foro de 48bits https://forum.48bits.com//index.php?topic=32.0

.text:08048F05                 call    _getppid
.text:08048F0A                 mov     [esp+288h+var_27C], 0
.text:08048F12                 mov     [esp+288h+var_280], 0
.text:08048F1A                 mov     [esp+288h+var_284], eax
.text:08048F1E                 mov     [esp+288h+var_288], 10h
.text:08048F25                 call    _ptrace
.text:08048F2A                 call    _wait
 

A partir de aquí nos pide name and key mediante read y tal…guardando la información en la sección de datos.No pongo el código porque no son más que reads y printfs y movidas así.
Por último escribe al socket todo esa información desde un buffer, el cual, en los primeros 4 bytes se encuentra una dword que corresponde a uno de los valores que cambiaban entre ficheros, (el segundo en la tabla de raw offsets 0x13c0 )

.text:08048F9E                 mov     byte ptr [eax+804A3C4h], 0
.text:08048FA5                 mov     [esp+288h+var_280], 38h
.text:08048FAD                 mov     [esp+288h+var_284], offset dword_804A3C0
.text:08048FB5                 mov     eax, [ebp+var_254]
.text:08048FBB                 mov     [esp+288h+var_288], eax
.text:08048FBE                 call    _write
 

Luego le envía una SIGCONT a la thread , recordad que se habia creado suspendida, para «reanimarla».

.text:08048FC3                 mov     [esp+288h+var_284], 12h
.text:08048FCB                 mov     eax, [ebp+var_250]
.text:08048FD1                 mov     [esp+288h+var_288], eax
.text:08048FD4                 call    tkill
.text:08048FD9                 mov     [esp+288h+var_288], offset unk_8049214
 

(Este es un buen sitio para romper «(gdb) b tkill» )

Por último, lee 4 bytes del socket que es la respuesta del servidor, lo guarda en hash y tras una comprobación, termina con un «Invalid Key».

text:08048FE5                 mov     [esp+288h+var_280], 4
.text:08048FED                 mov     [esp+288h+var_284], offset hash
.text:08048FF5                 mov     eax, [ebp+var_254]
.text:08048FFB                 mov     [esp+288h+var_288], eax
.text:08048FFE                 call    _read
.text:08049003                 not     eax
.text:08049005                 test    eax, eax
.text:08049007                 jnz     short loc_8049021
.text:08049009                 mov     [esp+288h+var_288], offset a131mInvalidKey ; "\x1B[1;31m  Invalid key\x1B[0;0m\n"
.text:08049010                 call    _puts
.text:08049015                 mov     [esp+288h+var_288], 0FFFFFFFFh
.text:0804901C                 call    _exit
 

2. Dándole al tema.

Vamos a examinar la thread «check». Tiene dos objetivos principales, por un lado, checkear la integridad del crackme con el fin de detectar si hemos parcheado algun byte. Esto lo hace mediante un crc32, el crc32 original se situa al final del archivo, os acordáis de último valor en la tabla de raw offsets para bytes diferentes? pues era esto.

mov     [esp+98h+var_98], offset crackme
call    _open
mov     [ebp+var_74], eax
lea     eax, [ebp+var_68]
mov     [esp+98h+var_94], eax
mov     [esp+98h+var_98], offset crackme
call    lstat
mov     eax, [ebp+var_3C]
mov     [esp+98h+var_98], eax
call    _malloc
mov     eax, [ebp+var_3C]
sub     eax, 4
mov     [esp+98h+var_90], eax
mov     eax, [ebp+var_78]
mov     [esp+98h+var_94], eax
mov     eax, [ebp+var_6C]
mov     [esp+98h+var_98], eax
call    _crc32
 

Se mapea a sí mismo (-4 bytes)a traves del enlace leido de /proc/self/exe, y genera el crc32
luego comprueba que el crc es el mismo que el original

mov     eax, [ebp+var_7C]
cmp     eax, [ebp+var_6C]
jz      short loc_8048CBF
 

En el caso de que no sea igual genera una violación de acceso de esta manera

 mov large dword ptr ds:0, 1
 

por lo que saltará el manejador de SIGSEGV que no hace nada más que sacarnos del programa con un «invalid key».

La segunda mision principal del la thread es generar un SIGTRAP para hacer saltar la rutina «th» que es el trap handler que posteriormente analizaremos. ¿cómo lo hace? Así…

mov     [esp+98h+var_90], 5
mov     [esp+98h+var_94], 0CCh
mov     [esp+98h+var_98], offset rompe
call    _memset
 

Sobreescribe los primeros 5 bytes de la funcion rompe con el opcode de la int3 (0xCC) la cual generará una SIGTRAP una vez ejecutada.

Esta thread se mantiene en un bucle hasta que el hash es distinto de 0, es decir hasta que el servidor al que se conecta el crackme ha recibido name y key y ha devuelto el valor que corresponda. Cuando se cumple esta condición, la thread llama a «rompe» por lo que se ejecuta la int3 y saltamos directamente a «th», es decir al trap handler.

Veamos «th».

.text:08048B28
.text:08048B28                 push    ebp
.text:08048B29                 mov     ebp, esp
.text:08048B2B                 push    esi
.text:08048B2C                 push    ebx
.text:08048B2D                 sub     esp, 10h
.text:08048B30                 mov     [ebp+var_C], offset good
.text:08048B37                 mov     [ebp+var_10], 0
.text:08048B3E
.text:08048B3E loc_8048B3E:                            ; CODE XREF: th+46j
.text:08048B3E                 cmp     [ebp+var_10], 2Eh
.text:08048B42                 ja      short loc_8048B70
.text:08048B44                 mov     eax, [ebp+var_10]
.text:08048B47                 lea     ebx, ds:0[eax*4]
.text:08048B4E                 mov     esi, [ebp+var_C]
.text:08048B51                 mov     eax, [ebp+var_10]
.text:08048B54                 lea     ecx, ds:0[eax*4]
.text:08048B5B                 mov     edx, [ebp+var_C]
.text:08048B5E                 mov     eax, hash
.text:08048B63                 xor     eax, [ecx+edx]
.text:08048B66                 mov     [ebx+esi], eax
.text:08048B69                 lea     eax, [ebp+var_10]
.text:08048B6C                 inc     dword ptr [eax]
.text:08048B6E                 jmp     short loc_8048B3E
.text:08048B70 ; —————————————————————————
.text:08048B70
.text:08048B70 loc_8048B70:                            ; CODE XREF: th+1Aj
.text:08048B70                 call    good
 

Como véis es una función muy sencilla, descifra 0x2e*sizeof(dword) bytes de una zona de memoria (good) mediante un xor con el hash obtenido del servidor. Una vez ha terminado,hace un call a la zona de memoria descifrada «good» por lo que interpretamos que lo que intenta descifrar es una función. Así que lo primero que se nos plantea es sacar un hash bueno para descifrar correctamente esa funcion y ver que hace, de otra manera estamos jodidos porque no tenemos nada con lo que operar al generarse toda la información relevante en el servidor.
Para descifrar la funcion sin necesidad de obtener la clave correcta mediante métodos «legítimos» vamos a usar una variante de una técnica usada habitualmente en el análisis de malware y virus, esta es x-ray. Al ser un cifrado sencillo como es xor lo que vamos a hacer es intentar averiguar la clave, tratando de «identificar» a través de la función cifrada algo que sepamos que esté ahí. En este caso lo que vamos a usar es una direccion de memoria de una cadena que por narices tiene que hacerse referencia desde esa función cifrada ya que no existe ninguna x-ref desde ninguna otra parte del ejecutable, y diréis, y por que esa string tiene que tener alguna xref?, pues porque esa string es :

«\x1B[1;32m Key is valid\n Congratulations :)\x1B[0;0m\n»

Sospechoso ¿no? :). Bien, pues esa cadena se situa en 0x08049184, pero no podemos símplemente intentar por fuerza bruta obtener esa dword porque matemáticamente podríamos obtenerla en cualquier dword si nos pusieramos a xorear con todos los valores posibles.
Lo que tenemos que hacer es generar un hash que aplicado a una dword nos devuelva ese offset y que además ese hash aplicado a otro offset donde sepamos que existe otro valor nos devuelva el valor que estamos esperando, esto confirmaría que hemos dado con la clave, ya que en un espacio tan reducido como el de la función es estadísticamente muy dificil que se diera un falso positivo. Entonces lo que vamos a hacer es buscar esta secuencia

mov [xxxx], offset NuestraCadena
Call _puts

ya que a lo largo del desensamblado del crackme hemos visto cómo se usaba esta combinación para mostrar texto a la terminal. Por ejemplo:

mov     [esp+28h+var_28], offset a131mInvalidKey
call    _puts
 

Entonces traduciendo esto a opcodes, tenemos que encontrar en un determinado offset de la cadena ValidKey,x, dentro de la funcion cifrada. En x+1 deberá estar 0xE8 del opcode de Call y en x-1= 0x24 , x-2=0x4 y x-3=0xC7 que corresponden al mov [xxxxx]

Para que quede claro, la incognita a encontrar es hash que es una dword, la dividimos en 4 claves de 1 byte diferentes, k1, k2, k3, k4. Cuando esta clave xoreada con una dword de la función cifrada nos devuelva el offset de la cadena ValidKey haremos esto

     
            k1   k2    k3    k4         x                         k1
xor             x-3   x-2   x-1   |   0x08049184 |        x+1
 

si obtenemos los valores anteriormente citados, habremos conseguido sacar el hash bueno!!. Y obviamente, despues de hacer un programín para este menester, nuestra teoría se confirma y hayamos el hash que descifra correctamente la función.

#include <stdio.h>
#include <stdlib.h>
//// Good cifrada
 unsigned char Crypte[]=
"\x3F\xE6\x34\x37\x86\x47\x16\xF1\x9A\x6F\xD1\xB4\x6A\xA8\x94\x40"
"\x6A\x6F\xD1\xB4\xAD\x6B\xF5\x62\xC9\x6B\xD9\x5C\xE9\x92\x2E\x4B"
"\x53\x2A\x25\xC7\x72\xE4\x94\x40\x6F\xBF\x72\xB0\x62\x60\x6F\xE4"
"\x6C\xE2\x94\x44\x6B\x7F\x5C\xF1\x9E\x90\xD1\x5F\xBD\xA8\x95\x90"
"\x62\x7F\xD1\xB4\x6A\xE2\x94\x58\xE3\x2B\xF5\xB0\xAD\x6B\xF5\x70"
"\xC9\x6B\xD9\x5C\x71\x91\x2E\x4B\xE3\x2A\x2D\x73\x2E\x4B\xD9\xA4"
"\x6A\x6F\xD1\x73\x2E\x4B\xD5\xB4\x6A\x6F\xD1\x3F\x2F\x83\x91\x3D"
"\x6E\x4B\x39\x48\x97\x90\x2E\x3D\x2F\x97\x5A\xE1\x92\xE2\x94\x48"
"\x5B\x7F\x5A\xE1\x9A\xCE\x29\x17\x6E\x67\xF8\x64\x53\x2A\x2D\xC1"
"\x64\xA8\xD5\x90\xEE\xFE\xD5\xBC\x82\xD9\x2D\x4B\x95\x84\xDD\x73"
"\x6E\x4B\xB9\x25\x6E\x67\x39\x1C\x96\x90\x2E\x73\x6E\x4B\xD1\xB4"
"\x6A\x6F\x39\xD8\x96\x90\x2E\x73\x6E\x4B\xD1\xB4";

 unsigned char do_xray( unsigned char cypher, unsigned char origin)
{
                 
   int b;
   unsigned char orig,xoredc;
   for(b=0;b<=0xFF;b++)
   {
     xoredc=cypher^b;
     if(xoredc==origin) return b;
   }
}

int check_xray(unsigned char orig,  unsigned char key, unsigned char result)
{
    unsigned char xored;
    xored= result^key;
    if(xored==orig)     return 1;
    else                return 0;
}

int main(int argc, char *argv)
{
   
    int i,b;
    unsigned char orig,xoredc,count,k1,k2,k3,k4;

    printf("Size: %x\n",sizeof(Crypte));
    for(i=0;i<sizeof(Crypte)-4;i++)
    {
      b=i;
   
     k1 = do_xray(Crypte[b],0x84);        // offset cadena 1
     k2 = do_xray(Crypte[b+1],0x91);    // offset cadena 2
     k3 = do_xray(Crypte[b+2],0x04);    // offset cadena 3
     k4 = do_xray(Crypte[b+3],0x08);    // offset cadena 4
     
     
     if(     check_xray(0xc7,k2,Crypte[b-3])      // mov[xxx],offset cadena
          && check_xray(0x04,k3,Crypte[b-2])    // mov[xxx],offset cadena
          && check_xray(0x24,k4,Crypte[b-1])    // mov[xxx],offset cadena
          && check_xray(0xe8,k1,Crypte[b+4])   // 0xE8 del Call _puts
        )
        {
          printf("I[%x] C1: %x C2: %x C3: %x C4:%x – k1: %x k2: %x k3: %x k4: %x\n"
                                          ,i,
                                          Crypte[b],
                                          Crypte[b+1],
                                          Crypte[b+2],
                                          Crypte[b+3],
                                          k1,
                                          k2,
                                          k3,
                                          k4);                              
          printf("Found: %01x %01x %01x %01x\n",k1,k2,k3,k4);
        }
    }          
}
 

Pues ahora con esta clave, xoreamos Good y ya la tenemos descifrada!!
Vamos a ver que hace, porque en esa función está la clave de todo.

.text:08048A79                 mov     [esp+28h+var_28], 804A3D6h ; name
.text:08048A80                 call    _strlen
.text:08048A85                 cmp     [ebp+var_C], eax
.text:08048A88                 jnb     short loc_8048AA2
.text:08048A8A                 mov     eax, [ebp+var_C]
.text:08048A8D                 add     eax, 804A3D0h
.text:08048A92                 movsx   edx, byte ptr [eax+6]
.text:08048A96                 lea     eax, [ebp+var_10]
.text:08048A99                 add     [eax], edx
.text:08048A9B                 lea     eax, [ebp+var_C]
.text:08048A9E                 inc     dword ptr [eax]
.text:08048AA0                 jmp     short loc_8048A79 ; name
 

Aquí vemos como lee el «Name» que nos pide el crackme y suma todos los valores de los bytes que compone la cadena. En c:

for(x=0;x<strlen(name);x++)
    y += (int) name[x] ;
 

A continuación :

.text:08048AA2                 mov     [esp+28h+var_20], 10h
.text:08048AAA                 lea     eax, [ebp+var_14]
.text:08048AAD                 mov     [esp+28h+var_24], eax
.text:08048AB1                 mov     [esp+28h+var_28], 804A3C4h ; key
.text:08048AB8                 call    _strtoul
.text:08048ABD                 mov     [ebp+var_4], eax
.text:08048AC0                 mov     [esp+28h+var_20], 10h
.text:08048AC8                 mov     [esp+28h+var_24], 0
.text:08048AD0                 mov     eax, [ebp+var_14]
.text:08048AD3                 inc     eax
.text:08048AD4                 mov     [esp+28h+var_28], eax ; name
.text:08048AD7                 call    _strtoul
 

Lo que hace aquí es pasar la «key» a dos unsigned longs desde valores hexadecimales en ascii. Como vemos en strtoul el segundo parámetro no es NULL por lo que ahí se guarda el caracter donde termina la primera cadena hexadecimal, a continuacion se incrementa y se usa este puntero para leer la segunda parte de la key.Es decir la key tiene que tener dos partes hexadecimales separadas por un caracter no hexadecimal. AAAAAAAA-BBBBBBBB por ejemplo.
Por último la comprobación final

.text:08048ADC                 mov     [ebp+var_8], eax
.text:08048ADF                 mov     edx, [ebp+var_8] ; part 1 key
.text:08048AE2                 lea     eax, [ebp+var_4] ; part 2 key
.text:08048AE5                 xor     [eax], edx
.text:08048AE7                 mov     edx, [ebp+var_10] ; name count
.text:08048AEA                 mov     eax, hash
.text:08048AEF                 sub     eax, edx
.text:08048AF1                 cmp     [ebp+var_4], eax
.text:08048AF4                 jnz     short loc_8048B04
.text:08048AF6                 mov     [esp+28h+var_28], offset a132mKeyIsValid ; "\x1B[1;32m  Key is valid\n   Congratulation"…
.text:08048AFD                 call    _puts
 

¿Cómo revertir esta funcion para generar nuestras propias claves? Hemos averiguado que a la función solo la descifra un valor que hemos sacado usando x-ray. Sabemos que el crackme envía un ID(Rawoffset 0x13c0) que cambia en todos los ejecutables, así mismo la función cifrada también cambia en todos los ejecutables. Blanco y en botella : kalimotxo.
Tiene que haber un patrón que relacione el ID y el HASH que sacamos por x-ray, tras varias operaciones elementales(suma,xor…) operando con los valores obtenidos de diferentes crackmes llegamos a que restando el ID – HASH obtenemos una constante= 0xco1dcafe en todos ellos. ¡¡ BINGO!!

Es decir la ecuación del hash es : HASH = ID – 0xc01dcafe;

Ahora ya podemos revertir la función para generar claves válidas para cada crackme, recordemos que es dependiente del ID que se encuentra en cada crackme.

Aquí tenéis un keygen, tendréis que obtener el HASH mediante el anterior programa de x-ray y cambiar los defines…

#include <stdio.h>
#include <stdlib.h>
 
#define HASH                0xB4D16F6A   // Hash obtained by x-raying
#define CHORRAVALUE    0xDEADBEEF
#define ID                     0x74EF3A68  // ID embedded within every crackme
#define CONST              0xc01dcafe  // Constant

void good(unsigned long id, char *myName)
{
  int a, b, x, y = 0 ;
  unsigned long hash = id – CONST ;
 
  printf("\nHash obtained by x-raying: %x\n",HASH);
  printf("\nHash obtained by reversing good: %x\n",hash);
 
  for(x=0;x<strlen(myName);x++)
    y += (int) myName[x] ;
 
 y += 10 ; // ‘\n’
 hash -= y ;
 
 a = CHORRAVALUE ;
 b = hash ^ CHORRAVALUE ;

 printf("\nName: %s\nKey: %X-%08X\n",myName,CHORRAVALUE,b);
 
}
 
int main(int argc, char *argv[])
{
   
    if(argc<2) return 0;
   
    good(ID,argv[1]) ;
   
    return 0 ;
}
 

Felicidades a erg0t porque el crackme está muy currado y entretenido.
Por alguna extraña razón, al terminar este «tuto» me siento como si tuviera 16 años 😉 …

sss
Un saludo
Rubén de los bosques.

4 Comentarios para “Solución al crackme de erg0t.”

  1. Comment por txipi | 06/12/07 at 9:29 am

    Genial el crackme y genial la solución. Seguid posteando cosillas linuxeras! ;-)))

  2. Comment por mballano | 06/12/07 at 10:25 am

    Muy bueno, felicidades a ambos, al final alguien se dignó a hacer tu crackme erg0t!! :-). BTW, hurra por el plugin de coloreado de código, nunca pensé que coloreara asm! 😀

    Por cierto, saludos txipi 😉

  3. ash
    Comment por ash | 06/12/07 at 4:15 pm

    Genial el post de la solución, muy completo. Y por supuesto el crackme original y divertido. Para el próximo no me pases los fuentes erg0t, a ver si puedo hacerlo yo tambien! 😉

    No sabía que txipi andase por aquí, bienvenido!

  4. Comment por tomac | 06/16/07 at 10:07 pm

    Jue que nivel, enhorabuena!

Se han cerrado los comentarios