|
|
 |
ESTUDIO COLECTIVO DE DESPROTECCIONES
Última Actualizacion:
25/10/2001
|
 |
|
Programa |
Visual Basic / P-Code |
W95 / W98 / NT |
Descripción |
Analisis de la máquina virtual de P-Code |
Tipo |
|
Tipo de Tutorial |
[X]Original, []Adaptación, []Aplicación, []Traducción |
Url |
|
Protección |
|
Dificultad |
1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista |
Herramientas |
|
Objetivo |
Conocer el funcionamiento de la VM a nivel interno |
Cracker |
Mr.Silver [WkT!] |
Grupo |
http://www.whiskeykontekila.org |
Fecha |
9 de Septiembre de 2001 |
INTRODUCCION
|
Cada día son más las aplicaciones realizadas en ese patético lenguaje que la
mayoría de crackers suelen odiar ;). Es por ello que se hace necesario este
tutorial. En él vamos a ver que tipos de aplicaciones de VB podemos
encontrarnos
y como podemos crackearlas sin demasiado esfuerzo. Las aplicaciones de VB
pueden estar compiladas de dos maneras: en modo nativo o en p-code.
El modo nativo indica que la aplicación se compila usando el juego de
instrucciones sobre la plataforma de desarrollo con la que se compiló , es
decir
en un PC se usaría en lenguaje ensamblador de la familia de los x86. Esto
permite el trazado con un debugger tal y como se suele hacer normalmente.
El modo P-code indica que la aplicación se compila usando un juego de
instrucciones fijo, es decir que el juego de instrucciones usado es el mismo
para todas las plataformas de desarrollo. Básicamente se parece mucho al
lenguaje Java, al igual que este, se utiliza una maquina virtual para
traducir
las instrucciones al lenguaje del procesador sobre el cual se ejecuta la
aplicación. En un PC esta máquina virtual reside en las famosas DLL
msvbvm50.dll
(para aplicaciones compiladas con VB5) y msvbvm60.dll (para aplicaciones
compiladas con VB6). Las aplicaciones generadas con P-code suelen ocupar
entre
un 40 y un 50% menos que las aplicaciones generadas con código nativo, en
contraposición se ejecutan más lentamente que estas ultimas.
En este tutorial vamos a tratar las aplicaciones compiladas con P-Code, ya
que
las otras no tienen ningún misterio a excepción de un API específico el cual
se
encuentra en msvbvmXX.dll.
Que problema/s se le plantea a un cracker cuando se enfrenta a una
aplicación
compilada en P-Code?
- Al trazar la aplicación con un debugger, nunca se llega a ver el código
real
de la aplicación, tan solo se ven los códigos de operación. En su lugar lo
que
veremos será la maquina virtual (msvbvmXX.dll)
- El desensamblado normal de un archivo compilado con P-code no es válido
para su
examen ya que las instrucciones no corresponden con el juego de
instrucciones
del P-code.
Pues ante tales desventajas más de uno se dará por vencido, pero como esto
es
ingeniería inversa vamos a ver como podemos solventar estos pequeños baches
en
el camino.
|
HIPOTESIS
|
- Si hay una máquina virtual que procesa códigos de operación, se da por
seguro
que esos códigos de operación tienen un significado al igual que los códigos
de
operación de nuestra plataforma. El problema es saber que significa cada
código
de operación.
- Si cuando trazamos la aplicación, lo que vemos es la máquina virtual,
seguro
que podemos ver que códigos está procesando y seguro que tambien podemos
acceder
a ellos para modificarlos.
|
COMPROBACION DE LAS HIPOTESIS
|
- La primera hipotesis se comprueba facilmente con una visita al site de
microsoft:
http://msdn.microsoft.com/library/backgrnd/html/msdn_c7pcode2.htm
encontraremos un artículo introductorio al P-code, que sin ser demasiado
específico si que nos proporciona bastante información importante. De la
lectura
de este artículo (gracias Black!) podemos comprobar que el juego de
instrucciones para aplicaciones de VB en P-code, consta de 255 opcodes
estandar
y 256 más que son menos utilizados (o sea 511 instrucciones diferentes) que
pueden
ser combinadas con 4 o menos bytes.
- La segunda hipotesis se comprueba con una corta sesión de SoftIce (a big
Hi!
to all the guys at Numega!). Seguramente la máquina virtual (msvbvmXX.dll)
guardará un buffer en memoria con los códigos de operación. De este buffer
se
irán leyendo los códigos de operación de uno en uno y según el código
encontrado
la maquina virtual saltará a una rutina específica para el código leído.
Para
comprobarlo creé un formulario de VB y en el método Load agregué el código
siguiente:
Private Sub Form_Load()
MsgBox "Hola esto es P-code!!!", vbInformation, "Ejemplo"
End Sub
Muy simple como podemos comprobar. Para comprobar la teoría puse un BPX
sobre la
función rtcMsgBox (que es exportada por la la dll msvbvmxx.dll). Ejecuté el
programa y pulse F12 para regresar a la dirección desde donde se llamó a
rtcMsgBox. Esto es lo que encontré:
call eax // llama a rtcMsgBox
cmp edi,esp // estamos aquí
jnz 66105595 // comprueba puntero de pila
xor eax,eax // prepara eax para leer siguiente opcode del buffer :-)
mov al,[esi] // lee opcode a ejecutar, en mi caso 36h
inc esi // incrementa puntero
jmp [eax*4+660FDA58] // y salta a la rutina que interpreta el opcode 36h
Vemos que tal y como habiamos deducido se lee el opcode de un buffer (ESI)
en
AL, y posteriormente se salta a la rutina de interpretación del opcode
usando
como índice el código de operación (esta es una manera inteligente y rápida
de
bifurcar el código sin tener que hacer miles de comprobaciones, parece
mentira que
sea de M$ :) ).
Si continuamos trazando veremos que el acceso al buffer
apuntado
por ESI se repite continuamente. Pues bien, resulta que mientras estemos en
la
máquina virtual, el registro ESI, siempre apuntará al buffer con los códigos
de
operación, de esta forma siempre podemos controlar que opcode va a
ejecutarse
con el comando de SoftIce:
> d *esi
Tambien podremos comprobar que la dirección 660FDA58 (depende de la versión
de la
máquina virtual), apunta al inicio de una tabla de punteros a funciones, es
decir
por cada opcode la máquina virtual guarda una dirección de salto en una
tabla. Una
investigación más a fondo sobre este hecho, nos revelará que existen más
direcciones
de salto direccionadas de esta manera, pero por el momento desconozco su
utilidad,
aunque es posible que sean las funciones que se encargan de interpretar los
posibles subcódigos de operación.
Hasta aquí todo parece obvio, el camino a seguir resulta ahora más claro,
solo
nos falta por saber que significa cada código de operación. Para ello
podemos
recurrir al unico desensamblador de p-code que yo conozco, el exdec, creado
por
JosephCo. Esta pequeña utilidad nos desensamblará (si se puede decir así)
cualquier aplicación en p-code creada con VB 5 o 6. La puedes bajar de
http://codersdomain.cjb.net.
Si utilizamos el exdec sobre nuestro pequeño ejemplo obtendremos:
Proc: 40183c
401808: 27LitVar_Missing
40180B: 27 LitVar_Missing
40180E: 3a LitVarStr: ( local_00B4 ) Ejemplo
401813: 4e FStVarCopyObj local_00C4
401816: 04 FLdRfVar local_00C4
401819: f5 LitI4: 0x40 64 (...@)
40181E: 3a LitVarStr: ( local_0094 ) Hola esto es P-code!!!
401823: 4e FStVarCopyObj local_00A4
401826: 04 FLdRfVar local_00A4
401829: 0a ImpAdCallFPR4: rtcMsgBox
40182E: 36 FFreeVar
401839: 13 ExitProcHresult
La verdad es que todo esto resulta bastante nuevo pero con un poco de arte y
paciencia podemos deducir lo siguiente:
Opcode 27 -> LitVar_Missing : Parece que se usa cuando un parámetro de la
función a la cual se va a llamar (en este caso rtcMsgBox), no fue
especificado
es el código, esto es lógico ya que la función MsgBox de VB tiene la
siguiente sintaxis:
MsgBox(prompt[, buttons][, title][, helpfile, context])
Un total de 5 parámetros, nosotros solo le dimos 3, por eso hay dos
instrucciones LitVar_Missing.
Opcode 3a -> LitVarStr : Parece claro tambien, si nos fijamos vemos que
corresponde a la cadena literal "Ejemplo", este cadena es el tercer
parámetro de
MsgBox y corresponde al titulo de la ventana. Puede deducirse que esta
instrucción pasa una cadena literal a la función rtcMsgbox.
Opcode f5 -> LitI4 : Pasa un entero de 4 bytes a rtcMsgBox, esto corresponde
al
estilo del MsgBox, en este caso corresponde a vbInformation, que muestra el
icono de información y un botón de aceptar. Vendria a ser como un PUSH
NUMERO_DE_32_BITS.
Opcode 0a -> ImpAdCallFPR4 : Llama a una rutina que se encuentra dentro de
la
DLL de la máquina virtual, en este caso rtcMsgBox.
Opcode 13 -> ExitProcHresult : Sale del proceso y retorna el control a la
rutina
que la llamó, vamos un ret en toda regla jeje.
El significado del resto de opcodes no es relevante y en algunos se puede
deducir su función con solo leer el nombre ;-)
Lo siguiente es una lista de unas cuantas instrucciones que nos pueden
servir de
ayuda:
6c -> ILdRf Empuja una dirección a la pila
1b -> LitStr5 Empuja una cadena literal a la pila
fb -> Lead0 Compara dos cadenas (jeje, para que podría servir esto :-)
30 -> EqStr Compara dos cadenas (jeje, para que podría servir esto :-)
2f -> FFree1Str Libera la memoria usada
1a -> FFree1Ad Libera la memoria usada
0f -> VCallAd Ejecuta código dentro de la máquina virtual
1c -> BranchF Salta si la comparación previa era falsa ( vamos lo mismo
que un jne/jnz de asm xDDD )
1d -> BranchT Salta si la comparación previa era cierta ( vamos lo mismo
que un je/jz de asm xDDD )
1e -> Branch Salta incondicionalmente ( jeje adivina ke utilidad tiene)
fc -> Lead1 Termina la ejecucíón del programa (jeje este es bueno...)
c8 -> End Termina la ejecucíón del programa (jeje este es bueno...)
f3 -> LitI2 Guarda el Integer especificado en la pila
f4 -> LitI2_Byte Convierte un valor de Byte a Integer y lo mete en la pila
70 -> FStI2 Guarda el último Integer que se haya en la pila en la
variable global especificada
6b -> FLdI2 Carga en la pila un Integer desde la variable local
especificada
a9 -> AddI2 Suma los dos últimos Integers que se empujaron a la pila y
mete en pila el resultado
ad -> SubI2 Resta los dos últimos Integers que se empujaron a la pila y
mete en pila el resultado
b1 -> MulI2 Multiplica los dos últimos Integers que se empujaron a la
pila y mete en pila el resultado como un Integer, creo que
si hay desbordamiento se ignora.
Con esto ya podemos empezar a trazar y a cambiar bytes a nuestro antojo, el
truco reside
en usar el comando BPM sobre la dirección de código que nos de el exdec,
esta dirección
no es la dirección de código de la maquina virtual que nosotros vemos, si no
que se
trata de la dirección dentro del buffer apuntado por ESI. Por ejemplo si
quisieramos
parar la ejecución del programa en esta línea:
401819: f5 LitI4: 0x40 64 (...@)
Tan solo tendriamos que poner un BPM tal como este:
> BPM 401819
Esto nos límita a un máximo de 4 breakpoints de memoria, ya que este tipo de
breakpoints
basan su funcionamiento en los registros de depuración,y solo existen 4
donde se pueden
almacenar direcciones de ruptura, el resto son de uso conjunto.
Una vez que el breakpoint surta efecto, podremos alterar el contenido del
buffer de opcodes
con:
> E *EDI
Ahora podremos sustituir el opcode por otro que nos interesE más, p. ej. un
salto condicional
podriamos cambiarlo por uno incondicional etc.
Pero esto no es todo, como he dicho antes la máquina virtual guarda una
tabla con direcciones de
salto para cada opcode, la dirección de esta tabla variará segun la versión
de la máquina virtual
con lo cual funcione el programa. Esto nos permite pensar en otra manera
alternativa de interrumpir
la ejecución del programa, me explico: sabiendo la dirección de salto de
cada opcode, podemos
establecer breakpoints (de tipo BPX) sobre estas direcciones y asi podriamos
controlar por ejemplo
cuando se va a ejecutar un BranchX o una VCallAd. Como se haría esto? Pues
fácil, primero
buscamos la dirección de la tabla en mi caso (660FDA58 ver arriba) para la
MSVBVM60.DLL y luego
vamos a esta posición en el desensamblado del código. A partir de esta
posición, cada DWORD que
encontremos es un salto a cada opcode segun su posición. Aquí teneis la
lista de direcciones para
las dos máquinas virtuales (MSVBVM60.DLL v6.00.8495 y MSVBVM50.DLL
v05.00.4319 (SP2)),
para que no tengais que andar buscando ;).
VB5 version 4319 -> 7B3EED94h
VB5 version 8244 -> 0F0FED94h
VB6 version 8176 -> 66106954h
VB6 version 8268 -> 66106D14h
VB6 version 8495 -> 660FDA58h
VB6 version 8877 -> 660FEA58h
VB6 version 8964 -> 660FEA58h
Bueno hasta aquí este tutorial, si lo habeis entendido, vereis que con un
poco de imaginación
y algo de suerte se pueden hacer cosas muy prácticas con la máquina virtual,
se puede modificar
para que nos loggee los datos que recibirá una instrucción o incluso para
saber cuando se
comparan dos cadenas, de hecho existe una máquina virtual de la versión 5.0
de VB modificada
por Lazarus que nos loggea los parámetros de varias funciones de la maquina
virtual, pero no
lo hace sobre los códigos de operación, sólo sobre las funciones API
exportadas por la maquina
virtual, y lo interesante y verdaderamente práctico es poder ver el código
que se esta ejecutando
gracias a nuestros propios estudios y información aportada por Josephco
(tio eres cojonudo)
se pudo realizar el WKTVBDebugger
Mr. Silver [WKT!] 2001
|
[ 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 |
|
|
|
|
|