Red de conocimiento informático - Material del sitio web - Cómo modificar archivos PE

Cómo modificar archivos PE

I. Cargador de Windows

El proceso de lectura del archivo PE por parte del cargador es el siguiente:

1. Primero lea el encabezado de DOS, el encabezado de PE y el párrafo. .

2. Luego, dependiendo de si la dirección de carga definida por ImageBase en el encabezado PE está disponible, si la dirección ya está ocupada por otros módulos, reasigne un espacio.

3. Según la información del encabezado de la sección, asigne cada sección del archivo al espacio asignado y modifique los atributos de la página de asignación según los datos definidos en cada sección.

4. Si la dirección de carga del archivo es diferente de la dirección definida por ImageBase, corrija ImageBase nuevamente.

5. Cargue la DLL requerida en el espacio de proceso de acuerdo con la tabla de entrada del archivo PE.

6. Luego reemplace los datos en la tabla IAT con la dirección de la función de llamada real.

7. Genere el montón y la pila de inicialización en función de los datos del encabezado PE.

8. Cree un hilo de inicialización y comience a ejecutar el proceso.

Es necesario mencionar aquí que el proceso de carga de la DLL requerida por el archivo PE se basa en seis API subyacentes.

LdrpCheckForLoadedDll: Comprueba si el módulo a cargar ya existe.

LdrpMapDll: Asigna módulos e información requerida a la memoria.

LdrpWalkImportDescriptor: recorre la tabla de entrada del módulo para cargar otros módulos necesarios.

LdrpUpdateLoadCount: Calcula el número de veces que se utiliza el módulo.

LdrpRunInitializeRoutines: Módulo de inicialización.

LdrpClearLoadInProgress: Borra ciertos indicadores que indican que la carga está completa.

II. Insertar código en archivos PE

Hay tres formas de insertar código en archivos PE:

1. Agregar código a la parte existente. .

2. Amplíe la sección existente y agregue código.

3. Agregar una nueva sección.

Método 1: Agregar código a una Sección existente.

Primero, necesitamos encontrar una sección que se asigne a un bloque con permisos de ejecución.

La forma más sencilla es utilizar la sección CÓDIGO directamente.

Luego necesitamos encontrar el espacio extra en la Sección (es decir, llenarlo con código). Sabemos que una parte tiene dos datos para representar su tamaño: VirtualSize y SizeOfRawData, donde VirtualSize representa el espacio en disco real ocupado por esta parte del código y SizeOfRawData representa el espacio ocupado según la alineación del disco. Normalmente, SizeofRawData es mayor que VirtualSize. Como se muestra a continuación.

El SizeOfRawData en la figura es 0002A000 y el VirtualSize es 00029E88. Cuando los archivos PE se cargan en la memoria, los datos en el espacio adicional entre ellos no se cargarán en la memoria. Por lo tanto, si desea agregar código para cargar en la memoria en el espacio intermedio, debe cambiar el valor de VirtualSize. Aquí puede cambiar el valor de VirtualSize a 00029FFF, para que tengamos un pequeño espacio para agregar el suyo. código.

Lo que debemos hacer es encontrar el punto de entrada del archivo PE OriginalEntryPoint. Por ejemplo, OriginalEntryPoint es 0002ADB4 e ImageBase es 400000. Entonces la dirección virtual real del punto de entrada es 0042ADB4. Luego se calcula el RVA inicial del código y se reemplaza el OriginalEntryPoint del archivo PE.

MOV EAX, 00042ADB4

JMP EAX

De esta manera puedes ejecutar tu propio código cuando se carga el archivo PE y luego ejecutar el código del PE. archivo en sí. El código se ha agregado correctamente al archivo PE.

Método 2: ampliar la sección existente para incluir el código.

Si no tienes suficiente espacio al final de una sección para almacenar tu código, otra opción es ampliar la sección existente. Generalmente solo ampliamos la última parte del archivo PE, porque esto puede evitar muchos problemas, como el impacto en otras partes.

Primero, necesitamos encontrar la última sección y hacerla legible y ejecutable. Esto se puede lograr modificando las propiedades de la parte correspondiente del encabezado. Luego modifique su SizeOfRawData de acuerdo con el tamaño de la alineación del archivo en el encabezado PE. Por ejemplo, si el tamaño de la alineación del archivo es 200h, entonces el SizeOfRawData original = 00008000h, entonces el tamaño del espacio que aumentamos debe ser un múltiplo entero de 200h. El tamaño modificado SizeOfRawData es al menos 00008200h. Después de agregar espacio, debemos modificar el encabezado PE para que sea legible y ejecutable. Después de agregar espacio, debemos modificar los valores de los campos SizeOfCode y SizeOfInitializedData en el encabezado PE y aumentar sus tamaños en 200 h respectivamente. De esta manera, expandimos con éxito la "sección" y luego agregamos el código al espacio aumentado como en el método uno.

