comment
IRC Chat
play_arrow
Este sitio utiliza cookies propias y de terceros. Si continúa navegando consideramos que acepta el uso de cookies. OK Más Información.

Tutorial Introduccion a la estructura PE

  • 3 Respuestas
  • 3624 Vistas

0 Usuarios y 4 Visitantes están viendo este tema.

Desconectado Stuxnet

  • *
  • Underc0der
  • Mensajes: 259
  • Actividad:
    0%
  • Reputación 2
    • Ver Perfil
    • Devel Security
  • Twitter: _stuxnet
« en: Abril 04, 2012, 10:34:41 pm »
el autor original de este aporte es JEP



Esta es una pequeña introducción de como funcionan los archivos PE

Que es?
El archivo PE (Portable Executable) es el formato que utiliza windows para los ejecutables. En fin, en pocas palabras es un .exe o un .dll

Para que sirve conocerlo?
En Ing inversa muchas veces no es necesario conocer con mucho detalle la estructura que tiene este archivo, sin embargo, para problemas más complicados si resultará util conocerlo.
Nos permite separar las secciones del exe, analizarlas por separado, crear nuevas secciones, o modificar las existentes.
Las aplicaciones son bastantes, pero lo mas importante es que con esto se entenderá mejor porque pasan algunas cosas y como podemos resolverlas

Estructura basica:
vamos por parte, primero con una división a grandes razgos de las partes del archivo, y despues vamos ir entrando en detalle en las mas importantes

El archivo PE simplemente un archivo binario, que tiene una estructura, que es basicamente esta:
Código: [Seleccionar]
--------------------
|  ENCABEZADO DOS  |
--------------------
|  STUB DOS        |
--------------------
|  ENCABEZADO NT   |
--------------------
|  ENCABEZADO SEC  |
--------------------
|  STUB NT         |
--------------------
Como se ve el archivo tiene 5 partes, asi de simple XD, los encabezados tienen un tamaño constante y en ellos están definidas todas las propiedades del archivo. Los stub contienen al programa en sí, los analizaremos por parte.



Sobre el Encabezado DOS y el Stub DOS
Tanto el encabezado DOS como el stub DOS no nos interesará tanto, están ahi por una especie de compatibilidad, si estamos ejecutando el programa en windows se ignorará toda esta parte.
generalmente eso contiene un pequeño programa en DOS que solamente imprime en pantalla algo como esto:
"This program cannot be run in DOS mode."
para indicar que estamos tratando de ejecutar un archivo que no corre en DOS.

Encabezado DOS
El encabezado DOS, asi como ya dije, es una estructura de datos que contiene variables que definen el programa, las variables que contiene este encabezado las podemos sacar de la ayuda de windows (msdn.microsoft.com)
Código: C
  1. typedef struct _IMAGE_DOS_HEADER
  2. {
  3.      WORD e_magic;
  4.      WORD e_cblp;
  5.      WORD e_cp;
  6.      WORD e_crlc;
  7.      WORD e_cparhdr;
  8.      WORD e_minalloc;
  9.      WORD e_maxalloc;
  10.      WORD e_ss;
  11.      WORD e_sp;
  12.      WORD e_csum;
  13.      WORD e_ip;
  14.      WORD e_cs;
  15.      WORD e_lfarlc;
  16.      WORD e_ovno;
  17.      WORD e_res[4];
  18.      WORD e_oemid;
  19.      WORD e_oeminfo;
  20.      WORD e_res2[10];
  21.      DWORD e_lfanew;
  22. } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
  23.  
esto es el encabezado, no nos interesa tanto, lo importante aqui es que, para leer todo el archivo debemos por lo menos saber el tamaño que ocupa este encabezado, y lo mas importante es que en este encabezado está la variable que indica donde comienza el encabezado del NT (que es realmente el encabezado que nos interesa)
dentro de esta variable e_lfanew se indica donde comienza el encabezado NT, vamos a ver con un ejemplo, esto es lo que se ve con un editor hexa
Código: [Seleccionar]
"000000000  4D 5A 90 00 03 00 00 00-04 00 00 00 FF FF 00 00   |MZ##############|"
"000000010  B8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00   |########@#######|"
"000000020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000030  00 00 00 00 00 00 00 00-00 00 00 00 80 00 00 00   |################|"

