En principio procederemos de
la forma normal, ejecutar el programa, introducir los datos
de registro y copiar el mensaje de error que nos aparece.
Desensamblamos el ejecutable
apis32.exe y vemos que no hay ninguna referencia a cadenas,
ni tabla de funciones de importación.Esto son dos de los
sintomas más normales en los archivos comprimidos/encriptados.
Si examinamos el código por encima veremos que no parece
un código muy lógico, repetición de instrucciones, escrituras
a puerto frecuentes etc. esto es debido a que este no es
el código real del programa. Bueno pero sabemos que de alguna
manera el ejecutable debe descomprimirse/desencriptarse
a si mismo y por lo tanto el código que realiza este trabajo
debe aparecer. Efectivamente, y sólo sólo puede estar en
un sitio: el punto de entrada del programa (Program Entry
Point). Vamos al menu Goto/Program Entry Point y el W32DAsm
nos llevará al punto de entrada del programa. Veremos algo
así:
:0041A000 |
669C |
pushf
|
:0041A002 |
60 |
pushad
|
:0041A003 |
E8CA000000 |
call
0041A0D2 |
//llamada
a la rutina de descompresión |
:0041A008 |
0300 |
add eax,
dword ptr [eax] |
// todo
esto ya no se ejecutará |
:0041A00A |
0400 |
add al,
00 |
:0041A00C |
0500060007 |
add eax,
07000600 |
:0041A011 |
0008 |
add byte
ptr [eax], cl |
:0041A013 |
0009 |
add byte
ptr [ecx], cl |
:0041A015 |
000A |
add byte
ptr [edx], cl |
:0041A017 |
000B |
add byte
ptr [ebx], cl |
:0041A019 |
000D000F0011 |
add byte
ptr [11000F00], cl |
:0041A01F |
0013 |
add byte
ptr [ebx], dl |
:0041A021 |
0017 |
add byte
ptr [edi], dl |
:0041A023 |
001B |
add byte
ptr [ebx], bl |
:0041A025 |
001F |
add byte
ptr [edi], bl a |
Ok, según el W32DAsm este es
el punto de entrada del programa, pero el código no parece
muy lógico a partir de la llamada (call) de la tercera linea.
Probablemente, después de la llamada, ya no se vuelve a
la siguiente línea, sino que el programa saltará al punto
de entrada del programa original (el punto de entrada del
programa descomprimido), es de suponer que lo que sigue
a la llamada, son datos de la sección.
Antes de continuar, vamos
a hechar un vistazo a las diferentes secciones del ejecutable,
para ello utilizaremos el ProcDump. Ejecutamos el ProcDump
y hacemos click en el botón PE Editor, seleccionaremos el
ejecutable apis32.exe y aceptamos (deberemos salir del W32DAsm
para que el Procdump pueda leer el archivo). Nos aparecerá
un cuadro de dialogo como el siguiente:

Para saber el punto de entrada
exacto del programa, sumaremos el valor de Image Base con
el valor Entry Point, resultando ser igual que el que nos
daba el W32DAsm. Podemos asegurar pues que este el el punto
de entrada. Ahora miraremos en que sección se encuentra
el punto de entrada, hacemos click en el botón Sections
y nos aparece un cuadro de diálogo como el siguiente:

