|
|
 |
ESTUDIO COLECTIVO DE DESPROTECCIONES
Última Actualizacion:
25/10/2001
|
 |
|
Programa |
Crackme de Mr.Silver nº 1 |
W95 / W98 / NT |
Descripción |
Crackme de Mr.Silver nº 1 |
Tipo |
Serial |
Tipo de Tutorial |
[X]Original, []Adaptación, []Aplicación, []Traducción |
Url |
http://crackme.whiskeykontekila.org |
Protección |
Tecnicas anti-debugging, encriptacion TEA |
Dificultad |
1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista |
Herramientas |
SoftIce, Masm |
Objetivo |
Keygenerator |
Cracker |
Crick |
Fecha |
14 de Octubre de 2001 |
AL ATAQUE
|
Mr.Silver, tu primer crackme... mmm... ¡juer! ¡¿cómo serán los siguientes?!
Vamos a ver q es lo q has hecho:
Empezamos por el OEP q es lo suyo.
0167:004010D0 55 PUSH EBP
0167:004010D1 8BEC MOV EBP,ESP
0167:004010D3 83EC08 SUB ESP,08
0167:004010D6 53 PUSH EBX
0167:004010D7 56 PUSH ESI
0167:004010D8 57 PUSH EDI
0167:004010D9 6A00 PUSH 00
0167:004010DB FF150C104000 CALL [KERNEL32!GetModuleHandleA]
Hasta aquí, lo normal.
0167:004010E1 8BF8 MOV EDI,EAX
0167:004010E3 8D45FC LEA EAX,[EBP-04]
0167:004010E6 50 PUSH EAX ;pOldProtect
0167:004010E7 6A04 PUSH 04 ;NewProtect = PAGE_READWRITE
0167:004010E9 6800020000 PUSH 00000200 ;Size
0167:004010EE 57 PUSH EDI ;Address
0167:004010EF FF1508104000 CALL [KERNEL32!VirtualProtect]
Desprotege 512 bytes de la cabecera del exe para, a continuación, machacar
esa zona con CEROS. Se jodió el DUMP!!!
0167:004010F5 B980000000 MOV ECX,00000080
0167:004010FA 33C0 XOR EAX,EAX
0167:004010FC F3AB REPZ STOSD
En realidad sólo sobreescribe 128 bytes.
EDI contiene el ImageBase = ModuleHandle = 400000.
0167:004010FE 6810154000 PUSH 00401510
0167:00401103 FF1504104000 CALL [KERNEL32!SetUnhandledExceptionFilter]
Lo siguiente es modificar la rutina de manejo de las excepciones desatendidas.
Ahora apuntan a la rutina q hay en 401510.
A partir de aquí, cualquier excepción q se produzca pasará a ser analizada
por esa rutina.
0167:00401109 8B3538104000 MOV ESI,[USER32!DialogBoxParamA]
0167:0040110F 56 PUSH ESI
0167:00401110 FF1500104000 CALL [KERNEL32!GetCurrentThread]
0167:00401116 50 PUSH EAX
0167:00401117 E8A4030000 CALL 004014C0
0167:0040111C 83C408 ADD ESP,08
Llama a la rutina q hay en 4014C0 pasándole dos parámetros:
La dirección de la función DialogBoxParamA de la API USER32.DLL
y el identificador del hilo (Thread) actual.
Esta rutina lo q hace es poner un breakpoint en la dirección de la función
q se le pasa como parámetro y lo hace modificando
directamente el "contexto" del proceso cuyo hilo se pasa como segundo parámetro.
Y de paso jodiendo a algún debugger q otro :( [El SICE funciona :)]
(Mirad los comentarios en esa rutina)
0167:0040111F 90 NOP
0167:00401120 90 NOP
0167:00401121 90 NOP
0167:00401122 90 NOP
Estos NOP'S son cosa del compilador de C del Silver ;)
0167:00401123 B8D0104000 MOV EAX,004010D0
0167:00401128 BB1F114000 MOV EBX,0040111F
0167:0040112D 2BD8 SUB EBX,EAX
0167:0040112F 895DF8 MOV [EBP-08],EBX
0167:00401132 8B4DF8 MOV ECX,[EBP-08]
0167:00401135 68F4164000 PUSH 004016F4
0167:0040113A 51 PUSH ECX
0167:0040113B 68D0104000 PUSH 004010D0
0167:00401140 E84B000000 CALL 00401190
A 401190 se llama con 3 parámetros:
La dirección de inicio de una zona de memoria (4010D0), el tamaño
de la zona de memoria
con la q va a trabajar y la dirección donde pondrá el resultado.
Si mirais los comentarios de esta rutina, vereis q lo q hace es
usando como fuente los 80h bytes que empiezan en 4010D0
genera un código de 16 bytes (4 DWORDS) q luego usará :)
Luego, ojito con tocar esa zona de memoria !!!
Si parcheais ahí, el resto no funcionará.
0167:00401145 8B4508 MOV EAX,[EBP+08]
Esto no lo entiendo (me lo expliquen).
Mete en EAX el primer parámetro q se le pase al programa principal (siempre vale 0) ¿¿¿...???
0167:00401148 83C40C ADD ESP,0C
0167:0040114B A3F0164000 MOV [004016F0],EAX
Ese "0" lo mete en 4016F0.
0167:00401150 6A00 PUSH 00
0167:00401152 68F0114000 PUSH 004011F0
0167:00401157 6A00 PUSH 00
0167:00401159 6A65 PUSH 65
0167:0040115B 50 PUSH EAX
0167:0040115C FFD6 CALL ESI
Acordaros q ESI todavía contiene la dirección de "DialogBoxParamA", luego
aquí la está llamando. En los parámetros le está
pasando como dirección del DlgProc: 4011F0
y si desensamblais esa zona de código vereis q está encriptada!!!
Ahora es cuando entra en juego el BreakPoint q se puso antes.
En cuanto se llama a DialogBoxParam, salta el Breakpoint y,
por tratarse de una excepción, se transfiere el control al manejador
de excepciones (recordad q estaba en 401510)
El manejador identifica la excepción y lo q hace, en este caso, es
desencriptar la zona [4011F0..401470[, con la clave q
calculó antes, y luego volver a ejecutar la instrucción q produjo la excepción.
0167:0040115E 5F POP EDI
0167:0040115F 5E POP ESI
0167:00401160 83F8FF CMP EAX,-01
0167:00401163 5B POP EBX
0167:00401164 751F JNZ 00401185
Si al volver de la API la función DialogBoxParamA devuelve -1, es q se ha producido
algún error y se muestra el mensaje:
"Can not create dialog!" y el programa acaba.
Nota: Este error se produce en Win95 y W2k Pro y puede ser debido a que el compilador
está pasando como hInstance a
DialogBoxParamA (en el PUSH EAX, EAX=0) un CERO, cuando
debería ser 400000h (que es lo q devuelve GetModuleHandleA)
0167:00401166 6A30 PUSH 30
0167:00401168 687C104000 PUSH 0040107C
0167:0040116D 6864104000 PUSH 00401064
0167:00401172 6A00 PUSH 00
0167:00401174 FF153C104000 CALL [USER32!MessageBoxA]
0167:0040117A B8FF000000 MOV EAX,000000FF
Valor de salida del programa: -1 = error!
0167:0040117F 8BE5 MOV ESP,EBP
0167:00401181 5D POP EBP
0167:00401182 C21000 RET 0010
Si al salir de DialogBoxParamA no hay error, se sale del programa con un 0 = ok!
0167:00401185 33C0 XOR EAX,EAX
0167:00401187 8BE5 MOV ESP,EBP
0167:00401189 5D POP EBP
0167:0040118A C21000 RET 0010
0167:0040118D 90 NOP
0167:0040118E 90 NOP
0167:0040118F 90 NOP
Más NOP'S del compilador :(
Esta es la rutina q genera una clave de 16 bytes (128 bits) para desencriptar
la zona [4011F0-401470[
Recibe tres parámetros:
La dirección de la zona de memoria de donde cogerá los bytes para generar la clave.
El tamaño de esa zona.
La dirección en donde dejará la clave generada.
0167:00401190 8B442408 MOV EAX,[ESP+08]
Carga en EAX el tamaño de la zona = 4F
0167:00401194 53 PUSH EBX
0167:00401195 56 PUSH ESI
0167:00401196 8BF0 MOV ESI,EAX
0167:00401198 83E603 AND ESI,03
0167:0040119B 57 PUSH EDI
0167:0040119C 8B7C2410 MOV EDI,[ESP+10]
0167:004011A0 03F0 ADD ESI,EAX
0167:004011A2 C1EE02 SHR ESI,02
En ESI está el tamaño de bloque de memoria = 14h = 20d bytes.
0167:004011A5 56 PUSH ESI ;tamaño
0167:004011A6 57 PUSH EDI ;dirección de inicio
0167:004011A7 6A01 PUSH 01 ;Esta variable cambia en cada llamada. Comienza valiendo 1.
0167:004011A9 E822040000 CALL 004015D0
En 4015D0 se genera a partir de esos 20 bytes un DWORD (4 bytes) que formarán
parte de la clave final. Lo devuelve en EAX.
0167:004011AE 8B5C2424 MOV EBX,[ESP+24]
EBX apunta a la zona donde se irá escribiendo la clave.
0167:004011B2 8D0C3E LEA ECX,[EDI+ESI]
0167:004011B5 56 PUSH ESI ;tamaño
0167:004011B6 51 PUSH ECX ;dirección de inicio (siguiente bloque de 20 bytes)
0167:004011B7 50 PUSH EAX ;La variable vale ahora el primer DWORD de la clave.
0167:004011B8 8903 MOV [EBX],EAX ;Guarda el primer DWORD de la clave en su sitio.
0167:004011BA E811040000 CALL 004015D0 ;Genera el segundo DWORD.
0167:004011BF 8D1477 LEA EDX,[ESI*2+EDI]
0167:004011C2 56 PUSH ESI ;tamaño
0167:004011C3 52 PUSH EDX ;siguiente bloque
0167:004011C4 50 PUSH EAX ;segundo DWORD
0167:004011C5 894304 MOV [EBX+04],EAX ;Guarda el segundo DWORD
0167:004011C8 E803040000 CALL 004015D0 ;Genera el tercer DWORD
0167:004011CD 8D0C77 LEA ECX,[ESI*2+EDI]
0167:004011D0 56 PUSH ESI
0167:004011D1 03F1 ADD ESI,ECX
0167:004011D3 894308 MOV [EBX+08],EAX ;Guarda el tercer DWORD
0167:004011D6 56 PUSH ESI
0167:004011D7 50 PUSH EAX
0167:004011D8 E8F3030000 CALL 004015D0 ;Genera el cuarto y último DWORD
0167:004011DD 83C430 ADD ESP,30
0167:004011E0 89430C MOV [EBX+0C],EAX ;Guarda el cuarto DWORD
0167:004011E3 5F POP EDI
0167:004011E4 5E POP ESI
0167:004011E5 5B POP EBX
0167:004011E6 C3 RET ;Restaura la pila y vuelve.
Este es el DlgProc del DialogBox principal.
0167:004011F0 8B442408 MOV EAX,[ESP+08]
0167:004011F4 83EC54 SUB ESP,54
0167:004011F7 2D10010000 SUB EAX,00000110
0167:004011FC 53 PUSH EBX
0167:004011FD 55 PUSH EBP
0167:004011FE 56 PUSH ESI
0167:004011FF 57 PUSH EDI
0167:00401200 0F8410020000 JZ 00401416
0167:00401206 48 DEC EAX
0167:00401207 740C JZ 00401215
0167:00401209 5F POP EDI
0167:0040120A 5E POP ESI
0167:0040120B 5D POP EBP
0167:0040120C 33C0 XOR EAX,EAX
0167:0040120E 5B POP EBX
0167:0040120F 83C454 ADD ESP,54
0167:00401212 C21000 RET 0010
Este es el ciclo de mensajes, si el mensaje es WM_INITDIALOG = 110 se salta a 401416.
Si es WM_COMMAND = 111 se salta
a 401215 y si es otro mensaje se continúa en el ciclo.
0167:00401215 8B442470 MOV EAX,[ESP+70]
0167:00401219 25FFFF0000 AND EAX,0000FFFF
0167:0040121E 48 DEC EAX
0167:0040121F 7423 JZ 00401244
0167:00401221 48 DEC EAX
0167:00401222 0F85E2010000 JNZ 0040140A
Aquí se procesa WM_COMMAND, si se ha pulsado el botón (ID=1) se salta a 401244 y si
se quiere cerrar la ventana se continúa
por aquí:
0167:00401228 8B442468 MOV EAX,[ESP+68]
0167:0040122C 6A00 PUSH 00
0167:0040122E 50 PUSH EAX
0167:0040122F FF1530104000 CALL [USER32!EndDialog]
0167:00401235 5F POP EDI
0167:00401236 5E POP ESI
0167:00401237 5D POP EBP
0167:00401238 B801000000 MOV EAX,00000001
0167:0040123D 5B POP EBX
0167:0040123E 83C454 ADD ESP,54
0167:00401241 C21000 RET 0010
Se llama a EndDialog y se sale.
Aquí se empieza a procesar el unlock code y el serial que se hayan introducido.
0167:00401244 8B742468 MOV ESI,[ESP+68]
0167:00401248 8B3D2C104000 MOV EDI,[USER32!GetDlgItem]
0167:0040124E 8D4C2420 LEA ECX,[ESP+20]
0167:00401252 6A41 PUSH 41
0167:00401254 51 PUSH ECX
0167:00401255 68E9030000 PUSH 000003E9
0167:0040125A 56 PUSH ESI
0167:0040125B FFD7 CALL EDI
0167:0040125D 8B1D28104000 MOV EBX,[USER32!GetWindowTextA]
0167:00401263 50 PUSH EAX
0167:00401264 FFD3 CALL EBX ;Lee el serial
0167:00401266 83F840 CMP EAX,40 ;Tiene que tener 64 bytes
0167:00401269 0F8588010000 JNZ 004013F7 ;Si no: fuera!
0167:0040126F 8D542414 LEA EDX,[ESP+14]
0167:00401273 6A09 PUSH 09
0167:00401275 52 PUSH EDX
0167:00401276 68EA030000 PUSH 000003EA
0167:0040127B 56 PUSH ESI
0167:0040127C FFD7 CALL EDI
0167:0040127E 50 PUSH EAX
0167:0040127F FFD3 CALL EBX ;Lee el unlock code
0167:00401281 83F808 CMP EAX,08 ;Tiene que tener 8 bytes
0167:00401284 0F854E010000 JNZ 004013D8 ;Si no: fuera!
0167:0040128A 8B2D20104000 MOV EBP,[MSVCRT!strtoul]
Carga en EBP la dirección de la función "strtoul" de la librería MSVCRT.DLL
Esta función no la he usado nunca y no sabía lo que hacía.
Ahora ya lo sé, y supongo que "strtoul" quiere decir
STRingTOUnsignedLong.
0167:00401290 8D442470 LEA EAX,[ESP+70]
0167:00401294 6A10 PUSH 10
0167:00401296 8D4C2418 LEA ECX,[ESP+18]
0167:0040129A 33FF XOR EDI,EDI
0167:0040129C 50 PUSH EAX
0167:0040129D 51 PUSH ECX
0167:0040129E 897C247C MOV [ESP+7C],EDI
0167:004012A2 FFD5 CALL EBP
0167:004012A4 8B54247C MOV EDX,[ESP+7C]
0167:004012A8 8944241C MOV [ESP+1C],EAX
0167:004012AC 83C40C ADD ESP,0C
0167:004012AF 803A00 CMP BYTE PTR [EDX],00
0167:004012B2 0F8552010000 JNZ 0040140A
Lo primero que hace es convertir el unlock code a un DWORD, eso lo hace
"strtoul" y devuelve en EAX el DWORD y en EDX
si ha habido error o no.
Es decir, si el unlock code estaba formado por caracteres
que forman dígitos HEX.
Por ejemplo: '1FC9240E' son los ocho caracteres que se pueden transformar
en un DWORD (1FC9240E).
Si es un DWORD correcto se sigue por aquí:
0167:004012B8 33DB XOR EBX,EBX ;EBX=0
0167:004012BA 8D742421 LEA ESI,[ESP+21] ;ESI apunta al segundo caracter del serial
0167:004012BE 885C246E MOV [ESP+6E],BL
bucle:
0167:004012C2 8B442410 MOV EAX,[ESP+10] ;Carga el unlock code (DWORD) en EAX
0167:004012C6 8BCF MOV ECX,EDI ;ECX variará desde 0 hasta 1F
0167:004012C8 D3E8 SHR EAX,CL ;Rota el unlock code a la derecha CL bits
0167:004012CA A801 TEST AL,01 ;El bit 0 es un 1?
0167:004012CC 7561 JNZ 0040132F ;SI: salta a 40132F para continuar el bucle. Los 1's no nos interesan.
;NO: era un 0. Estos sí que nos interesan.
0167:004012CE 8A4EFF MOV CL,[ESI-01] ;Mete en CL un caracter del serial
0167:004012D1 8A16 MOV DL,[ESI] ;Mete en DL el caracter siguiente del serial
Recordad que el serial son 64 bytes y su formato debe ser igual que en el unlock code, es
decir, caracteres que formen bytes
hexadecimales, luego DLCL forman un byte HEX en ASCII. ok?
0167:004012D3 884C246C MOV [ESP+6C],CL
0167:004012D7 8D442470 LEA EAX,[ESP+70]
0167:004012DB 6A10 PUSH 10
0167:004012DD 8D4C2470 LEA ECX,[ESP+70]
0167:004012E1 50 PUSH EAX
0167:004012E2 51 PUSH ECX
0167:004012E3 88542479 MOV [ESP+79],DL
0167:004012E7 FFD5 CALL EBP
0167:004012E9 8B54247C MOV EDX,[ESP+7C]
0167:004012ED 83C40C ADD ESP,0C
0167:004012F0 803A00 CMP BYTE PTR [EDX],00
0167:004012F3 0F85A5000000 JNZ 0040139E
Se llama con DLCL a "strtoul" y si la conversión es correcta
en EAX (concrétamente en AL) estará el byte resultado.
0167:004012F9 8A4C4420 MOV CL,[EAX*2+ESP+20]
0167:004012FD 8A544421 MOV DL,[EAX*2+ESP+21]
ESP+20 apunta al serial en ASCII (al que introdujimos), y ahora usa EAX
como índice para cargar en CL y DL otros dos caracteres
del serial que formarán otro byte.
0167:00401301 884C246C MOV [ESP+6C],CL
0167:00401305 8D442470 LEA EAX,[ESP+70]
0167:00401309 6A10 PUSH 10
0167:0040130B 8D4C2470 LEA ECX,[ESP+70]
0167:0040130F 50 PUSH EAX
0167:00401310 51 PUSH ECX
0167:00401311 88542479 MOV [ESP+79],DL
0167:00401315 FFD5 CALL EBP
0167:00401317 83C40C ADD ESP,0C
0167:0040131A 8883E0164000 MOV [EBX+004016E0],AL
El byte convertido es YA parte de la clave de 16 bytes (similar a la anterior)
que se usará más adelante para desencriptar
otra zona de código.
0167:00401320 43 INC EBX ;Se incrementa el contador de bytes producidos.
0167:00401321 83FB10 CMP EBX,10 ;LLevamos más de 16 (=10h)?
0167:00401324 7752 JA 00401378 ;SÍ: Serial incorrecto y fuera!
0167:00401326 8B542470 MOV EDX,[ESP+70]
0167:0040132A 803A00 CMP BYTE PTR [EDX],00
0167:0040132D 7549 JNZ 00401378 ;Si el byte no era correcto: tmb fuera!
0167:0040132F 47 INC EDI ;EDI lleva la cuenta de las rotaciones
0167:00401330 83C602 ADD ESI,02 ;Actualiza el puntero al serial
0167:00401333 83FF1F CMP EDI,1F ;Hemos rotado los 32 bits del unlock code?
0167:00401336 7E8A JLE 004012C2 ;NO: seguimos en el bucle
Si os habeis fijado lo que hace este bucle es que por cada bit del unlock code
(convertido en DWORD) que esté a cero, toma su posición (de 0 a 1F) y la
utiliza como índice para conseguir dos bytes ASCII del serial que luego
convierte a un sólo byte HEX, y vuelve a utilizar ese byte como índice
dentro del serial para coger otros dos bytes del serial que también
convertirá en un solo byte HEX y ése es ya parte del código de 16 bytes
que formarán la clave. ¿Un poco lioso no?
Mejor un ejemplo:
Unlock Code = E18B556A
En binario:
+-------+-------+-------+-------+-------+-------+-------+-------+
| E | 1 | 8 | B | 5 | 5 | 6 | A |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1|1|1|0|0|0|0|1|1|0|0|0|1|0|1|1|0|1|0|1|0|1|0|1|0|1|1|0|1|0|1|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Si contais vereis que hay 16 0's y 16 1's.
Serial = 0E72016B066E690C64106C136318431D6B65085F111F0563430A1E17036F6F55
Si mapeamos el unlock code en binario sobre el serial, tendremos algo como esto:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Posición:|1F |1E |1D |1C |1B |1A |19 |18 |17 |16 |15 |14 |13 |12 |11 |10 |0F |0E |0D |0C |0B |0A |09 |08 |07 |06 |05 |04 |03 |02 |01 |00 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Valor:|5|5|6|F|6|F|0|3|1|7|1|E|0|A|4|3|6|3|0|5|1|F|1|1|5|F|0|8|6|5|6|B|1|D|4|3|1|8|6|3|1|3|6|C|1|0|6|4|0|C|6|9|6|E|0|6|6|B|0|1|7|2|0|E|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Mapa:| 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
En las posiciones en las que hay un 0 hay valores que van desde 00 hasta 1F.
Son los índices a las posiciones del serial
que contienen los bytes de la clave.
Por ejemplo: el primer 0 del unlock code está en el bit 0, en esa posición
del serial tenemos 0Eh que, si lo usamos como índice dentro del serial, vemos
que la posición 0Eh contiene 43h = primer byte de la clave.
Y así hasta completar los 16 bytes de la clave indexados por los 16 ceros del unlock code. ok?
Nota: La clave de este serial NO es la buena :(
Para comprender lo siguiente, primero hay que ver lo que hace el programa
cuando recibe WM_INITDIALOG en 401416.
0167:00401338 8B15F0164000 MOV EDX,[004016F0] ;=0
0167:0040133E 6A00 PUSH 00
0167:00401340 6870144000 PUSH 00401470
0167:00401345 6A00 PUSH 00
0167:00401347 6A66 PUSH 66
0167:00401349 52 PUSH EDX ;Otra vez 0. Debería ser 400000.
0167:0040134A FF1538104000 CALL [USER32!DialogBoxParamA]
Al producirse la llamada a la API, salta el BreakPoint y
el manejador de excepciones la identificará e intentará
desencriptar la zona [401470..4014C0[ con la clave que ha
extraido del serial. Después devolverá el control al DlgProc que
empieza en 401470. Obviamente, si no se ha desencriptado bien esa zona, los
resultados son impredecibles.
Aunque si se produce una nueva excepción (que es lo más probable) el manejador
finalizará el proceso.
0167:00401350 83F8FF CMP EAX,-01
0167:00401353 756F JNZ 004013C4
0167:00401355 6A30 PUSH 30
0167:00401357 687C104000 PUSH 0040107C
0167:0040135C 6864104000 PUSH 00401064
0167:00401361 6A00 PUSH 00
0167:00401363 FF153C104000 CALL [USER32!MessageBoxA]
0167:00401369 5F POP EDI
0167:0040136A 5E POP ESI
0167:0040136B 5D POP EBP
0167:0040136C B8FF000000 MOV EAX,000000FF
0167:00401371 5B POP EBX
0167:00401372 83C454 ADD ESP,54
0167:00401375 C21000 RET 0010
Si a la salida del DialogBox no hay error, se continúa por 4013C4 que llama a ExitProcess y se acabó.
Pero si lo hay se muestra el mensaje: "Can not create dialog!"
Esta rutina muestra el mensaje: "Invalid serial number"
0167:00401378 8B442468 MOV EAX,[ESP+68]
0167:0040137C 6A30 PUSH 30
0167:0040137E 685C104000 PUSH 0040105C
0167:00401383 6844104000 PUSH 00401044
0167:00401388 50 PUSH EAX
0167:00401389 FF153C104000 CALL [USER32!MessageBoxA]
0167:0040138F 5F POP EDI
0167:00401390 5E POP ESI
0167:00401391 5D POP EBP
0167:00401392 B801000000 MOV EAX,00000001
0167:00401397 5B POP EBX
0167:00401398 83C454 ADD ESP,54
0167:0040139B C21000 RET 0010
Esta rutina muestra el mensaje:
"Invalid serial number" (Sí, está repetida!!! Estos compiladoreeees... :( )
0167:0040139E 8B4C2468 MOV ECX,[ESP+68]
0167:004013A2 6A30 PUSH 30
0167:004013A4 685C104000 PUSH 0040105C
0167:004013A9 6844104000 PUSH 00401044
0167:004013AE 51 PUSH ECX
0167:004013AF FF153C104000 CALL [USER32!MessageBoxA]
0167:004013B5 5F POP EDI
0167:004013B6 5E POP ESI
0167:004013B7 5D POP EBP
0167:004013B8 B801000000 MOV EAX,00000001
0167:004013BD 5B POP EBX
0167:004013BE 83C454 ADD ESP,54
0167:004013C1 C21000 RET 0010
Si todo ha ido bien, el programa saldrá por aquí después de habernos dicho
lo buenos que somos ;)
0167:004013C4 6A00 PUSH 00
0167:004013C6 FF1510104000 CALL [KERNEL32!ExitProcess]
0167:004013CC 5F POP EDI
0167:004013CD 5E POP ESI
0167:004013CE 5D POP EBP
0167:004013CF 33C0 XOR EAX,EAX
0167:004013D1 5B POP EBX
0167:004013D2 83C454 ADD ESP,54
0167:004013D5 C21000 RET 0010
Esta rutina muestra el mensaje: "Unlock code must be 8 chars long."
0167:004013D8 6A40 PUSH 40
0167:004013DA 68C8104000 PUSH 004010C8
0167:004013DF 68A4104000 PUSH 004010A4
0167:004013E4 56 PUSH ESI
0167:004013E5 FF153C104000 CALL [USER32!MessageBoxA]
0167:004013EB 5F POP EDI
0167:004013EC 5E POP ESI
0167:004013ED 5D POP EBP
0167:004013EE 33C0 XOR EAX,EAX
0167:004013F0 5B POP EBX
0167:004013F1 83C454 ADD ESP,54
0167:004013F4 C21000 RET 0010
Esta rutina muestra el mensaje: "Serial must be 64 chars long."
0167:004013F7 6A40 PUSH 40
0167:004013F9 68C8104000 PUSH 004010C8
0167:004013FE 6884104000 PUSH 00401084
0167:00401403 56 PUSH ESI
0167:00401404 FF153C104000 CALL [USER32!MessageBoxA]
0167:0040140A 5F POP EDI
0167:0040140B 5E POP ESI
0167:0040140C 5D POP EBP
0167:0040140D 33C0 XOR EAX,EAX
0167:0040140F 5B POP EBX
0167:00401410 83C454 ADD ESP,54
0167:00401413 C21000 RET 0010
Aquí se procesa WM_INITDIALOG.
0167:00401416 A138104000 MOV EAX,[USER32!DialogBoxParamA]
0167:0040141B 50 PUSH EAX
0167:0040141C FF1500104000 CALL [KERNEL32!GetCurrentThread]
0167:00401422 50 PUSH EAX
0167:00401423 E898000000 CALL 004014C0
Con la llamada a 4014C0 se vuelve a poner un BreakPoint en DialogBoxParamA.
0167:00401428 8B742470 MOV ESI,[ESP+70]
0167:0040142C 8B3D34104000 MOV EDI,[USER32!SendDlgItemMessageA]
0167:00401432 83C408 ADD ESP,08
0167:00401435 6A00 PUSH 00
0167:00401437 6A40 PUSH 40
0167:00401439 68C5000000 PUSH 000000C5
0167:0040143E 68E9030000 PUSH 000003E9
0167:00401443 56 PUSH ESI
0167:00401444 FFD7 CALL EDI
0167:00401446 6A00 PUSH 00
0167:00401448 6A08 PUSH 08
0167:0040144A 68C5000000 PUSH 000000C5
0167:0040144F 68EA030000 PUSH 000003EA
0167:00401454 56 PUSH ESI
0167:00401455 FFD7 CALL EDI
0167:00401457 5F POP EDI
0167:00401458 5E POP ESI
0167:00401459 5D POP EBP
0167:0040145A B801000000 MOV EAX,00000001
0167:0040145F 5B POP EBX
0167:00401460 83C454 ADD ESP,54
0167:00401463 C21000 RET 0010
Se pintan las cajas de introducción de texto y se vuelve.
0167:00401466 90 NOP
0167:00401467 90 NOP
0167:00401468 90 NOP
0167:00401469 90 NOP
0167:0040146A 90 NOP
0167:0040146B 90 NOP
0167:0040146C 90 NOP
0167:0040146D 90 NOP
0167:0040146E 90 NOP
0167:0040146F 90 NOP
Más NOP's del compilador.
Aquí empieza el DlgProc del DialogBox que se mostrará
únicamente si el serial es correcto.
Como podeis ver, el código
no tiene mucho sentido: está encriptado!!!
Para los que les interese, en el DialogBox se muestran dos mensajes:
"Cool!! You've keygened this proggy, what do you think it was easy?"
"Coded by Mr. Silver of [WKT!], big thanks to MrJade, Sirene, NeSTe and Duffie for their great support.
You guys deceive the whole world. "
0167:00401470 A09B2CE8FE MOV AL,[FEE82C9B]
0167:00401475 D4B9 AAM
0167:00401477 EB5E JMP 004014D7
0167:00401479 F680AC606857D2 TEST BYTE PTR [EAX+576860AC],D2
0167:00401480 47 INC EDI
0167:00401481 D509 AAD
0167:00401483 A9C3D1780A TEST EAX,0A78D1C3
0167:00401488 CC INT 3
0167:00401489 6E OUTSB
0167:0040148A F4 HLT
0167:0040148B BBBC416992 MOV EBX,926941BC
0167:00401490 FFC9 DEC ECX
0167:00401492 6E OUTSB
0167:00401493 0086D7A8DC10 ADD [ESI+10DCA8D7],AL
0167:00401499 AF SCASD
0167:0040149A 1212 ADC DL,[EDX]
0167:0040149C F9 STC
0167:0040149D 2329 AND EBP,[ECX]
0167:0040149F 1CB0 SBB AL,B0
0167:004014A1 2C6D SUB AL,6D
0167:004014A3 90 NOP
0167:004014A4 5B POP EBX
0167:004014A5 C5D4 LDS EDX,ESP
0167:004014A7 D835FA7E96BE FDIV REAL4 PTR [BE967EFA]
0167:004014AD 53 PUSH EBX
0167:004014AE 9E SAHF
0167:004014AF D8BD13EBC473 FDIVR REAL4 PTR [EBP+73C4EB13]
0167:004014B5 F60D13FDE0C9F3 TEST BYTE PTR [C9E0FD13],F3
0167:004014BC 0281ED3781EC ADD AL,[ECX+EC8137ED]
Esta es la rutina que putea a los debuggers (bueno, al SICE NO XDD)
004014C0 /$ 81EC CC020000 SUB ESP,2CC ;Hace hueco en la pila para guardar el contexto
004014C6 |. 56 PUSH ESI
004014C7 |. 8BB424 D4020000 MOV ESI,DWORD PTR SS:[ESP+2D4] ;Thread
004014CE |. 8D4424 04 LEA EAX,DWORD PTR SS:[ESP+4]
004014D2 |. 50 PUSH EAX ; /pContext
004014D3 |. 56 PUSH ESI ; |hThread
004014D4 |. FF15 18104000 CALL DWORD PTR DS:[<&KERNEL32.GetThreadContext>>; \GetThreadContext
Carga el contexto del thread que se le pasa como parámetro
en la zona pContext (en la pila)
004014DA |. 8B8C24 D8020000 MOV ECX,DWORD PTR SS:[ESP+2D8] ;DialogBoxParamA
004014E1 |. 8D5424 04 LEA EDX,DWORD PTR SS:[ESP+4]
004014E5 |. 52 PUSH EDX ; /pContext
004014E6 |. 56 PUSH ESI ; |hThread
004014E7 |. C74424 0C 1000010>MOV DWORD PTR SS:[ESP+C],10010 ; |
004014EF |. C74424 24 0102000>MOV DWORD PTR SS:[ESP+24],201 ; |
004014F7 |. 894C24 10 MOV DWORD PTR SS:[ESP+10],ECX ; |
004014FB |. FF15 14104000 CALL DWORD PTR DS:[<&KERNEL32.SetThreadContext>>; \SetThreadContext
Si mirais la estructura CONTEXT (por ejemplo en el artículo sobre el SEH de Silver) vereis que lo que está modificando
aquí es el contenido de ContextFlags, DR7 y DR0.
Una buena técnica para no tener que bajar al ring-0 !!!!
Para poder hacer esas modificaciones hay que cambiar el valor de ContextFlags según lo siguiente:
CONTEXT_i386 equ 00010000h
CONTEXT_i486 equ 00010000h
CONTEXT_CONTROL equ CONTEXT_i386 OR 00000001h
CONTEXT_INTEGER equ CONTEXT_i386 OR 00000002h
CONTEXT_SEGMENTS equ CONTEXT_i386 OR 00000004h
CONTEXT_FLOATING_POINT equ CONTEXT_i386 OR 00000008h
CONTEXT_DEBUG_REGISTERS equ CONTEXT_i386 OR 00000010h
Como estamos modificando los registros de depuración, el valor de ContextFlags deberá ser:
CONTEXT_i386 OR CONTEXT_DEBUG_REGISTERS (10010h)
El valor de DR7 lo cambia a 201h, mirando la estructura de este registro vereis que pone a 1 el flag GE (Global Exact),
pone a 1 también el L0 (Local 0) y el resto lo pone a 0.
Es decir, pone un BreakPoint de un byte de tamaño (LEN0=0),
en ejecución(R/W0=0) y en DR0 (L0=1,GE=1).
Sólo falta cargar el DR0 con la dirección (DialogBoxParamA) y recargar el contexto para que se encuentre activo.
00401501 |. 5E POP ESI
00401502 |. 81C4 CC020000 ADD ESP,2CC
00401508 \. C3 RETN
0167:00401509 90 NOP
0167:0040150A 90 NOP
0167:0040150B 90 NOP
0167:0040150C 90 NOP
0167:0040150D 90 NOP
0167:0040150E 90 NOP
0167:0040150F 90 NOP
Joder con los NOP's >:(
"La madre del cordero" Rutina de manejo de excepciones desatendidas (Unhandled Exceptions)
0167:00401510 8B442404 MOV EAX,[ESP+04] ;Puntero al EXCEPTION_RECORD
0167:00401514 53 PUSH EBX
0167:00401515 8B5804 MOV EBX,[EAX+04] ;Puntero al CONTEXTO del proceso que causa la excepción
0167:00401518 8B00 MOV EAX,[EAX] ;EAX apunta al EXCEPTION_RECORD
0167:0040151A 813804000080 CMP DWORD PTR [EAX],80000004 ;El primer DWORD de EXCEPTION_RECORD es ExceptionCode
Lo compara con STATUS_SINGLE_STEP = 80000004, es decir, comprueba que se trate de un BreakPoint de ejecución.
0167:00401520 0F8596000000 JNZ 004015BC ;Si no lo fuera salta a 4015BC
0167:00401526 8B8BC4000000 MOV ECX,[EBX+000000C4] ;Carga en ECX el valor de ESP desde el CONTEXTO
0167:0040152C 56 PUSH ESI
0167:0040152D 57 PUSH EDI
0167:0040152E 8B7110 MOV ESI,[ECX+10] ;En ESI carga la dirección que se le pasó a
DialogBoxParamA como DlgProc (La extrae de la pila del proceso a través del CONTEXTO)
0167:00401531 81FEF0114000 CMP ESI,004011F0 ;Esta es la dirección del primer DlgProc
0167:00401537 7530 JNZ 00401569
Como las dos partes que desencriptan la zona de código correspondiente son básicamente iguales, sólo explicaré una de ellas.
0167:00401539 B870144000 MOV EAX,00401470
0167:0040153E 2DF0114000 SUB EAX,004011F0
0167:00401543 99 CDQ
0167:00401544 83E207 AND EDX,07
0167:00401547 03C2 ADD EAX,EDX
0167:00401549 C1F803 SAR EAX,03
0167:0040154C 85C0 TEST EAX,EAX
0167:0040154E 7E4F JLE 0040159F
0167:00401550 8BF8 MOV EDI,EAX
0167:00401552 68F4164000 PUSH 004016F4
0167:00401557 56 PUSH ESI
0167:00401558 56 PUSH ESI
0167:00401559 E8E2000000 CALL 00401640
0167:0040155E 83C40C ADD ESP,0C
0167:00401561 83C608 ADD ESI,08
0167:00401564 4F DEC EDI
0167:00401565 75EB JNZ 00401552
0167:00401567 EB36 JMP 0040159F
0167:00401569 81FE70144000 CMP ESI,00401470 ;Esta es la dirección del segundo DlgProc
0167:0040156F 752E JNZ 0040159F
0167:00401571 B8C0144000 MOV EAX,004014C0 ;Fin zona a desencriptar
0167:00401576 2D70144000 SUB EAX,00401470 ;Fin-Inicio zona a desencriptar
0167:0040157B 99 CDQ
0167:0040157C 83E207 AND EDX,07
0167:0040157F 03C2 ADD EAX,EDX
0167:00401581 C1F803 SAR EAX,03
0167:00401584 85C0 TEST EAX,EAX
0167:00401586 7E17 JLE 0040159F
0167:00401588 8BF8 MOV EDI,EAX ;Tamaño zona
bucle:
0167:0040158A 68E0164000 PUSH 004016E0 ;Clave de 16 bytes (128 bits) (extraída del serial)
0167:0040158F 56 PUSH ESI ;Inicio zona
0167:00401590 56 PUSH ESI
0167:00401591 E8AA000000 CALL 00401640 ;Desencripta 8 bytes = 2 DWORDS
0167:00401596 83C40C ADD ESP,0C
0167:00401599 83C608 ADD ESI,08
0167:0040159C 4F DEC EDI
0167:0040159D 75EB JNZ 0040158A ;Sigue en el bucle
Aquí se llega siempre que la excepción fuera STATUS_SINGLE_STEP.
0167:0040159F 5F POP EDI
0167:004015A0 C7430400000000 MOV DWORD PTR [EBX+04],00000000
0167:004015A7 C7431800070000 MOV DWORD PTR [EBX+18],00000700
0167:004015AE C70317000100 MOV DWORD PTR [EBX],00010017
Lo que se hace aquí es eliminar el BreakPoint:
DR0=0
DR7=700 (Rsvd=1,GE=1,LE=1, el resto a 0)
ContextFlags=CONTEXT_i386 OR CONTEXT_DEBUG_REGISTERS OR CONTEXT_CONTROL OR CONTEXT_INTEGER OR CONTEXT_SEGMENTS
0167:004015B4 5E POP ESI
0167:004015B5 83C8FF OR EAX,-01 ;EXCEPTION_CONTINUE_EXECUTION
0167:004015B8 5B POP EBX
0167:004015B9 C20400 RET 0004
Se devuelve EXCEPTION_CONTINUE_EXECUTION con lo que se ejecutará de nuevo la instrucción que causó la excepción.
0167:004015BC 68FF000000 PUSH 000000FF
0167:004015C1 FF1510104000 CALL [KERNEL32!ExitProcess]
0167:004015C7 33C0 XOR EAX,EAX ;EXCEPTION_CONTINUE_SEARCH
0167:004015C9 5B POP EBX
0167:004015CA C20400 RET 0004
Si la excepción no era STATUS_SINGLE_STEP, el programa llama a ExitProcess y termina.
0167:004015CD 90 NOP
0167:004015CE 90 NOP
0167:004015CF 90 NOP
Otro puñao de NOP's :)
Esta rutina genera un DWORD que devuelve en EAX y que forma parte de la primera clave de 16 bytes (ver más arriba)
0167:004015D0 55 PUSH EBP
0167:004015D1 8BEC MOV EBP,ESP
0167:004015D3 53 PUSH EBX
0167:004015D4 56 PUSH ESI
0167:004015D5 8B4508 MOV EAX,[EBP+08] ;Tercer parámetro (variable)
0167:004015D8 8B5D08 MOV EBX,[EBP+08]
0167:004015DB 25FFFF0000 AND EAX,0000FFFF
0167:004015E0 C1EB10 SHR EBX,10
0167:004015E3 81E3FFFF0000 AND EBX,0000FFFF
0167:004015E9 8B5510 MOV EDX,[EBP+10] ;Primer parámetro (tamaño)
0167:004015EC 8B750C MOV ESI,[EBP+0C] ;Segundo parámetro (dirección zona)
0167:004015EF 33C9 XOR ECX,ECX
0167:004015F1 8DA42400000000 LEA ESP,[ESP+00000000] ;Esto no hace nada
0167:004015F8 8DA42400000000 LEA ESP,[ESP+00000000] ;Esto no hace nada
0167:004015FF 90 NOP ;Esto no hace nada
bucle:
0167:00401600 8A0E MOV CL,[ESI]
0167:00401602 03C1 ADD EAX,ECX
0167:00401604 3DF1FF0000 CMP EAX,0000FFF1
0167:00401609 7C05 JL 00401610
0167:0040160B 2DF1FF0000 SUB EAX,0000FFF1
0167:00401610 03D8 ADD EBX,EAX
0167:00401612 81FBF1FF0000 CMP EBX,0000FFF1
0167:00401618 7C06 JL 00401620
0167:0040161A 81EBF1FF0000 SUB EBX,0000FFF1
0167:00401620 46 INC ESI
0167:00401621 4A DEC EDX
0167:00401622 75DC JNZ 00401600 ;Sigue en el bucle
0167:00401624 C1E310 SHL EBX,10
0167:00401627 81E30000FFFF AND EBX,FFFF0000
0167:0040162D 0BC3 OR EAX,EBX
0167:0040162F 5E POP ESI
0167:00401630 5B POP EBX
0167:00401631 5D POP EBP
0167:00401632 C3 RET
0167:00401633 90 NOP
0167:00401634 90 NOP
0167:00401635 90 NOP
0167:00401636 90 NOP
0167:00401637 90 NOP
0167:00401638 90 NOP
0167:00401639 90 NOP
0167:0040163A 90 NOP
0167:0040163B 90 NOP
0167:0040163C 90 NOP
0167:0040163D 90 NOP
0167:0040163E 90 NOP
0167:0040163F 90 NOP
Rutina de desencriptación. MrSilver ha usado el TEA (Tiny Encryption Algorithm)
Lo podeis ver en el keygen.
0167:00401640 83EC08 SUB ESP,08
0167:00401643 53 PUSH EBX
0167:00401644 55 PUSH EBP
0167:00401645 56 PUSH ESI
0167:00401646 8B742420 MOV ESI,[ESP+20] ;ESI apunta a la clave
0167:0040164A 8B442418 MOV EAX,[ESP+18] ;EAX apunta al inicio de la zona
0167:0040164E 57 PUSH EDI
0167:0040164F 8B3E MOV EDI,[ESI] ;Primer DWORD de la clave
0167:00401651 BA2037EFC6 MOV EDX,C6EF3720
0167:00401656 8B08 MOV ECX,[EAX] ;Primer DWORD a desencriptar
0167:00401658 8B4004 MOV EAX,[EAX+04] ;Segundo DWORD a desencriptar
0167:0040165B 897C2414 MOV [ESP+14],EDI
0167:0040165F 8B7E04 MOV EDI,[ESI+04] ;Segundo DWORD de la clave
0167:00401662 897C2410 MOV [ESP+10],EDI
0167:00401666 8B7E08 MOV EDI,[ESI+08] ;Tercer DWORD de la clave
0167:00401669 8B760C MOV ESI,[ESI+0C] ;Cuarto DWORD de la clave
0167:0040166C 897C2424 MOV [ESP+24],EDI
0167:00401670 8974241C MOV [ESP+1C],ESI
0167:00401674 BF20000000 MOV EDI,00000020
bucle:
0167:00401679 8B5C241C MOV EBX,[ESP+1C]
0167:0040167D 8B6C2424 MOV EBP,[ESP+24]
0167:00401681 8BF1 MOV ESI,ECX
0167:00401683 C1EE05 SHR ESI,05
0167:00401686 03F3 ADD ESI,EBX
0167:00401688 8BD9 MOV EBX,ECX
0167:0040168A C1E304 SHL EBX,04
0167:0040168D 03DD ADD EBX,EBP
0167:0040168F 8B6C2414 MOV EBP,[ESP+14]
0167:00401693 33F3 XOR ESI,EBX
0167:00401695 8D1C0A LEA EBX,[ECX+EDX]
0167:00401698 33F3 XOR ESI,EBX
0167:0040169A 8B5C2410 MOV EBX,[ESP+10]
0167:0040169E 2BC6 SUB EAX,ESI
0167:004016A0 8BF0 MOV ESI,EAX
0167:004016A2 C1EE05 SHR ESI,05
0167:004016A5 03F3 ADD ESI,EBX
0167:004016A7 8BD8 MOV EBX,EAX
0167:004016A9 C1E304 SHL EBX,04
0167:004016AC 03DD ADD EBX,EBP
0167:004016AE 33F3 XOR ESI,EBX
0167:004016B0 8D1C02 LEA EBX,[EAX+EDX]
0167:004016B3 33F3 XOR ESI,EBX
0167:004016B5 81C24786C861 ADD EDX,61C88647
0167:004016BB 2BCE SUB ECX,ESI
0167:004016BD 4F DEC EDI
0167:004016BE 75B9 JNZ 00401679 ;Sigue en el bucle
0167:004016C0 8B542420 MOV EDX,[ESP+20]
0167:004016C4 5F POP EDI
0167:004016C5 5E POP ESI
0167:004016C6 5D POP EBP
0167:004016C7 890A MOV [EDX],ECX ;ECX = primer DWORD desencriptado
0167:004016C9 894204 MOV [EDX+04],EAX ;EAX = segundo DWORD desencriptado
0167:004016CC 5B POP EBX
0167:004016CD 83C408 ADD ESP,08
0167:004016D0 C3 RET
Conclusiones:
Crackme interesante.
Vemos buenas técnicas anti-debugging y cómo modificar el contexto de un programa a pelo.
Aunque esto último
ya lo utilizó ni2 en su crackme#3.
Aunque la clave es fija (supongo que conoceis los problemas de distribuir programas con claves fijas), el unlock code y el
serial son variables, lo que le dota de cierta flexibilidad.
He hecho un keygen que genera el unlock code y el serial. Pero en el interior del serial no está la clave buena.
Lo he hecho símplemente para que veais cómo se puede implementar ese keygen. (Está hecho en Win32ASM con MASM)
También os meto un Brute Force para el que tenga varios Main-Frames en su casa y quiera petarlo a lo bruto. ;)
Saludos. Crick
Keygen & Brute Force
|
[ Entrada | Documentos Genéricos | WkT! Web Site ]
|
[ Todo el ECD | x Tipo de Protección | x Fecha de Publicación | x Orden Alfabético ]
|
|
(c)
Whiskey Kon Tekila [WkT!] - The Original Spanish Reversers.
Si necesitas contactar con
nosotros
, lee
esto
antes e infórmate de cómo puedes
ayudarnos |
|
|
|
|
|