|
|
 |
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 Innovative |
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 Shkolnik |
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 |
|
|
|
|
|