"000000040  0E 1F BA 0E 00 B4 09 CD-21 B8 01 4C CD 21 54 68   |######.#!##L#!Th|"
"000000050  69 73 20 70 72 6F 67 72-61 6D 20 63 61 6E 6E 6F   |is program canno|"
"000000060  74 20 62 65 20 72 75 6E-20 69 6E 20 44 4F 53 20   |t be run in DOS |"
"000000070  6D 6F 64 65 2E 0D 0D 0A-24 00 00 00 00 00 00 00   |mode....$#######|"
"000000080  50 45 00 00 4C 01 05 00-24 A9 8A 4C 00 6C 00 00   |PE##L###$##L#l##|"
ahora hay que fijarse en la estructura del DOS_HEADER y lo que se ve en el archivo.
el tamaño del encabezado podemos calcular 30*WORD+1*DWORD = 64 bytes el encabezado tiene 64 bytes = 0x40 bytes (resulta mas comodo leer todo en hexa)
en el archivo separé con una linea, donde termina este encabezado, es exactamente en el offset 0x40.
Ahora vamos a leer la variable que nos interesa (DWORD e_lfanew), que son los ultimos 4 bytes del encabezado. en el archivo vemos que en esos 4 bytes tenemos "80 00 00 00" no hay que perder de vista que, dentro del archivo las variables se guardan con el byte menos significativo a la izquierda, asi que, si queremos leer esto como un numero hexa, debemos invertir este valor, tomando de 2 en 2, este valor en hexa será 0x00000080 (si no se entendió coloco aqui un ejemplo, si vemos en el archivo una variable asi "01 02 03 04" el valor en hexa de esa variable es 0x04030201, se debe invertir los numero  de 2 en 2)

bueno, con esto ya podemos leer donde empieza el encabezado del NT, está en el offset 0x00000080
Código: [Seleccionar]
"000000070  6D 6F 64 65 2E 0D 0D 0A-24 00 00 00 00 00 00 00   |mode....$#######|"

"000000080  50 45 00 00 4C 01 05 00-24 A9 8A 4C 00 6C 00 00   |PE##L###$##L#l##|"
"000000090  31 03 00 00 E0 00 07 03-0B 01 02 38 00 5A 00 00   |1##########8#Z##|"
"0000000A0  00 68 00 00 00 0C 00 00-30 11 00 00 00 10 00 00   |#h######0#######|"
aqui vemos, separando con una linea donde comienza.

Stub DOS
El codigo en DOS realmente no nos interesa, pero solo para marcarlo, donde comienza y donde termina, está a continuación del Encabezado DOS y termina antes del encabezado del NT, asi que, en este caso, lo que tenemos en el stub del DOS es esto:
Código: [Seleccionar]
"000000040  0E 1F BA 0E 00 B4 09 CD-21 B8 01 4C CD 21 54 68   |######.#!##L#!Th|"
"000000050  69 73 20 70 72 6F 67 72-61 6D 20 63 61 6E 6E 6F   |is program canno|"
"000000060  74 20 62 65 20 72 75 6E-20 69 6E 20 44 4F 53 20   |t be run in DOS |"
"000000070  6D 6F 64 65 2E 0D 0D 0A-24 00 00 00 00 00 00 00   |mode....$#######|"
como se puede ver, ahi esta el texto que dice que la aplicación no corre en DOS


Encabezado NT
El encabezado NT es un poco mas complicado que el de DOS, veamos su estructura
Código: C
  1. typedef struct _IMAGE_NT_HEADERS {
  2.   DWORD                 Signature;
  3.   IMAGE_FILE_HEADER     FileHeader;
  4.   IMAGE_OPTIONAL_HEADER OptionalHeader;
  5. } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
  6.  