Vemos que aparecen las secciones
.itext , .idata , .rsrc y por último .madmat. Si miramos
el valor Virtual Offset de la sección .madmat, vemos que
coincide con el valor Entry Point anterior, esto nos indica
que esta sección es la encargada de descomprimir/desencriptar
el ejecutable y que por lo tanto contiene el código que
estamos buscando.Es curioso también que coincida con parte
de la URL del autor http://madmat.hypermart.net :)
Salimos del ProcDump cancelando
todos los cuadros de diálogo abiertos.Volvemos a desensamblar
el ejecutable y nos vamos al punto de entrada del programa
(menu Goto/Program Entry Point), una vez allí vamos a ver
que es lo que hace la llamada que hay en la segunda línea.
Nos situamos sobre esta (linea azul encima de la linea de
código ) y pulsamos el botón Call de la barra de botones.
Esto nos llevará al código siguiente:
:0041A0D5 |
50 |
push
eax |
:0041A0D6 |
8BC8 |
mov
ecx, eax |
:0041A0D8 |
8BD0 |
mov
edx, eax |
:0041A0DA |
81C168D20000 |
add
ecx, 0000D268 |
:0041A0E0 |
81C2DC150000 |
add
edx, 000015DC |
:0041A0E6 |
8920 |
mov
dword ptr [eax], esp |
:0041A0E8 |
8BE1 |
mov
esp, ecx |
:0041A0EA |
50 |
push
eax |
:0041A0EB |
812C2400A00100 |
sub
dword ptr [esp], 0001A000 |
:0041A0F2 |
FF30 |
push
dword ptr [eax] |
:0041A0F4 |
50 |
push
eax |
:0041A0F5 |
80042408 |
add
byte ptr [esp], 08 |
:0041A0F9 |
50 |
push
eax |
:0041A0FA |
80042446 |
add
byte ptr [esp], 46 |
:0041A0FE |
50 |
push
eax |
:0041A0FF |
80042465 |
add
byte ptr [esp], 65 |
:0041A103 |
50 |
push
eax |
:0041A104 |
800424A1 |
add
byte ptr [esp], A1 |
:0041A108 |
50 |
push
eax |
:0041A109 |
800424BF |
add
byte ptr [esp], BF |
*
Referenced by a (U)nconditional or (C)onditional Jump
at Address: |:0041A172(U) |
:0041A10D |
833A00 |
cmp
dword ptr [edx], 00000000 |
:0041A110 |
0F84A7140000 |
je
0041B5BD |
:0041A116 |
F70200000080 |
test
dword ptr [edx], 80000000 |
:0041A11C |
741B |
je
0041A139 |
:0041A11E |
FD |
std
|
:0041A11F |
8B0A |
mov
ecx, dword ptr [edx] |
:0041A121 |
81E1FFFFFF7F |
and
ecx, 7FFFFFFF |
:0041A127 |
8B742418 |
mov
esi, dword ptr [esp+18] |
:0041A12B |
8BFE |
mov
edi, esi |
:0041A12D |
037204 |
add
esi, dword ptr [edx+04] |
:0041A130 |
037A08 |
add
edi, dword ptr [edx+08] |
:0041A133 |
F3 |
repz
|
:0041A134 |
A5 |
movsd
|
:0041A135 |
83C20C |
add
edx, 0000000C |
:0041A138 |
FC |
cld
|
*
Referenced by a (U)nconditional or (C)onditional Jump
at Address: |:0041A11C(C) |
:0041A139 |
52 |
push
edx |
:0041A13A |
FF32 |
push
dword ptr [edx] |
:0041A13C |
8B5A08 |
mov
ebx, dword ptr [edx+08] |
:0041A13F |
035C2420 |
add
ebx, dword ptr [esp+20] |
:0041A143 |
53 |
push
ebx |
:0041A144 |
8B5A04 |
mov
ebx, dword ptr [edx+04] |
:0041A147 |
035C2424 |
add
ebx, dword ptr [esp+24] |
:0041A14B |
53 |
push
ebx |
:0041A14C |
E84A000000 |
call
0041A19B |
:0041A151 |
85C0 |
test
eax, eax |
:0041A153 |
741F |
je
0041A174 |
:0041A155 |
8B7C2404 |
mov
edi, dword ptr [esp+04] |
:0041A159 |
83C40C |
add
esp, 0000000C |
:0041A15C |
5A |
pop
edx |
:0041A15D |
8B4A0C |
mov
ecx, dword ptr [edx+0C] |
:0041A160 |
C1F902 |
sar
ecx, 02 |
:0041A163 |
33C0 |
xor
eax, eax |
:0041A165 |
F3 |
repz
|
:0041A166 |
AB |
stosd
|
:0041A167 |
8B4A0C |
mov
ecx, dword ptr [edx+0C] |
:0041A16A |
83E103 |
and
ecx, 00000003 |
:0041A16D |
F3 |
repz
|
:0041A16E |
AA |
stosb
|
:0041A16F |
83C210 |
add
edx, 00000010 |
:0041A172 |
EB99 |
jmp
0041A10D |
*
Referenced by a (U)nconditional or (C)onditional Jump
at Address: |:0041A153(C) |
:0041A174 |
8B642424 |
mov
esp, dword ptr [esp+24] |
:0041A178 |
83C426 |
add
esp, 00000026 |
:0041A17B |
C3 |
ret
|
Fijate en los las instrucciones
repz movsd, son muy típicas en las rutinas de descompresion/desencriptación,
ya que se debe copiar el código y esta es una de las pocas
formas de hacerlo. No nos interesa el algoritmo de compresión
que se este utilizando, nuestro objetivo es encontrar la
línea de código que nos envie al punto de entrada del programa
descomprimido. Puede que lo que busquemos este en la línea
41a17b (ret), ya que es posible que allí se retorne al punto
de entrada del programa descomprimido.Es hora del Soft-Ice.
Cargaremos el Symbol Loader
del Soft-Ice y haremos click en Files/Open Module y seleccionaremos
el fichero apis32.exe. Esto cargará el EXE en memoria y
podremos trazarlo desde su punto de entrada original con
Load/Module. Pulsaremos F8 una sola vez y entraremos en
el código.Ahora pondremos un BPX en la línea 41a17b con:
> bpx
41a17b
y pulsaremos g para continuar.
El BPX no surtirá efecto y en su lugar obtendremos un cuadro
anunciandonos que el programa a efectuado una operación
no válida. Parece que estamos equivocados y el ret no se
ejecuta como esperabamos.Volviendo a examinar el código
anterior buscando otro punto donde se pueda saltar, vemos
una comparación bastante sospechosa en la línea 41a10D:
:0041A10D
833A00 cmp dword ptr [edx], 00000000
:0041A110 0F84A7140000 je 0041B5BD
Si nos fijamos, el salto se
ejecutara cuando el dword apuntado por [EDX] sea igual a
0, enviandonos a una dirección bastante lejana a este código,
el resto de los saltos, nunca llegan a salir de esta rutina,
por lo que bamos a trazar de nuevo con el Soft-Ice hasta
llegar a esta línea, una vez allí, pondremos un BPX a la
dirección donde se saltará con:
> bpx
41B5BD
ahora pulsamos g+ENTER para
continuar y nuestro BPX habrá surtido efecto, veremos lo
siguiente:
:0041B5BD |
8B642414 |
mov
esp, dword ptr [esp+14] |
:0041B5C1 |
5E |
pop
esi |
:0041B5C2 |
8BFE |
mov
edi, esi |
:0041B5C4 |
81C6D7150000 |
add
esi, 000015D7 |
:0041B5CA |
6A05 |
push
00000005 |
:0041B5CC |
59 |
pop
ecx |
:0041B5CD |
F3 |
repz
|
:0041B5CE |
A4 |
movsb
|
:0041B5CF |
61 |
popad
|
:0041B5D0 |
669D |
popf
|
:0041B5D2 |
E9E99FFEFF |
jmp
004055C0 |
//
salto incondicional a una dirección bastante "diferente"
|
:0041B5D7 |
E9BBB5FEFF |
jmp
00406B97 |
Esto si que parece el fín de
la rutina de descompresión, ya que restauran los registros
y los flags que fueron empujados al inicio del programa
y se salta incondicionalmente a una posición de código bastante
diferente.Si seguimos trazando con F8, ejecutando el salto,
veremos que este nos lleva a lo siguiente:
:004055C0 |
55 |
push
ebp |
:004055C1 |
8BEC |
mov
ebp, esp |
:004055C3 |
6AFF |
push
FFFFFFFF |
:004055C5 |
6810804000 |
push
00408010 |
:004055CA |
6818544000 |
push
00405418 |
:004055CF |
64A100000000 |
mov
eax, dword ptr fs:[00000000] |
:004055D5 |
50 |
push
eax |
:004055D6 |
64892500000000 |
mov
dword ptr fs:[00000000], esp |
:004055DD |
83C4A8 |
add
esp, FFFFFFA8 |
:004055E0 |
53 |
push
ebx |
:004055E1 |
56 |
push
esi |
:004055E2 |
57 |
push
edi |
:004055E3 |
8965E8 |
mov
dword ptr [ebp-18], esp |
*
Reference To: KERNEL32.GetVersion, Ord:014Ch |
:004055E6 |
FF15B8024100 |
Call
dword ptr [004102B8] |
//
Esto es el tipico inicio de todos los ejecutables |
:004055EC |
33D2 |
xor
edx, edx |
:004055EE |
8AD4 |
mov
dl, ah |
:004055F0 |
8915A0BB4000 |
mov
dword ptr [0040BBA0], edx |
:004055F6 |
8BC8 |
mov
ecx, eax |
:004055F8 |
81E1FF000000 |
and
ecx, 000000FF |
:004055FE |
890D9CBB4000 |
mov
dword ptr [0040BB9C], ecx |
:00405604 |
C1E108 |
shl
ecx, 08 |
:00405607 |
03CA |
add
ecx, edx |
:00405609 |
890D98BB4000 |
mov
dword ptr [0040BB98], ecx |
:0040560F |
C1E810 |
shr
eax, 10 |
:00405612 |
A394BB4000 |
mov
dword ptr [0040BB94], eax |
:00405617 |
E8840E0000 |
call
004064A0 |
:0040561C |
85C0 |
test
eax, eax |
:0040561E |
750A |
jne
0040562A |
:00405620 |
6A1C |
push
0000001C |
:00405622 |
E839010000 |
call
00405760 |
:00405627 |
83C404 |
add
esp, 00000004 |
*
Referenced by a (U)nconditional or (C)onditional Jump
at Address: |:0040561E(C) |
:0040562A |
C745FC00000000 |
mov
[ebp-04], 00000000 |
:00405631 |
E86A0C0000 |
call
004062A0 |
:00405636 |
E8550C0000 |
call
00406290 |
*
Reference To: KERNEL32.GetCommandLineA, Ord:00AAh |
:0040563B |
FF1574034100 |
Call
dword ptr [00410374] |
//
Esto es tipìco al inicio de todos los ejecutables |
Las llamadas a GetVersion y
GetCommandLineA, delatan con toda seguridad, que este es
el punto de entrada del programa original, por lo que apuntaremos
su dirección que es 41B5BD. Si continuamos con la ejecución
del programa, veremos que este funciona correctamente.
Ahora llega el momento de
la verdad, vamos a usar el ProcDump para volcar el ejecutable
una vez se halla descomprimido, para ello vamos a utilizar
un script del ProcDump. Abriremos el fichero script.ini
que se encuentra con el ProcDump, y agregaremos una linea
al final de la sección [INDEX], la sintaxis será:
Pxx=APIS32
v2.4
xx= Numero de línea que toce,
teniendo en cuenta que va en hexadecimal, por ejemplo si
tenemos una sección INDEX tal que así:
[INDEX]
P1=Hasiuk/NeoLite
P2=PESHiELD
P3=Standard
P4=Shrinker 3.3
P5=Wwpack32 I
P6=Manolo
P7=Petite<1.3
P8=Wwpack32 II
P9=Vbox Dialog
deberemos escribir
PA=APIS32
v2.4
ahora creamos una sección que
se llame igual que la línea que indica PXX (en nuestro caso
[APIS32 v2.4] justo debajo y segiidamente le añadiremos
las siguiente lineas:
L1=LOOK E9,E9,9F,FE,FF
L2=BP
L3=WALK
L4=EIP
L5=STEP
La línea L1, le indica al ProcDump
que busque esta secuencia de bytes en el código del ejecutable,
esta secuencia de bytes es el salto que hay en la línea
41B5D2
:0041B5D2
E9E99FFEFF jmp 004055C0
Este es el salto que nos envia
al punto de entrada del ejecutable original, por lo que
el ProcDump buscará esta posición de memoria.
Con la línea L2 le decimos
que ponga un breakpoint en esa misma posición de memoria
que se encontró.
La línea L3 le indica al ProcDump
que ejecute la siguiente instrucción a partir de la posición
donde se puso el breakpoint, en este caso lo que se hace
es que el salto se ejecuta, situandonos en el punto de entrada
del programa original.
Una vez en el punto de entrada
original, la línea L4 le indica al ProcDump, que use el
valor actual de EIP como punto de entrada del programa que
va a volcar y posteriormente con la línea L4 finalizamos
el trazado y procedemos al volcado del ejecutable.
Salvamos los cambios al fichero
script.ini y ejecutamos el ProcDump. (Antes, entrar
en el Soft-Ice y desactivar todos los breakpoints, ya que
sino, el ProcDump encontrará los breakpoint del SI y se
colcagará al descomprimir el ejecutable). Pulsamos
sobre Unpack, seleccionamos APIS 32 v2.4 y pulsamos OK,
ahora buscamos el archivo apis32.exe y lo seleccionamos
para descomprimirlo. Nos aparecerá un cuadro de mensaje
solicitando un nombre para el fichero descomprimido, le
llamaremos apis32un.exe y lo guardaremos junto al original.
Ahora, sin salir del ProcDump, podemos ejecutar el fichero
recien descomprimido y veremos que este funciona perfectamente,
y además ocupa más que el comprimido.
Ahora ya casi tenemos el fichero
original, a este fichero le sobra el código que antes usaba
para descomprimirse, ese código está en la sección .madmat,
por lo que vamos a eliminarla para reducir el tamaño del
ejecutable. Dentro del ProcDump, hacemos click en PE Editor,
seleccionamos el archivo descomprimido y en el marco que
dice "Apply changes method" activaremos la opción
"To PE file", ya que sino, solo eliminariamos
la sección de la cabezera y no del archivo. ahora pinchamos
sobre el botón Sections, veremos los nombres de todas las
secciones. Click con el botón derecho, nos aparece un menú
con varias opciones, seleccionamos Kill Section. Esto eliminará
la sección. Pulsamos Ok y Ok de nuevo y salimos del ProcDump.
Si miramos el ejecutable veremos que ahora tiene unos 45K
menos que antes, debido a que le hemos eliminado la sección
.madmat. El ejecutable debe ser totalmente funcional, puedes
comprobarlo tu mismo.
Bueno la parte más difícil
ya está, ahora sólo quedá buscar donde se comprueba el número
de série. Para ello procederemos de la manera estandar,
buscando la referencia a la cadena que nos muestra el mensaje
que se nos da al intentar registrarnos sin éxito, esta cadena
dice algo así:
The
registration information you entered... bla bla
Vale, desensamblamos el archivo
que hemos descomprimido y buscamos esta cadena. Cuando la
encontremos, doble click sobre esta y el W32DAsm nos transportará
al siguiente código:
:00401764 |
FF15F8024100 |
Call
dword ptr [004102F8] |
:0040176A |
E8312F0000 |
call
004046A0 |
//
llama a la rutina en 4046a0 |
:0040176F |
A374CE4000 |
mov
dword ptr [0040CE74], eax |
//
guarda valor de retorno en memoria mmm.... |
:00401774 |
EB01 |
jmp
00401777 |
//
salto incondicional de un solo byte |
:00401776 |
B8 |
BYTE
B8 |
*
Referenced by a (U)nconditional or (C)onditional Jump
at Address: |:00401774(U) |
:00401777 |
0AC0 |
or
al, al |
//
comprueba si el valor de retorno es 0 |
:00401779 |
7402 |
je
0040177D |
//
si es así, nos envia al código que hace referencia a
la cadena de error |
:0040177B |
EB2C |
jmp
004017A9 |
//
salto incondicional, buen chico, programa registrado.
|
*
Referenced by a (U)nconditional or (C)onditional Jump
at Address: |:00401779(C) |
*
Possible StringData Ref from Data Obj ->"The
registration information you " ->"provided
is incorrect. Please" |
:0040177D |
BFE8904000 |
mov
edi, 004090E8 |
//
referencia a la cadena |
:00401782 |
BAE0D14000 |
mov
edx, 0040D1E0 |
Que te parece? Pues lo tipico,
llamada a una función que comprueba los datos, se guarda
el resultado de la comprobación, se comprueba este resultado
y se actua en consecuencia. Podrimos hacer los cambios aquí,
pero eso no es lo más elegante, vamos a ver que hace la
función 4046a0.
:004046A0 |
51 |
push
ecx |
:004046A1 |
53 |
push
ebx |
:004046A2 |
55 |
push
ebp |
:004046A3 |
56 |
push
esi |
:004046A4 |
57 |
push
edi |
:004046A5 |
6A50 |
push
00000050 |
:004046A7 |
68A0C64000 |
push
0040C6A0 |
*
Possible StringData Ref from Data Obj ->"UserKey"
|
:004046AC |
6808964000 |
push
00409608 |
//
clave del registro que contiene el código introducido.
|
:004046B1 |
E81A030000 |
call
004049D0 |
//
obtiene valor de la clave y retorna su longitud en EAX
|
:004046B6 |
83C40C |
add
esp, 0000000C |
//
restaura la pila |
:004046B9 |
83F810 |
cmp
eax, 00000010 |
//
comprueba que el código tenga una longitud superior
a 10h caracteres, |
:004046BC |
7D08 |
jge
004046C6 |
//
si, es válido, se pasa a comprobar el nombre |
:004046BE |
33C0 |
xor
eax, eax |
//
código no válido debido a su longitud |
:004046C0 |
5F |
pop
edi |
:004046C1 |
5E |
pop
esi |
:004046C2 |
5D |
pop
ebp |
:004046C3 |
5B |
pop
ebx |
:004046C4 |
59 |
pop
ecx |
:004046C5 |
C3 |
ret
|
*
Referenced by a (U)nconditional or (C)onditional Jump
at Address: |:004046BC(C) |
:004046C6 |
6A2F |
push
0000002F |
:004046C8 |
6800CF4000 |
push
0040CF00 |
*
Possible StringData Ref from Data Obj ->"UserName"
|
:004046CD |
68F8954000 |
push
004095F8 |
//
clave del registro que contiene el nombre del usuario
introducido |
:004046D2 |
E8F9020000 |
call
004049D0 |
//
obtiene valos de la clave y su longitud en caracteres
|
:004046D7 |
83C40C |
add
esp, 0000000C |
//
restaura la pila |
:004046DA |
83F805 |
cmp
eax, 00000005 |
//
comprueba que el nombre tenga una longitud de 5 caracteres
o más |
:004046DD |
7D08 |
jge
004046E7 |
//
superior o igual a 5 caracteres, pasa a computar si
el nombre y código son correctos |
:004046DF |
33C0 |
xor
eax, eax |
//
si es inferior a 5 caracteres, error, nombre muy corto
|
:004046E1 |
5F |
pop
edi |
:004046E2 |
5E |
pop
esi |
:004046E3 |
5D |
pop
ebp |
:004046E4 |
5B |
pop
ebx |
:004046E5 |
59 |
pop
ecx |
:004046E6 |
C3 |
ret
|
El código que sigue a este,
se encarga de comprobar si el número y el nombre són válidos,
no nos interesa como lo hace, por lo que avanzaremos el
código hasta llegar al primer ret que encontremos, veremos
lo siguiente:
*
Referenced by a (U)nconditional or (C)onditional Jump
at Address: |:00404807(U) |
:00404815 |
03EA |
add
ebp, edx |
:00404817 |
41 |
inc
ecx |
:00404818 |
4E |
dec
esi |
:00404819 |
75DA |
jne
004047F5 |
:0040481B |
33C0 |
xor
eax, eax |
:0040481D |
5F |
pop
edi |
:0040481E |
85ED |
test
ebp, ebp |
//
comprueba si ebp es 0 |
:00404820 |
5E |
pop
esi |
//
restaura registro |
:00404821 |
5D |
pop
ebp |
:00404822 |
0F94C0 |
sete
al |
//
pone al a 1 si ebp=0 |
:00404825 |
5B |
pop
ebx |
:00404826 |
59 |
pop
ecx |
:00404827 |
C3 |
ret
|
//
retorna, si al=1, el código es válido. |
|
La línea 404822 parece bastante
sospechosa, ya que se pone AL a 0 o a 1 según una comparación
anterior, si cambiamos la instrucción sete al (0F94C0) por
setne al (0F95C0), y intentamos registrar el programa introduciendo
un nombre más largo de 5 caracteres y un código más largo
de 12 (10h) caracteres, veremos que el programa se registrará.
Pruebaló, apunta el offset de esta instrucción, abre el
archivo descomprimido, vete al offset que apuntaste y cambia
el 94 por un 95. Salva los cambios y pruebaló, verás que
el programa se ha registrado. Para desregistrarlo, puedes
borrar la clave del registro H_KEY_LOCAL_MACHINE/SoftWare/APIS32/UserKey.
Pero si lo hacemos así, resultará
que si por casualidad se acierta el código de registro,
este no se registrará, por lo que la manera más correcta
será cambiar el test ebp,ebp (85 ED) por xor ebp,ebp (33
ED), haciendo que ebp sea siempre 0 y por lo tanto el sete
al posterior siempre pondra al a 1. Por lo tanto sabemos
que el único byte de código que es necesario cambiar es
el 85 (test) por un 33 (test) que se halla en la posición
40481. Apuntaremos estos datos, ya que los necesitamos para
realizar el parche.
Ahora ya sabemos que es lo
que hay que cambiar, falta saber cómo vamos a hacer para
cambiarlo en el ejecutable comprimido. Pues es sencillo,
vamos a buscar un sitio dentro del ejecutable donde podamos
colocar nuestro parche. Podemos editar el archivo comprimido
con el editor hexadecimal, y ver que después del mensaje
de copyright "API Spy 32 - Copyright (c) 1999 Vitaly
Evseenko", hay bastante sitio. Pues nada podemos colocar
nuestro código detrás del mensaje en el offset 250h del
archivo (Nota: esto es posible, porque la primera sección
del ejecutable empieza en el offset 1000h, por lo tanto
si nosotros colocamos nuestro código aquí, no estaremos
sobresccribiendo nada, ya que estamos escribiendo el código
en el espacio sobrante de la cabezera del archivo). Por
lo tanto si la Image Base Address del programa comprimido
es 40000h (puedes comprobarlo con el ProcDump si editas
la cabezera de este), sabemos que nuestro código se encontrará
en la posición 400250h una vez que este se haya cargado
en memoria. Por lo tanto, deberemos saltar a esta posición
después de que el programa se haya descomprimido, este salto
lo podemos realizar justo donde se hace el salto de la línea:
:0041B5D2
E9E99FFEFF jmp 004055C0 // salto al punto de entrada del
programa
por lo tanto deberemos cambiar
esta línea por
:0041b5d2
E9794CFEFF jmp 400250 // salta a nuestra rutina que parchea
el código.
Cómo sabemos que se debe cambiar
el E9E99FFEFFF por E9794CFEFF? Pues fácil, si estamos en
la posición 41B5D2 y queremos saltar a la 400250, restaremos
(porque vamos hacia atras) a 400250 la dirección 41B5D2
dando como resultado FFFE4C7E, a este dirección faltan restarle
los 5 bytes que ocupa el salto quedando FFFE4C79, que en
ordenamiento Intel resulta 794CFEFF que es el valor que
junto con el código del salto (E9) debemos cambiar.
Bien ahora, hay que saber que
instrucción haremos servir para parchear el código, sabemos
que el byte que debemos cambiar está en la dirección 40481E
y debemos cambiarlo por un 33 (xor), esto deberá hacerse
con la instrucción siguiente:
mov
byte ptr [40481e],33
luego faltá que saltemos al
punto de entrada del programa con un:
jmp 4055c0
Para saber la codificación
de estas instrucciones, vamos a usar el SoftIce. Activaremos
los breakpoints que teniamos y ejecutaremos el programa
comprimido, aparecerá el SoftIce y iremos trazando hasta
llegar al salto jmp 4055c0 (línea 41B5D2) , la cambiaremos
por jmp 400250 (nuestra posición de código) con:
> a
41B5D2
> jmp 400250
> pulsar enter 2 veces
Pulsaremos F8 y esto nos enviará
al lugar donde deberá estar nuestro código, allí ensamblarenos
las lineas
de código que necesitamos con :
> a
> mov byte ptr [40481e],33
> jmp 4055c0
> pulsar enter 2 veces
Ahora vamos a ver el código
de operación necesario para estas instrucciones con:
> d
400250
en la ventana de datos veremos
los valores:
66C6051E48400033E963530000
esta es la cadena de bytes
que corresponden a las instrucciones que hemos ensamblado,
se divide de la siguiente manera:
66C6051E48400033
-> mov byte ptr [40841E],33
E963530000 -> jmp 4055C0
Continuaremos con la ejecución
del programa y veremos que este funciona correctamente y
nos dirá que está registrado al nombre que pusimos anteriormente.
Si no pusijmos ningún nombre, bastará que
introduzcamos un nombre de más de 5 carácteres y un código
de más de 12 para que se registre.
Tiempo para hacer las modificaciones
al ejecutable comprimido, el primer cambio será cambiar
el salto de la rutina de descompresión por el salto a nuestra
código de parcheado. Recordar que el offset para este salto
es E9D2, (puedes comprobarlo con el W32DAsm) cambiaremos
la cadena que encontremos en este lugar del archivo (E9E99FFEFF)
por la que calculamos anteriormente (E9794CFEFF). Luego
iremos al offset 250h y allí reemplazaremos lo que haya
(debe haber ceros) por los bytes 66,C6,05,1E,48,40,00,33,E9,63,53,00,00
nuestro código de parcheado.
Pues ya está a correr. Este
a sido un tutorial bastante largo pero creo que el contenido
a sido bastante interesante, y hemos visto cosas nuevas,
podiamos haber hecho un keygenerator, pero mi propósito
era mostrar como se puede parchear un archivo comprimido
en tiempo de ejecución. Podriamos haber prescindido de descomprimir
el ejecutable y haber buscado la rutina de comprobación
del número con el Soft-Ice, pero lo he hecho así para mostrar
el uso del ProcDump, con el cual podemos descomprimir un
ejecutable, sin tener que hacerlo manualmente.
|