[Taller en construcción]Secciones en archivos PE.

Iniciado por The Swash, 23 Mayo 2012, 18:44 PM

0 Miembros y 2 Visitantes están viendo este tema.

The Swash


Un saludo a todos los foreros, hoy les traigo un taller práctico-teórico sobre secciones de archivos ejecubles (PE).
Creo que será conveniente recordar conceptos y estructuras básicas sobre el formato PE, si desean profundizar no duden en leer otro documento que he realizado anteriormente sobre el tema "Formato PE bajo Windows", está en español y lo pueden encontrar en el siguiente enlace:
http://foro.elhacker.net/empty-t332157.0.html

O preferiblemente el documento que muchos llamamos la bíblia del Formato PE, el PECOFF que es la documentación oficial de Microsoft sobre el tema:
http://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx

Herramientas de práctica:
HexWorkShop v6.0.1: Editor hexadecimal.
http://dl.dropbox.com/u/26610132/HexWorkShop%206.rar

OllyDBG 1.10: Depurador.
http://dl.dropbox.com/u/26610132/OllyDGB%201.10.rar

Temas que tratará el taller:

  • Conceptos básicos y repaso sobre la estructura PE.
  • Agregando una sección.
  • El problema de la BOUND_IMPORT_TABLE.
  • Desorden en datos de secciones.
  • Ampliando el tamaño de una sección.
  • Datos entre secciones (no mapeados).
  • Secciones "Dummy".
  • Archivos sin ninguna sección.
  • Lo que realmente hace el TinyPE.
  • Explicación de medios de inyección (RunPE e inyección dependiente de relocaciones). -> A pedido de mi amigo _Enko

Repaso de conceptos y estructuras:
Bueno, nuestro enfoque es hacia los archivos ejecutables y su estructura. Un archivo ejecutable, ahora en adelante lo llamaré PE (Portable Executable) y presenta la siguiente estructura:


Estructuras:
IMAGE_DOS_HEADER:

  • DOS_SIGNATURE = 'MZ' -> WORD
  • [29 WORDS] -> Aquí abreviaremos son campos poco utilizados.
  • l_fanew = Apunta hacia IMAGE_NT_HEADERS -> DWORD

IMAGE_NT_HEADERS:

  • Signature = PEx0x0 -> Firma de validación de archivos PE. DWORD
  • IMAGE_FILE_HEADER 0x20
  • IMAGE_OPTIONAL_HEADER 0xE0

IMAGE_FILE_HEADER:

  • Machine: WORD -> Define la arquitectura del computador o emulador para ejecutar el programa
  • NumberOfSections: WORD -> Este valor numerico define la cantidad de secciones que tendrá el archivo.
  • TimeDateStamp: DWORD -> Fecha de creación del archivo.
  • PointerToSymbolTable: DWORD -> Utilizado solo en archivos COFF (.obj)
  • NumberOfSymbols: DWORD -> -> Utilizado solo en archivos COFF (.obj)
  • SizeOfOptinalHeader: WORD -> "Contiene el tamaño del IMAGE_OPTIONAL_HEADER"
  • Characteristics: DWORD -> Atributos u opciones que puede tener el archivo.

IMAGE_OPTIONAL_HEADER:

  • Magic: WORD -> Determina si el programa es para 32 o 64 bits.
  • MajorLinkerVersion: BYTE Versión más alta del linker.
  • MinorLinkerVersion: BYTE Versión más baja del linker.
  • SizeOfCode: DWORD Tamaño de la sección de código generalmente .text o .code.
  • SizeOfInitializedData: DWORD -> Tamaño de datos inicializados.
  • SizeOfUninitializedData: DWORD -> Tamaño de datos no inicializados.
  • AddressOfEntryPoint: DWORD -> RVA del punto de entrada del programa.
  • BaseOfCode: DWORD -> RVA Base de la sección de código.
  • BaseOfData: DWORD -> RVA Base de la sección de datos.
  • ImageBase: DWORD -> VA Base donde se cargará el PE.
  • SectionAlignment: DWORD -> Tamaño de alineación de datos en memoria.
  • FileAlignment: DWORD -> Tamaño de alineación de datos en disco.
  • MajorOperatingSystemVersion: WORD -> Versión principal del S.O requerido.
  • MinorOperatingSystemVersion: WORD -> Versión mínima del S.O requerido.
  • MajorImageVersion: WORD -> Versión principal del PE.
  • MinorImageVersion: WORD -> Versión mínima del PE.
  • MajorSubsystemVersion: WORD -> Versión principal del subsistema requerido.
  • MinorSubsystemVersion: WORD -> Versión mínima del subsistema requerido.
  • Win32VersionValue: DWORD -> Reservado.
  • SizeOfImage: DWORD -> Tamaño que ocupará el ejecutable en memoria, multiplo del SectionAlignment.
  • SizeOfHeaders: DWORD -> Tamaño de toda la cabecera PE.
  • CheckSum: DWORD -> Valor de comprobación por suma.
  • Subsystem: WORD -> Subsistema (consola, gráfico, etc).
  • DllCharacteristics: WORD -> Atributos u opciones pero en caso de librerías DLL.
  • SizeOfStackReserve: DWORD -> Tamaño de reserva para la pila.
  • SizeOfStackCommit: DWORD -> Tamaño de datos de la pila comprometidos.
  • SizeOfHeapReserve: DWORD -> Tamaño de reserva del montículo.
  • SizeOfHeapCommit: DWORD -> Tamaño de datos comprometidos del montículo.
  • LoaderFlags: DWORD -> Parámetros pasados al Loader.
  • NumberOfRvaAndSizes: DWORD -> Número de directorios en IMAGE_DATA_DIRECTORY.
  • IMAGE_DATA_DIRECTORY[NumberOfRVAAndSizes] -> Directorios de datos, conocidos como Tablas, donde tenemos otras estructuras como las importaciones y demás.

