Intro
El crackme que analizamos hoy está hecho en ensamblador y si bien su dificultad es baja, la creación del keygen es un poco liosa. Al keygen que veremos más adelante, le he dado cierta aleatoriedad para que quede más elegante.
El crackme comprueba el serial en función de un identificador de 4 dígitos que el mismo crackme genera.
Análisis
Coje nuestro serial mediante la función GetDlgItemTextA.
004010D3 |. 68 FF000000 PUSH 0FF ; /MaxCount = 255. 004010D8 |. 68 40324000 PUSH OFFSET 00403240 ; |String 004010DD |. 68 EC030000 PUSH 3EC ; |ItemID = 1004. 004010E2 |. FF75 08 PUSH DWORD PTR SS:[ARG.1] ; |hDialog => [ARG.1] 004010E5 |. E8 6E010000 CALL <JMP.&user32.GetDlgItemTextA> ; \USER32.GetDlgItemTextA 004010EA |. 68 40324000 PUSH OFFSET 00403240 ; /String 004010EF |. E8 52010000 CALL <JMP.&kernel32.lstrlenA> ; \KERNEL32.lstrlen 004010F4 |. A3 47334000 MOV DWORD PTR DS:[403347],EAX 004010F9 |. 33DB XOR EBX,EBX 004010FB |. 33C0 XOR EAX,EAX 004010FD |. EB 54 JMP SHORT 00401153
Comprueba que nuestro serial esté formado por números (30h – 39h), letras de la A a la F (41h – 46h) y el guión (2Dh), es decir, el alfabeto hexadecimal más el guión. Si hay algún dígito indeseado nos tira fuera.
004010FF |> 8A83 40324000 /MOV AL,BYTE PTR DS:[EBX+403240] 00401105 |. 3C 2D |CMP AL,2D 00401107 |. 74 40 |JE SHORT 00401149 00401109 |. 3C 30 |CMP AL,30 0040110B |. 74 3C |JE SHORT 00401149 0040110D |. 3C 31 |CMP AL,31 0040110F |. 74 38 |JE SHORT 00401149 00401111 |. 3C 32 |CMP AL,32 00401113 |. 74 34 |JE SHORT 00401149 00401115 |. 3C 33 |CMP AL,33 00401117 |. 74 30 |JE SHORT 00401149 00401119 |. 3C 34 |CMP AL,34 0040111B |. 74 2C |JE SHORT 00401149 0040111D |. 3C 35 |CMP AL,35 0040111F |. 74 28 |JE SHORT 00401149 00401121 |. 3C 36 |CMP AL,36 00401123 |. 74 24 |JE SHORT 00401149 00401125 |. 3C 37 |CMP AL,37 00401127 |. 74 20 |JE SHORT 00401149 00401129 |. 3C 38 |CMP AL,38 0040112B |. 74 1C |JE SHORT 00401149 0040112D |. 3C 39 |CMP AL,39 0040112F |. 74 18 |JE SHORT 00401149 00401131 |. 3C 41 |CMP AL,41 00401133 |. 74 14 |JE SHORT 00401149 00401135 |. 3C 42 |CMP AL,42 00401137 |. 74 10 |JE SHORT 00401149 00401139 |. 3C 43 |CMP AL,43 0040113B |. 74 0C |JE SHORT 00401149 0040113D |. 3C 44 |CMP AL,44 0040113F |. 74 08 |JE SHORT 00401149 00401141 |. 3C 45 |CMP AL,45 00401143 |. 74 04 |JE SHORT 00401149 00401145 |. 3C 46 |CMP AL,46 00401147 |. 75 07 |JNE SHORT 00401150 00401149 |> 8305 4B334000 |ADD DWORD PTR DS:[40334B],1 00401150 |> 83C3 01 |ADD EBX,1 00401153 |> 3B1D 47334000 |CMP EBX,DWORD PTR DS:[403347] 00401159 |.^ 76 A4 \JBE SHORT 004010FF 0040115B |. A1 47334000 MOV EAX,DWORD PTR DS:[403347] 00401160 |. 3905 4B334000 CMP DWORD PTR DS:[40334B],EAX ; si no coincide el tamaño del serial con el 00401166 |. 0F85 94000000 JNE 00401200 ; contador nos tira fuera
La comprobación del serial la realiza sumando el valor ascii del primer dígito al valor ascii del tercero y sucesivos y a continuación restando la suma anterior al ID. Cuando finalice la comprobación de todos los dígitos del serial, el restador tiene que ser cero, de lo contrario nos tira fuera. Si el ID es cero también nos tira fuera.
Ejemplo (base 10)para ID = 4011 y SERIAL: 1-23456
- Valores del serial: 1(49) -(no se usa) 2(50) 3(51) 4(52) 5(53) 6(54)
- 1º + 3º: 49 + 50 = 99
- 4011 – 99 = 3912
- 1º + 4º: 49 + 51 = 100
- 3912 – 100 = 3812
- 1º + 5º: 49 + 52 = 101
- 3812 – 101 = 3711
- 1º + 6º: 49 + 53 = 102
- 3711 – 102 = 3609
- 1º + 7º: 49 + 54 = 103
- 3609 – 103 = 3506
- ¿3506 = 0?
0040116C |. 33C0 XOR EAX,EAX 0040116E |. BB 02000000 MOV EBX,2 00401173 |. A0 40324000 MOV AL,BYTE PTR DS:[403240] 00401178 |. A3 43334000 MOV DWORD PTR DS:[403343],EAX 0040117D |. EB 13 JMP SHORT 00401192 0040117F |> 8A83 40324000 /MOV AL,BYTE PTR DS:[EBX+403240] ; Coje el dígito correspondiente 00401185 |. 0305 43334000 |ADD EAX,DWORD PTR DS:[403343] ; 1ºdig + dig 0040118B |. 2905 4F334000 |SUB DWORD PTR DS:[40334F],EAX ; ID - (1ºdig + dig) 00401191 |. 43 |INC EBX 00401192 |> 3B1D 47334000 |CMP EBX,DWORD PTR DS:[403347] 00401198 |.^ 72 E5 \JB SHORT 0040117F 0040119A |. 833D 4F334000 CMP DWORD PTR DS:[40334F],0 ; CHECK RESTADOR SEA = 0 004011A1 |. 75 49 JNE SHORT 004011EC 004011A3 |. 833D 3F334000 CMP DWORD PTR DS:[40333F],0 ; CHECK ID <> 0 004011AA |. 74 40 JE SHORT 004011EC 004011AC |. FF35 3F334000 PUSH DWORD PTR DS:[40333F] ; /<%d> = 0 004011B2 |. 68 00304000 PUSH OFFSET 00403000 ; |Format = "REGISTRADO CON ID:%d" 004011B7 |. 68 40324000 PUSH OFFSET 00403240 ; |Buf 004011BC |. E8 A9000000 CALL <JMP.&user32.wsprintfA> ; \USER32.wsprintfA
Como veis, el resultado de ir restando todos los dígitos de nuestro serial con la ID debe ser cero para que el serial sea correcto.
Keygen
Lo primero que se me ocurre para obtener una solución directa es buscar una combinación de dígito + dígito que sea múltiplo del ID. Para ello podemos usar la función módulo. La función módulo lo que hace es darnos el resto de la división de dos números, de modo que si el resto es cero los números son múltiplos. Para ello debemos cruzar todos los números y letras hasta encontrar los dígitos múltiplos del ID. Un serial de este primer tipo quedaría algo así como 1-FFFFFFFFFFFFFFFFFF ya que como el primer dígito es fijo el otro se repetirá tanta veces como sea necesario para hacer que el ID sea cero.
Con nuestro reducido alfabeto, cabe la posibilidad de que no encontremos una combinación válida, por lo que tendremos que pensar en un plan B. El plan B que se me ocurre a mi es intentar forzar el plan A restando caracteres aleatorios al ID y volviendo a comprobar si encontramos múltiplos del nuevo ID. Un serial de este tipo quedaría más elegante, por ejemplo 3-A6D53B628BBBBB.
Os dejo unos cuantos números de serie.
- Tipo A
- ID: 1111 SERIAL: 0-55555555555
- ID: 2500 SERIAL: 0-4444444444444444444444444
- ID: 4982 SERIAL: 1-99999999999999999999999999999999999999999999999
- ID: 4992 SERIAL: 0-0000000000000000000000000000000000000000000000000000
- Tipo B
- ID: 1112 SERIAL: 9-19247C5555
- ID: 2499 SERIAL: A-C5ADC2233333333333333
- ID: 4981 SERIAL: 7-C6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
- ID: 4999 SERIAL: 4-A37BEEB8146A5CE6ECFB422B1BFF8474E852314F5A999
'Keygen for Flamer's asm keygenme Dim id As Integer Dim serial As String Dim tmp, tmp2, na, nb As Integer Dim alfabeto As Integer() = New Integer() {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70} Dim r As Random = New Random 'Button generate Private Sub btngen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btngen.Click ini: If txtid.TextLength <> 4 Then GoTo Mal id = txtid.Text txtdebug.Text = "" na = alfabeto(r.Next(1, 16)) serial = Chr(na) & "-" tmp = id For i = 0 To alfabeto.Length - 1 For y = 0 To alfabeto.Length - 1 'Solución directa If id Mod (alfabeto(i) + alfabeto(y)) = 0 Then tmp = id / (alfabeto(i) + alfabeto(y)) txtserial.Text = Chr(alfabeto(i)) & "-" For z = 0 To tmp - 1 txtserial.Text &= Chr(alfabeto(y)) Next GoTo fuera End If 'Indirecta con aleatoriedad nb = alfabeto(r.Next(1, 16)) tmp = tmp - (na + nb) serial &= Chr(nb) If tmp Mod (na + nb) = 0 Then tmp2 = tmp / (na + nb) For z = 0 To tmp2 - 1 serial &= Chr(nb) Next txtserial.Text = serial GoTo fuera End If If tmp < 0 Then GoTo ini Else txtdebug.Text &= tmp & " " End If Next Next Mal: txtserial.Text = "¿id?" fuera: End Sub
Me doy cuenta que en el keygen no he utilizado el guión, pero no pasa nada, se lo dejo al lector como curiosidad.