esta es su definición, la podemos encontrar en esta pagina IMAGE_NT_HEADERS
Esta estructura todavia no nos dice mucho, solo que tiene un DWORD, que es un valor de referencia, siempre vale 0x00004550, realmente conviene leer esta variable como un char* "50 45 00 00", en ascii significa 'PE\0\0', esto se puede ver facilmente y permite identificar donde comienza el encabezado NT sin tener que hacer el cálculo que hicimos hace un rato.
Esto lo vemos aqui, (ver los primeros 4 bytes)
Código: [Seleccionar]
"000000080  50 45 00 00 4C 01 05 00-24 A9 8A 4C 00 6C 00 00   |PE##L###$##L#l##|"


Veamos las estructuras contenidas en esta estructura: IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER

IMAGE_FILE_HEADER
Código: C
  1. typedef struct _IMAGE_FILE_HEADER {
  2.   WORD  Machine;
  3.   WORD  NumberOfSections;
  4.   DWORD TimeDateStamp;
  5.   DWORD PointerToSymbolTable;
  6.   DWORD NumberOfSymbols;
  7.   WORD  SizeOfOptionalHeader;
  8.   WORD  Characteristics;
  9. } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
  10.  
No vamos a ver con detalle todas las variables de este encabezado, solo las que nos interesan
WORD  Machine: nos dice el tipo de maquina para la que fue compilado: 386,486,586,...
DWORD NumberOfSections: numero de secciones del archivo, esto es importante, si es que queremos agregar secciones.
DWORD TimeDateStamp: la fecha en la que fue compilado
DWORD PointerToSymbolTable: puntero a la tabla de simbolos
DWORD NumberOfSymbols: numero de simbolos (despues veremos esto en detalle)
WORD  SizeOfOptionalHeader: tamaño del IMAGE_OPTIONAL_HEADER, esto es interesante ya que, haciendo unas operaciones podemos llegar al stub del NT (donde comienza realmente el codigo) del encabezado DOS tenemos el offset del inicio de esta estructura, si a eso le sumamos el tamaño del IMAGE_FILE_HEADER y del IMAGE_OPTIONAL_HEADER, llegaremos al inicio del Stub NT (despues veremos en detalle como hacer este calculo)

sigamos con el IMAGE_OPTIONAL_HEADER
Código: C
  1. typedef struct _IMAGE_OPTIONAL_HEADER {
  2.   WORD                 Magic;
  3.   BYTE                 MajorLinkerVersion;
  4.   BYTE                 MinorLinkerVersion;
  5.   DWORD                SizeOfCode;
  6.   DWORD                SizeOfInitializedData;
  7.   DWORD                SizeOfUninitializedData;
  8.   DWORD                AddressOfEntryPoint;
  9.   DWORD                BaseOfCode;
  10.   DWORD                BaseOfData;
  11.   DWORD                ImageBase;
  12.   DWORD                SectionAlignment;
  13.   DWORD                FileAlignment;
  14.   WORD                 MajorOperatingSystemVersion;
  15.   WORD                 MinorOperatingSystemVersion;
  16.   WORD                 MajorImageVersion;
  17.   WORD                 MinorImageVersion;
  18.   WORD                 MajorSubsystemVersion;
  19.   WORD                 MinorSubsystemVersion;
  20.   DWORD                Win32VersionValue;
  21.   DWORD                SizeOfImage;
  22.   DWORD                SizeOfHeaders;
  23.   DWORD                CheckSum;
  24.   WORD                 Subsystem;
  25.   WORD                 DllCharacteristics;
  26.   DWORD                SizeOfStackReserve;
  27.   DWORD                SizeOfStackCommit;
  28.   DWORD                SizeOfHeapReserve;
  29.   DWORD                SizeOfHeapCommit;
  30.   DWORD                LoaderFlags;
  31.   DWORD                NumberOfRvaAndSizes;
  32.   IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
  33. } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
  34.  
  DWORD SizeOfCode; indica el tamaño total del code, osea, es la suma de los tamaños de las secciones que contienen codigo.
  DWORD AddressOfEntryPoint; direccion del EntryPoint
  DWORD BaseOfCode esto indica donde empieza la seccion que contiene el codigo (generalmente la seccion .text)
  DWORD BaseOfData; indica donde empieza la seccion que contiene las constantes del programa (generalmente .data)
  DWORD ImageBase; todas las direcciones virtuales están colocadas relativas a esta direccion, asi que a todas hay que sumarle este valor para sacar la direccion
  DWORD SectionAlignment; Alineacion de las secciones, en memoria
  DWORD FileAlignment; alineacion de las secciones, en el archivo
  DWORD SizeOfImage; suma de los tamaños de las secciones
  DWORD SizeOfHeaders; suma de los tamaños de los encabezados de las secciones
  DWORD NumberOfRvaAndSizes; esto da el numero de secciones y tamaños de cada seccion, esto es necesario para leer la info de IMAGE_DATA_DIRECTORY DataDirector, esta estructura que contiene la direccion de las secciones en el archivo, y los tamaños de estas secciones.
  estas secciones son secciones especiales, que siempre deben estar en los programas, ya vamos a ver que tienen una relacion con las secciones reales del programa, pero en nuestro programa podemos tener otras secciones que no estén en esta tabla.