IMAGE_SECTION_HEADER

  • Name: (BYTE*8) -> Nombre de la sección.
  • VirtualSize: DWORD -> Tamaño de la sección en memoria.
  • VirtualAddress: DWORD -> RVA donde se carga la sección en memoria.
  • SizeOfRawData: DWORD -> Tamaño de la sección en disco.
  • PointerToRawData: DWORD -> Ubicación de la sección en disco.
  • PointerToRelocations: DWORD -> Campo ya no utilizado, reemplazado por uno de los directorios de datos.
  • PointerToLineNumbers: DWORD -> Solo usado en COFF (.obj).
  • NumberOfRelocations: WORD -> Campo no utilizado ya reemplazado.
  • NumberOfLineNumbers: WORD -> Solo usado en COFF (.obj).
  • Characteristics: DWORD -> Características de la sección (opciones).

Direcciones físicas, virtuales relativas y virtuales:
Antes de continuar quiero también recordar un poco del tema, ya que será necesario que tengan claro que es cada una de estas direcciones para el desarrollo práctico del taller.

Direcciones físicas:
También podemos llamar posición física y corresponden a los valores del archivo en disco osea físicamente al archivo. Va en rango de (0, Tamaño del archivo).

Direcciones virtuales relativas:
Decimos que son relativas porque son el desplazamiento respecto a una dirección virtual. Como se explicará a continuación cuando el archivo es cargado en memoria cambia mucho respecto a como esta en disco. Las direcciones virtuales relativas funcionan como lo sería una dirección física pero en memoria y además son relativas al ImageBase que recordemos es donde se carga el ejecutable. Si hablamos por ejemplo de un RVA 0x3000 en memoria correspondderá a ImageBase + 0x3000, si os verificáis sería muy distinto en obtener el mismo dato en dicha posición.

Direcciones virtuales:
Son direcciones que directamente se encuentran en el rango ImageBase, (ImageBase + SizeOfImage). En escencia sería eso, por ejemplo 0x401000 teniendo como ImageBase 0x400000 estaríamos dentro de dicho rango. Estas hacen referencia directa a una posición en memoria.

Un archivo cargado en memoria:
Bueno justamente quería hacer énfasis en como se vería un archivo cargado en memoria, podemos saber que será distinto que en disco empezando por el detalle de que en memoria será más "grande". Si miramos el SizeOfImage y comparamos en disco tendremos un valor relativamente más grande. Cabe decir que la carga de un archivo en memoria depende su un factor importante y es el campo "SectionAligment" No lo olvidéis.

Lo primero que hará es reservar el espacio, y justo en el ImageBase empezará a "plasmar" el archivo en memoria.

Lo primero que mapeará será la cabecera, cuyo tamaño está determinado por el campo SizeOfHeaders.
Posteriormente tendrá que cargar el cuerpo del archivo, pero es aquí donde entran las alineaciones. Generalmente el SectionAligment tiene el valor correspondiente al tamaño de una página en memoria (0x1000), entonces el mínimo tamaño que tendrá una sección en memoria será el valor del SectionAlignment asi en disco tenga 1 solo byte. Si es mayor entonces su tamaño corresponderá a (VirtualAddress + VirtualSize) redondeado al múltiplo mayor inmediato del SectionAligment.

Ejemplo:
VirtualAddress = 0x1000
VirtualSize = 0x250

Recueden que vamos en múltiplos del SectionAlignment generalmente 0x1000 entonces 0x1000 + 0x250 = 0x1250 el múltiplo mayor inmediato sería 0x2000. El resto será plasmado con muchos 00 hasta completar el tamaño.

Todo lo de la alineación es con el fin de tener orden y fácilidad de acceso a los datos. Entonces las secciones van cargadas en orden de su VirtualAddres sin importar el orden en que se encuentren en el IMAGE_SECTION_HEADER.

Os ilustro con u pequeño gráfico con el que os aclararéis un poco:


Si comprendiste seguramente se te van vieniendo ideas respecto a las secciones.

Nuestro trabajo con secciones:
Bueno esta será la parte que más agradará seguramente, aquí trataremos teoría y práctica de cosas que podemos hacer con las secciones de un archivo.


Parte teórica:
Espero si si estáis leyendo esto tengas los conceptos anteriores muy claros, serán bastante necesarios para que no te confundas.

¿Que define una sección?
Bueno hablemos sobre los campos involucrados en una sección.

  • NumerOfSections: Este campo contiene el número de secciones del ejecutable. Si agregamos una sección deberemos modificarle.
  • SizeOfImage: Este valor contiene el tamaño del ejecutable en memoria, si añadimos una sección entonces agregaremos datos, necesitamos sumar el valor "físico" de la sección al SizeOfImage.
Ahora hay unos campos que entran de manera opcional de acuerdo al campo "Characteristics" de la sección que agregaremos. Por ejemplo si utilizamos las opciones de InitializedData, deberemos actualizar el valor SizeOfInitializedData perteneciente al IMAGE_OPTIONAL_HEADER.

Parte práctica involucrando teoría:

