Introducción

Hoy tenemos aquí un bonito crackme matemático realizado por Spider. El crackme está realizado en ensamblador y precisamente por eso, vamos a tener que lidiar con ciertas peculiaridades al realizar el keygen con un lenguaje de bajo nivel.

Al inicio comprueba la longitud del nombre y de el número de serie. El nombre debe tener al menos 6 caracteres y el número de serie debe tener 10. Os adelanto ya que la asignación de memoria del nombre es de 9 caracteres, es decir, da igual la longitud del nombre que solo va a usar 9.

004014AD | E8 1A 02 00 00           | call <pythagoras.GetWindowTextA>        | ;Lee el nombre
004014B2 | 83 F8 06                 | cmp eax,6                               | ;Nombre >=6 caracteres
004014B5 | 0F 82 03 01 00 00        | jb pythagoras.4015BE                    |
004014BB | 6A 14                    | push 14                                 |
004014BD | 68 D9 31 40 00           | push pythagoras.4031D9                  | ;004031D9:"1234567890"
004014C2 | FF 35 10 32 40 00        | push dword ptr ds:[403210]              |
004014C8 | E8 FF 01 00 00           | call <pythagoras.GetWindowTextA>        | ;Lee el serial
004014CD | 83 F8 0A                 | cmp eax,A                               | ;Serial debe tener 10 (A) caracteres
004014D0 | 0F 85 E8 00 00 00        | jne pythagoras.4015BE                   |

Sabiendo esto introducimos Nombre: deurus y Serial: 1234567890

A continuación chequea que nuestro serial tenga caracteres hexadecimales.

004014DA | 8A 81 D9 31 40 00        | mov al,byte ptr ds:[ecx+4031D9]         | ; ecx+004031D9:"1234567890"
004014E0 | 3C 00                    | cmp al,0                                | ; contador del bucle
004014E2 | 74 1F                    | je pythagoras.401503                    | ; fin del bucle
004014E4 | 3C 30                    | cmp al,30                               | ; 0x30 = número 1
004014E6 | 0F 82 D2 00 00 00        | jb pythagoras.4015BE                    | ; < 30 bad boy
004014EC | 3C 46                    | cmp al,46                               | ; 0x46 = letra F
004014EE | 0F 87 CA 00 00 00        | ja pythagoras.4015BE                    | ; > 46 bad boy
004014F4 | 3C 39                    | cmp al,39                               | ; 0x39 = número 9
004014F6 | 76 08                    | jbe pythagoras.401500                   | ; <=39 ok continua el bucle
004014F8 | 3C 41                    | cmp al,41                               | ; 0x41 = letra A
004014FA | 0F 82 BE 00 00 00        | jb pythagoras.4015BE                    | ; <41 bad boy
00401500 | 41                       | inc ecx                                 | ; contador += 1
00401501 | EB D7                    | jmp pythagoras.4014DA                   | ; bucle

Continua realizando un sumatorio con nuestro nombre, pero tenemos que tener especial cuidado al tratamiento de los datos, ya que el crackme al estar hecho en ensamblador puede jugar con los registros como quiere y eso nos puede inducir a error.

0040150B | 3C 00                    | cmp al,0                                | ; ¿Fin bucle?
0040150D | 74 05                    | je pythagoras.401514                    | ; Salta fuera del bucle si procede
0040150F | 02 D8                    | add bl,al                               | ; bl = bl + al
00401511 | 41                       | inc ecx                                 | ; contador +=1
00401512 | EB F1                    | jmp pythagoras.401505                   | ; bucle

Si os fijáis utiliza registros de 8 bits como son AL y BL. Debajo os dejo una explicación de EAX pero para EBX es lo mismo.

               EAX
-----------------------------------
                         AX
                  -----------------
                     AH       AL
                  -------- --------
00000000 00000000 00000000 00000000
 (8bit)   (8bit)   (8bit)   (8bit)
 

  EAX     (32 bit)
--------
     AX   (16 bit)
    ----
    AHAL  (AH y AL 8 bit)
--------
00000000

El uso de registros de 8 bits nos implica tomar precauciones al realizar el Keygen debido a que por ejemplo, en .Net no tenemos la capacidad de decirle que haga una suma y que nos devuelva solamente 8 bits del resultado. Veamos como ejemplo para el nombre «deurus». La suma de los caracteres hexadecimales quedaría:

64+65+75+72+75+73 = 298, es decir, EAX = 00000298

Pero recordad que el crackme solo cogerá el 98 que es lo correspondiente al registro AL. De momento nos quedamos con nuestro SUMNOMBRE = 98.

Primera condición