las secciones de esta tabla son:
{"Export Table","Import Table","Resource Table","Exception Table","Certificate Table","Base Relocation Table","Debug","Architecture","Global Ptr","TLS Table","Load Config Table","Bound Import","IAT","Delay Import Descriptor","COM+ Runtime Header","Reserved"}
si alguna de estas secciones no existe en el programa se coloca su direccion 0x0 y tamaño 0x0, como podremos ver en los programas reales
 
aqui se puede ver la definicion de esa estructura 
Código: C
  1. typedef struct _IMAGE_DATA_DIRECTORY {
  2.   DWORD VirtualAddress;
  3.   DWORD Size;
  4. } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;  
  5.  

El IMAGE_OPTIONAL_HEADER en el ejecutable de ejemplo se puede ver aqui: (comienza en el offset 0x000000098 y termina en el 000000177)
Código: [Seleccionar]
"000000090  31 03 00 00 E0 00 07 03-0B 01 02 38 00 5A 00 00   |1##########8#Z##|"
"0000000A0  00 68 00 00 00 0C 00 00-30 11 00 00 00 10 00 00   |#h######0#######|"
"0000000B0  00 70 00 00 00 00 40 00-00 10 00 00 00 02 00 00   |#p####@#########|"
"0000000C0  04 00 00 00 01 00 00 00-04 00 00 00 00 00 00 00   |################|"
"0000000D0  00 B0 00 00 00 04 00 00-A9 4C 01 00 03 00 00 00   |#########L######|"
"0000000E0  00 00 20 00 00 10 00 00-00 00 10 00 00 10 00 00   |## #############|"
"0000000F0  00 00 00 00 10 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000100  00 A0 00 00 B8 04 00 00-00 00 00 00 00 00 00 00   |################|"
"000000110  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000170  00 00 00 00 00 00 00 00-2E 74 65 78 74 00 00 00   |########.text###|"

aqui vemos eso mismo, pero ordenando cada variable (visto con el ollydebg):
hice un salto de linea donde comienza la tabla de secciones (esto no es el encabezado de las secciones, que está a continuación)
Código: [Seleccionar]
00400098        0B01         DW 010B              ; MagicNumber = PE32
0040009A        02           DB 02                ;  MajorLinkerVersion = 2
0040009B        38           DB 38                ;  MinorLinkerVersion = 38 (56.)
0040009C        005A0000     DD 00005A00          ;  SizeOfCode = 5A00 (23040.)
004000A0        00680000     DD 00006800          ;  SizeOfInitializedData = 6800 (26624.)
004000A4        000C0000     DD 00000C00          ;  SizeOfUninitializedData = C00 (3072.)
004000A8        30110000     DD 00001130          ;  AddressOfEntryPoint = 1130
004000AC        00100000     DD 00001000          ;  BaseOfCode = 1000
004000B0        00700000     DD 00007000          ;  BaseOfData = 7000
004000B4        00004000     DD 00400000          ; ImageBase = 400000
004000B8        00100000     DD 00001000          ;  SectionAlignment = 1000
004000BC        00020000     DD 00000200          ;  FileAlignment = 200
004000C0        0400         DW 0004              ;  MajorOSVersion = 4
004000C2        0000         DW 0000              ;  MinorOSVersion = 0
004000C4        0100         DW 0001              ;  MajorImageVersion = 1
004000C6        0000         DW 0000              ;  MinorImageVersion = 0
004000C8        0400         DW 0004              ;  MajorSubsystemVersion = 4
004000CA        0000         DW 0000              ;  MinorSubsystemVersion = 0
004000CC        00000000     DD 00000000          ;  Reserved
004000D0        00B00000     DD 0000B000          ;  SizeOfImage = B000 (45056.)
004000D4        00040000     DD 00000400          ;  SizeOfHeaders = 400 (1024.)
004000D8        A94C0100     DD 00014CA9          ;  CheckSum = 14CA9
004000DC        0300         DW 0003              ;  Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
004000DE        0000         DW 0000              ;  DLLCharacteristics = 0
004000E0        00002000     DD 00200000          ;  SizeOfStackReserve = 200000 (2097152.)
004000E4        00100000     DD 00001000          ;  SizeOfStackCommit = 1000 (4096.)
004000E8        00001000     DD 00100000          ;  SizeOfHeapReserve = 100000 (1048576.)
004000EC        00100000     DD 00001000          ;  SizeOfHeapCommit = 1000 (4096.)
004000F0        00000000     DD 00000000          ;  LoaderFlags = 0
004000F4        10000000     DD 00000010          ;  NumberOfRvaAndSizes = 10 (16.)