Creando los datos para nuestra sección:
Ahora necesitaremos armar una sección aplicando la parte conceptual vista más arriba. Miremos los campos de una sección:


  • Name.
  • VirtualSize.
  • VirtualAddress.
  • SizeOfRawData.
  • PointerToRawData.
  • PointerToRelocations.
  • PointerToLineNumbers.
  • NumberOfRelocations.
  • NumberOfLineNumbers.
  • Characteristics.

Hay valores que deberemos calcular y otros que simplemente deberemos utilizarlos. Antes de pasar a como obtener los valores quiero mencionar que el IMAGE_FILE_HEADER tiene la declaración de NumberOfSections secciones. Todas las sección ubicada de manera consecutiva y todas utilizando la estructura standard ya mencionada. Cada sección tiene un tamaño de 0x28 bytes, entonces nuestra sección irá al final de la última sección.

De la sección necesitamos:

  • Su definición.
  • Su contenido.

Su definición es lo que va en IMAGE_SECTION_HEADERS donde proporciona todos los datos para que se cargue con el ejecutable.

Bueno para trabajar lo haremos con un pequeño EXE standard para todos nos podamos ubicar correctamente y puedamos seguir este taller.

Es un ejecutable de 1.5k y solo muestra un mensaje y se cierra, será suficiente por ahora.

Descarga:
http://dl.dropbox.com/u/26610132/Practica%201.exe

Para armar nuestra sección necesitaremos leer información del archivo, donde estan ubicadas las secciones, etc. Vamos!

Utilizaremos un editor hexadecimal y nada más.
Comencemos:

  • Primero deberemos ubicarnos al inicio y comprobar que es un archivo ejecutable válido, para ello comparamos los 2 primeros bytes con 'MZ'. (Está con azul en la imagen).
  • Ahora deberemos proceder a encontrar el campo l_fanew recueden ver la estructura, lo conseguimos así:
    El campo se encuentra despues de justo 30 WORDs y un WORD equivale a 2 bytes. Entonces 30*2 = 60, ahí se encuentra el campo l_fanew. (Está con rojo en la imagen).


Citar¿Ey, se han dado cuenta?
Justo en el offset 60 en decimal, se encuentra este valor "80 00 00 00", recueden que el campo es de tamaño DWORD -> 4 bytes. Pero y este campo ¿no apuntaba a la firma 'PE\x0\x0'?
Bueno si, contiene el valor donde empieza la firma 'PE\x0\x0' y posteriormente las demás estructuras. Pero ese valor ¿no es muy grande? Bueno, en realidad no. En windows se utiliza algo llamado Orden Little Endian, donde los bytes se organizan por decirlo al "reves", esto sucede con valores numericos y no con cadenas. Es cuestión de arquitectura y demás, en memoria este valor "80 00 00 00" será equivalente a "00 00 00 80" Vén como ahora tiene sentido? Ah una cosa, todos los valores de la cabecera PE estarán en formato Hexadecimal, y para que nos entendamos, cuando les menciones 60d = 60 en decimal y si menciono 0x80 = 80 en hexadecimal. Con la calculadora de Windows podréis hacer las conversiones.

Bien, ahora tenemos que PE\x0\x0 estará ubicado en el offset: 0x80 o 128d. ¿Es cierto?, bueno podemos darnos cuenta de que sí!. Ahora posterior a esta firma tendremos el IMAGE_FILE_HEADER, justo 4 bytes más adelante, osea en 0x84 o 132d. Tomemos nota con los valores reales:

  • Machine: 01 4C -> Posición: 0x84 o 132d
  • NumberOfSections: 00 02 -> Posición 0x86 o 134d
  • TimeDateStamp: 4F BD 28 3D -> Posición: 0x88 o 136d
  • PointerToSymbolTable: 00 00 00 00 -> Posición: 0x8C o 140d
  • NumberOfSymbols: 00 00 00 00 -> Posición: 0x90 o 144d
  • SizeOfOptionalHeader: 00 E0 -> Posición: 0x94 o 148d
  • Characteristics: 01 0F -> Posición: 0x96 o 150d

Tenemos el primer directorio, vamos con el segundo (?). Bueno era broma quería que vieran el mecanismo. Tienen que leer muy bien la estructura y sus tamaños recuerden:

  • BYTE = 1 byte
  • WORD = 2 bytes
  • DWORD = 4 bytes
  • BYTE * X = X bytes

Haremos exáctamente lo mismo, nos ubicamos al principio de cada directorio y así podemos obtener los datos que necesitamos. Entonces nos ubicamos en 0x98, porque Characteristics está en 0x96 y su tamaño es WORD, entonces el otro campo empieza en 0x98.
De IMAGE_OPTIONAL_HEADER necesitamos por ahora:

  • SizeOfImage -> Lo encontramos con 0x98 + 0x38 = 0xD0 y tiene un valor de 00 30 00 00 (real: 00 00 30 00)
  • SectionAlignment: -> Lo encontramos con 0x98(base IMAGE_OPTIONAL_HEADER) + 0x20 = 0xB8 y tiene un valor 00 10 00 00 (real: 00 00 10 00).
  • FileAlignment: -> Lo encontramos con 0x98 + 0x24 = 0xBC y tiene un valor 00 02 00 00 (real: 00 00 02 00).