A continuación coge los dos primeros caracteres del serial y les resta nuestro SUMNOMBRE y comprueba que el resultado esté entre 4 (0x4) y -4 (0xFC).

0040154B | 0F B6 05 F3 31 40 00     | movzx eax,byte ptr ds:[4031F3]          |
00401552 | 8A C8                    | mov cl,al                               |
00401554 | 2A CB                    | sub cl,bl                               | ; CL = CL - BL | CL = 12 - 98 = 7A
00401556 | 80 F9 04                 | cmp cl,4                                | ; Compara CL con 4
00401559 | 7F 63                    | jg pythagoras.4015BE                    | ; Salta si es mayor
0040155B | 80 F9 FC                 | cmp cl,FC                               | ; Compara CL con FC (-4)
0040155E | 7C 5E                    | jl pythagoras.4015BE                    | ; Salta si es menor

Como veis, el resultado de la resta da 7A (122) que al ser mayor que 4 nos echa vilmente. Aquí de nuevo utiliza registros de 8 bits por lo que debemos tener cuidado con las operaciones matemáticas para no cometer errores, veamos un ejemplo para clarificar de aquí en adelante.

Utilizando 8 bits
-----------------
12 - 98 = 7A que en decimal es 122

Utilizando 16 bits
------------------
0012 - 0098 = FF7A que en decimal es -134

Ahora ya veis la diferencia entre FC (252) y FFFC (-4). Estrictamente, el crackme comprueba el rango entre 4 (4) y FC (122) al trabajar con registros de 8 bits pero nosotros, como veremos más adelante tomaremos el rango entre 4 y -4. De momento, para poder continuar depurando cambiamos los dos primeros caracteres del serial de 12 a 98, ya que 98 – 98 = 0 y cumple la condición anterior.

Introducimos Nombre: deurus y Serial: 9834567890

Segunda condición

Analicemos el siguiente código.

00401560 | F7 E0                    | mul eax                                 | ; EAX = EAX * EAX
00401562 | 8B D8                    | mov ebx,eax                             | ; EBX = EAX
00401564 | 0F B7 05 F4 31 40 00     | movzx eax,word ptr ds:[4031F4]          | ; EAX = 3456 (4 dígitos siguientes del serial)
0040156B | F7 E0                    | mul eax                                 | ; EAX = EAX * EAX
0040156D | 03 D8                    | add ebx,eax                             | ; EBX = EBX + EAX
0040156F | 0F B7 05 F6 31 40 00     | movzx eax,word ptr ds:[4031F6]          | ; EAX = 7890 (4 últimos dígitos del serial)
00401576 | F7 E0                    | mul eax                                 | ; EAX = EAX * EAX
00401578 | 33 C3                    | xor eax,ebx                             | ; EAX
0040157A | 75 42                    | jne pythagoras.4015BE                   | ; Salta si el flag ZF no se activa

En resumen:

  • 98 * 98 = 5A40 (98²)
  • 3456 * 3456 = 0AB30CE4 (3456²)
  • 0AB36724 + 5A40 = 0AB36724
  • 7890 * 7890 = 38C75100 (7890²)
  • 38C75100 XOR 0AB36724 = 32743624
  • Si el resultado del XOR no es cero nuestro serial no pasa la comprobación.

Es decir, Pitágoras entra en escena -> 7890² = 98² + 3456²

Serial = aabbbbcccc

Tercera condición

Finalmente comprueba lo siguiente:

0040157C | 66 A1 F6 31 40 00        | mov ax,word ptr ds:[4031F6]             | ; AX = 7890
00401582 | 66 2B 05 F4 31 40 00     | sub ax,word ptr ds:[4031F4]             | ; AX = 7890 - 3456 = 443A
00401589 | 2C 08                    | sub al,8                                | ; AL = 3A - 8 = 32
0040158B | 75 31                    | jne pythagoras.4015BE                   | ; Si el resultado de la resta no ha sido cero, serial no válido
0040158D | 6A 30                    | push 30                                 |
0040158F | 68 B0 31 40 00           | push pythagoras.4031B0                  | ;004031B0:":-) Well done!!!"
00401594 | 68 7F 31 40 00           | push pythagoras.40317F                  | ;0040317F:"Bravo, hai trovato il seriale di questo CrackMe!"
00401599 | FF 75 08                 | push dword ptr ds:[ebp+8]               |

En resumen:

  • 7890 – 3456 – 8 = 0

Creación del Keygen

Nuestro serial tiene que cumplir tres condiciones para ser válido.

  • a – SUMNOMBRE debe estar entre 4 y -4
  • c² = a² + b²
  • c – b – 8 = 0

