Intro
Es un crackme realizado en ensamblador y en el que el objetivo es remover la NAG de la forma más limpia posible.
Analizando a la víctima
Abrimos el crackme con Olly y ya a simple vista vemos los mensajes de la Nag y parte del código interesante. Si necesitaramos localizar la Nag podemos mirar en las intermodular calls las típicas subrutinas, en este caso se ve claramente a MessageBoxA, bastaría con poner un breakpoint para localizar quien llama.
Aquí vemos la implicación de MessageBoxA.
004010A7 |> \6A 40 PUSH 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL 004010A9 |. 68 61304000 PUSH Nag1.00403061 ; |Title = "[NAG] Please register this software!" 004010AE |. 68 86304000 PUSH Nag1.00403086 ; |Text = "[BULLSHIT] Please register this software for support and you'll receive the full version!" 004010B3 |. FF75 08 PUSH [ARG.1] ; |hOwner = 7FFDF000 004010B6 |. E8 49010000 CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA ........ 00401137 |. 6A 40 PUSH 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL 00401139 |. 68 6E324000 PUSH Nag1.0040326E ; |Title = "Thank you!" 0040113E |. 68 79324000 PUSH Nag1.00403279 ; |Text = "Thank you for registering this software!" 00401143 |. FF75 08 PUSH [ARG.1] ; |hOwner = 7FFDF000 00401146 |. E8 B9000000 CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA ........ 00401155 |. 6A 40 PUSH 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL 00401157 |. 68 E0304000 PUSH Nag1.004030E0 ; |Title = "About" 0040115C |. 68 E6304000 PUSH Nag1.004030E6 ; |Text = "Remove the NAG by TDC\r\n\n..: Coded by\t: TDC\t\t\t:..\t\r\n..: Also known as\t: The Dutch Cracker\t:..\t\r\n..: Protection\t: Custom\t\t\t:..\t\r\n..: Contact info\t: tdc123@gmail.com\t:..\t\r\n..: Release date\t: 09-08-2005\t\t:..\t"... 00401161 |. FF75 08 PUSH [ARG.1] ; |hOwner = 7FFDF000 00401164 |. E8 9B000000 CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA
Un poco encima vemos la función SetDlgItemTextA, que nos mostrará el mensaje de que hemos parcheado correctamente.
00401106 |> \68 21304000 PUSH Nag1.00403021 ; /Text = "Dirty crack! Nag removed not registered!" 0040110B |. 6A 73 PUSH 73 ; |ControlID = 73 (115.) 0040110D |. FF75 08 PUSH [ARG.1] ; |hWnd = 7FFDF000 00401110 |. E8 FB000000 CALL <JMP.&user32.SetDlgItemTextA> ; \SetDlgItemTextA 00401115 |. EB 36 JMP SHORT Nag1.0040114D 00401117 |> 68 10304000 PUSH Nag1.00403010 ; /Text = "Nag not removed!" 0040111C |. 6A 73 PUSH 73 ; |ControlID = 73 (115.) 0040111E |. FF75 08 PUSH [ARG.1] ; |hWnd = 7FFDF000 00401121 |. E8 EA000000 CALL <JMP.&user32.SetDlgItemTextA> ; \SetDlgItemTextA 00401126 |. EB 25 JMP SHORT Nag1.0040114D 00401128 |> 68 4A304000 PUSH Nag1.0040304A ; /Text = "Clean crack! Good Job!" 0040112D |. 6A 73 PUSH 73 ; |ControlID = 73 (115.) 0040112F |. FF75 08 PUSH [ARG.1] ; |hWnd = 7FFDF000 00401132 |. E8 D9000000 CALL <JMP.&user32.SetDlgItemTextA> ; \SetDlgItemTextA
Encima de SetDlgItemTextA vemos el código que analiza si la Nag tiene que aparecer.
004010E6 |. E8 C4000000 CALL Nag1.004011AF ; ; Llamada interesante a analizar 004010EB |. 803D B0324000 03 CMP BYTE PTR DS:[4032B0],3 004010F2 |. 74 12 JE SHORT Nag1.00401106 ; ; Si de la llamada volvemos con un 3 -> Parcheo chapuza 004010F4 |. 803D B0324000 02 CMP BYTE PTR DS:[4032B0],2 004010FB |. 74 1A JE SHORT Nag1.00401117 ; ; Si de la llamada volvemos con un 2 -> Sin parchear 004010FD |. 803D B0324000 01 CMP BYTE PTR DS:[4032B0],1 00401104 |. 74 22 JE SHORT Nag1.00401128 ; ; Si de la llamada volvemos con un 1 -> Buen trabajo Joe! ........ 004011AF /$ 68 A2324000 PUSH Nag1.004032A2 ; /String2 = "Value1" 004011B4 |. 68 A9324000 PUSH Nag1.004032A9 ; |String1 = "Value2" 004011B9 |. E8 64000000 CALL <JMP.&kernel32.lstrcmpA> ; \lstrcmpA 004011BE |. 50 PUSH EAX ; kernel32.BaseThreadInitThunk 004011BF |. 85C0 TEST EAX,EAX ; kernel32.BaseThreadInitThunk 004011C1 |. 75 10 JNZ SHORT Nag1.004011D3 004011C3 |. 33C0 XOR EAX,EAX ; kernel32.BaseThreadInitThunk 004011C5 |. 58 POP EAX ; kernel32.75CDEE1C 004011C6 |. 85C0 TEST EAX,EAX ; kernel32.BaseThreadInitThunk 004011C8 |. 74 15 JE SHORT Nag1.004011DF 004011CA |. C605 B0324000 03 MOV BYTE PTR DS:[4032B0],3 004011D1 |. EB 17 JMP SHORT Nag1.004011EA 004011D3 |> 58 POP EAX ; kernel32.75CDEE1C 004011D4 |. 33C0 XOR EAX,EAX ; kernel32.BaseThreadInitThunk 004011D6 |. C605 B0324000 02 MOV BYTE PTR DS:[4032B0],2 004011DD |. EB 0B JMP SHORT Nag1.004011EA 004011DF |> 33C0 XOR EAX,EAX ; kernel32.BaseThreadInitThunk 004011E1 |. C605 B0324000 01 MOV BYTE PTR DS:[4032B0],1 004011E8 |. EB 00 JMP SHORT Nag1.004011EA 004011EA \> C3 RETN
Vemos dentro del Call 4011AF que Compara si Value1 = Value2 y dependiendo de esa comparación guarda en memoria (4032B0), los valores 1, 2 ó 3.
Basta con modificar en un editor hexadecimal la parabra «Value2» por «Value1» y ya tenemos el problema resuelto.
Al pulsar Re-Check
Notas finales
Se podía haber parcheado un montón de código para obtener el mismo resultado pero fijándonos en el código lo hemos conseguido parcheando un solo byte. Recuerda, cuando halla que parchear, cuantos menos bytes mejor.