| ESTUDIO COLECTIVO DE DESPROTECCIONES Última Actualizacion: 25/10/2001 |
Programa | 1stClass 1.01 Dream Collection 3.1 LMD-Tools 4.51 Multimedia Tools 1.7 OaAgent Component Set SMExport Component Suite 3.50 SMS Driver 1.0 TUsersSTD 1.0 VCLZip 2.20 | W95 / W98 / NT
Descripción | Componentes o colecciones de componentes VCL para Borland/Inprise Delphi.
Tipo | Trial
Tipo de Tutorial | [X]Original, []Adaptación, []Aplicación, []Traducción (de todo un poco ... lo dejamos en 3.5 ;-)
Url | ftp://ftp.woll2woll.com/pub/wol2wol/1stclass/Trial1stclass_d5.exe ftp://ftp.woll2woll.com/pub/wol2wol/1stclass/Demo1st.exe http://www.dream-com.com/Download/trials/dccol_d5.exe http://www.webwatson.de/download/files/lmdt4_d4.exe http://www.swiftsoft.de/files/mmtool5.exe http://www.swiftsoft.de/files/mmdemos.exe http://www.swiftsoft.de/files/mmdocs.exe http://www.o2a.com/downloads/agntd5e.exe http://www.torry.css.pl/vcl/database/smetrial.zip http://www.torry.css.pl/vcl/comms/vnsmsdriver.zip http://www.toolsandcomps.com/download/usersstd.zip http://vclzip.bizland.com/kp220_d5.exe http://vclzip.bizland.com/kpdemosd.exe http://vclzip.bizland.com/helpfile.exe
Protección | Los programas que incluyan alguno de los componentes sólo funcionan cuando el IDE de Delphi está en ejecución.
Dificultad | 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas | Depurador: SoftICE 4.01 NT Desensambladores: W32Dasm 8.93, IDA Pro 3.7 Editor hexadecimal: Cygnus binary file editor 1.02 Buscador multi-fichero de cadenas hexadecimales: LookDisk 1.51 Visor de procesos: ProcDump 1.6 Espía de ventanas: Spy32 2.70 for Win9x/NT Memory dumper (aaargh, ¿cómo se traduce?): nticedump 1.6 Compiladores: Borland/Inrpise Delphi 4.03, Borland/Inprise Delphi 5 Música ambiente: My Dying Bride "The light at the end of the world", Children of Bodom "Hatebreeder", Dissection "Storm of the light's bane" Estimulantes: yogur de frutas, zumo de naranja, zumo de melocotón ;-) Y, como siempre ... ilusión, paciencia y un poco de cerebro.
Objetivo | Mostrar el funcionamiento de uno de los tipos de protección más utilizados por los programadores de componentes VCL: "works only when IDE is running" (sólo funciona cuando el entorno de desarrollo está en ejecución).
Cracker | +Aitor
Grupo | Whiskey Kon Tekila
Fecha | Febrero/Marzo 2000
INTRODUCCION
Son muchas, y cada vez más, las ocasiones en las que nos encontramos con desarrolladores de módulos para otros desarrolladores (léase librerías, controles, componentes, etc.) que, para proteger su trabajo de un uso ilegal, recurren al sistema que hoy nos ocupa. Dependiendo del entorno de desarrollo (IDE) en el que utilicemos dichos componentes, las técnicas varían, aunque en algunos casos son muy parecidas. En esta ocasión, comentaremos la que con más frecuencia nos podemos encontrar cuando el protagonista sea un componente escrito para Delphi: “works only when IDE is running” (sólo funciona cuando el IDE está en ejecución). Para ilustrar el tema con ejemplos prácticos, nos hemos dado una vuelta por uno de los repositorios de mayor audiencia, la Torry’s Page, y hemos seleccionado unos cuantos productos VCL (componentes o paquetes de componentes) shareware, compatibles con Delphi 4 o 5, que decían estar limitados con este tipo de protección. Antes de continuar, recuerda en todo momento que este tutorial NO está pensado para enseñarte a robar, sino para ofrecerte otra cara más del mundo de las protecciones de software y, en el caso de que seas programador de este tipo de material, darte ideas para mejorar la codificación de tus protecciones en un futuro. Los productos anteriormente citados han sido elegidos al azar, intentando coger un poco de todo para podernos hacer una idea más o menos fiable de cuál es el panorama actual en lo que se refiere a protecciones de este tipo. Los hay buenos y los hay menos buenos, los hay muy caros y los hay muy baratos, y sus funciones varían desde la programación de aplicaciones multimedia hasta la transmisión de mensajes cortos. Y un mensaje para los autores “afectados”. Aunque haya algún que otro estúpido que opine lo contrario, soy de la opinión de que tutoriales como éste no sólo no perjudican a los autores de shareware, sino que en cualquier caso suponen una ayuda. Si tu producto es bueno y competitivo no tienes nada que temer, aunque si no lo es, tal vez deberías estar ahora invvirtiendo el tiempo en mejorarlo en lugar de “perder” el tiempo leyendo este documento. Y, por supuesto, otra nota para los lamers caza-cracks. Aunque las protecciones de todos los productos mencionados están concienzudamente detalladas en este documento, no esperes encontrar ni un solo parche para ninguno de ellos, ni en un archivo adjunto a este texto ni en la sección de cracks de WKT. Bastardos como vosotros son los que nos hacen la vida menos agradable a los +crackers honrados. Sin más, empezamos.
LA BASE TEORICA. BUSCANDO VENTANAS
Es la base del tipo de protección más habitual, asegurarse de que el componente sólo funciona desde dentro del entorno de desarrollo (IDE) de Delphi. Consiste en buscar mediante llamadas a FindWindow o FindWindowEx, ventanas características de presencia obligada cuando el IDE de Delphi está arrancado. Arrancamos el IDE de Delphi 4, cerramos todos los proyectos y, con ayuda de nuestro espía favorito, obtenemos lo siguiente: Algo muy parecido obtenemos con el de Delphi 5: Lo que aparece en estas capturas es, de izquierda a derecha, el icono, el handle, el título y el nombre de la clase, de cada ventana presente, visible o no visible, cuando el IDE está funcionado. Sin fijarnos mucho, nos damos cuenta de que hay ventanas significativas comunes, y es precisamente su presencia la que utiliza el programador para saber si su componente está siendo utilizado desde dentro de Delphi o no, aunque no sea esta la manera más exacta de hacerlo, veremos más adelante el porqué. Ilustraremos lo dicho anteriormente con algunos ejemplos ...
LOS PEREZOSOS
De los 9 productos estudiados para la redacción de este tutorial, 6 caen en esta categoría. En este caso, el programador se ha limitado a chequear, mediante llamadas consecutivas a FindWindow la presencia de una o más de las “ventanas obligadas”. Son un ejemplo de cómo NO hay que proteger un programa. Vamos con ellos ...
Dream Collection 3.1, © 1997-2000 Dream Company
Lo primero que hacemos, tras la instalación, es arrancar Delphi 5 y compilar el proyecto de ejemplo H:\Dream\demo\common\d5\project1.dpr. Obtenemos un ejecutable, project1.exe, de 2.689.024 bytes. Aunque no hay nada en la documentación que lo diga notamos que, cuando ejecutamos el programa con Delphi arrancado, todo va perfecto. Sin embargo, cuando no está Delphi presente, el programa nos muestra la siguiente ventana al arrancar: Un vistazo al código desensamblado nos da lo siguiente:
`:4721C0 803D00BF5F0000 cmp byte ptr [5FBF00], 0 ; Flag = FALSE` `:4721C7 7523 jne 4721EC ; nos vamos` `:4721C9 C60500BF5F0001 mov byte ptr [5FBF00], 1 ; Flag = TRUE` `:4721D0 E80FFBFFFF call 471CE4 ; ¿está Delphi presente?` `:4721D5 84C0 test al, al` `:4721D7 7513 jne 4721EC ; sí, nos vamos` `:4721D9 6A30 push 30 ; no, mensaje de aviso` `:4721DB 68F0214700 push 4721F0 ; "Warning"` `:4721E0 68F8214700 push 4721F8 ; "This application uses ..."` `:4721E5 6A00 push 0` `:4721E7 E8CC6EF9FF call 4090B8 ; MessageBoxA` `:4721EC C3 ret`
No hace falta mucha imaginación para darse cuenta del papel que juega el flag apuntado por [5FBF00], ¿verdad? Pero vamos a lo que nos interesa, cómo se produce el chequeo de la presencia de Delphi:
`:471CE4 53 push ebx` `:471CE5 56 push esi` `:471CE6 6A00 push 0` `:471CE8 68241D4700 push 471D24 ; "TAlignPalette"` `:471CED E84671F9FF call 408E38 ; FindWindowA` `:471CF2 8BD8 mov ebx, eax` `:471CF4 6A00 push 0` `:471CF6 68341D4700 push 471D34 ; "TPropertyInspector"` `:471CFB E83871F9FF call 408E38 ; FindWindowA` `:471D00 8BF0 mov esi, eax` `:471D02 6A00 push 0` `:471D04 68481D4700 push 471D48 ; "TAppBuilder"` `:471D09 E82A71F9FF call 408E38 ; FindWindowA` `:471D0E 85DB test ebx, ebx` `:471D10 7408 je 471D1A` `:471D12 85F6 test esi, esi` `:471D14 7404 je 471D1A` `:471D16 85C0 test eax, eax` `:471D18 7505 jne 471D1F` `:471D1A 33C0 xor eax, eax` `:471D1C 5E pop esi` `:471D1D 5B pop ebx` `:471D1E C3 ret`
No nos podemos ir sin rematar la faena, y un parche elegante podría ser el siguiente:
`:4721C0 C60500BF5F0000 mov byte ptr [5FBF00], 0 ; Flag = FALSE, por si acaso` `:4721C7 EB23 jmp 4721EC ; nos vamos siempre`
Como siempre, el origen de la protección lo hallaremos en los archivos .DCU, localizándolo esta vez en el fichero dclib.dcu.
LMD-Tools 4.51, ©1995-1999 LMD Innovativ e
Lo primero que hacemos, tras la instalación, es arrancar Delphi 4 y compilar el ejemplo que acompaña al paquete. Se trata del proyecto C:\LMD40\DEMOS\delphi\NewMegaDemo.dpr que, una vez compilado, nos genera el ejecutable NewMegaDemo.exe, de 5.381.632 bytes, que será el ejecutable sobre el que trabajaremos. Nada más ejecutarlo (una vez apagado Delphi) aparece la siguiente ventana: Nos tiramos a por un “crackeo muerto” y, tras unos minutos de paciente espera, W32Dasm nos regala un fichero de 35 Mb del cual rápidamente extraemos esta hermosura:
`:466CA0 55 push ebp` `:466CA1 8BEC mov ebp, esp` `:466CA3 6A00 push 0` `:466CA5 6A00 push 0` `:466CA7 6A00 push 0` `:466CA9 33C0 xor eax, eax` `:466CAB 55 push ebp` `:466CAC 68D46D4600 push 466DD4` `:466CB1 64FF30 push dword ptr fs:[eax]` `:466CB4 648920 mov dword ptr fs:[eax], esp` `:466CB7 C645F700 mov [ebp-9], 0 ; contador de ventanas encontradas` `:466CBB 33C0 xor eax, eax` `:466CBD 55 push ebp` `:466CBE 68B26D4600 push 466DB2` `:466CC3 64FF30 push dword ptr fs:[eax]` `:466CC6 648920 mov dword ptr fs:[eax], esp` `:466CC9 8D45FC lea eax, dword ptr [ebp-4]` `:466CCC BAE86D4600 mov edx, 466DE8 ; "TAPPBUILDER"` `:466CD1 E84AD2F9FF call 403F20` `:466CD6 6A00 push 0` `:466CD8 8B45FC mov eax, dword ptr [ebp-4]` `:466CDB E8E8D5F9FF call 4042C8` `:466CE0 50 push eax` `:466CE1 E8A20DFAFF call 407A88 ; FindWindowA` `:466CE6 85C0 test eax, eax ; ¿Primera ventana encontrada?` `:466CE8 750A jne 466CF4 ; sí, vamos a por la segunda` `:466CEA E8ADCCF9FF call 40399C ; no, nag-screen y nos vamos` `:466CEF E9C5000000 jmp 466DB9` `:466CF4 FE45F7 inc [ebp-9] ; ya tenemos una en el bote` `:466CF7 8D45FC lea eax, dword ptr [ebp-4]` `:466CFA BAFC6D4600 mov edx, 466DFC ; "TAPPLICATION"` `:466CFF E81CD2F9FF call 403F20` `:466D04 8D45F8 lea eax, dword ptr [ebp-8]` `:466D07 BA146E4600 mov edx, 466E14 ; "DELPHI 4"` `:466D0C E80FD2F9FF call 403F20` `:466D11 8B45F8 mov eax, dword ptr [ebp-8]` `:466D14 E8AFD5F9FF call 4042C8` `:466D19 50 push eax` `:466D1A 8B45FC mov eax, dword ptr [ebp-4]` `:466D1D E8A6D5F9FF call 4042C8` `:466D22 50 push eax` `:466D23 E8600DFAFF call 407A88 ; FindWindowA` `:466D28 85C0 test eax, eax ; ¿Segunda ventana encontrada?` `:466D2A 750A jne 466D36 ; sí, vamos a por la tercera` `:466D2C E86BCCF9FF call 40399C ; no, nag-screen y nos vamos` `:466D31 E983000000 jmp 466DB9` `:466D36 FE45F7 inc [ebp-9] ; ya tenemos 2` `:466D39 8D45FC lea eax, dword ptr [ebp-4]` `:466D3C BA286E4600 mov edx, 466E28 ; "TPropertyInspector"` `:466D41 E8DAD1F9FF call 403F20` `:466D46 6A00 push 0` `:466D48 8B45FC mov eax, dword ptr [ebp-4]` `:466D4B E878D5F9FF call 4042C8` `:466D50 50 push eax` `:466D51 E8320DFAFF call 407A88 ; FindWindowA` `:466D56 85C0 test eax, eax ; ¿Tercera ventana encontrada?` `:466D58 7507 jne 466D61 ; sí, vamos a por la última` `:466D5A E83DCCF9FF call 40399C ; no, nag-screen y nos vamos` `:466D5F EB58 jmp 466DB9` `:466D61 FE45F7 inc [ebp-9] ; ya tenemos 3` `:466D64 8D45FC lea eax, dword ptr [ebp-4]` `:466D67 BA446E4600 mov edx, 466E44 ; "TAlignPalette"` `:466D6C E8AFD1F9FF call 403F20` `:466D71 6A00 push 0` `:466D73 8B45FC mov eax, dword ptr [ebp-4]` `:466D76 E84DD5F9FF call 4042C8` `:466D7B 50 push eax` `:466D7C E8070DFAFF call 407A88 ; FindWindowA` `:466D81 85C0 test eax, eax ; ¿Cuarta ventana encontrada?` `:466D83 7507 jne 466D8C ; sí, continuamos` `:466D85 E812CCF9FF call 40399C ; no, nag-screen y nos vamos` `:466D8A EB2D jmp 466DB9` `:466D8C FE45F7 inc [ebp-9] ; ya tenemos 4` `:466D8F 33C0 xor eax, eax` `:466D91 5A pop edx` `:466D92 59 pop ecx` `:466D93 59 pop ecx` `:466D94 648910 mov dword ptr fs:[eax], edx` `:466D97 68B96D4600 push 466DB9` `:466D9C 807DF704 cmp byte ptr [ebp-9], 4 ; ¿Hemos encontrado las 4 ventanas?` `:466DA0 740F je 466DB1 ; sí, seguimos como si tal` `:466DA2 B85C6E4600 mov eax, 466E5C ; no, nag-screen y nos vamos` `:466DA7 E8404AFFFF call 45B7EC` `:466DAC E823CFF9FF call 403CD4` `:466DB1 C3 ret`
Bueno, sobran los comentarios, ¿no? Busca cuatro de las ventanas de presencia obligada cuando el IDE de Delphi 4 está arrancado, y si no encuentra alguna de ellas, interrumpe la ejecución del programa. Buscamos un parche elegante, por ejemplo:
`:466CA0 C3 ret ; nos vamos nada más llegar`
Lo probamos y la demo corre perfectamente. Sin embargo, nuestro trabajo no termina aquí, debemos localizar el código de chequeo en el fichero *.DCU de turno, para que el compilador nos proporcione ejecutables parcheados cada vez que compilemos. Tras buscar un poco lo encontramos en el interior del fichero lmdclass.dcu. Lo parcheamos de la misma forma que antes, recompilamos NewMegaDemo.dpr y probamos el nuevo ejecutable. Ni rastro de la protección. Probamos compilando un par de ejemplos más, y todo parece correcto.
SMExport Component Suite 3.50, © 1998-1999 Mike Shkolni k
Lo primero que hacemos, tras la instalación, es arrancar Delphi y compilar el proyecto de ejemplo H:\Cracking\SME\EXAMPLE\SMEDemo.dpr. Obtenemos un ejecutable, SMEDemo.exe, de 770.048 bytes. Una vez fuera de Delphi, ejecutamos el ejemplo y obtenemos el siguiente mensaje: (Sí, yo también me he fijado, se le habrá pasado actualizar aquí el número de versión.) Echamos mano una vez más de nuestro inseparable W32Dasm para obtener rápidamente el siguiente trozo de código:
`:495108 53 push ebx` `:495109 56 push esi` `:49510A 6A00 push 0` `:49510C 68040D4A00 push 4A0D04 ; "TAlignPalette"` `:495111 E8DA23F7FF call 4074F0 ; FindWindowA` `:495116 8BD8 mov ebx, eax ; Guardamos el handle en EBX` `:495118 6A00 push 0` `:49511A 68140D4A00 push 4A0D14 ; "TPropertyInspector"` `:49511F E8CC23F7FF call 4074F0 ; FindWindowA` `:495124 8BF0 mov esi, eax ; Guardamos el handle en ESI` `:495126 6A00 push 0` `:495128 68280D4A00 push 4A0D28 ; "TAppBuilder"` `:49512D E8BE23F7FF call 4074F0 ; FindWindowA, handle devuelto en EAX` `:495132 85DB test ebx, ebx ; ¿Encontrada la primera ventana?` `:495134 7408 je 49513E ; No, nos largamos` `:495136 85F6 test esi, esi ; ¿Encontrada la segunda ventana?` `:495138 7404 je 49513E ; No, nos largamos` `:49513A 85C0 test eax, eax ; ¿Y la tercera?` `:49513C 7505 jne 495143 ; Sí, todas encontradas, todo bien` `:49513E 33C0 xor eax, eax` `:495140 5E pop esi` `:495141 5B pop ebx` `:495142 C3 ret` `:495143 B001 mov al, 1` `:495145 5E pop esi` `:495146 5B pop ebx` `:495147 C3 ret`
Una vez más, pensar en un parche elegante es lo único que nos ofrece la protección, ¿qué tal éste?:
`:49510A EB37 jmp 495143 ; Nada más llegar nos vamos por el camino correcto`
¿Y el código original? Pues esta vez le ha tocado comérselo al fichero ExportDS.dcu.
SMS Driver 1.0, © 1996-1999, Valentin Nagacevschi
Esta vez no hay ejemplos con los que “experimentar”, así que tendremos que fabricarnos uno. Uno sencillo nos vale. Tiramos sobre un formulario vacío un componente TSMSDriver y un botón para activarlo: El único código que necesitamos escribir es una línea en el evento OnClick del botón:
`procedure TForm1.Button1Click(Sender: TObject);` `begin` `SMSDriver1.Connect;` `end;`
Lo compilamos y ya estamos listos para “trabajar”. Una vez Delphi terminado, ejecutamos nuestro ejemplo y obtenemos la siguiente ventana: Siguiendo nuestro método habitual, echamos una ojeada al código desensamblado y obtenemos lo siguiente:
`:44221C 55 push ebp` `:44221D 8BEC mov ebp, esp` `:44221F 6A00 push 0` `:442221 53 push ebx` `:442222 33C0 xor eax, eax` `:442224 55 push ebp` `:442225 6884224400 push 442284` `:44222A 64FF30 push dword ptr fs:[eax]` `:44222D 648920 mov dword ptr fs:[eax], esp` `:442230 8D45FC lea eax, dword ptr [ebp-4]` `:442233 BA9C224400 mov edx, 44229C ; "TAppBuilder"` `:442238 E81F16FCFF call 40385C` `:44223D 6A00 push 0` `:44223F 8B45FC mov eax, dword ptr [ebp-4]` `:442242 E8BD19FCFF call 403C04` `:442247 50 push eax` `:442248 E8233FFCFF call 406170 ; FindWindowA` `:44224D 85C0 test eax, eax` `:44224F 0F94C3 sete bl` `:442252 80F301 xor bl, 1` `:442255 84DB test bl, bl ; ¿BL = 0? (IDE no encontrado)` `:442257 7515 jne 44226E ; No, nos vamos` `:442259 6A00 push 0 ; Sí, mensaje de aviso` `:44225B 668B0DA8224400 mov cx, word ptr [4422A8]` `:442262 33D2 xor edx, edx` `:442264 B8B4224400 mov eax, 4422B4 ; "Thank you for using ..."` `:442269 E82EDFFFFF call 44019C` `:44226E 33C0 xor eax, eax` `:442270 5A pop edx` `:442271 59 pop ecx` `:442272 59 pop ecx` `:442273 648910 mov dword ptr fs:[eax], edx` `:442276 688B224400 push 44228B` `:44227B 8D45FC lea eax, dword ptr [ebp-4]` `:44227E E84115FCFF call 4037C4` `:442283 C3 ret` `:442284 E9FB0FFCFF jmp 403284` `:442289 EBF0 jmp 44227B` `:44228B 8BC3 mov eax, ebx` `:44228D 5B pop ebx` `:44228E 59 pop ecx` `:44228F 5D pop ebp` `:442290 C3 ret`
Supongo que, a estas alturas de la película sobra cualquier comentario. Nuestro parche habitual para quedarnos satisfechos, por ejemplo, el siguiente:
`:442252 32DB xor bl, bl ; BL = 0` `:442254 43 inc ebx ; BL = 1, IDE siempre encontrado`
Y, para terminar, esta vez no hay duda ninguna respecto a la localización del código original, ya que sólo existe un archivo DCU en donde buscar ;-).
TUsersSTD 1.0, © 1999-2000 ToolsAndComps
Tras la instalación, buscamos un ejemplo que, una vez compilado, nos sirva para encontrar la protección. Esta vez el afortunado será H:\Cracking\UsersSTD\ExamplesD4\ShowAcessLevels\SALevels.dpr que tras la compilación, se convierte en una monstruosidad llamada SALevels.exe, de 756.736 bytes. Tras la primera ejecución, nos encontramos con el siguiente mensaje: Un par de minutos después, ya somos capaces de encontrar la siguiente preciosidad:
`:499EC8 53 push ebx` `:499EC9 56 push esi` `:499ECA 57 push edi` `:499ECB 6878DC4900 push 49DC78 ; "Delphi 4"` `:499ED0 6838DC4900 push 49DC38 ; "TApplication"` `:499ED5 E86AD3F6FF call 407244 ; FindWindowA` `:499EDA 8BD8 mov ebx, eax ; Guardamos el handle en EBX` `:499EDC 6A00 push 0` `:499EDE 6848DC4900 push 49DC48 ; "TAlignPalette"` `:499EE3 E85CD3F6FF call 407244 ; FindWindowA` `:499EE8 8BF0 mov esi, eax ; Guardamos el handle en ESI` `:499EEA 6A00 push 0` `:499EEC 6858DC4900 push 49DC58 ; "TPropertyInspector"` `:499EF1 E84ED3F6FF call 407244 ; FindWindowA` `:499EF6 8BF8 mov edi, eax ; Guardamos el handle en EDI` `:499EF8 6A00 push 0` `:499EFA 686CDC4900 push 49DC6C ; "TAppBuilder"` `:499EFF E840D3F6FF call 407244 ; FindWindowA, handle devuelto en EAX` `:499F04 85DB test ebx, ebx ; ¿Hemos encontrado la primera ventana?` `:499F06 740C je 499F14 ; No, nos largamos` `:499F08 85F6 test esi, esi ; ¿Hemos encontrado la segunda ventana?` `:499F0A 7408 je 499F14 ; No, nos largamos` `:499F0C 85FF test edi, edi ; ¿Hemos encontrado la tercera ventana?` `:499F0E 7404 je 499F14 ; No, nos largamos` `:499F10 85C0 test eax, eax ; ¿Y la cuarta?` `:499F12 7504 jne 499F18 ; Sí, estaban todas, perfecto` `:499F14 33C0 xor eax, eax` `:499F16 EB02 jmp 499F1A` `:499F18 B001 mov al, 1` `:499F1A 5F pop edi` `:499F1B 5E pop esi` `:499F1C 5B pop ebx` `:499F1D C3 ret`
Esto está empezando a resultar aburrido, ¿no? Probamos un parche cualquiera (elegir el parche es lo más “excitante” que nos permiten protecciones como ésta), por ejemplo:
`:499F16 EB00 jmp 499F18 ; Pase lo que pase, siempre salimos bien`
Y, por si alguien siente aún alguna curiosidad, el código de chequeo original se encuentra en el fichero usersstd.dcu.
VCLZip Native Delphi Zip/UnZip Component! 2.20 Beta 1, © 1997-1999 Kevin L. Boylan
Lo primero que hacemos, tras la instalación, es arrancar Delphi 5 y compilar el proyecto de ejemplo H:\VCLZip\Examples\ZipUtil\Proj_32\zipp.dpr. Obtenemos un ejecutable, zipp.exe, de 623.616 bytes. Salimos de Delphi y al ejecutar nuestro recién compilado ejemplo, obtenemos el siguiente mensaje: y tras ésta, un mensaje de excepción del sistema que, cuando menos, nos causa una mala sensación. Tras urgar entre el código desensamblado, obtenemos lo siguiente:
`:463098 6830E74700 push 47E730 ; "Delphi 5"` `:46309D 6804E74700 push 47E704` `:4630A2 E89939FAFF call 406A40 ; FindWindowA` `:4630A7 85C0 test eax, eax` `:4630A9 7420 je 4630CB` `:4630AB 6A00 push 0` `:4630AD 6814E74700 push 47E714 ; "TAlignPalette"` `:4630B2 E88939FAFF call 406A40 ; FindWindowA` `:4630B7 85C0 test eax, eax` `:4630B9 7410 je 4630CB` `:4630BB 6A00 push 0` `:4630BD 6824E74700 push 47E724 ; "TAppBuilder"` `:4630C2 E87939FAFF call 406A40 ; FindWindowA` `:4630C7 85C0 test eax, eax` `:4630C9 7503 jne 4630CE` `:4630CB 33C0 xor eax, eax ; IDE no encontrado` `:4630CD C3 ret` `:4630CE B001 mov al, 1 ; IDE encontrado` `:4630D0 C3 ret`
Sin comentarios, ¿no? Intentemos uno de nuestro parches favoritos:
`:4630CD 90 nop ; pase lo que pase, devolvemos siempre AL = 1`
Y, como siempre, el origen de la protección lo buscamos en el interior de los archivos .DCU, siendo esta vez dos los ficheros en los que encontramos idéntico código: Kpzipobj.dcu y VCLUnZip.dcu. Probamos el parche, todo perfecto, parece que nuestro trabajo ha terminado.
LOS DESPISTADOS
La verdad es que uno no sabe qué decir ante un caso así. Lo cierto es que no recuerdo que me haya ocurrido nunca encontrarme algo parecido. Un producto supuestamente protegido, que se distribuye desprotegido. Es posible que haya truco, pero no he encontrado nada. (Si tú, lector espabilado, tienes más noticias acerca de este caso, no lo dudes y escríbenos.)
1stClass Trial 1.01, ©1995-99 Woll2Woll Software
Lo primero que hacemos, tras la instalación, es arrancar Delphi 5 y compilar el proyecto de ejemplo C:\Archivos de programa\Woll2Woll\1stClassTrial_d5\demos\Demo1stClass.dpr. Obtenemos un ejecutable, Demo1stClass.exe, de 1.850.368 bytes. Tras cerrar Delphi, ejecutamos el ejemplo. Sin embargo, al contrario de lo que esperábamos, nos encontramos que el programa arranca de forma normal, sin ventanas de mensajes ni ningún otro rastro de protección. Tras “jugar” un rato con el ejemplo y probarlo de arriba abajo, nos invade el aburrimiento al no ver nada “raro”, y nos largamos. Aquí ocurre algo extraño, y sólo se nos ocurren las siguientes explicaciones: Woll2Woll, en un alarde de valentía, se ha arriesgado con una estrategia peligrosa, la de meter miedo a clientes estúpidos con protecciones inexistentes. (Y estúpidos, con todas las letras, tienen que ser si no prueban algo tan sencillo como ejecutar sus pruebas fuera de Delphi, ¿o me equivoco?). La protección está oculta, y no aparece así sin más en cuanto ejecutamos cualquier cosa. El equipo de Woll2Woll encargado de preparar este paquete en concreto se ha colado y ha metido una versión desprotegida. Tras meditarlo medio segundo, descartamos desde ya la explicación (a). Nadie (o casi nadie ;-) puede ser tan necio como para decir que su producto está protegido y luego venderlo desprotegido, y mucho menos “insultar” de esa forma a posibles futuros clientes llamándolos imbéciles a la cara. La explicación (c) no es tan descabellada como pudiera parecer. Sin embargo, no podemos descartar la opción (b) sin haber al menos indagado un poco, ¿verdad que no? Estrategias a seguir, por orden según nos van viniendo a la cabeza: Desensamblar “Demo1stClass.exe” y buscar llamadas a FindWindow o FindWindowEx. Estudiar si el programa carga en algún momento DLL’s o módulos de algún tipo que no hayamos detectado a simple vista tras la instalación. Estudiar si en algún momento de su ejecución el programa crea timers o threads, en los que puedan camuflarse bombas de tiempo y/o chequeos de IDE. Si tras estudiar todo esto, los ejemplos que compilemos siguen funcionando bien, consideraremos el tema cerrado hasta nuevo aviso. Todo tiene un límite, y tenemos mucho trabajo que hacer aún ;-). Intentamos el desensamblado con W32Dasm, pero algo extraño ocurre, ya que el desensamblador se queda colgado sin terminar siquiera de cargar el ejecutable. ¿La configuración de mi sistema? ¿tal vez trucos anti-W32Dasm? Probaremos con IDA antes de llegar a conclusiones precipitadas. Tras un par de minutos, obtenemos un fichero de 43 Mb con nuestro protagonista desensamblado. Ni una sóla llamada a “FindWindow”, al menos explícitas que podamos detectar a simple vista. Tampoco hay rastro de cadenas del tipo “Delphi” o “C++Builder”. Guardamos el listado por si lo necesitamos más tarde, pero no perdemos más tiempo y vamos a por nuestro siguiente paso, buscar DLL’s. Arrancamos el ejemplo y con ayuda de nuestra herramienta favorita obtenemos un listado de las DLL’s que el ejecutable carga de forma estática junto con él. Obtenemos lo siguiente: Dejando aparte las librerías del sistema (*.drv, *.dll) detectamos módulos de extensión .BPL. Los archivos BPL, también llamados “paquetes” o runtime packages, no son más que DLL’s en cuyo interior se encuentra encapsulado código compilado relativo a componentes VCL de Delphi/C++Builder. En la mayor parte de los casos, los programadores de Delphi compilan sus programas con la opción por defecto de “compilar sin paquetes”. De esta forma, el ejecutable generado contiene en su interior todo el código necesario para desarrollar las funcionalidades ofrecidas de cualquier componente VCL que utilice. Sin embargo, hay ocasiones (más de las que algunos piensan) en las que es conveniente cambiar la opción por defecto por la de “compilar con paquetes”. Así, obtenemos ejecutables de dimensiones menores, además de disfrutar de ventajas adicionales, pero no es éste el lugar ni el momento de extendernos, continuemos ... Todos los “paquetes” cargados son paquetes VCL de los que se incluyen con Delphi 5. ¿Todos? No, no todos, y me imagino que sabemos cuál no lo es: FirstClassTrial_d5.bpl. Pocas dudas nos quedan ya, puesto que este hecho nos confirma que la segunda limitación descrita en la documentación tampoco se cumple:
`1stClass Trial version for Delphi 5` `With the trial version you will only be able to run programs from within the Delphi IDE. The trial version does not support runtime packages so you will need to make sure that the \1stclass\lib is in your component search path.`
No sólo podemos ejecutar el ejemplo de forma, al menos aparentemente, normal, sino que además lo podemos hacer sea cual sea el modo de compilación (con o sin paquetes) seleccionado. No vamos a perder más tiempo, ya que todo apunta a nuestra tercera posible explicación, se han colado y parece que disponemos de una versión sin restricciones.
LOS CURRANTES
Entre tanta mediocridad, de vez en cuando uno se encuentra con gente que, aunque no se coma mucho la cabeza para diseñar la protección, al menos, intenta llegar más allá de la implementación cutre que hemos visto en apartados anteriores. Esta tampoco es la forma, pero sí el camino.
oaAgent Component Set, © 1999-2000 O&A Productions
Lo primero que hacemos, tras la instalación, es arrancar Delphi 5 y compilar el proyecto de ejemplo H:\Archivos de programa\O&A\oaAgent\Demos\Agent Test\AgentTest.dpr. Obtenemos un ejecutable, AgentTest.exe, de 560.128 bytes. Tras salir de Delphi, ejecutamos el ejemplo y obtenemos el siguiente mensaje: Como es habitual, indagamos entre el código desensamblado buscando las clásicas llamadas a FindWindow/FindWindowEx, sin embargo, ¡no encontramos nada! Esto se pone interesante (por fin ;-), aunque son ya casi las 3 de la mañana y hay que madrugar ... [Unas horas de sueño y una jornada de trabajo después ...] Una mini-sesión de Soft-ICE y localizamos rápidamente el código de chequeo:
`:460D78 55 push ebp` `:460D79 8BEC mov ebp, esp` `:460D7B B904000000 mov ecx, 4` `:460D80 6A00 push 0` `:460D82 6A00 push 0` `:460D84 49 dec ecx` `:460D85 75F9 jne 460D80` `:460D87 51 push ecx` `:460D88 53 push ebx` `:460D89 56 push esi` `:460D8A 57 push edi` `:460D8B 33C0 xor eax, eax` `:460D8D 55 push ebp` `:460D8E 68F40E4600 push 460EF4` `:460D93 64FF30 push dword ptr fs:[eax]` `:460D96 648920 mov dword ptr fs:[eax], esp` `:460D99 33DB xor ebx, ebx` `:460D9B 8D55FC lea edx, dword ptr [ebp-4]` `:460D9E A16C3D4700 mov eax, dword ptr [473D6C] ; "USER32.DLL" encriptado` `:460DA3 E880FFFFFF call 460D28 ; lo desencriptamos` `:460DA8 8B45FC mov eax, dword ptr [ebp-4]` `:460DAB E84831FAFF call 403EF8` `:460DB0 50 push eax ; Obtenemos el handle de USER32.DLL` `:460DB1 E87A5AFAFF call 406830 ; (GetModuleHandleA)` `:460DB6 8BF0 mov esi, eax` `:460DB8 85F6 test esi, esi` `:460DBA 0F8419010000 je 460ED9` `:460DC0 8D55F8 lea edx, dword ptr [ebp-8]` `:460DC3 A1703D4700 mov eax, dword ptr [473D70] ; "KERNEL32.DLL" encriptado` `:460DC8 E85BFFFFFF call 460D28 ; lo desencriptamos` `:460DCD 8B45F8 mov eax, dword ptr [ebp-8]` `:460DD0 E82331FAFF call 403EF8` `:460DD5 50 push eax ; Obtenemos el handle de` `:460DD6 E8555AFAFF call 406830 ; KERNEL32.DLL (GetModuleHandleA)` `:460DDB 8BF8 mov edi, eax` `:460DDD 85FF test edi, edi` `:460DDF 0F84F4000000 je 460ED9` `:460DE5 8D55F4 lea edx, dword ptr [ebp-0C]` `:460DE8 A1743D4700 mov eax, dword ptr [473D74] ; "GetProcAddress" encriptado` `:460DED E836FFFFFF call 460D28 ; lo desencriptamos` `:460DF2 8B45F4 mov eax, dword ptr [ebp-0C]` `:460DF5 E8FE30FAFF call 403EF8` `:460DFA 50 push eax` `:460DFB 57 push edi ; Obtenemos la dirección de la` `:460DFC E8375AFAFF call 406838 ; función "GetProcAddress"` `:460E01 8BF8 mov edi, eax ; y la guardamos en EDI` `:460E03 85FF test edi, edi` `:460E05 0F84CE000000 je 460ED9` `:460E0B 8D55F0 lea edx, dword ptr [ebp-10]` `:460E0E A1783D4700 mov eax, dword ptr [473D78] ; "FindWindowA" encriptado` `:460E13 E810FFFFFF call 460D28 ; lo desencriptamos` `:460E18 8B45F0 mov eax, dword ptr [ebp-10]` `:460E1B E8D830FAFF call 403EF8` `:460E20 50 push eax` `:460E21 56 push esi ; Buscamos la dirección de la` `:460E22 FFD7 call edi ; función "FindWindowA"` `:460E24 8BF0 mov esi, eax ; y la guardamos en ESI` `:460E26 85F6 test esi, esi` `:460E28 0F84AB000000 je 460ED9` `:460E2E 6A00 push 0` `:460E30 8D55EC lea edx, dword ptr [ebp-14]` `:460E33 A17C3D4700 mov eax, dword ptr [473D7C] ; "TAppBuilder" encriptado` `:460E38 E8EBFEFFFF call 460D28 ; lo desencriptamos` `:460E3D 8B45EC mov eax, dword ptr [ebp-14]` `:460E40 E8B330FAFF call 403EF8` `:460E45 50 push eax ; ¿Hay presente alguna ventana de` `:460E46 FFD6 call esi ; clase TAppBuilder?` `:460E48 85C0 test eax, eax` `:460E4A 0F95C3 setne bl` `:460E4D 84DB test bl, bl` `:460E4F 741F je 460E70 ; No, nos vamos` `:460E51 6A00 push 0` `:460E53 8D55E8 lea edx, dword ptr [ebp-18]` `:460E56 A1803D4700 mov eax, dword ptr [473D80] ; "TApplication" encriptado` `:460E5B E8C8FEFFFF call 460D28 ; lo desencriptamos` `:460E60 8B45E8 mov eax, dword ptr [ebp-18]` `:460E63 E89030FAFF call 403EF8` `:460E68 50 push eax ; ¿Hay presente alguna ventana de` `:460E69 FFD6 call esi ; clase TApplication?` `:460E6B 85C0 test eax, eax` `:460E6D 0F95C3 setne bl` `:460E70 84DB test bl, bl` `:460E72 741F je 460E93 ; No, nos vamos` `:460E74 6A00 push 0` `:460E76 8D55E4 lea edx, dword ptr [ebp-1C]` `:460E79 A1843D4700 mov eax, dword ptr [473D84] ; "TPropertyInspector"` `:460E7E E8A5FEFFFF call 460D28 ; lo desencriptamos` `:460E83 8B45E4 mov eax, dword ptr [ebp-1C]` `:460E86 E86D30FAFF call 403EF8` `:460E8B 50 push eax ; ¿Hay presente alguna ventana de` `:460E8C FFD6 call esi ; clase TPropertyInspector?` `:460E8E 85C0 test eax, eax` `:460E90 0F95C3 setne bl` `:460E93 84DB test bl, bl` `:460E95 741F je 460EB6 ; No, nos vamos` `:460E97 6A00 push 0` `:460E99 8D55E0 lea edx, dword ptr [ebp-20]` `:460E9C A1883D4700 mov eax, dword ptr [473D88] ; "TAlignPalette" encriptado` `:460EA1 E882FEFFFF call 460D28 ; lo desencriptamos` `:460EA6 8B45E0 mov eax, dword ptr [ebp-20]` `:460EA9 E84A30FAFF call 403EF8` `:460EAE 50 push eax ; ¿Hay presente alguna ventana de` `:460EAF FFD6 call esi ; clase TAlignPalette?` `:460EB1 85C0 test eax, eax` `:460EB3 0F95C3 setne bl` `:460EB6 84DB test bl, bl` `:460EB8 741F je 460ED9 ; No, nos vamos` `:460EBA 6A00 push 0` `:460EBC 8D55DC lea edx, dword ptr [ebp-24]` `:460EBF A18C3D4700 mov eax, dword ptr [473D8C] ; "TMenuBuilder" encriptado` `:460EC4 E85FFEFFFF call 460D28 ; lo desencriptamos` `:460EC9 8B45DC mov eax, dword ptr [ebp-24]` `:460ECC E82730FAFF call 403EF8` `:460ED1 50 push eax ; ¿Hay presente alguna ventana de` `:460ED2 FFD6 call esi ; clase TMenuBuilder?` `:460ED4 85C0 test eax, eax` `:460ED6 0F95C3 setne bl` `:460ED9 33C0 xor eax, eax` `:460EDB 5A pop edx` `:460EDC 59 pop ecx` `:460EDD 59 pop ecx` `:460EDE 648910 mov dword ptr fs:[eax], edx` `:460EE1 68FB0E4600 push 460EFB` `:460EE6 8D45DC lea eax, dword ptr [ebp-24]` `:460EE9 BA09000000 mov edx, 9` `:460EEE E8E52BFAFF call 403AD8` `:460EF3 C3 ret` `:460EF4 E99325FAFF jmp 40348C` `:460EF9 EBEB jmp 460EE6` `:460EFB 8BC3 mov eax, ebx ; EAX > 0, IDE presente` `:460EFD 5F pop edi` `:460EFE 5E pop esi` `:460EFF 5B pop ebx` `:460F00 8BE5 mov esp, ebp` `:460F02 5D pop ebp` `:460F03 C3 ret`
Un poco largo, pero muy ameno ;-). Vamos a ver si analizamos de forma rápida lo que el programador ha hecho aquí, incluyendo alguna curiosidad: Todos los nombres de las clases de ventana a buscar están encriptados. La función FindWindowA no aparece de forma explícita, sino que es llamada calculando su dirección de rutina mediante llamada a la función del API GetProcAddress. Por supuesto, lo mismo ocurre con GetProcAddress. Aquí está una de las curiosidades, ya que para obtener la dirección de GetProcAddress se ejecuta explícitamente la propia GetProcAddress, aunque el nombre de la rutina necesario como parámetro también esté encriptado. Para obtener las direcciones de GetProcAddress y FindWindowA, es necesario conocer los handles de las DLL’s a las que pertenecen, KERNEL32.DLL y USER32.DLL, respectivamente. Una vez más, los nombres de estas DLL’s también están encriptados. Por lo demás, el sistema es el ya conocido, buscar una serie de ventanas determinadas para ver si el IDE de Delphi/C++Builder está presente. Sin embargo, aquí va otra curiosidad, ya que una de las clases de ventana que chequea es TApplication, chequeo que devolverá siempre TRUE si el ejecutable ha sido creado con el IDE de Delphi/C++Builder. Para terminar, un parche para quedarnos satisfechos e irnos contentos a dormir, éste por ejemplo:
`:460EFB B001 mov al, 1 ; Siempre devolvemos EAX > 0`
Y, cómo no, no nos podemos ir sin localizar el código original, que encontraremos en esta ocasión dentro del fichero Agent.dcu.
ASÍ SI!
De vez en cuando (muy de vez en cuando), uno se encuentra con protecciones trabajadas, de esas que no ventilas en 5 o 10 minutos y que tardas menos en destripar que en preparar el crack. Esta es una de esas. No es la protección perfecta (ninguna lo es), pero al menos lo han intentado. Mis felicitaciones.
Multimedia Tools 1.7, ©1995-1999 SwiftSoft
Tomamos uno cualquiera de los ejemplos incluídos, lo compilamos y obtenemos el siguiente ejecutable: c:\MMTOOLS\demos\cdrom\cddacopy\mmdemo32.exe, 653.312 bytes Al intentar ejecutarlo sin Delphi arrancado nos aparece el siguiente mensaje: Con ayuda de nuestro depurador favorito, y un bonito punto de ruptura en FindWindowA, localizamos el chequeo en toda su plenitud:
`CHEQUEO #1` `:45DECF 6A00 push 0 ; cualquier título nos vale` `:45DED1 6828DF4500 push 45DF28 ; clase de la ventana "TAppBuilder"` `:45DED6 E8598DFAFF call 406C34 ; FindWindowA` `:45DEDB 85C0 test eax, eax ; EAX = handle de la ventana` `:45DEDD 7430 je 45DF0F ; EAX = 0, no encontrada` `:45DEDF 6A50 push 50 ; ventana encontrada, nos preparamos` `:45DEE1 8D55AB lea edx, dword ptr [ebp-55] ; para obtener su título` `:45DEE4 52 push edx ; EDX = destino del título` `:45DEE5 50 push eax ; handle de la ventana` `:45DEE6 E8A98EFAFF call 406D94 ; GetWindowTextA` `:45DEEB BA34DF4500 mov edx, 45DF34 ; "Delphi"` `:45DEF0 8D45AB lea eax, dword ptr [ebp-55] ; el título obtenido antes` `:45DEF3 E854AFFAFF call 408E4C ; los comparamos` `:45DEF8 85C0 test eax, eax ; ¿son iguales?` `:45DEFA 7511 jne 45DF0D ; por supuesto, seguimos adelante` `:45DEFC BA3CDF4500 mov edx, 45DF3C ; no hemos encontrado el IDE de Delphi` `:45DF01 8D45AB lea eax, dword ptr [ebp-55] ; a lo mejor es que trabajamos con` `:45DF04 E843AFFAFF call 408E4C ; C++Builder ... intentamos lo mismo` `:45DF09 85C0 test eax, eax ; ¿título = "C++Builder"?` `:45DF0B 7402 je 45DF0F ; no, no hay ningún IDE válido` `:45DF0D B301 mov bl, 1 ; todo ha ido bien, algún IDE presente`
Momento de parchear y probar. Como casi siempre, se nos presentan múltiples soluciones, ahí va una de las posibles:
`:45DECF EB3C jmp 45DF0D ; saltamos hacia la salida correcta`
Parcheamos adecuadamente MMDEMO32.EXE, probamos una nueva ejecución y nos encontramos con la siguiente ventana: que no es más que el cuadro de Acerca de habitual con un timer que retrasa la rehabilitación del botón de OK. Al pulsar OK nos encontramos con una ventana de IDE not found como la del principio, y a continuación el programa termina. Efectivamente, el chequeo anterior no era el único, y poco tardamos en encontrar el siguiente, muy parecido al anterior:
`CHEQUEO #2` `:2B 6A00 push 0 ; cualquier título nos vale` `:2D 8B5638 mov edx, dword ptr [esi+38]` `:30 52 push edx ; clase de la ventana "TAppBuilder"` `:31 FFD0 call eax ; FindWindowA` `:33 85C0 test eax, eax ; EAX = handle de la ventana` `:35 0F8422020000 je 25D ; EAX = 0, no encontrada` `:3B 8D5330 lea edx, dword ptr [ebx+30] ; ventana encontrada, nos preparamos` `:3E 8B12 mov edx, dword ptr [edx]` `:40 8B12 mov edx, dword ptr [edx]` `:42 6A50 push 50` `:44 8D8C2484030000 lea ecx, dword ptr [esp+384]` `:4B 51 push ecx ; EDX = destino del título` `:4C 50 push eax ; handle de la ventana` `:4D FFD2 call edx ; GetWindowTextA` `:4F 8B463C mov eax, dword ptr [esi+3C] ; Ahora comparamos carácter a carácter` `:52 8A10 mov dl, byte ptr [eax] ; el título obtenido con la cadena` `:54 3A942480030000 cmp dl, byte ptr [esp+380] ; "Delphi"` `:5B 7544 jne A1 ; ¿alguno es diferente?, nos vamos` `:5D 8A5001 mov dl, byte ptr [eax+1] ; "e"` `:60 3A942481030000 cmp dl, byte ptr [esp+381]` `:67 7538 jne A1` `:69 8A5002 mov dl, byte ptr [eax+2] ; "l"` `:6C 3A942482030000 cmp dl, byte ptr [esp+382]` `:73 752C jne A1` `:75 8A5003 mov dl, byte ptr [eax+3] ; "p"` `:78 3A942483030000 cmp dl, byte ptr [esp+383]` `:7F 7520 jne A1` `:81 8A5004 mov dl, byte ptr [eax+4] ; "h"` `:84 3A942484030000 cmp dl, byte ptr [esp+384]` `:8B 7514 jne A1` `:8D 8A4005 mov al, byte ptr [eax+5] ; "i"` `:90 3A842485030000 cmp al, byte ptr [esp+385]` `:97 7508 jne A1` `:99 893424 mov dword ptr [esp], esi ; IDE de Delphi encontrado, nos vamos` `:9C E980000000 jmp 121` `:A1 8B4640 mov eax, dword ptr [esi+40] ; lo intentamos con el de C++Builder` `...`
Como casi siempre, se nos presentan mútliples soluciones, ahí va una de las posibles:
`:2B EB6C jmp 99 ; saltamos hacia la salida correcta`
Momento de parchear y probar, de nuevo. Esta vez, el código no se encuentra en el ejecutable, ¿tal vez en alguna DLL utilizada por éste? Buscamos en MMUTIL32.DLL, pero no hay rastro de nuestro código ... ¿estamos tal vez ante código encriptado? Colocamos puntos de ruptura para detectar en qué momento el programa escribe en la zona de memoria donde se encuentra nuestro código, y encontramos finalmente lo siguiente:
`:06 55 push ebp` `:07 8BEC mov ebp, esp` `:09 57 push edi` `:0A 53 push ebx` `:0B 89C3 mov ebx, eax` `:0D 89D7 mov edi, edx` `:0F F76D10 imul [ebp+10]` `:12 03450C add eax, dword ptr [ebp+0C]` `:15 99 cdq` `:16 F77D08 idiv [ebp+8]` `:19 89D0 mov eax, edx` `:1B 25FF000080 and eax, 800000FF` `:20 7907 jns 29` `:22 48 dec eax` `:23 0D00FFFFFF or eax, FFFFFF00` `:28 40 inc eax` `:29 8A27 mov ah, byte ptr [edi] ; leemos el siguiente byte` `:2B 001F add byte ptr [edi], bl ; lo desencriptamos` `:2D 2807 sub byte ptr [edi], al` `:2F 88E3 mov bl, ah` `:31 89D0 mov eax, edx` `:33 47 inc edi` `:34 E2D9 loop 0F ; seguimos hasta terminar con todos` `:36 5B pop ebx` `:37 5F pop edi` `:38 5D pop ebp` `:39 C20C00 ret 0C`
La rutina desencripta una zona de memoria que va desde ES:FF9618 hasta ES:FF9C43. Ante semejante panorama se nos presentan dos alternativas: Hacer un volcado de memoria del bloque de memoria desencriptado, sustituirlo por el original encriptado, y anular la llamada a la rutina de desencriptación anterior. Con ayuda de nuestro compilador favorito escribir un pequeño programa que encripte y desencripte un bloque de memoria siguiendo el algoritmo anterior, y generar un bloque encriptado que, al ser desencriptado por la rutina original nos genere el código de chequeo #2 perfectamente parcheado. Si observamos con un poco de detenimiento la rutina anterior, nos daremos cuenta de que la desencriptación no se realiza contra una tabla byte a byte ni nada parecido (lo que nos facilitaría sustanciosamente la labor) sino que para la desencriptación de cada byte se utiliza el valor del byte anterior encriptado. Teniendo esto en cuenta, y aunque más entretenida, olvidaremos para otro día la segunda opción, y nos tiraremos a por la primera. Con ayuda de la excelente utilidad IceDump hacemos un volcado de la zona de memoria afectada tras la encriptación, con la intención de sustituirla físicamente por su equivalente encriptada. Y hacemos lo propio tras la desencriptación: Ahora buscamos el bloque encriptado (lo encontramos en MMUTIL32.DLL) y lo sustituimos por el desencriptado parcheado: quedando la DLL así: y, en concreto, la parte que ahora nos interesa, nuestro parche: Ahora nos queda localizar la llamada a la rutina de desencriptación para anularla. Una vez más, encontramos el código correspondiente en MMUTIL32.DLL:
`:409C48 53 push ebx` `:409C49 803DC0D2400000 cmp byte ptr [40D2C0], 0` `:409C50 7441 je 409C93` `:409C52 33C0 xor eax, eax` `:409C54 A3D8E54000 mov dword ptr [40E5D8], eax` `:409C59 33C0 xor eax, eax` `:409C5B A3DCE54000 mov dword ptr [40E5DC], eax` `:409C60 BB187C4000 mov ebx, 407C18` `:409C65 BAC87E4000 mov edx, 407EC8` `:409C6A B86C874000 mov eax, 40876C` `:409C6F E8E8E1FFFF call 407E5C` `:409C74 8BC3 mov eax, ebx` `:409C76 E8ADE2FFFF call 407F28 ; desencriptación` `:409C7B C605C8D2400001 mov byte ptr [40D2C8], 1` `:409C82 891DB8D24000 mov dword ptr [40D2B8], ebx` `:409C88 E81FECFFFF call 4088AC` `:409C8D 8BC3 mov eax, ebx` `:409C8F 03C0 add eax, eax` `:409C91 5B pop ebx` `:409C92 C3 ret`
Parcheamos adecuadamente MMUTIL32.DLL con una de las múltiples soluciones:
`:409C74 EB05 jmp 409C7B ; saltamos la desencriptación`
Ya estamos listos para probar nuestro segundo parche. Una vez más, la misma ventana de About que antes, y es que, efectivamente, existe un tercer chequeo de existencia de IDE de Delphi. Un nuevo punto de ruptura en FindWindowA y obtenemos el código del tercer chequeo:
`CHEQUEO #3` `:409E24 6A00 push 0 ; cualquier título nos vale` `:409E26 68649F4000 push 409F64 ; clase de la ventana "TAppBuilder"` `:409E2B E820ABFFFF call 404950 ; FindWindowA` `:409E30 BF07000000 mov edi, 7` `:409E35 85C0 test eax, eax` `:409E37 0F8419010000 je 409F56 ; ventana no encontrada, nos vamos` `:409E3D 33F6 xor esi, esi ; ventana encontrada, nos preparamos` `:409E3F 6A50 push 50 ; para obtener su título` `:409E41 8D942458030000 lea edx, dword ptr [esp+358]` `:409E48 52 push edx ; EDX = destino del título` `:409E49 50 push eax ; EAX = handle de la ventana` `:409E4A E811ABFFFF call 404960 ; GetWindowTextA` `:409E4F BA709F4000 mov edx, 409F70 ; "Delphi"` `:409E54 8D842454030000 lea eax, dword ptr [esp+354]` `:409E5B E85CFEFFFF call 409CBC ; comparamos con el título obtenido` `:409E60 85C0 test eax, eax ; ¿son iguales?` `:409E62 7407 je 409E6B ; no, no está el IDE de Delphi` `:409E64 BED4D24000 mov esi, 40D2D4 ; sí, IDE de Delphi detectado` `:409E69 EB1A jmp 409E85 ; nos largamos` `:409E6B BA789F4000 mov edx, 409F78 ; probamos con "C++Builder"` `:409E70 8D842454030000 lea eax, dword ptr [esp+354]` `:409E77 E840FEFFFF call 409CBC ; comparamos con el título obtenido` `:409E7C 85C0 test eax, eax ; ¿título = "C++Builder"?` `:409E7E 7405 je 409E85 ; sí, IDE presente, nos largamos` `...`
Como casi siempre, se nos presentan mútliples soluciones, ahí va una de las posibles:
`:409E24 EB3E jmp 409E64 ; saltamos hacia la salida correcta`
Esta vez tenemos suerte, ya que, aunque el código de este tercer chequeo estaba también encriptado, los programadores han sido tan amables de desencriptarlo de una sóla vez junto con el chequeo #2, de forma que lo encontramos perfectamente legible en MMUTIL32.DLL. No nos queda más que ejecutar nuevamente y probar nuestro tercer parche. El programa funciona perfectamente. Probamos con un par de ejemplos cualesquiera y vemos que, aunque hayamos susituído la MMUTIL32.DLL original por la nuestra, aún es necesario parchear cada ejecutable producido para saltar el chequeo #1. Parémonos un poco a pensar, intentando ponernos en el lugar del programador, y preguntémonos ¿dónde podríamos incluir nuestro código de chequeo para que cualquier ejecutable compilado con alguno de mis componentes lo incluya al arrancar? Las respuestas pueden ser variadas, dependiendo de los conocimientos que posea el programador, del tiempo de que disponga, de lo vago que sea o de su grado de implicación con el producto. La primera solución que suele venir a la cabeza es la de incluir todo el chequeo en la sección de inicialización de una unidad compartida, algo así como:
`unit ChequeoIDE;` `interface` `[...]` `implementation` `[...]` `initialization` `[... código de chequeo ...]` `end.`
e incluir luego el uso de esta unidad en cada unidad correspondiente a un componente:
`unit MiPrimerComponente;` `uses` `ChequeoIDE, ...;` `interface` `[...]` `implementation` `[...]` `end.`
Con este uses garantizamos la ejecución del código de chequeo al comienzo de cada programa. Nos bastan unos segundos para echar un vistazo a los ficheros *.DCU (pequeño recordatorio: el compilador genera un fichero *.DCU con código compilado por cada fichero fuente *.PAS, el linkador se encargará de “copiar” de cada *.DCU el código necesario y ubicarlo en el ejecutable final) incluídos con el producto y nos encontramos con uno que, cuando menos, despierta nuestra sospechas:
`[...]` `30/01/00 21:54 2.877 MMTimer.dcu` `12/06/00 22:33 4.281 MMTools.dcu` `12/06/00 22:33 10.925 mmtools_d5.dcu` `30/01/00 21:54 16.587 MMTrigg.dcu` `30/01/00 21:54 7.031 MMTrnPrp.dcu` `30/01/00 21:54 34.940 MMUtils.dcu <----` `30/01/00 21:54 61.123 MMVFW.dcu` `12/06/00 22:33 2.041 MMVisReg.dcu` `30/01/00 21:54 5.603 MMVolume.dcu` `30/01/00 21:54 40.499 MMWave.dcu` `[...]`
Tentador, ¿no?. Está pidiendo a gritos que le echemos una ojeada y busquemos en su interior el código del chequeo #1:
`:7232 6A00 push 0` `:7234 68A0010000 push 1A0` `:7239 E800000000 call 723E` `:723E 85C0 test eax, eax` `:7240 7430 je 7272` `:7242 6A50 push 50` `:7244 8D55AB lea edx, dword ptr [ebp-55]` `:7247 52 push edx` `:7248 50 push eax` `:7249 E800000000 call 724E` `:724E BAAC010000 mov edx, 1AC` `:7253 8D45AB lea eax, dword ptr [ebp-55]` `:7256 E800000000 call 725B` `:725B 85C0 test eax, eax` `:725D 7511 jne 7270` `:725F BAB4010000 mov edx, 1B4` `:7264 8D45AB lea eax, dword ptr [ebp-55]` `:7267 E800000000 call 726C` `:726C 85C0 test eax, eax` `:726E 7402 je 7272` `:7270 B301 mov bl, 1`
Efectivamente, dentro de MMUtils.dcu hay un código de chequeo idéntico a nuestro chequeo #1. Probamos el mismo parche, sólo que esta vez lo hacemos sobre el *.DCU y no sobre el ejecutable. Compilamos un par de ejemplos más cualesquiera y, tras ejecutarlos, nos damos cuenta de que estábamos en lo cierto. Sin embargo, ¿qué hay del chequeo de 30 días del que se habla en el acuerdo de licencia?
`[...]` `A. Registration: Registering MMTools gains you the Delphi installable VCL units or OCX controls with no runtime limitations. Applications compiled with the evaluation version of MMTools execute for 30 days and only if a development system is running. Registration is valid for the current major version of MMTools. As a registered user of MMTools you are entitled to all maintenance releases for the current version.` `[...]`
Un vistazo al código desensamblado de MMUTIL32.DLL nos proporciona esta hermosura:
`Exported fn(): _CheckTime - Ord:0003h` `:409F90 FF05B4D24000 inc dword ptr [40D2B4]` `:409F96 833DB0D2400002 cmp dword ptr [40D2B0], 2` `:409F9D 7C1B jl 409FBA` `:409F9F 833DB4D2400002 cmp dword ptr [40D2B4], 2` `:409FA6 7C12 jl 409FBA` `:409FA8 803DCCD2400000 cmp byte ptr [40D2CC], 0` `:409FAF 750C jne 409FBD` `:409FB1 E852FDFFFF call 409D08` `:409FB6 84C0 test al, al` `:409FB8 7503 jne 409FBD` `:409FBA 33C0 xor eax, eax ; período de evaluación terminado` `:409FBC C3 ret` `:409FBD B001 mov al, 1 ; todo va bien` `:409FBF C3 ret`
Si la función devuelve 0, nuestro período de evaluación ha terminado, y los programas nos muestran una ventana como la siguiente al arrancar: Lo cierto es que, aunque durante nuestras pruebas nunca hayamos conseguido hacer que esta función devuelva 0, más nos valdría parchearla, por si las moscas. Una solución rápida y “económica” podría ser la siguiente:
`:409FBC 90 nop ; ocurra lo que ocurra siempre devolvemos "1"`
Y EN EL PRÓXIMO CAPÍTULO:
Este tutorial empezó de una forma, y ha acabado de otra, así que mal podemos prever de qué podríamos tratar en una próxima entrega de esta serie. Sin embargo ahí va alguna pista ... ¿os habéis fijado en que ninguno de los 9 productos analizados se asegura de que el ejecutable esté realmente corriendo “desde dentro” del IDE? Sin más, un saludo y hasta la próxima, agur. ______________________ Los saludos de rigor (en ningún orden especial) para ... +ORC, +Fravia y la +HCU al completo, el Equipo RHF/ZHR al completo (Txente "Mr. 400 Mb", Soraya "Pikatxu", Ainhoa "La Bruja" y Tommy), Tron, Pipo, Itziar, Ibon, Fredo, Jon, Alazne, Gloom Lord, Nieves, Roberto y Mari Mar, Pin y Pon (donde quiera que estéis ... ¡seguid allí!) y, por supuesto, a todos los colores de [WkT!], desde el Blanco hasta el Negro, incluyendo a todos los que se fueron y a los que aún quedan por llegar (¿tal vez tú, estimado lector?). ______________________ Euskal Herria, 2000/03/12 © 2000 Aitor, +HCU & [WkT!]
[ 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