Como hemos dicho anteriormente, tomaremos el SUMNOMBRE y le sumaremos y restaremos valores siempre y cuando el resultado esté entre 4 y -4. Para deurus hemos dicho que el SUMNOMBRE es 98 por lo que los posibles valores de «a» se pueden ver debajo. Además debemos tener en cuenta que el crackme solo lee los 9 primeros dígitos del nombre.

98-4 = 94		
98-3 = 95		
98-2 = 96		
98-1 = 97		
98-0 = 98		
98+1 = 99		
98+2 = 9A		
98+3 = 9B		
98+4 = 9C

Es evidente que para encontrar el valor de «c» vamos a tener que utilizar fuerza bruta chequeando todos los valores  de «b» comprendidos entre 0 y FFFF (65535). Además, como trabajaremos en un lenguaje de alto nivel, debemos descartar los resultados decimales. Esto nos limitará los seriales válidos asociados a un determinado nombre. Si realizáramos el keygen en ensamblador obtendríamos bastantes más seriales válidos.

Una vez encontrados los valores enteros de la operación «c² = a² + b²», se debe cumplir que «c – b – 8 = 0», lo que nos limitará bastante los resultados.

    Private Sub btn_generar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_generar.Click
        Try
            If txt_nombre.TextLength > 5 Then
                lst_serials.Items.Clear()
                Dim tmp, c, cx As String
                Dim sumanombre, tmp2 As Integer
                If txt_nombre.TextLength > 9 Then tmp2 = 8 Else tmp2 = txt_nombre.TextLength - 1
                'Calculo el SUMNOMBRE
                For i = 0 To tmp2
                    sumanombre += Asc(Mid(txt_nombre.Text, i + 1, 1)) 'Acumulo suma
                    tmp = Strings.Right(Hex(sumanombre).ToString, 2)  'Solo 8 bits (Registro AL)
                    sumanombre = Val("&H" & tmp) 'Paso a decimal
                Next
                tmp = Strings.Right(Hex(sumanombre).ToString, 2)
                sumanombre = CInt("&H" & tmp)
                txtdebug.Text = "- SumNombre = " & Hex(sumanombre) & vbCrLf
                txtdebug.Text &= "----------------------------------------------" & vbCrLf
                Dim a(8) As Integer
                '
                'a - sumanombre >=4 y <=4
                '
                a(0) = sumanombre - 4
                a(1) = sumanombre - 3
                a(2) = sumanombre - 2
                a(3) = sumanombre - 1
                a(4) = sumanombre
                a(5) = sumanombre + 1
                a(6) = sumanombre + 2
                a(7) = sumanombre + 3
                a(8) = sumanombre + 4
                txtdebug.Text &= "- Posibles valores de 'a'" & vbCrLf
                For i = 0 To a.Length - 1
                    txtdebug.Text &= Hex(a(i)) & " "
                Next
                txtdebug.Text &= "----------------------------------------------" & vbCrLf
                txtdebug.Text &= "- Buscando valores de b y c" & vbCrLf
                txtdebug.Text &= "Serial = aabbbbcccc" & vbCrLf
                '
                'c = sqr(a^2 + b^2)
                '
                txtdebug.Text &= "(1) c = raiz(a^2 + b^2)" & vbCrLf
                txtdebug.Text &= "(2) c - b - 8 = 0" & vbCrLf
                For i = 0 To a.Length - 1 ' todas las posibilidades de a
                    For b = 0 To 65535 'b -> 0000 - FFFF
                        c = Math.Sqrt(a(i) ^ 2 + b ^ 2)
                        If c.Contains(".") Then 'busco enteros
                        Else
                            cx = c - b - 8
                            cx = Hex(cx).PadLeft(4, "0"c)
                            lbl_info.Text = cx
                            If cx = "0000" Then
                                txtdebug.Text &= " (1) " & Hex(c).PadLeft(4, "0"c) & " = raiz(" & Hex(a(i)).PadLeft(2, "0"c) & "^2 + " & Hex(b).PadLeft(4, "0"c) & "^2)" & vbCrLf
                                lst_serials.Items.Add(Hex(a(i)).PadLeft(2, "0"c) & Hex(b).PadLeft(4, "0"c) & Hex(c).PadLeft(4, "0"c))
                                txtdebug.Text &= " (2) " & Hex(c).PadLeft(4, "0"c) & " - " & Hex(b).PadLeft(4, "0"c) & " - 8 = 0" & vbCrLf
                            End If
                        End If
                        Application.DoEvents()
                    Next
                Next
                lbl_info.Text = "Búsqueda finalizada"
            End If
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try

Enlaces