Bien lo tenemos, ahora necesitamos ubicarnos en el inicio de IMAGE_SECTION_HEADER. Para ello utilizaremos el inicio del IMAGE_OPTIONAL_HEADER y sumaremos el valor de SizeOfOptionalHeader = 0xE0. Tenemos como resultado: 0x178, como podéis ver estamos en la primera sección. Pero necesitamos la estar parados justo al final de la última para escribir nuestra sección no? Bueno es fácil conseguirla, les había mencionado que el tamaño de esta estructura es 0x28, lo multiplicamos por el valor de NumberOfSections = 2 y tenemos que el final de la última sección está en: 0x178 + (0x28*2) = 0x1C8.

Pero que cerca estamos ahora deberemos generar los datos de nuestra sección, agregaremos una simple cadena "TALLER DE SECCIONES EHN" cuyo tamaño de 0x17 + :

  • Name: El que ustedes quieran que contenga 8 bytes. Usemos '.EHN' sin comillas.
  • VirtualSize: Corresponderá al tamaño de los datos de la sección, nuestro caso es 0x17 bytes
  • VirtualAddress: Corresponderá a la dirección virtual relativa donde se cargará la sección. Como sabemos nuestra sección esta justo al final, si tomamos el Valor del VirtualSize y el VirtualAddress de la sección anterior a nosotros, los sumamos y lo alineamos al SectionAligment podremos calcular nuestro VirtualAddress. Entonces:
    - Tendremos que ubicarnos en la sección anterior, recuerdan que estamos en el final? si restamox 0x28 a 0x1C8 estaremos parados justo en la sección anterior: 0x1A0. Ahora utilizando el mismo procedimiento obtendremos lo valores VirtualSize: 00 00 00 17 y VirtualAddress: 00 00 20 00, procedemos a sumarlos: 0x2017 y alineamos, recuerden el múltiplo mayor inmediato es 0x3000 y listo, tenemos nuestro VirtualAddress.
  • SizeOfRawData: Corresponderá al tamaño de los datos de la sección en disco, piensan que 0x17? Bueno pues no!, En disco al igual que en memoria todo debe ser alineado, y como en disco no hay nadie que nos alineé los datos deberemos hacerlo. ¿Como? Igual que como hacíamos antes solo que ahora alineamos al múltiplo mayor inmediato del FileAlignment. Ya lo habíamos conseguido entonces sería 0x17 y su múltiplo mayor inmediato es 0x200. Esos será el total de datos que deberemos agregar en disco al final del archivo, correspondientes a los datos de nuestra nueva sección.
  • PointerToRawData: Corresponderá a la ubicación o posición en el archivo físicamente de los datos de nuestra nueva sección. Tenemos 2 formas de conseguir dicho valor, una es con el tamaño exácto del archivo y ya, y la otra es sumando los valores PointerToRawData + SizeOfRawData de la sección anterior. De ambas formas llegamos a que es: 0x600, si se dan cuenta es múltiplo del FileAlignment por lo cúal constatamos que la sección anterior a nosotros está alineada.
  • PointerToRelocations: Como ya habíamos mencionado no se utiliza, lo dejamos a 0.
  • PointerToLineNumbers: Como ya lo habíamos mencionado no se utiliza, lo dejamos a 0.
  • NumberOfRelocations: Tampoco se utiliza, lo dejamos a 0.
  • NumberOfLineNumbers: Tampoco se utiliza lo dejamos a 0.
  • Characteristics: Sabía que este momento llegaría  :¬¬. Bueno habíamos mencionado ya algo de este campo en las secciones y era sobre atributos u opciones. Aquí podremos definir que atributos tendrá nuestra sección. Entre los atributos tenemos: Lectura, Escritura, Ejecución y demas. Para referencia exácta de estos valores consulte en las 2 fuentes mencionadas al principio. Por ahora utilizaremos 0xC0 00 00 00 el cual corresponde a atributos de Lectura y escritura.

Datos concretos y legíbles:

  • Name: .EHN -> Valor a plasmar: 2E 45 48 4E 00 00 00 00
  • VirtualSize: 0x17 -> Valor a plasmar: 17 00 00 00
  • VirtualAddress: 0x3000 -> Valor a plasmar: 00 30 00 00
  • SizeOfRawData: 0x200 -> Valor a plasmar: 00 02 00 00
  • PointerToRawData: 0x600 -> Valor a plasmar: 00 06 00 00
  • PointerToRelocations: 00 00 00 00
  • PointerToLineNumbers: 00 00 00 00
  • NumberOfRelocations: 00 00
  • NumberOfLineNumbers: 00 00
  • Characteristics: C0 00 00 00 -> Valor a plasmar: 00 00 00 C0

Cuando digo plasmar no me refiero a "INSERTAR" bytes sino a sobreescribir los 00 suficientes, si insertamos dañamos el ejecutable.
Quedará así:


Ahora nos restan los siguientes detalles:

  • Actualizar el valor del SizeOfImage.
  • Actualizar el valor del NumberOfSections.
  • Añadir los datos de la sección justo al final.

Nuevos valores:

  • SizeOfImage: SizeOfImage + SizeOfRawData de nuestra sección =  0x3200 y lo alineamos al SectionAlignment dando como resultado: 0x4000. Editarlo como 00 40 00 00 recordad lo del Little Endian.
  • NumberOfSections: NumberOfSections +1 = 3 Editarlo como 03 00
  • Añadimos al final 0x200 bytes, entre esos al principio estará nuestra cadena: "TALLER DE SECCIONES EHN", osea la cadena + 0x1E9 byes en 00.

