Iniciando
En la fase anterior encontramos el lugar donde se desencriptaba la contraseña
de acceso. Nos pillamos el W32dasm, desensamblamos 'MsJet35.dll' para echarle
un ojo a la rutina completa. Como recordaréis del tutorial anterior,
encontramos la rutina de desencriptación en la dirección
4035793. Nos vamos ahí con el W32dasm y buscamos el inicio de la
rutina. El principio está en 4035768, y además vemos que
es llamada desde muchos sitios. Umm, parece que es una rutina "muy popular"
...
Echando un vistazo por encima a la rutina desde el W32dasm parece que
consta de un bucle que se repite el número de veces indicado por
el registro EBP. Este bucle va desde la dirección 403578F hasta
el salto en 40357AB. Los registros ESI y EDI son utilizados para apuntar
a zonas de memoria, al igual que EDX, EBX y EAX que parecen actuar como
"índices" de la zona apuntada por ESI. Lógicamente, una de
estas zonas de memoria será el inicio del fichero de la base de
datos, donde se encuentra encriptada la contraseña.
Cargamos el SoftIce para ver el funcionamiento de la rutina. Igual que
en tutorial precedente, colocamos un punto de ruptura en los accesos a
CreateFileA, y procedemos a abrir una de nuestras bases de datos
de prueba, por ejemplo 'bd2.mdb', que tenía 'WKT!' como contraseña
de acceso. En cuanto nos salte el acceso a nuestra base de datos pulsamos
F11 y debemos aparecer en el código de 'MsJet35.dll'. Plantamos
el punto de ruptura en 4035768 y ejecutamos.
:04035768 |
55 |
push
ebp |
:04035769 |
8BEC |
mov ebp,
esp |
:0403576B |
83EC04 |
sub esp,
00000004 |
:0403576E |
53 |
push ebx |
:0403576F |
894DFC |
mov dword
ptr [ebp-04], ecx |
:04035772 |
56 |
push esi |
:04035773 |
57 |
push edi |
:04035774 |
33DB |
xor ebx,
ebx |
:0403577C |
33D2 |
xor edx,
edx |
:0403577E |
8A5904 |
mov bl,
byte ptr [ecx+04] |
:04035781 |
8A5105 |
mov dl,
byte ptr [ecx+05] |
:04035784 |
FEC3 |
inc bl |
:04035786 |
55 |
push ebp |
:04035787 |
33C0 |
xor eax,
eax |
:04035789 |
8B6D0C |
mov ebp,
dword ptr [ebp+0C] |
:0403578C |
8A0C1E |
mov cl,
byte ptr [esi+ebx] |
Antes de empezar a curiosear, buscamos en la pila a ver de dónde
venimos (ya he dicho en otras ocasiones que es muy importante saber a dónde
vamos, de dónde venimos, quienes somos, ....). La pila nos indica
que cuando termine la rutina, a no ser que ocurra una catástrofe
(o que la rutina modifique la dirección de retorno de la pila),
volveremos a la dirección 4036028 (sí novatillo, ese número
de cuatro bytes al que apunta el contenido del registro ESP). Volvamos
a esa dirección o no, lo que nos importa es que se nos ha llamado
desde el 'call' que hay justo delante. Esta información es útil
por si se nos hace necesario "volver al pasado".
Nos encontramos en la zona de inicialización de las variables
que va a utilizar la rutina durante la desencriptación. Interesa
conocer quién es quién. Los registros ESI y EDI no se inicializan
dentro de la rutina, por lo que han debido ser inicializados por la rutina
que llama a la de desencriptación. Uno de ellos, EDI, apunta al
inicio del fichero de la base de datos, concretamentamente a la posición
18h. El otro, ESI, apunta a una zona que ahora mismo nos suena a chino.
Se inicializan a cero EBX y EDX para cargar enseguida los bytes apuntados
por ECX+4 y ECX+5 (dos ceros). El registro ECX apunta delante de la zona
"en chino" apuntada por ESI, exactamente 6 bytes antes. En esos 6 bytes,
encontramos el valor 6B39DAC7 seguidos de dos bytes a cero. Se incrementa
EBX (ahora es uno), se inicializa EAX a cero, se carga en EBP un 7Eh (126
en decimal) y en CL el byte apuntado por ESI+EBX = ESI+1.
Barajando bytes
Ya estamos en el inicio del bucle que vamos a intentar comprender. Apuntamos
las condiciones iniciales:
-
EDI apunta a la zona a desencriptar.
-
ESI apunta a "algo". Este algo, ya se verá que es una especie de
tabla de desencriptación.
-
CL es el segundo byte de la tabla de desencriptación.
-
EBX = 1, EDX = 0, EAX = 0
-
EBP = 7E. Tal como vimos en nuestra ojeada inicial a la rutina, EBP hará
las veces de contador, por lo que el bucle se va a repetir 126 veces. Esperemos
que antes de que se acabe hayamos comprendido su funcionamiento ;-)
:0403578F |
02D1 |
add dl, cl |
:04035791 |
8A2F |
mov ch, byte ptr [edi] |
:04035793 |
47 |
inc edi |
:04035794 |
8A0416 |
mov al, byte ptr [esi+edx] |
:04035797 |
880C16 |
mov byte ptr [esi+edx], cl |
:0403579A |
88041E |
mov byte ptr [esi+ebx], al |
:0403579D |
02C1 |
add al, cl |
:0403579F |
FEC3 |
inc bl |
:040357A1 |
322C06 |
xor ch, byte ptr [esi+eax] |
:040357A4 |
4D |
dec ebp |
:040357A5 |
886FFF |
mov byte ptr [edi-01], ch |
:040357A8 |
8A0C1E |
mov cl, byte ptr [esi+ebx] |
:040357AB |
75E2 |
jne 0403578F |
:040357AD |
5D |
pop ebp |
Cogemos un folio en blanco, lápiz, papel y cerramos la puerta
para que no molesten.
1. Sumamos DL+CL. |
DL = DL + CL |
2. Cargamos en CH el primer byte de nuestra cadena
a desencriptar. |
CH = (EDI) |
3. Apuntamos al siguiente byte de nuestra cadena a
desencriptar. |
EDI = EDI + 1 |
4. Cargamos en AL el byte tabla apuntado por EDX. |
AL = (ESI+EDX) |
5. Escribimos en esa misma posición, el contenido
de CL. |
(ESI+EDX) = CL |
6. Escribimos AL en el byte de la tabla apuntado por
EBX. |
(ESI+EBX) = AL |
7. Sumamos AL y CL. |
AL = AL + CL |
8. Incrementamos BL. |
BL = BL + 1 |
9. "Xoreamos" CH con el byte de la tabla apuntado por
EAX. |
CL ^ (ESI+EAX) |
10. Decrementamos el contador. |
EBP = EBP - 1 |
11. Guardamos CH en la cadena a desencriptar. |
(EDI-1) = CH |
12. Cargamos en CL el byte de la tabla apuntado por
EBX. |
CL = (ESI+EBX) |
13. Repetimos hasta que EBP sea cero. |
Rep. si EBP != 0 |
Aquí está todo el meollo de la desencriptación.
En síntesis, lo que se hace es recorrer todo la cadena a desencriptar.
Cada byte encriptado (se carga en 2), es sustituído por el correspondiente
desencriptado (se sustituye en 11), que no es más que el encriptado
"xoreado" con otro byte (en 9). Lo más complicado de explicar es
de dónde sale ese otro byte que nos realizar la desencriptación.
Como se ha dicho, disponemos de una tabla de desencriptación.
El contenido de esta tabla de desencriptación, se va barajando a
medida que se va desencriptando. Vamos a ver si soy capaz de explicarme...
Fijándonos en la rutina, el registro EBX se utiliza como índice
para recorrer toda la tabla de desencriptación. En todo momento,
se cumple que si estamos desencriptando el n-ésimo byte de la cadena
encriptada, EBX estará apuntando al (n+1) byte de la tabla de desencriptación.
En cada iteración, el byte de la tabla apuntado por EBX se carga
en CL (paso 12) y sumado a DL (1), donde se va acumulando la suma de todos
los bytes que se van cargando en CL. Como consecuencia de esta suma, el
byte de la tabla apuntado por EDX (4) es sumado con CL (7), sustituye al
que estamos apuntando con EBX (6) y es sustituído a su vez por el
contenido de CL (5), que es el que estaba apuntando EBX. Lo que se está
realizando en un intercambio entre dos bytes de la tabla, el apuntado por
EBX y el apuntado por EDX. El byte apuntando por la suma realizada en (7),
es el que se "xorea" con el que queremos desencriptar.
Si alguien no se ha enterado ya sabe, papel, lápiz y paciencia
;-)
Y una cosa importante. Puesto que para desencriptar tan sólo
se realiza un "xor" lógico sobre el byte encriptado para obtener
el byte desencriptado, si realizamos sobre el desencriptado la misma operación
con el mismo operando, volveremos a obtener el byte encriptado. Las operaciones
de encriptado y desencriptado son idénticas, siempre usando la misma
tabla de encriptación. Esto significa que si tenemos una cadena
encriptarda, si aplicamos sobre ella esta rutina, obtenemos la cadena desencriptada,
y si volvemos a aplicarla, volvemos a obtener la encriptada.
Retrocediendo
Ya sólo nos queda encontrar esa tabla de desencriptación.
¿Y dónde puede estar?. La tabla, puede encontrarse almacenada
en 'MsJet35.dll'. Usando los primeros bytes de dicha tabla, realizamos
una búsqueda con un editor hexadecimal en la .dll. Lo primero que
hacemos es buscarla con un editor hexadecimal en la .dll. Vemos que no
aparece en 'MsJet35.dll'.
También puede estar en el mismo fichero de la base de datos.
Así, cada base de datos tendría una tabla de desencriptación
distinta. Repetimos la búsqueda en 'bd2.mdb' y tampoco.
Pueden ocurrir dos cosas, o se encuentra almacenada pero encriptada,
o es generada en tiempo de ejecución. No puede estar en ningún
otro fichero porque no nos salta el punto de ruptura en CreateFileA
en un fichero distinto de la base de datos que estamos abriendo.
Tenemos que ir retrocediendo en la ejecución del programa y ver
donde se genera la tabla. Para eso, nos será de gran ayuda el W32dasm.
Como recordareis, al comenzar a ejecutar la rutina de desencriptación
(que ya hemos visto que es la misma para encriptación), la dirección
de retorno que encontramos en la pila era 4036028. La buscamos en el listado
del W32dasm:
:04036013 |
6A7E |
push 0000007E |
:04036015 |
8D4518 |
lea eax, dword ptr [ebp+18] |
:04036018 |
8D8C241C010000 |
lea ecx, dword ptr [esp+0000011C] |
:0403601F |
50 |
push eax |
:04036020 |
8D7542 |
lea esi, dword ptr [ebp+42] |
:04036023 |
E840F7FFFF |
call 04035768 |
:04036028 |
803E00 |
cmp byte ptr [esi], 00 |
En esta porción de código se está inicializando
los parámetros que va a utilizar la rutina de desencriptación.
Lo que nos interesa es la carga del registro ESI con la dirección
de la tabla de desencriptación, que se realiza en 4036020 con el
valor EBP+42. Ahora tendremos que buscar donde se carga el registro EBP
con el valor correspondiente. Subimos un poco más en el listado
y encontramos esto:
:04035FCD |
68C7DA396B |
push 6B39DAC7 |
:04035FD2 |
E817F7FFFF |
call 040356EE |
:04035FD7 |
8B6E04 |
mov ebp, dword ptr [esi+04] |
EBP se carga en 4035FD7, justo después de la llamada a la rutina
40356EE, por lo que parece que esta es la rutina que carga en memoria la
tabla de desencriptación. Además, justo antes de la llamada
a esa rutina, se le pasa como parámetro en la pila el valor 6B39DAC7,
que es el valor que encontramos justo delante de la tabla de desencriptación.
Saltamos a la dirección 40356EE con el W32dasm, y vemos que esta
rutina es también llamada desde un montón de sitios.
Volvemos al SoftIce, y colocamos un punto de ruptura en 40356EE, para
ver como se crea la famosa tabla. Ejecutamos y nos encontramos en 40356EE.
Lo primero que se hace es cargar el 6B39DAC7 en EDX y almacenarlo en memoria
(bucle 40356FA-4035704) a partir de la dirección apuntada por ECX.
Durante todo el proceso, ECX estará simepre apuntando al inicio
de la tabla que se va a contruir.
Después, a partir de ECX+6, se almacena una tabla de 256 bytes
(bucle 4035708-4035712). La cadena que se almacena es 00 01 02 .... FE
FF.
A continuación se almacenan dos bytes cero en ECX+4 y ECX+5,
se inicializa a cero (ESP+10) y (ESP+0F), y se comienza con un bucle (4035724-403575B)
que barajará la tabla de 256 bytes que hemos creado. La forma que
se barajan los bytes, es idéntica a la que se utiliza para barajar
la tabla durante el proceso de encriptación o desencriptación.
(ESP+10) se utilizará como contador, y marcará cuando
se ha terminado el bucle. Quien quiera conocer a fondo como se crea la
tabla que se busque igual que antes lápiz y papel y que tracee la
rutina. Para no enrrollarme, os lo pongo en pseudo-código. 'tabla'
estará apuntando al inicio de la tabla y el resto de la variables
están inicializadas a cero:
FOR contador=0 TO 0FFh |
op1 = (tabla+acu) |
op2 = (tabla+contador+6) |
res = res + op1 + op2 |
acu = (acu + 1) AND 3 |
(tabla+contador+6) = (tabla+res+6) |
(tabla+res+6) = op2 |
END FOR |
En el código, 'res' es el byte apuntado por (ESP+0F), 'acu' es
AL, 'tabla' es ECX y 'contador' es el dword apuntado por (ESP+10).
La tabla generada dependerá del valor inicial que se haya utilizado.
Así, para la contraseña de acceso, se utiliza una tabla generada
a partir del valor 6B39DAC7. Si buscamos con el W32dasm el resto de direccciones
desde la que se invoca la rutina para la creación de la tabla, veremos
que el valor que se le pasa como parámetro es distinto. Esto significa
que estas mismas rutinas de desencriptación son utilizadas por Access
para encriptar y desencriptar otros datos de la base de datos aparte de
la contraseña de acceso. Como veis, hay muchas mas cosas por investigar...
Al retornar esta rutina, se hacen una serie de comprobaciones para asegurar
que la base de datos que se está abriendo es una base de Access
válida. Para ello, se comprueba que la primera palabra del fichero
sea 0100h, la segunda 0, que el DWORD que comienza en el offset 13h sea
0 y que a partir de la posición 4 aparece la cadena 'Standard
Jet DB'. Si estás comprobaciones se realizan con éxito,
se salta a la rutina de desencriptación.
Como nota curiosa, si dejáis el punto de ruptura puesto en la
rutina de desencriptación, os dareis cuenta de que al abrir la base
de datos, Access desencripta la parte de la contraseña de acceso,
mira a vere si la base de datos tiene contraseña, vuelve a encriptar
usando las mismas rutina y después es cuando pregunta por la contraseña.
Al introducir la contraseña en el cuadro de diálogo, se vuelve
a desencriptar la misma para realizar la comparación.
|