004000F8        00000000     DD 00000000          ;  Export Table address = 0
004000FC        00000000     DD 00000000          ;  Export Table size = 0
00400100        00A00000     DD 0000A000          ;  Import Table address = A000
00400104        B8040000     DD 000004B8          ;  Import Table size = 4B8 (1208.)
00400108        00000000     DD 00000000          ;  Resource Table address = 0
0040010C        00000000     DD 00000000          ;  Resource Table size = 0
00400110        00000000     DD 00000000          ;  Exception Table address = 0
00400114        00000000     DD 00000000          ;  Exception Table size = 0
00400118        00000000     DD 00000000          ;  Certificate File pointer = 0
0040011C        00000000     DD 00000000          ;  Certificate Table size = 0
00400120        00000000     DD 00000000          ;  Relocation Table address = 0
00400124        00000000     DD 00000000          ;  Relocation Table size = 0
00400128        00000000     DD 00000000          ;  Debug Data address = 0
0040012C        00000000     DD 00000000          ;  Debug Data size = 0
00400130        00000000     DD 00000000          ;  Architecture Data address = 0
00400134        00000000     DD 00000000          ;  Architecture Data size = 0
00400138        00000000     DD 00000000          ;  Global Ptr address = 0
0040013C        00000000     DD 00000000          ;  Must be 0
00400140        00000000     DD 00000000          ;  TLS Table address = 0
00400144        00000000     DD 00000000          ;  TLS Table size = 0
00400148        00000000     DD 00000000          ;  Load Config Table address = 0
0040014C        00000000     DD 00000000          ;  Load Config Table size = 0
00400150        00000000     DD 00000000          ;  Bound Import Table address = 0
00400154        00000000     DD 00000000          ;  Bound Import Table size = 0
00400158        00000000     DD 00000000          ;  Import Address Table address = 0
0040015C        00000000     DD 00000000          ;  Import Address Table size = 0
00400160        00000000     DD 00000000          ;  Delay Import Descriptor address = 0
00400164        00000000     DD 00000000          ;  Delay Import Descriptor size = 0
00400168        00000000     DD 00000000          ;  COM+ Runtime Header address = 0
0040016C        00000000     DD 00000000          ;  Import Address Table size = 0
00400170        00000000     DD 00000000          ;  Reserved
00400174        00000000     DD 00000000          ;  Reserved


Diferencia entre Dirección RAW y Virtual
Para poder entender el significado de algunas de estas variables, hace falta una pequeña introduccion de como las secciones se colocan en memoria.
Cuando se coloca un programa en memoria, para ejecutarlo, no se coloca exactamente como está en el archivo, en el archivo se tiene todo en forma mas compactada.
en memoria se alinean las secciones, generalmente de a 0x1000 bytes (esto depende de la variable que se encuentra en este encabezado, SectionAlignment es la variable que dice el valor de la alineacion, solo que generalmente vale 0x1000), esto hace mas rapida la lectura, sin embargo las secciones en el archivo, no hace falta que se separen unas de otras, aunque tambien son alineadas (la variable FileAlignment tiene el valor al cual se alinea en el archivo)