Nuestro ejecutable ya tiene su sección y debe funcionar correctamente. Podemos constatar que la función se agregó con éxito si al cargarlo en OllyDBG e ir a la ventana Memory (M) ver la ImageBase de nuestro ejecutable y constatar que está cargada la otra sección, veamos:

Sección en memoria:


Datos de la sección mapeados en memoria:


Descarga archivo de práctica con sección agregada:
http://dl.dropbox.com/u/26610132/Practica%201-With%20section.exe

Con esto finalizamos la introducción en las secciones, posteriormente veremos otras situaciones en las que agregar una sección se puede complicar un poco, podéis empezar a seguir el taller con confianza.


Anteriormente había mencionado que se nos vendrían un par de obstáculos, que si no los miramos bien nos pueden dejar algo perdidos. Uno de ellos es el Directorio de datos #12 conocido como BOUND_IMPORT_TABLE.

Descarga archivo de practica #2:
http://dl.dropbox.com/u/26610132/Practica%202.exe

¿Que problema me puede causar este directorio?
Analicemos un poco las secciones, tiene 3 secciones código, datos y recursos. Es un ejecutable normal compilado en Visual Basic. Se preguntarán ¿Por qué lo elegí?, bueno por que la mayoría de los ejecutables Visual Basic tienen la BOUND_IMPORT_TABLE.
Ustedes se preguntarán ¿Que bronca me traigo con esa estructura?, no es solo mi bronca será la suya también  :D, Me interesa que nos ubiquemos en el final de la última sección (ya sabemos como, sino sabes vuelve a leer la parte superior del taller).


Si miramos nuestro anterior archivo de práctica veremos una gran diferencia, y es que hay algo despues de la última sección, bueno es la BOUND_IMPORT_TABLE.

Te cuento..
Te cuento que necesitas ese espacio para agregar la declaración de tu sección, pero alguien más ya lo está ocupando. Si intentamos escribir encima de ello nos toparemos con esto:


En realidad es fácil explicar el porque nuestro programa termina explotando, mirando el error:

  • 0xC0000005 - STATUS_ACCESS_VIOLATION
En realidad corresponde a una violación por acceso a un lugar al cúal no se puede acceder, cómo punteros nulos o valores inaccesibles en nuestro anillo de permisos. Todo se dá cuando se trata de validar el BOUND_IMPORT_DIRECTORY.

¿Hay solución?
Si, te cuento que hay 2 una menos ortodoxa que la otra pero ambas funcionan. Una preservadora y otra no presevadora.

  • Primera solución: Yoda(creador de LordPE) alguna vez mencionó que se puede establecer el RVA del directorio BOUND_IMPORT_DIRECTORY a 00 porque este era poco importante. Mi amigo karmany opinó al respecto y estoy de acuerdo con él, este directorio tiene como utilidad optimizar entonces me parece que importa. El ejecutable no deja de funcionar pero no preserva el estado original del ejecutable.
  • Segunda solución: Personalmente me parece la mejor y consiste en mover de sitio el BOUND_IMPORT_DIRECTORY. Si sabemos que está justo despues de la última sección, tranquilamente lo desplazamos 0x28 bytes y así tenemos espacio para una función. Posteriormente se actualiza el RVA en el Directorio de datos y lo habríamos solucionado.

Manos a la obra!
Bueno, probemos que todo lo que hemos dicho es cierto e intentemos añadir una sección al ejecutable de práctica teniendo en cuenta las 2 soluciones.

Creo que necesito hacer un breve recuerdo sobre el IMAGE_DATA_DIRECTORY asi que nombraré algunas de sus características.

  • Se ubíca justo antes de IMAGE_SECTION_HEADER.
  • Tiene como tamaño (8*IMAGE_OPTIONAL_HEADER.NumberOfRvaAndSizes).
  • Cuenta con un total de 16 directorios generalmente (aunque puede alterarse).
  • Cada directorio tiene 2 entradas, RVA(DWORD) y Size(DWORD).

Ya les había mencionado que el directorio correspondiente al BOUND_IMPORT_TABLE es el número 12, si aplicamos un poco de matemáticas y nos paramos justo al inicio de IMAGE_SECTION_HEADER y restamos (5*8) estaremos justo en la declaración del directorio. 0x1B0 - 0x28 = 0x188 -> BOUND_IMPORT_DIRECTORY, leamos los valores del directorio.

  • RVA: 0x228
  • Size: 0x20

Relizamos los desplazamientos:
Lo unico que deberemos hacer es cambiar el RVA del BOUND_IMPORT_DIRECTORY por RVA + 0x28 y posteriormente desplazar los datos en el archivo (Con desplazar me refiero a correr, nada de insertar.)

Nuevos datos:

  • RVA: 0x250
  • Size: 0x20

Y desplazando los datos tendríamos algo así:


Si miramos quedó desplazado y los bytes que quedaron en color rojo vendrá a ser el espacio para nuestra sección. Aún si no la agregasemos el ejecutable de igual forma quedaría funcionando y se preservaría.

Igual ya teníamos los datos sobre el archivo de práctica original, para aplicar el segundo método cambiamos el RVA por 00 00 00 00 y el Size por 00 00 00 00 y listo.

Descarga práctica con sección y método de desplazamiento de la BOUND_IMPORT_TABLE:
http://dl.dropbox.com/u/26610132/Metodo%20desplazamiento.exe

Descarga práctica con sección y método de eliminación de la BOUND_IMPORT_TABLE:
http://dl.dropbox.com/u/26610132/Metodo%20de%20eliminacion.exe

