Intro
Hoy tenemos aquí otro crackme sacado del baúl de los recuerdos. En este caso se trata de una protección por tiempo límite a través de un keyfile llamado «data.det«. Disponemos de tres días o nueve sesiones antes de que el crackme expire.
El algoritmo
La primera vez que ejecutamos el crackme, crea el fichero «data.det» y realiza lo siguiente:
- Lee el fichero data.det que inicialmente tiene 10 bytes a cero y el último byte un 60(`).
- Comprueba que tenga 11 bytes (B) y continúa.
- Al detectar el fichero vacío le mete valores codificandolos con XOR 6969. Los almacena en memoria 4030AB y siguientes.
00401000 t>/$ 6A 00 PUSH 0 ; /pModule = NULL 00401002 |. E8 0B020000 CALL <JMP.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA 00401007 |. A3 F4304000 MOV DWORD PTR DS:[4030F4],EAX ; kernel32.BaseThreadInitThunk 0040100C |. 6A 00 PUSH 0 ; /hTemplateFile = NULL 0040100E |. 68 80000000 PUSH 80 ; |Attributes = NORMAL 00401013 |. 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING 00401015 |. 6A 00 PUSH 0 ; |pSecurity = NULL 00401017 |. 6A 00 PUSH 0 ; |ShareMode = 0 00401019 |. 68 000000C0 PUSH C0000000 ; |Access = GENERIC_READ|GENERIC_WRITE 0040101E |. 68 A2304000 PUSH timetria.004030A2 ; |FileName = "DATA.DET" 00401023 |. E8 DE010000 CALL <JMP.&KERNEL32.CreateFileA> ; \CreateFileA 00401028 |. 83F8 FF CMP EAX,-1 0040102B |. 74 07 JE SHORT timetria.00401034 0040102D |. A3 14314000 MOV DWORD PTR DS:[403114],EAX ; kernel32.BaseThreadInitThunk 00401032 |. EB 18 JMP SHORT timetria.0040104C 00401034 |> 6A 30 PUSH 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL 00401036 |. 68 38304000 PUSH timetria.00403038 ; |Title = "I don't like this !" 0040103B |. 68 02304000 PUSH timetria.00403002 ; |Text = "Where is my DATA.DET file?\r\nI can't run without it..." 00401040 |. 6A 00 PUSH 0 ; |hOwner = NULL 00401042 |. E8 B3010000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA 00401047 |. E9 22010000 JMP timetria.0040116E 0040104C |> 6A 00 PUSH 0 ; /pOverlapped = NULL 0040104E |. 68 E0304000 PUSH timetria.004030E0 ; |pBytesRead = timetria.004030E0 00401053 |. 6A 32 PUSH 32 ; |BytesToRead = 32 (50.) 00401055 |. 68 AB304000 PUSH timetria.004030AB ; |Buffer = timetria.004030AB 0040105A |. FF35 14314000 PUSH DWORD PTR DS:[403114] ; |hFile = NULL 00401060 |. E8 B9010000 CALL <JMP.&KERNEL32.ReadFile> ; \ReadFile 00401065 |. 833D E0304000 0B CMP DWORD PTR DS:[4030E0],0B 0040106C |. 0F85 E9000000 JNZ timetria.0040115B 00401072 |. BB AB304000 MOV EBX,timetria.004030AB 00401077 |. 68 E4304000 PUSH timetria.004030E4 ; /pSystemTime = timetria.004030E4 0040107C |. E8 97010000 CALL <JMP.&KERNEL32.GetSystemTime> ; \GetSystemTime 00401081 |. 803B 00 CMP BYTE PTR DS:[EBX],0 00401084 |. 75 22 JNZ SHORT timetria.004010A8 ; Si existe el fichero salta a las comprobaciones 00401086 |. 51 PUSH ECX 00401087 |. 33C9 XOR ECX,ECX 00401089 |. EB 15 JMP SHORT timetria.004010A0 0040108B |> 66:8B81 E4304000 /MOV AX,WORD PTR DS:[ECX+4030E4] ; | 00401092 |. 66:35 6969 |XOR AX,6969 ; | 00401096 |. 66:8981 AB304000 |MOV WORD PTR DS:[ECX+4030AB],AX ; | 0040109D |. 83C1 02 |ADD ECX,2 ; | Bucle de codificacion de data.det por primera vez 004010A0 |> 83F9 08 CMP ECX,8 ; | 004010A3 |.^ 76 E6 \JBE SHORT timetria.0040108B ; | 004010A5 |. 59 POP ECX ; kernel32.7580EE1C 004010A6 |. EB 3A JMP SHORT timetria.004010E2
Vigilando el proceso de creación del archivo podemos llegar a la conclusión de como se genera.
- Los dos primeros bytes son el año = 2014 = 0x7DE. 7DE XOR 6969 = 6EB7.
- Los dos siguientes son el mes = 10 = 0xA. A XOR 6969 = 6963.
- Los dos siguientes usa un 4 (día de la semana???) = 0x4. 4 XOR 6969 = 696D.
- Los dos siguientes son el día del mes = 2 = 0x2. 2 XOR 6969 = 696B
- Los dos siguientes usa un 1 = 0x1. 1 XOR 6969 = 6968.
- El número de sesiones lo deja como está, 60.
Estado de la memoria:
004030AB B7 6E 63 69 6D 69 6B 69 68 69 60 ·ncimikihi`
- Finalmente le resta 1 al número de sesiones y guarda el fichero.
004010E2 |> \A0 B5304000 MOV AL,BYTE PTR DS:[4030B5] 004010E7 |. 34 69 XOR AL,69 004010E9 |. 3C 00 CMP AL,0 004010EB |. /74 59 JE SHORT timetria.00401146 004010ED |. |FEC8 DEC AL 004010EF |. |A2 01304000 MOV BYTE PTR DS:[403001],AL 004010F4 |. |34 69 XOR AL,69 004010F6 |. |A2 B5304000 MOV BYTE PTR DS:[4030B5],AL 004010FB |. |6A 00 PUSH 0 ; /Origin = FILE_BEGIN 004010FD |. |6A 00 PUSH 0 ; |pOffsetHi = NULL 004010FF |. |6A 00 PUSH 0 ; |OffsetLo = 0 00401101 |. |FF35 14314000 PUSH DWORD PTR DS:[403114] ; |hFile = 00000034 (window) 00401107 |. |E8 18010000 CALL <JMP.&KERNEL32.SetFilePointer> ; \SetFilePointer 0040110C |. |6A 00 PUSH 0 ; /pOverlapped = NULL 0040110E |. |68 E0304000 PUSH timetria.004030E0 ; |pBytesWritten = timetria.004030E0 00401113 |. |6A 0B PUSH 0B ; |nBytesToWrite = B (11.) 00401115 |. |68 AB304000 PUSH timetria.004030AB ; |Buffer = timetria.004030AB 0040111A |. |FF35 14314000 PUSH DWORD PTR DS:[403114] ; |hFile = 00000034 (window) 00401120 |. |E8 05010000 CALL <JMP.&KERNEL32.WriteFile> ; \WriteFile
En cada ejecución realiza tres comprobaciones.
Recordemos el contenido del fichero:
B7 6E 63 69 6D 69 6B 69 68 69 60 ·ncimikihi`
1) Mes y año (4 primeros bytes)
004010A8 |> \8B0D AB304000 MOV ECX,DWORD PTR DS:[4030AB] ; ECX=69636EB7 004010AE |. 81F1 69696969 XOR ECX,69696969 ; 69636EB7 xor 69696969 = A07DE (A = mes y 7DE = año) 004010B4 |. A1 E4304000 MOV EAX,DWORD PTR DS:[4030E4] 004010B9 |. 3BC1 CMP EAX,ECX ; Compara con mes y año actuales 004010BB |. 0F85 85000000 JNZ timetria.00401146 ; Bad boy
2) Día (7º y 8º byte)
004010C1 |. 66:8B0D B1304000 MOV CX,WORD PTR DS:[4030B1] ; CX = 696B 004010C8 |. 66:81F1 6969 XOR CX,6969 ; 696B xor 6969 = 2 004010CD |. 66:A1 EA304000 MOV AX,WORD PTR DS:[4030EA] ; AX = día actual obtenido con GetSystemTime 004010D3 |. 66:2BC1 SUB AX,CX ; Los resta 004010D6 |. 66:83F8 03 CMP AX,3 ; Compara con 3 004010DA |. 77 6A JA SHORT timetria.00401146 ; Si el resultado >=3 Bad Boy
3) Sesiones (11º byte)
004010DC |. 2805 00304000 SUB BYTE PTR DS:[403000],AL ; 004010E2 |> A0 B5304000 MOV AL,BYTE PTR DS:[4030B5] ; AL = numero de sesiones actual 004010E7 |. 34 69 XOR AL,69 ; 61 Xor 69 = 8 004010E9 |. 3C 00 CMP AL,0 ; Compara con 0 004010EB |. 74 59 JE SHORT timetria.00401146 ; Salta si hemos superado las 9 sesiones. Bad boy 004010ED |. FEC8 DEC AL ; Si no le resta 1 004010EF |. A2 01304000 MOV BYTE PTR DS:[403001],AL 004010F4 |. 34 69 XOR AL,69 ; y le hace xor 69 para codificar el nuevo valor de sesión 004010F6 |. A2 B5304000 MOV BYTE PTR DS:[4030B5],AL
Con esto ya podemos alterar el archivo a nuestro antojo sin necesidad de parchear.
Keygen
Try ano = ano Xor 26985 mes = mes Xor 26985 dia = dia Xor 26985 anos = Hex(ano).ToString mess = Hex(mes).ToString dias = Hex(dia).ToString If txtsesiones.Text <= 255 Then sesioness = Hex(sesiones) Else sesiones = 255 End If sesioness = Hex(sesiones) 'key = 00 00 00 00 00 00 00 00 00 00 00 'key = año+año+mes+mes+X+X+dia+dia+X+sesiones key = Chr(Convert.ToInt32(anos.Substring(2, 2), 16)) & Chr(Convert.ToInt32(anos.Substring(0, 2), 16)) _ & Chr(Convert.ToInt32(mess.Substring(2, 2), 16)) & Chr(Convert.ToInt32(mess.Substring(0, 2), 16)) _ & Chr(106) & Chr(105) _ & Chr(Convert.ToInt32(dias.Substring(2, 2), 16)) & Chr(Convert.ToInt32(dias.Substring(0, 2), 16)) _ & Chr(103) & Chr(105) _ & Chr(Convert.ToInt32(sesioness.Substring(0, 2), 16)) 'Creo el archivo llave Dim ruta As String = Application.StartupPath & "\DATA.DET" If File.Exists(ruta) Then File.Delete(ruta) End If Using sw As StreamWriter = New StreamWriter(ruta, True, System.Text.Encoding.Default) sw.Write(key) sw.Close() End Using MsgBox("DATA.DET generado correctamente", MsgBoxStyle.Information + MsgBoxStyle.OkOnly, "Info") Catch ex As Exception MsgBox("Ocurrió algún error" & vbCrLf & ex.Message) End Try