ademas de esta diferencia, el codigo del programa no comienza de la direccion 0x0 como pasa en el archivo, asi que una direccion en el archivo es totalmente distinta a una direccion en memoria. en el encabezado, cuando se refiere a memoria Rva se refiere a la memoria en el archivo, y cuando habla de memoria virtual se refiere al programa ya colocado en memoria para la ejecución
eso es necesario diferenciar para entender el encabezado.
Si queremos encontrar, donde empieza una seccion en el archivo, debemos buscar su direccion RVA, si queremos ver donde empieza esa misma seccion en memoria, debemos buscar su direccion Virtual (mas adelante veremos como identificar eso, con unos ejemplos)



seguido de esto viene el encabezado de las secciones
esta es la estructura de cada encabezado de seccion
Código: C
  1. typedef struct _IMAGE_SECTION_HEADER {
  2.   BYTE  Name[IMAGE_SIZEOF_SHORT_NAME]; /* IMAGE_SIZEOF_SHORT_NAME = 8 bytes */
  3.   union {
  4.     DWORD PhysicalAddress;
  5.     DWORD VirtualSize;
  6.   } Misc;
  7.   DWORD VirtualAddress;
  8.   DWORD SizeOfRawData;
  9.   DWORD PointerToRawData;
  10.   DWORD PointerToRelocations;
  11.   DWORD PointerToLinenumbers;
  12.   WORD  NumberOfRelocations;
  13.   WORD  NumberOfLinenumbers;
  14.   DWORD Characteristics;
  15. } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
  16.  
por la union no hay que preocuparse, considerese solo una variable, que indica el tamaño (VirtualSize)

la cantidad de secciones tenemos ya, del encabezado NT, al ir leyendo todos los encabezados de las secciones, tendremos su posicion en el archivo (PointerToRawData), y en memoria (VirtualAddress), el tamaño es el mismo, la diferencia es que, estando en memoria, se llena de 0x0 hasta ajustarse a la alineación

en fin, despues de esto solo se tiene que ir leyendo las secciones.

.text es generalmente la seccion que contiene el codigo
.data y .rdata  contiene constantes, son secciones de solo lectura.
.bss es una seccion de lectura y escritura, se usa para las variables globales generalmente, las variables locales se colocan en el stack (eso lo veremos en otro tuto)
.idata es la tabla de importacion de funciones
.edata es la tabla de exportacion de funciones (es mas comun en los DLL, aunque un exe tambien lo puede tener)

en nuestro code de ejemplo aqui esta el encabezado de las secciones
Código: [Seleccionar]
"000000170  00 00 00 00 00 00 00 00-2E 74 65 78 74 00 00 00   |########.text###|"
"000000180  E8 59 00 00 00 10 00 00-00 5A 00 00 00 04 00 00   |#Y#######Z######|"
"000000190  00 00 00 00 00 00 00 00-00 00 00 00 60 00 50 60   |############`#P`|"
"0000001A0  2E 64 61 74 61 00 00 00-44 00 00 00 00 70 00 00   |.data###D####p##|"
"0000001B0  00 02 00 00 00 5E 00 00-00 00 00 00 00 00 00 00   |#####^##########|"
"0000001C0  00 00 00 00 40 00 30 C0-2E 72 64 61 74 61 00 00   |####@#0#.rdata##|"
"0000001D0  20 05 00 00 00 80 00 00-00 06 00 00 00 60 00 00   | ############`##|"
"0000001E0  00 00 00 00 00 00 00 00-00 00 00 00 40 00 60 40   |############@#`@|"
"0000001F0  2E 62 73 73 00 00 00 00-78 0A 00 00 00 90 00 00   |.bss####x.######|"
"000000200  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   |################|"
"000000210  00 00 00 00 80 00 40 C0-2E 69 64 61 74 61 00 00   |######@#.idata##|"
"000000220  B8 04 00 00 00 A0 00 00-00 06 00 00 00 66 00 00   |#############f##|"
"000000230  00 00 00 00 00 00 00 00-00 00 00 00 40 00 30 C0   |############@#0#|"

