CRACKEAR ARCHIVOS PE ENPAQUETADOS ( II )

Usando DLLs

por   n u M I T_o r   [ kUT ]


Target:  AWAVE v4.5. (http://www.fmjsoft.com)

Herramientas:            Code SnippetCreator
                                    HexWorkShop v2.5x
                                    SICE 3.XX o superior (¿qué haríamos sin él?)
                                    MASM 6.X o TASM 5.0
                       
NOTA: Estas herramientas las puedes hallar en: http://members.xoom.com/crk10/archivos

Autor: n u M I T_o r


 

 

Contenido: · INTRO
                        Insertar un objeto nuevo en un archivo PE
                        El recorte es grande y no quiero aumentar el programa
                        MALDITAS PROTECCIONES: quebrando a AWAVE v4.5

                        ALGUNAS PREGUNTAS - ALGUNAS RESPUESTAS
                                
 ·
¿Las jmp de las apis están siempre en secciones .text?
                                  · ¿Qué significan las características de las secciones de un PE?
                                  · ¿Qué es una DLL y para qué sirve? ¿cómo se crea?
 


INTRO
======

Particularmente veo el mayor potencial de conocer los encabezados del PE en la personalización de programas. Sin un editor de recursos se puede cambiar el comportamiento de los menúes, de los diálogos y mucho más. También podríamos ampliar la funcionalidad de las aplicaciones y mejorarlas. Es un trabajo que podemos hacer "manualmente", sin necesidad de un programa especial para ello.

 

Insertar un objeto nuevo en un archivo PE
==================================

Como un ejercicio, vamos a insertar una rutina nueva en notepad.exe. Es la libreta de notas que trae el sistema Windows, generalmente en el directorio C:\WINDOWS. Así que copialo y guárdalo en un directorio temporal ya que le haremos algunos cambios.

Para insertar rutina nueva en notepad.exe vamos a utilizar Snippet Creator. Existen otras herramientas que pueden hacerlo también, pero yo utilizo ésta.

Repasemos como configurar Snippet Creator para insertar código nuevo en un archivo PE.

1. Debemos primero hacer unos ajustes: vamos a Action/Options.

No sé si sea necesario explicarlo, pero hay que:

1.1. Indicar si usarás MASM o TASM. Son los ensambladores comerciales más populares.

1.2. Para cada uno de ellos, si los tienes, eliges los comandos con sus respectivos comutadores.

1.3. Hay que indicar también el directorio donde será creado el proyecto es decir, la rutina que injertarás en el PE.

1.4. OK.

1.5. Si prefieres y no eres tan quisquilloso, simplemente pulsa DEFAULT y se establecerán los valores por defecto. No es una mala opción.

2. Ahora debes crear un proyecto (File/New Project) u opcionalmente determinar el target (Acton/New Target). A nuestro proyecto nuevo lo podemos llamar NP.

3. Luego viene lo interesante: crear el recorte (snippet). Hay que escribir el código del recorte en la ventana de edición del programa. Los siguientes son más o menos los ejemplos de Icz:

Para MASM:

include windows.inc
include user32.inc
includelib user32.lib
jmp init

Message db "Probando, un, dos, tres. probando",0

init: invoke MessageBox,NULL,addr Message,NULL,MB_OK

 

Para TASM:

UNICODE=0
include w32.inc
jmp ComienzoReal

Message db "Probando, un, dos, tres. probandot",0

ComienzoReal: call MessageBox,NULL,offset Message, NULL,MB_OK

Ahora guarda el código escrito ("File/Save Project"). Snippet Creator creará en la carpeta del proyecto un archivo .INF que salvará la información sobre los avances realizados en el proyecto.

Es necesario observar que el código anotado arriba sólo funciona si, en este caso, MessageBox es una de las funciones importadas por el archivo target. Para asegurarse de esto, simplemente vamos a la ventana "Import Information" a través de "PEInfo\View Import Fuctions". Hacemos click con el botón derecho del ratón sobre USER32.DLL y revisamos. Ya, aparece la función MessageBox.

4. El siguiente paso es ensamblar el recorte: pulsa el botón "Assemble" debajo de la ventana de edición o ejecuta el comando "Action/Assemble". Si todo va bien, vamos al siguiente punto.

5. Exportar el recorte. Al ejecutar el comando "File/Export", se abre la caja de diálogo común "SAVE AS". Busca la carpeta del proyecto y salva el archivo .BINSe trata del recorte propiamente dicho. Si lo abres con HIEW.EXE (Hacker View) podrás ver un desensamblado de lo que escribiste. Esto ya es bastante. Puedes tomar el contenido de este archivo y pegarlo manualmente en el sitio indicado del target y ya. Pero SC te puede ahorrar este trabajo.

6. Ahora el momento crucial: injertar el recorte en el target. Para hacer primero debes ejecutar el comando "Acton/Project Options". Esto desplegará una caja de diálogo donde:

6.1. Debes llenar la caja VA con la dirección virtual donde deseas insertar el recorte (snippet). Para esto, hay que encontrar un punto disponible dónde hacerlo. Los sitios posibles son:

a. Al final del archivo, como una nueva sección. Esto incrementa el tamaño del archivo.

b. Entre las tablas de las secciones y la primera sección. También como una nueva sección.

c. En algún espacio no utilizado de una sección. Pienso que esta es la mejor opción ya que no tiene que crearse una nueva sección ni aumenta el tamaño del archivo.

Para ubicar el sitio donde ubicar el recorte, abrimos el target con un editor hexadecimal y buscamos un área vacía, llena de ceros. Una vez encontrada, anotamos el desplazamiento donde empieza y el desplazamiento donde termina. En mi versión de notepad.exe, por ejemplo, hayamos un gran espacio en el desplazamiento 4020h (revisa tu versión, si esto no es posible elige otro desplazamiento). Luego, empleando el comando "PE Info/View Section Info", abrimos el diálogo "Section Information" y en la columna "RAW offset" revisamos en cual sección se halla el espacio que he elegido. Es la sección .data. Luego calculamos la dirección virtual equivalente al desplazamiento dentro del archivo:

VA del recorte = (Desplazamiento del recorte - RawOffset de la Sección) + (ImageBase + VirtualOffset de la sección)

(00004020h - 00003E00h) + (00400000h + 00006000h) = 00406220h

Colocamos este valor en el campo "Snippet VA" del diálogo "Project Options".

6.2. Según sea la opción que hayamos elegido, hay que indicarlo en el área "Patch Options" de la caja de diálogo "Project Options". Elijamos "Patch into Existing Section".

6.3. Después de escoger dónde insertar el recorte, debemos decidir como el recorte ha de tomar el control.

Si queremos que el código se ejecute antes del código del archivo objeto o víctima, debemos redirigir el control desde el punto de entrada RVA (opción "Redirect Entry Point RVA"). Con esta opción, el programá dará el control al recorte cuando el archivo esté listo para ser ejecutado.

Si queremos que el recorte reciba el control después de que algunas rutinas del archivo objeto se haya ejecutado, debemos elegir entre dos opciones: "redirect-from-code-section" (redirigir-desde-la-sección-de-código) o "redirect-from-call/jmp" (redirigir-desde-call/jmp). La opción "redirect-from-code-section" insertará las instrucciones en la dirección virtual que pasará el control al recorte. Sin embargo, debes tener cuidado al elegir la dirección desde donde redirigirás el control: debe estar en el límite de una instrucción. Por ejemplo, para una sección como esta:

:00401007 6819304000 push 00403019
:0040100C 6A00 push 00000000
* Reference To: USER32.MessageBoxA, Ord:0195h |
:0040100E E807000000 Call 0040101A
:00401013 6A00 push 00000000

puedes escoger 40100C como la dirección de redirección, pero no 40100D. Snippet Creator no será capaz de capturar este tipo de error, así que debes ser cuidadoso en esto. Para pasar el control al recorte, Snippet Creator reemplazará la(s) instrucción(es) en esa dirección virtual con "push [VA del recorte]" y luego"ret".

Redirection from call/jmp es otro método. Esta opción requiere que se diga a Snippet Creator donde está la instrucción call/jmp y automáticamente reemplazará la instrucción original call/jmp con la dirección de tu recorte. En este ejemplo, si empleamos esta opción, debemos pasar 40100E como la dirección virtual que redirigirá el control. Snippet Creator almacena esta dirección, la de rutina original, en una variable llamada OriginalLocation. Luego, si se desea entregar de nuevo el control a la rutina original, en el recorte se puede hacer algo como "jmp dword ptr [OriginalLocation]" o "call dword ptr [OriginalLocation]", dependiendo de si originalmente era una llamada (call) o un salto (jump).

Si prefieres redirigir el control desde algún sitio en el código existente, puedes almacenar las instrucciones sobre-escritas después de que tu recorte obtenga el control. Lo puedes hacer estableciendo la caja de chequeo (checkbox) restore-overwritten-instruction en la parate inferior del cuadro de diálogo "Project Option". Snippet Creator colocará el código necesario para restaurar las instrucciones originales dentro del recorte.

Luego, hay que considerar qué hacer después de que la última instrucción del recorte haya sido ejecutada. Se puede elegir devolver el control al archivo objeto (target) en una dirección virtual específica. Si no, se debería poner alguna instrucción que sería la última del recorte, tal como ExitProcess.

En nuestro caso particular, ya hemos calculado la dirección virtual del nuevo punto de entrada, que corresponde al desplazamiento donde colocaremos el recorte: 00406220h. Como queremos que nuestro recorte tome el control al iniciar el programa, elegimos la opción "Redirect Entry Point RVA". Esto dará el control al recorte desde el comienzo. Además queremos que el programa siga su curso después de la ejecución del recorte, entonces elegimos la opción "Return To Program" y en la casilla "Virtual Address" colocamos la dirección del punto de entrada original: 00401000.

6.4. Una vez establecidas las opciones del proyecto, se inserta el recorte en el archivo objeto usando "Action/Patch Target File". Snippet Creator no preservará registros/banderas (flags), es tu responsabilidad. Si algo no funciona como se esperaba, se puede examinar el código fuente del recorte en el directorio del proyecto y ver cómo va todo.

Listo.

Ahora activemos el target (NOTEPAD). ¿Qué te parece?

Icz termina su tutorial recordando que SC puede ser empleado también como un simple editor de archivos PE, lo mismo que ProcDump.

 

El recorte es grande y no quiero aumentar el programa

En ocasiones el tamaño del archivo empacado es un parámetro crítico a través del cual un sistema de protección puede evaluar si ha habido intento de abuso. En estos casos es posible implementar una DLL. Así sólo tengo que insertar la llamada a la DLL en el archivo objeto y listo. Si no sabes qué es una DLL, como se programa, y para qué sirve, revisa el final de este documento.

 

MALDITAS PROTECCIONES: quebrando AWAVE v5.4
===========================================

Parchando archivos empaquetados con SnippetCreator

Voy a intentar desproteger un programita llamado AWAVE v5.4. Es un programa ejemplar para mostrar como romper protecciones de archivos ejecutables empaquetados a través de la inserción de código nuevo en ellos empleando Snippet Creator. Quiero subrayar que no tengo nada contra el programador que creó este buen programa. Por el trabajo que me dio, tengo la impresión de que sabía lo que hacía. Yo aconsejaría que quienes deseen usar el programa, lo registren. Si yo pudiera lo haría. Pero mi objetivo no es aprovechar el programa sino enseñar el estilo de romper protecciones que estoy implementando.

Para romper las protecciones de archivos empaquetados, la táctica empleada generalmente es esperar que el archivo se despliegue en memoria y sobreescribir la rutina de protección en tiempo de ejecución. Esto comúnmente se ha hecho con un cargador (loader) que atrapa una llamada a alguna API de W32 que realice el programa, y entrega el control a la rutina que desprotegerá el programa: el mismo cargador tomará el control y cambiará en memoria el esquema de protección.

El uso de loaders me parece que presenta una dificultad: en este método entran en juego dos procesos distintos, el del loader y el del programa mismo. Esto dificulta las cosas y nos obliga a escribir un número significativo de líneas de código. Las funciones de W32 para escribir procesos remotos, son complejas. El loader puede tener fácilmente un tamaño de 100 KB, especialmente si no lo escribimos en lenguaje ensamblador.

Si hacemos que la rutina que rompe las protecciones del programa se ejecuten en su propio proceso, me liberaré de la carga que implica enganchar instrucciones en procesos remotos, sobrescribir y leer procesos remotos, etc. Esto lo podemos lograr simplemente insertando la rutina desprotectora en el mismo ejecutable del programa. Veamos el caso particular de AWAVE v4.5.

El programa presenta dos limitaciones engorrosas. La primera es un NAG que aparece al comienzo y que recuerda que se trata de un programa no registrado y retarda el inicio del programa. La otra protección importante es que el programa no permite guardar más de un archivo por sección. Hay un par de cuestiones secundarias: son dos cadenas, una dice "Unregistered copy" y aparece en la parte izquierda del área cliente; la otra dice "(Unreg)" y aparece en la "Caption" de la ventana. Aunque no es tarea fácil, eliminaremos estas molestias.

Si intentamos cargar el programa con SICE, no se disparará la ventana del depurador porque el programa está empaquetado y se han cambiado los atributos de las secciones. Entonces creamos en SnippetCreator un nuevo proyecto (File/New Project) y llamémosle awave, cuyo target sería awave.exe. Revisamos los atributos de las secciones y cambiamos los de las secciones .text y .adata, que son, respectivamente las secciones de código ejecutable y la rutina de descompresión. El punto de entrada está en esta última sección. Los atributos deben ser E0000020.

Cambiados los atributos, encontramos que ahora sí se dispara la ventana de SICE cuando cargamos awave.exe con e lloader de SoftIce. Tenemos que buscar varias direcciones:

1. La dirección donde finaliza la rutina de descompresión (VA1: Virtual Addres 1). Cuando el programa llega a este punto, ya el archivo está totalmente descomprimido en la memoria.

2. Las direcciones de los puntos que deseamos cambiar o parchar.

3. El desplazamiento en el archivo donde escribiremos el recorte nuevo. Esto no se hace con SoftIce sino con un editor hexadecimal.

Como el archivo está empaquetado, existen diferencias sustanciales entre la lista muerta que es el archivo mismo y el código vivo ya desempacado que vemos con SoftIce. Por este motivo no sirve establecer el desplazamiento en el archivo correspondiente a las direcciones de memoria donde deben hacerse cambios. Así que la estrategia es agregar una rutina al programa que sobreescriba en tiempo de ejecución las rutinas que queremos cambiar.

Otro cambio que debemos introducir al archivo es el concerniente a la instrucción que entregará el control a nuestro nuevo recorte.

Determinemos el desplazamiento del recorte. Con el editor hexadecimal podemos ubicar un espacio vacío en el desplazamiento 0004 3140h. A partir de él y los calculamos la dirección virtual de su ubicación utilizando la fórmula:

Dir Virtual = (Desplazamiento del dato - RawData) + (ImageBase + RVA Offset)

Los datos que necesitamos para este cálculo los hallamos con el mismo Snippet Creator. En Snippet Creator activamos el comando "Action\New Target". Pulsamos BROWSE y ubicamos AWAVE.EXE y pulsamos SAVE. Ahora activamos "PE Info/View PE header". Encontramos que:

ImageBase = 0040000h

Luego activamos "PE Info/View Section Info". Para la sección .text encontramos:

RawData = 1000h
RVA Offset = 1000h
RawSize = 042200h

Comprobamos que es en esta sección donde se encuentra el desplazamiento para nuestro injerto. Ahora calculemos la dirección virtual:

(43140h - 1000h) + (400000h + 1000h) = 00443140h

Debemos entregar el control a esta rutina cuando el archivo ya esté desempacado. Con SoftIce hemos ubicado esta rutina:

0050B54B     0385AC504400  add eax,[ebp+004450AC] ; <- VA0
0050B551     5B                        pop eax
0050B552     .....
0050B558     61                         popad
0050B559     7508                     jnz
0050B55B     B801000000        mov eax,1
0050B560     02C000                ret 0Ch
0050B563     50                         push eax
0050B564     C3                        ret

Debemos insertar en alguna parte de este código la siguiente instrucción, que entregará el control al recorte:

68 00 44 31 40     push 00443140h
C3                         ret

Como vemos, la instrucción consta de seis bytes. Analizando el código de más arriba, vemos que la instrucción en 50B54B tiene ese tamaño, así que tomamos esa. Calculamos el desplazamiento en el archivo correspondiente a esta dir. virtual y es 05454Bh. Si revisamos con el editor hexadecimal este desplazamiento veremos que no se corresponden los valores con los que vemos en SICE. Lo que ocurre, me imagino, es que se trata de un desempacador compuesto de dos partes. El desempacador mismo está empacado. Hay pues dos desempacadores, uno desempaca el desempacador del programa. Así que necesitamos una dirección más: la dirección final del primer desempacador o ubicar una punto en el primer desempacador donde la instrucción que nos toca ya esté desempacada. Ubicada esta dirección, pasamos desde aquí el control al recorte; hacemos que el recorte escriba la instrucción 0050B54B para que vuelva a pasar el control al recorte, que romperá ahora las protecciones sobre el archivo desplegado.

Para hallarla, cargamos el ejecutable en SoftIce y hacemos:

D  50B54B

Esto desplegará en la ventana de datos de SoftIce el contenido en hexadecimal que hay en 0050B54B. Luego ejecutamos F10 hasta ver en la ventana de datos de SoftIce algo como esto: 0385AC504400.

Yo he determinado esta dirección:

0050B0B7     8B85B5504400     mov eax, [ebp + 004450B5] ; <- VA0

Como ven, la instrucción tiene seis bytes.

El desplazamiento de esta dirección en el archivo es 0540B7h. Con el heditor hexadecimal escribimos en esta instrucción:

68 40 31 44 00 C3

que es el equivalente octal de las instrucciones "push 443140h" - "ret". Cuando se ejecute el programa y llegue a este punto, pasará el control a las rutinas escritas en el desplazamiento 043140h del archivo, correspondiente a la dirección virtual 00443140h del programa.

La rutina del recorte deberá incluir una rutina que restaure la instrucción que hemos modificado (en 0050B0B7) y que modifique el final del segundo desempacador, cuando el programa ya está totalmente desempacado, para que se entregue el control de nuevo a una rutina que introducirá por fin los cambios definitivos en el programa.

Tenemos ya las siguientes direcciones:

1. Dirección de la instrucción que entregará por primera vez el control al recorte. Final del primer desempacador: 50B0B7h. La llamamos VA0.
2. Dirección del recorte en el archivo: 00443140h. VA2.
3. Dirección de la instrucción que devolverá por segunda vez el control al recorte 50B54Bh. La llamamos VA1.

Ahora, queda romper las protecciones para obtener las últimas direcciones: las que hay que cambiar para desproteger el programa. Necesito estas direcciones para escribir el código del recorte. Por razones de tiempo no entraré en detalles y las daré de inmediato:

Para registrar el programa debes enviar un email a su autor y cancelarle no sé cuántos dólares. Luego recibirás a través de cualquier mecanismo un archivo .TXT o .KEY que debes abrir con el programa: "Options\Program Setup\Register". Este proceso es engorroso, así que puedes hacer lo siguiente para disfrutar el programa en toda su plenitud:

1. En el registro de Win, en la clave HKCU\Software\FMJ-Software\Awave:
Crear nuevo valor de cadena: Username "mi nombre"

2. Aplicar los parches

En        0047EFCC: 7540   jnz 0047F00E
Poner   0047EFCC: EB0D  jmp 0047EFDB

En        0047EFEC: 7405   jz 0047E7F3
Poner   0047EFEC: EB09  jmp 0047EFF3

Como ya lo he mencionado, esto no se puede hacer directamente. Tenemos que injertar un recorte en el PE que lo haga. Pero tenemos que anotar estas direcciones: 0047EFCCh y 0047EFECh, así como las instrucciones nuevas: "EB0Dh" y"EB09h".

 

Problemas

Puedo escribir el código del recorte de manera que realice los parches deseados, injertarlo donde he elegido y supuestamente todo andaría bien. Pero hay un problema: cuando el programa es desempacado en la memoria ocupa y reescribe el espacio donde ubicamos el recorte destruyéndolo : (. Así que estamos obligados a otra estrategia: escribir el recorte para que cargue en memoria una librería DLL que escribiremos y que realizará los parches en el momento preciso. Como la librería DLL va a ocupar un espacio de memoria reservado para ella, el archivo, al ser desempacado no podrá dañar su código : )

La estrategia es escribir el recorte para que sólo haga los siguiente:

1. restaurar la instrucción original en la dirección donde el programa entrega el control al recorte
2. cargar nuestra DLL en el espacio de direcciones del proceso.
3. cambiar la dirección del final del desempacador para que pase el control a la DLL.

Procedamos

1. Abrimos el target con el editor hexadecimal y colocamos la siguiente cadena de valores hexadecimales en el desplazamiento 50B54Bh correspondiente a la dirección VA0

2. Escribimos el siguiente código en la ventana de edición de SC:

3. Antes de ensamblarlo nos aseguramos de especificar los siguiente:

4. Ensamblamos el recorte. Si no hubo error lo exportamos:

5. Cerramos SC y abrimos el target con HexWorkShop. Nos movemos al desplazamiento 0043140h donde colocaremos el recorte.

6. Abrimos con HexWorkshop el archivo .BIN que exportamos, lo marcamos todo con el ratón y lo copiamos (CTRL+C).

7. Pasamos a la ventana de Hexworkshop con la vista de .EXE. Marcamos con el ratón desde 0043140h hasta 00431B0h; el área que hemos marcado corresponde exactamente al tamaño del recorte, y hay que ser muy preciso en esta operación ya que es la más delicada del proceso. Pegamos el recorte (CTRL+V).

8. Guardamos y salvamos un respaldo por seguridad.

9. Ahora escribimos la DLL. En editor de texto aparte escribamos el siguiente código y salvémosle como AW_PATCH.ASM.

; -------------------------------------------------------------------------------------------------

TITLE AW_PATCH.DLL: Registra el programa awave v5.4

.386
.model flat,STDCALL

public patch
public Start

.data
OldInstruction db 003h,085h,0ACh,050h,044h,000h

.code
Start:
dll proc instance:DWORD, reason:DWORD, reserved:DWORD
mov eax,1
ret
dll endp

patch:
; Parchar el target para simular registro
pushad
mov edi,0047EFCCh
mov word ptr [edi],00DEBh
mov edi,0047EFECh
mov word ptr [edi],009EBh

; Restaurar código en 0050B54Bh
mov edi,0050B54Bh
lea esi,OldInstruction
mov ecx,6
rep movsb
; regresar
popad
push 0050B54Bh
ret

End Start

; -------------------------------------------------------------------------------------------------

Para hacer la DLL necesitamos también escribir otros archivos:

; ----------------------------------- aw_patch.def ------------------------------------------------

 

LIBRARY aw_patch
EXPORTS MyAboutDialog
EXPORTS patch
DESCRIPTION 'ASM program'
EXETYPE WINDOWS
CODE PRELOAD MOVEABLE
DATA PRELOAD MOVEABLE MULTIPLE

; --------------------------------------------------------------------------------------------------

; ---------------------------------- makefile (TASM) --------------------------------------------

NAME = aw_patch
OBJS = $(NAME).obj
DEF = $(NAME).def
RCS = $(NAME).rc
RES = $(NAME).res

IMPORT=C:\TA\lib\import32

TASMDEBUG=/zi
LINKDEBUG=/v

$(NAME).DLL: $(OBJS) $(RES) $(DEF)
tlink32 /Tpd /aa /c $(OBJS),$(NAME),, $(IMPORT), $(DEF), $(NAME)

.asm.obj:
tasm32 /ml /m2 $&.asm

.rc.res
: brc32 -r $(NAME).rc

; -------------------------------------------------------------------------------------------------

Con esto sólo tenemos que ejecutar desde la línea de comando la orden MAKE, mientras apunta al directorio donde está los archivos fuentes ( C:\temp\aw_patch, por ejemplo).

Una vez creada nuestra DLL, la colocamos en el mismo directorio del recorte. Ensamblado el recorte y pegado en la dirección indicada de la víctima.

Finalmente awave.exe: ¡Ja, ja!

 

 

ALGUNAS PREGUNTAS - ALGUNAS RESPUESTAS

 

¿Las jmp de las apis están siempre en secciones .text?

Creo que no necesariamente. Por el trabajo con SC y por un post en el foro Spanish Reverse Engineering me he enterado que los nombres importados no tienen que estar en la sección .idata. Los programas W32 trabajan con un modelo de memoria FLAT; es como un archivo DOS .COM, pero sin el límite estrecho de 65 KB: el límite son 4 GB. Todo está un gran segmento de 4 GB. Datos y Código puede ir en cualquier parte. El único límite, hasta dónde he visto, son precisamente los atributos de las secciones donde se ubican los datos brutos. Por eso, yo creo, aunque no lo he probado, que uno puede poner en una sección de datos inicializados lo siguiente:

THUNK1: jmp [api_address]

Y llamar a la API desde una instrucción así:

call THUNK1

En teoría debería trabajar. No lo he probado todavía.

 

¿Cuál es el significado de las características de las secciones? Todavía no entiendo porque modificándolas, también se modifica la carga del ejecutable (y se despliega el Sice en la primera instrucción).

He adelantado alguito antes. Las PCs están basadas en una arquitectura llamada von Newmann. Si quieres saber quien es este tipo teo envío la info. Se le considera erróneamente el padre de la computadora moderna. La verdad es que fue un matemático bestial, muy activo durante la 2da. Guerra. Fue uno de los impulsores del proyecto ENIAC, si no me equivoco. Necesitaban computadoras para cálculos avanzados y, especialmente para crackear claves y mensajes. De ahí surgió también la cibernética de Norbert Wiener. Más importante en esta arquitectura, para mí, es A. Turing, el de la máquina universal.

Lo cierto es que el descubrimiento de estos tipos fue el tratamiento del código de un programa como si fueran sus datos. Gracias a esto, se pueden almacenar las secuencias de instrucciones como si fueran datos. W32 le saca la punta a esto y aprovecha el modelo de memoria FLAT de los procesadores INTEL x86. Ya te he explicado qué es este modelo. Hay más info en las referencias de los procesadores de este tipo. Ahora bien, como cualquier cosa puede ser dato, incluso el código, a cada sección del PE se le dan algunos atributos durante el enlazado para discriminar un poco entre lo que es dato y lo que es código, donde puede escribirse y donde no, etc. Cuando un archivo es comprimido, a las secciones se le dan atributos de datos, por lo que el depurador, que sólo lee código, no las leerá. Es más, si le das a las secciones de datos atributos de código, te las desplegará hasta el W32DASM.

La siguiente tabla ilustra el significado de las características:

      · 000000020h __Código.
      · 000000040h __Datos inicializados.
      · 000000080h __Datos no inicializados.
      · 040000000h __Sección cacheable.
      · 080000000h __Sección paginable.
      · 100000000h __Sección compartida.
      · 200000000h __Ejecutable.
      · 400000000h __Se puede leer.
      · 800000000h __Se puede escribir en la sección.

Por ejemplo, si las características es E0000020H, entonces se trata de una sección en la que

1. se pueden escribir datos        80000000h
2. se pueden leer datos             40000000h
3. ejecutable                             20000000h
4. hay código                            00000020h
                                               ---------------
                                                 E0000020h

 

¿Qué es una DLL y para qué sirve? ¿cómo se crea?

DLL es la extensión de archivos empleados por Window$ para enlace dinámico. DLL es la abreviatura de Dinamic Link Library, que significa librería de enlace dinámico.

Un archivo DLL está compuesto por un conjunto de funciones y rutinas que pueden ser empleadas y llamadas desde un archivo ejecutable .EXE o desde cualquier otra .DLL. El uso de estas funciones se llama enlace dinámico porque se trata de un "enlace" semejante al realizado cuando enlazamos archivos .OBJ y .RES con el enlazador (linker) para crear ejecutables .EXE, pero que ocurre en tiempo de ejecución y sin el uso de enlazador.

Esto fue implementado por Window$ considerando que muchas rutinas empleadas por varios programas eran las mismas. Para evitar perdida de espacio de memoria, Window$ reúne esas rutinas comunes en archivos DLL que pueden ser accedidas, una vez cargadas en el área de memoria compartida de un proceso, por más de un programa o proceso.

Pero las DLL no sólo ahorran espacio de memoria útil. También ofrecen otras ventajas. Cómo se encuentran en un área de memoria compartida del proceso, son una vía de acceso a proyectos remotos. Es algo complejo, pero se puede hacer. Además de esto, las DLL permiten agregar código a un programa sin tener que cambiar el ejecutable para nada: se podría hacer actualizaciones importantes de programas sólo cambiando una DLL.

 

¿Cómo escribir una DLL?

Toda DLL tiene un punto de entrada el cual será llamado por Win cada vez que

- la DLL sea cargada
- la DLL sea descargada
- un hilo sea creado en el mismo proceso

Este es un ejemplo del código del punto de entrada de una DLL:

DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
        mov  eax,TRUE
        ret DllEntry Endp

El punto de entrada puede tener cualquier nombre y apunta a una función con tres parámetros:

· hInstDll = manejador de instancia del módulo DLL.

· reason = bandera con uno los siguientes valores:
DLL_PROCESS_ATTACH: La DLL recibe este valor cuando injertado por vez primera en el espacio de direcciones del proceso. Se puede usar esta posibilidad para hacer inicialización. DLL_PROCESS_DETACH: La DLL recibe este valor cuando está siendo descargada del espacio de direcciones del proceso. Se puede usar esta posibilidad para hacer una limpieza como deslocalización de memoria.
DLL_THREAD_ATTACH: La DLL recibe este valor cuando el proceso crea un nuevo hilo. DLL_THREAD_DETACH : La DLL recibe este valor cuando un hilo del proceso es destruido

Para que la DLL sea cargada dentro de un proceso, debe retornar TRUE en eax. En caso contrario, no será cargada.

Las funciones de la DLL pueden ser colocadas antes o después de la entrada. Pero para que puedan ser llamadas desde otros programas, deben colocarse las siguientes líneas en el archivo DEF de definición:

LIBRARY   DLL_Name
EXPORTS   Function_Name

La directiva LIBRARY define el nombre del módulo DLL. Debe señalarse con el nombre de archivo de la DLL. La directiva EXPORTS dice al enlazador (linker) cuales funciones de la DLL son exportadas, es decir, pueden ser llamadas desde otros programas.

 

¿Cómo compilar y enlazar un archivo DLL?

También hay que indicar en los conmutadores del enlazador la opción /DLL y /DEF:DEF_name:

link /DLL /SUBSYSTEM:WINDOWS /DEF:DEF_name /LIBPATH:c:\masm32\lib OBJ_name.obj

Los conmutadores del ensamblador son los mismos:

/c /coff /Cp

En el caso de Borland Turbo Assembler, en vez de /DLL, se coloca /Tpd:

tlink32 /Tpd /aa /c /v $(OBJS),$(NAME),, $(IMPORT), $(DEF), $(NAME)

Después de enlazar el archivo objeto, se obtendrá la DLL y un archivo .lib. Este archivo .lib es la librería de importación que puede ser usada para enlazar programas que usan las funciones que está en la DLL.

Para que un ejecutable EXE u otra DLL llame a funciones dentro de una DLL, debe proyectar primero la DLL en el espacio de direcciones del proceso que llama.

 

¿Como cargar la DLL en el espacio de nombres del proceso?

Hay dos maneras:

1. Enlazado implícito: es el más común. Como hemos visto al crear aplicaciones W32, al enlazar un ejecutable debemos indicar un conjunto de archivos LIB al enlazador. Estos archivos LIB contienen una lista de las funciones de la DLL que pueden ser importadas desde otros archivos EXE o DLL. Cuando hacemos el enlace, el enlazador toma información de los archivos LIB correspondientes y la incrusta en el archivo EXE creado. Luego, cuando el sistema cargue el EXE, el cargador examinará el encabezado de este archivo y establecerá las DLLs que deberán ser cargadas en el espacio de direcciones del proceso para que se ejecute la aplicación. El sistema buscará las DLLs requeridas en los directorios de sistema e intentará cargarlas.

 

2. Enlazado explícito: llamando a LoadLibrary con el nombre de la DLL deseada. Si la función tiene éxito, devuelve un manejador a la librería (DLL). Si no, retornará NULL:

invoke LoadLibrary,addr LibName

El manejador devuelto se puede pasar a GetProcAddress o a cualquier otra librería que requiera este manejador como parámetro:

mov hLib,eax
                invoke GetProcAddress,hLib,addr FunctionName

Esta llamada devuelve la dirección de la función cuyo nombre ha sido pasado como segundo parámetro. De otra manera, retorna NULL. El valor devuelto ahora puede ser usado para llamar a la función deseada:

mov TestHelloAddr,eax
                call [TestHelloAddr]

Usada la librería DLL, se descarga con FreeLibrary:

               invoke FreeLibrary,hLib 2.


Gracias a:

GERZA, ManiacPC y KuT.

Comentarios y correcciones: nuMIT_or@iname.com
Mi página de programación: n u M I T_o r's   P r o g r a m m i n g   P a g e

www.000webhost.com