Con esto habríamos terminado la parte de agregar secciones aunque agregaré lo siguiente:
El otro problema sería para más que un problema un caso de profundización y vendría a ser el siguiente. Primero habría que hablar de un límite de secciones que si mal no recuerdo en Windows XP es de 96 y en >= Windows Vista es de 0xFFFF, todo cuestion de versión del Loader. Ahora si físicamente te quedas sin espacio porque por ejemplo ya tienes enseguida de la última sección la primera sección del ejecutable.

¿No se pueden agregar más?
Claro que se puede y tienes que tener en cuenta el SizeOfHeaders, lo puedes expandir y seguir agregando secciones. Solo que deberás tener un factor en cuenta, si aumentaste el tamaño ahí deberías arreglar nuevamente los datos de todas las secciones, VirtualAddress y PointerToRawData de cada una.

Esto es todo sobre agregar secciones, practiquen y si tienen dudas para eso estamos, disfrutenlo!, hasta el siguiente tema.


Bueno hablaremos sobre el orden de las secciones en el cuerpo del archivo. Si hemos visto determinada cantidad de archivos ejecutables nos daremos cuenta que en el cuerpo del archivo se preserva el orden en el que están declaradas las secciones.

¿Es necesario que se respete este orden?
No, realmente las secciones en disco pueden estar ubicadas en completo desorden mientras cumplan el requisito de estar bien referenciadas en su declaración (IMAGE_SECTION_HEADER). Me refiero a que todos los punteros deben estar correctos.

¿Por que no afecta el orden de las secciones?
En realidad como mencioné las secciones en disco se ven referenciadas o "apuntadas" por su definición en el IMAGE_SECTION_HEADER, el mapeo en memoria no se verá afectado ya que igual esta mapeando datos que provienen de distintias posiciones en el archivo. Si os preguntáis por qué no se puede cambiar el orden de las secciones en su declaración? En realidad por problemas de fragmentación y garantía, no se puede asegurar si no se escriben en orden posicional no se pisen entre sí.

¿No entiendes lo de desorden?
Bueno espero este gráfico te ilustre un poco:


Si lo llevamos a la parte práctica podríamos sacar las siguientes conclusiones:

  • El orden del IMAGE_SECTION_HEADER no se debe alterar.
  • Tendremos que modificar el orden del contenido de las secciones.
  • Tendremos que actualizar los campos PointerToRawData y SizeOfRawdata de las secciones.

Descar archivo de práctica número #3:
http://dl.dropbox.com/u/26610132/Practica%203.exe

Nuestro archivo:

  • Tiene 3 secciones (.text, .idata y .data).
  • Intercambiaremos .text y .data en el cuerpo del archivo.

Manos a la obra!

  • Primero obtendremos datos de las secciones, recordad lo siguiente: los cambios serán en físico con el hecho de que en memoria el archivo se cargará igual siempre.

    • Sección .text: PointerToRawData: 0x200 SizeOfRawData: 0x200
    • Sección .idata: PointerToRawData: 0x400 SizeOfRawData: 0x200
    • Sección .data: PointerToRawData: 0x600 SizeOfRawData: 0x200
  • Todas las secciones tienen el mismo tamaño, lo cual nos facilitará las cosas.
  • Nuevos datos:

    • Sección .text: PointerToRawData: 0x600 SizeOfRawData: 0x200
    • Sección .idata: PointerToRawData: 0x400 SizeOfRawData: 0x200
    • Sección .data: PointerToRawData: 0x200 SizeOfRawData: 0x200

Declaración de IMAGE_SECTION_HEADER antes:


Declaración de IMAGE_SECTION_HEADER después:


En disco debemos cambiar todos los 0x200 bytes de una sección por otra:

Nueva ubicación sección .text:


Nueva ubicación sección .data

Eso es todo, lo considero uno de esos detallitos que son importantes resaltar. Ya podéis prácticar cualquier duda pregunta!, Hasta el siguiente tema.


Hola chicos, en este nuevo capitulo hablaremos sobre como ampliar la sección de un archivo ejecutable. Requiere todo lo visto previamente, algo de cuidado y solo un poquito de imaginación, eso es todo.

¿Por qué podemos querer ampliar una sección?
Podemos verlo desde distintas perspectivas pero siempre llegando a la conclusión de que nos crearemos un espacio en el archivo. Al agregar una sección agregamos datos en un espacio que nosotros mismos previamente creamos, al ampliar una sección con seguridad queremos meter datos y nuevamente nos estamos haciendo un espacio, solo que ahora es "compartido".

¿Que debemos hacer para ampliar una sección?
Debemos tener claro que ampliaremos el rango de los valores de una sección física y virtualmente. Hay que tener especial cuidado si ampliamos una sección que se encuentra en medio de otras 2, puesto que tendremos que modificar los valores de la siguiente sección para no desalinear el ejecutable.

Ilustración de una sección ampliada:


Descargar archivo de práctica #4:
https://dl.dropbox.com/u/26610132/Practica%204.exe

Manos a la obra!
Ampliaremos las sección .idata 0x150 bytes en el archivo de práctica.