Método tres, agregar una nueva sección para agregar código.

Si desea agregar una gran cantidad de código, debe agregar una nueva sección para almacenar el código.

l?Primero, necesitamos encontrar NumberOfSections en el encabezado PE y agregarle 1.

l?Luego, agregue un nuevo espacio al final del archivo, digamos que son 200 h, recordando el desplazamiento desde la línea de inicio hasta el comienzo del archivo PE.

l? Luego, busque el encabezado "Sección" en el encabezado PE. Normalmente, hay algo de espacio entre el final del encabezado "Parte" y el comienzo de la sección de datos de "Parte", busque el final del encabezado "Parte" y agregue un nuevo encabezado. Supongamos que los datos del último encabezado de Sección son:

1.?Desplazamiento virtual: 34000h

2.?Tamaño virtual: 8E00h

3.?Original Desplazamiento: 2F400h

4.

Los datos de alineación del archivo y de alineación de la sección son:

5.?Alineación de la sección: 1000h

6 .?Alineación del archivo: 200h

l?Entonces la Sección recién agregada debe estar alineada con el límite de la última Sección. Los datos son:

1. "Desplazamiento virtual": 3D000h (porque el último límite de la última "sección" es 34000h 8E00h = 3CE00h, más la alineación de la "sección", el valor del "desplazamiento virtual" es 3D000h ).

2. Tamaño virtual: 200h.

3. Desplazamiento original: 00034500h.

4. Características: E0000060 (legible, escribible, ejecutable).

Finalmente, simplemente modifica los campos SizeOfCode y SizeOfInitializedData en el encabezado PE, agregando 200h en cada campo.

En tercer lugar, agregue la entrada de entrada del archivo ejecutable.

En algunas aplicaciones especiales, necesitamos agregar API que no están incluidas en el archivo ejecutable o DLL. Esto se puede lograr agregando el registro de estas API en la tabla de entrada.

1.

1. Cada DLL de entrada tiene un IMAGE_IMPORT_DESCRIPTOR (IID) asociado.

2. Cada IID requiere al menos dos campos Name1 y FirstThunk, y todos los demás campos se pueden configurar en 0. Los datos de cada FirstThunk deben ser un RVA que apunte a la matriz IMAGE_THUNK_DATA. Cada IMAGE_THUNK_DATA a su vez contiene un RVA que apunta al nombre de la API. Si la matriz IID cambia, solo necesita modificar el iSize de la estructura de datos IMAGE_DATA_DIRECTORY correspondiente a la tabla de entrada en la matriz del directorio de datos.

Para agregar un nuevo IID al final de la tabla de entrada, simplemente cambie el IID de ceros al final de la tabla de entrada al IID recién agregado y luego agregue un IID de ceros como nuevo final de la tabla de entrada. Pero si no hay espacio al final de la tabla de entrada, debe copiar toda la tabla de entrada en un nuevo espacio que sea lo suficientemente grande y modificar el RVA y el iSize de la estructura de datos IMAGE_DATA_DIRECTORY correspondiente a la tabla de entrada en los datos. matriz de directorios.

Paso 1: Agregar nuevo IID.

Mueva toda la matriz IID a un nuevo espacio con suficiente espacio para agregar nuevos IID. Esta ubicación puede ser el final de la sección .idata o una sección nueva.

Modifique el RVA y el iSize de la estructura de datos IMAGE_DATA_DIRECTORY correspondiente a la tabla de entrada de la matriz del directorio de datos.

Si es necesario, redondee el tamaño del "segmento" que contiene la nueva matriz IID de acuerdo con la "alineación del segmento" (por ejemplo, si el tamaño original es 1500h y la "alineación del segmento" es 1000h, entonces ajustar a 2000h) para que todo el "segmento" se asigne a la memoria.

Ejecute el archivo ejecutable usando la matriz IID movida. Si se ejecuta normalmente, continúe con el paso 2. Si no funciona, debe verificar que el nuevo IID esté asignado a la memoria y que el nuevo desplazamiento de la matriz IID sea correcto.

Paso 2: Agregar la nueva DLL y sus funciones requeridas.

En la sección .idata agregue dos cadenas terminadas en nulo, una para contener el nombre de la nueva DLL. Un nombre utilizado para guardar la API que debe importarse. La cadena debe estar precedida por un campo WORD vacío para formar la estructura de datos Image_Import_By_Name.

Calcule el RVA de la cadena de nombre de DLL agregada.

Asigne el RVA al campo Nombre1 del IID agregado.

Encuentra un espacio DWORD para el RVA de Image_Import_by_name.

Este RVA es la tabla IAT para la DLL agregada.

Calcule el RVA del espacio DWORD anterior y asígnelo al campo FirstThunk del IID agregado.

Ejecutar el programa modificado.