seguido a esto (hay un espacio lleno de 0x0 donde se pueden agregar mas encabezados de secciones), y luego ya están las secciones en sí (a lo que le llamé Stub NT)

Esto es todo, se supone que con esto se debe entender como es el formato del ejectuable de windows ( iba ser mas facil si supiese explicar mejor las cosas, pero eso me cuesta XD )



a continuacion veremos un ejemplo de como encontrar una zona especifica de un programa, en memoria y en el archivo.

Para empezar buscaremos el famoso EntryPoint, esta direccion indica el lugar donde se comienza a ejecutar el code, asi que, se supone que debe caer dentro de la seccion de code, ( en el caso del ejemplo solo tiene una seccion con code .text )

Dentro del OptionalHeader tenemos una variable llamada AddressOfEntryPoint, para encontrarlo en el archivo, vamos al inicio de la estructura del OptionalHeader, y contamos los tamaños de las variables que están antes WORD+2*BYTE+3*DWORD = 0x10 bytes (16 en decimal)
sabemos que el inicio de esa estructura esta en 0x000000098 (eso ya lo vimos mas arriba), asi que la direccion del EntryPint debe estar en el Offset 0x0000000A8, tambien sabemos que ocupa 1 DWORD = 4 bytes
Código: [Seleccionar]
"0000000A0  00 68 00 00 00 0C 00 00-30 11 00 00 00 10 00 00   |#h######0#######|"
"0000000B0  00 70 00 00 00 00 40 00-00 10 00 00 00 02 00 00   |#p####@#########|"
vemos que en el archivo es este el valor "30 11 00 00", como ya dijimos hay que voltearlo de 2 en 2 para tener el valor en Hexa  0x00001130, listo esta es la direccion del entrypoint, pero, tambien como vimos, todas estas direcciones estan referidas al ImageBase, de la misma forma que buscamos el valor del EntryPoint busquemos el ImageBase, esta variable está 3 Dword abajo del entrypoint, osea 8bytes, 0x0000000A8 + 0xC = 0x0000000B4  y ocupa 4 bytes, este es el valor "00 00 40 00" volteando = 0x00400000, bien, asi que el EntryPoint está realmente en 0x00400000 + 0x00001130 = 0x00401130 (eso lo podemos comprobar con algun otro programa como el olly)

que pasa si queremos ver ese entrypoint en el archivo, como lo vemos?
primero hay que saber a que seccion corresponde, en este caso, es evidente que corresponde a .text
veamos El VirtualAdress de .text, y el tamaño para acegurarnos de que está en esa seccion
este es el encabezado de esa seccion (comienza en 0x000000178)
Código: [Seleccionar]
"000000170  00 00 00 00 00 00 00 00-2E 74 65 78 74 00 00 00   |########.text###|"
"000000180  E8 59 00 00 00 10 00 00-00 5A 00 00 00 04 00 00   |#Y#######Z######|"
"000000190  00 00 00 00 00 00 00 00-00 00 00 00 60 00 50 60   |############`#P`|"
su VirtualAdress esta a 8 Bytes + 1 Dword del inicio del encabezado, osea a 0xC bytes "00 10 00 00" = 0x00001000
tambien nos hara falta conocer el PointerToRawData "00 04 00 00"= 0x00000400

El virtual adress sabemos que es relativo al ImageBase, pero solo nos interesa saber la distancia a la que está el EntryPoint del inicio de esa seccion. asi que ese Offset sera EntryPint - VirtualAdress = 0x130
Ese es el offset desde el inicio de la seccion, en el archivo esa seccion inicia en el PointerToRawData, asi que, el entrypoint en el archivo está en PointerToRawData + 0x130 = 0x530