Debemos:

  • Obtener los siguientes valores de la sección que ampliaremos:

    • VirtualSize: 0x98
    • PointerToRawData: 0x400
    • SizeOfRawData: 0x200
  • Si hay una sección posterior a la que ampliaremos obtedremos de dicha sección (.data) los siguientes valores:

    • VirtualAddress: 0x3000
    • VirtualSize: 0x4
    • PointerToRawData: 0x600
    • SizeOfRawData: 0x200
  • Debemos alinear la cantidad de bytes que ampliaremos en base al FileAlignmente: 0x150 Alineado 0x200 : 0x200 y será la cantidad que ampliaremos.
  • Nos ubicamos al final de la sección que ampliaremos: PointerToRawData + SizeOfRawData: 0x600.
  • Añadimos la cantidad de bytes ya caculada.
  • Actualizaremos valores de la sección de la siguiente manera:

    • VirtualSize: Teniendo en cuenta que este tamaño corresponde a los bytes que se mapearán en memoria debemos saber cuanto espacio usaremos. Podemos escribir en el nuevo espacio generado y modificar este campo en base hasta donde queremos que mapee. En este caso pongámoslo a 0x400 para que cubra todos los bytes de la sección.
    • SizeOfRawData: Aumentaremos el valor de bytes alineados que agregamos: 0x200 + 0x200 : 0x400.
  • Debemos actualizar de la sección posterior (si la hay) los siguientes valores:

    • VirtualAddress: Se actualizará unicamente si al sumar VirtualAddress + SizeOfRawData de la sección ampliada el resultado es mayor que el VirtualAddress de esta sección. En nuestro caso no se cumple y no modificamos el valor.
    • PointerToRawData: Se actualizará con el valor resultante de la suma de los valores PointerToRawData + SizeOfRawData de la sección ampliada, en nuestro caso 0x400 + 0x400 : 0x800.
  • Debemos actualizar el SizeOfImage con el valor equivalente a: VirtualAddress + (VirtualSize Alineado a SectionAlignment): 0x3000 + (0x400 Alineado 0x1000 = 0x1000) = 0x4000.

Con esto ya debebemos tener nuestro ejecutable funcionando y totalmente alineado.
Nota: Cuando la sección que se amplíe también este referenciada por el directorio de datos, debes actualizar el tamaño en el mismo.


Nuevos bytes en disco:


Nuevos bytes en memoria:


Descarga archivo resultante (con una pequeña modificación):
https://dl.dropbox.com/u/26610132/Practica%204%20Resultado.exe

Con esto terminamos este capitulo, practiquen y no olviden poner sus dudas, Nos vemos en el próximo.



x64core

 :D
Grande el taller, muchas gracias por compartirlo con todos nosotros The Swash  :)

Belial & Grimoire

hola The Swash

muy bueno el taller espero pongas mas, tenia ganas de continuar con esto hace tiempo pero tuve un problema que espero me puedas decir que podria ser

hace tiempo intente modificar PE en windows XP y me funciono perfecto, pero quise hacer el intento en windows 7 y no logre expandir PE para agregar mi sección en notepad

y quisiera preguntarte... con cual windows te basaste para hacer este taller?, y haz logrado hacerlo en windows 7?

espero me puedas responder, y ojala continues agregando mas cosas a tu taller

salu2
.                                 

The Swash

Hola,
Gracias a todos por vuestros comentarios, en realidad todo fue hecho bajo Windows 7 pero puedo asegurarte que lo hecho hasta el momento funciona bajo todas las versiones de Windows, porque lo hacemos de manera correcta y standard.

En cuanto a lo de la ampliación de las secciones también hace parte del taller, aún no llegamos pero en nada lo veréis.


Un saludo.

$Edu$

Perfecto, buen aporte, muchas gracias por compartir!

Belial & Grimoire


que bien, a ver si ahora puedo lograrlo en windows 7, bueno, pues ya ansioso de la continuacion del taller  ;-)

salu2 y suerte
.                                 

jackgris

Exelente, ya habia leido el pdf Formato PE bajo Windows, pero con este taller esta muy bueno recordar cosas escenciales, muchas gracias  ;-) ;-) ;-)

_Enko


Belial & Grimoire

hola

espero me puedas explicar algunas cosas que no entendi, todavia no lo hago en windows, por ahorita solo revise las aplicaciones que pusiste con linux para poder seguir el taller

pero tengo algunas dudas

Citar1.- Pero que cerca estamos ahora deberemos generar los datos de nuestra sección, agregaremos una simple cadena "TALLER DE SECCIONES EHN" con un tamaño de 0x17

de donde sale ese tamaño 0x17?

tampoco entendi muy bien como encontrar VirtualAddress

Citar2.-VirtualAddress: Corresponderá a la dirección virtual relativa donde se cargará la sección. Como sabemos nuestra sección esta justo al final, si tomamos el Valor del VirtualSize y el VirtualAddress de la sección anterior a nosotros, los sumamos y lo alineamos al SectionAligment podremos calcular nuestro VirtualAddress. Entonces:
- Tendremos que ubicarnos en la sección anterior, recuerdan que estamos en el final? si restamox 0x28 a 0x1C8 estaremos parados justo en la sección anterior: 0x1A0. Ahora utilizando el mismo procedimiento obtendremos lo valores VirtualSize: 00 00 00 98 y VirtualAddress: 00 00 20 00, procedemos a sumarlos: 0x2098 y alineamos, recuerden el múltiplo mayor inmediato es 0x3000 y listo, tenemos nuestro VirtualAddress.

segun lo que entendi, es que para encontrar VirtualAddress... ¿hay que sumar 98, que seria el inicio de IMAGE_OPTIONAL_HEADER + FileAlignment 00 00 02 00?

lo que seria 2098 y como multiplo seria 3000

aqui vienen 2 preguntas... ¿siempre es asi para encontrar VirtualAddress? y ¿cuando se habla de multiplos se refieren a redondear?, por ejemplo

si fuera 2234, aun seria 3000?, y si fuera 3005, entonces seria 4000?

Citar3.-SizeOfRawData: Corresponderá al tamaño de los datos de la sección en disco, piensan que 0x98? Bueno pues no!, En disco al igual que en memoria todo debe ser alineado, y como en disco no hay nadie que nos alineé los datos deberemos hacerlo. ¿Como? Igual que como hacíamos antes solo que ahora alineamos al múltiplo mayor inmediato del FileAlignment. Ya lo habíamos conseguido entonces sería 0x98 y su múltiplo mayor inmediato es 0x200. Esos será el total de datos que deberemos agregar en disco al final del archivo, correspondientes a los datos de nuestra nueva sección.

SizeofRawData es lo mismo que FileAlignment?, y por ejemplo si fuera asi?

FileAlignment 00 00 02 05, entonces SizeofRawData seria 0x205, y entonces lo tendria que pasar al multiplo mayor?, para que quede 0x300?

Citar4.-PointerToRawData: Corresponderá a la ubicación o posición en el archivo físicamente de los datos de nuestra nueva sección. Tenemos 2 formas de conseguir dicho valor, una es con el tamaño exácto del archivo y ya, y la otra es sumando los valores PointerToRawData + SizeOfRawData de la sección anterior. De ambas formas llegamos a que es: 0x600, si se dan cuenta es múltiplo del FileAlignment por lo cúal constatamos que la sección anterior a nosotros está alineada.

Este la verdad no entendi como encontrar, solo se que SizeofRawData equivale a 0x200, pero como llegaste a 0x600?

y la ultima que es una confusion seria

CitarName: .EHN -> Valor a plasmar: 2E 45 48 4E 00 00 00 00
    VirtualSize: 0x98 -> Valor a plasmar: 98 00 00 00
    VirtualAddress: 0x3000 -> Valor a plasmar: 00 30 00 00
    SizeOfRawData: 0x200 -> Valor a plasmar: 00 02 00 00
    PointerToRawData: 0x600 -> Valor a plasmar: 00 06 00 00
    PointerToRelocations: 00 00 00 00
    PointerToLineNumbers: 00 00 00 00
    NumberOfRelocations: 00 00
    NumberOfLineNumbers: 00 00
    Characteristics: C0 00 00 00 -> Valor a plasmar: 00 00 00 C0

en VirtualSize pusiste 0x98, lo que me imagino seria el inicio de IMAGE_OPTIONAL_HEADER, pero en el antiguo taller de Ferchu menciona

CitarName:   .Ferchu   (8 bytes)
VirtualSize:    0x00000050    (4 bytes)

Y finalmente agregamos nuestra frase "Hola yo soy la sección de prueba, y ocupo exactamente la cantidad de 0x50 bytes." al final del archivo, como los datos de la nueva sección.


aqui esto me confundio, cual es la diferencia entre los 0x50 de la frase y 0x98 que tu pusiste

bueno, por el momento seria todo, jeje creo tuve muchas dudas, pero me interesa el tema y espero no te moleste jeje XD

bueno salu2
.                                 

The Swash

En cuanto a lo de la cadena me refería a su tamaño, ya lo he editado de una manera que quede más explícito.

En cuanto a lo del campo VirtualAddress de la estructura IMAGE_SECTION_HEADER, me refiero a la dirección virtual relativa(explicado al principio) donde se cargará la sección en memoria. Osea es allí donde empieza a cargarse la sección que como te darás cuenta es distinto comparandolo con la posición en disco, esto se debe a los alineamientos virtuales y físicos.

Para encontrar el VirtualAddress debe tomar como base la sección inmediatamente anterior a la que vamos a agregar, la que era la última. De aquí tomaremos los datos VirtualAddress y SizeOfRawData, los sumamos y luego alineamos al SectionAlignment -> Alineación virtual.

Cuando me refiero a alinear en teoría si, es redondear pero hacia el múltiplo de determinado valor pero mayor mayor. Ejemplo:

0x1000 + 0x05 = 0x1005 -> Múltiplo más proximo de 0x1000 es 0x2000.
Además el ejemplo que diste también es válido.

SizeOfRawData NO es lo mismo que FileAlignment aunque pueden coincidir sus valores, este corresponde al tamaño que ocupa la sección en el archivo. El FileAlignment es la base de alineamientos físicos. Osea que todo lo que que en disco deba estar alineado a este campo debe cumplir como valor mínimo, y cualquier otro valor mayor debe ser múltiplo mayor inmediato de este.

Ejemplo:
0x202 -> Alineado 0x400 (FileAlignment = 0x200).

El PointerToRawData es la ubicación de los datos de la nueva sección en disco. Igualmente debes tomar como referencia la sección inmediatamente anterior. Si el PointerToRawData de la sección inmediatamente anterior es 0x400 esa será su posición en disco, pero para llegar al final de los datos de esta sección debes sumar su tamaño SizeOfRawData, con eso estarás al final de la sección inmediatamente anterior. Así llego a 0x600.

En cuanto al valor del VirtualSize correpsonde al tamaño de los datos que mapeará en memoria el Loader, 0x98 fue un error mío de puro despiste, en realidad sería 0x17, en este caso no se alinea a ningún valor entonces pasamos el valor neto de los datos explicitos de la sección.

Espero haya resuelto tus dudas.

Un saludo,
Iván Portilla