Vemos el archivo
Código: [Seleccionar]
"000000530  55 89 E5 83 EC 18 C7 04-24 01 00 00 00 FF 15 50   |U#######$######P|"
"000000540  A1 40 00 E8 D8 FE FF FF-90 8D B4 26 00 00 00 00   |#@#########&####|"
"000000550  55 89 E5 53 83 EC 14 8B-45 08 8B 00 8B 00 3D 91   |U##S####E#####=#|"
"000000560  00 00 C0 77 3B 3D 8D 00-00 C0 72 4B BB 01 00 00   |###w;=####rK####|"
"000000570  00 C7 44 24 04 00 00 00-00 C7 04 24 08 00 00 00   |##D$#######$####|"
y lo comparamos con el dump del Entrypoint en memoria (visto con el Ollydbg)
Código: [Seleccionar]
00401130  55 89 E5 83 EC 18 C7 04 24 01 00 00 00 FF 15 50  U.å.ì.Ç.$......P
00401140  A1 40 00 E8 D8 FE FF FF 90 8D B4 26 00 00 00 00  .@.èØþ....&....
00401150  55 89 E5 53 83 EC 14 8B 45 08 8B 00 8B 00 3D 91  U.åS.ì..E.....=.
00401160  00 00 C0 77 3B 3D 8D 00 00 C0 72 4B BB 01 00 00  ..Àw;=..ÀrK....
00401170  00 C7 44 24 04 00 00 00 00 C7 04 24 08 00 00 00  .ÇD$.....Ç.$....
podemos ver que el code coincide exactamente, ya que estamos parados en el mismo lugar XD


Espero que haya servido de algo esto,
Saludos, y gracias por leerlo



Si bien este tuto lo escribí solo, no pude haberlo hecho sin aprender este tema, estos son los materiales que utilicé para aprender
44-ESTUDIO DE LOS ENCABEZADOS PE (parte 1) POR SICK TROEN
48-ESTUDIO DE LOS ENCABEZADOS PE (parte 2) POR SICK TROEN
291- Estudios sobre las cabeceras AE - 1 - Las Secciones
292- Estudios sobre las cabeceras AE-2

Un gran agadecimiento a la gente de CLS que hizo estos tutos

Conectado ANTRAX

  • *
  • Administrator
  • Mensajes: 5409
  • Actividad:
    18.33%
  • Reputación 33
  • ANTRAX
    • Ver Perfil
    • Underc0de
    • Email
  • Skype: underc0de.org
  • Twitter: @Underc0de
« Respuesta #1 en: Abril 04, 2012, 10:38:51 pm »
Mother of god! Increible aporte!!!
muchisimas gracias bro!!
Saludos!


Desconectado 5TU4RT

  • *
  • Underc0der
  • Mensajes: 111
  • Actividad:
    0%
  • Reputación 0
  • Si se puede imaginar... se puede programar.!
    • Ver Perfil
    • Underc0de
    • Email
« Respuesta #2 en: Abril 05, 2012, 06:24:39 am »
Muy buen aporte man!!! Sigue asi...
Gracias por compartir.

SALU2
Si se puede imaginar... se puede programar.!

Desconectado isic

  • *
  • Underc0der
  • Mensajes: 25
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
« Respuesta #3 en: Junio 06, 2012, 02:42:26 pm »
Buen aporte hermano.
Gracias por compartirlo
Salu2.

 

¿Te gustó el post? COMPARTILO!



Encontrar el número de serie de un programa [Tutorial con Práctica]

Iniciado por ANTRAX

Respuestas: 4
Vistas: 6586
Último mensaje Septiembre 11, 2017, 06:37:09 pm
por th3binary
Tutorial M003 - Beto JAS' Protection - Part II

Iniciado por Stuxnet

Respuestas: 1
Vistas: 2934
Último mensaje Agosto 10, 2013, 09:35:29 pm
por betojas
[1/10 Tutorial] Resolviendo crackme Linux Blah

Iniciado por yoyomismo

Respuestas: 0
Vistas: 2035
Último mensaje Abril 10, 2013, 11:13:30 am
por yoyomismo
Tutorial Programando Parche en Visual Basic

Iniciado por CronuX

Respuestas: 2
Vistas: 2829
Último mensaje Marzo 22, 2010, 02:01:09 pm
por Fakedo0r
Tutorial Desempacando Pain Crew Protector 1.1

Iniciado por CronuX

Respuestas: 1
Vistas: 1878
Último mensaje Mayo 06, 2010, 04:18:02 pm
por apuromafo