Descabezando archivos ejecutables portables ( I I )

Sección de recursos .rsrc


AUTOR: nuMIT_or
HERRAMIENTAS: o Hex Workshop u otro buen editor hexdecimal.                                  
                                   o KrackPE
                                   o Snippet Creator v1.5


Es un árbol

La sección .rsrc contiene información sobre los recursos para un módulo. Esta sección organiza sus datos como un árbol de datos.

Un árbol es una estructura de datos particular que constituye una especie particular de GRAFO.

Un grafo es un conjunto de puntos (llamados VÉRTICES), donde cada punto está asociado a otros dos, que constituyen los vértices del primero, a través de líneas (llamadas BORDES). El grafo se emplea para representar estructuras de datos, es decir, un grafo puede concebirse como un tipo
especial de variable muy empleado actualmente para la representación del conocimiento en Inteligencia Artificial .

Para que un grafo sea un árbol debe cumplir las siguientes propiedades:

1. Tener por lo menos un nodo principal llamado la raíz del árbol.

2. Tener organizados los vértices en una jerarquía de niveles. De esta manera los nodos que no son raíces se dividen en m > 0, conjuntos disjuntos que a su vez son árboles denominados subárboles de la raiz.

Tenemos entonces los siguientes conceptos:
- Hoja: nodo o vértice terminal, que no contiene ningon subárbol.
- Descendientes: nodos asociados a nodos no terminales.
- Ascendiente o padre: nodo asociado a nodos descendentes.
- Hermano: nodos asociados a un mismo padre.
- Bosque: colección de más de un árbol.
- Raiz: el onico nodo de un árbol que no posee padre.
- Camino: enlace entre dos nodos consecutivos.
- Rama: camino que termina en una hoja.
- Nivel: número asociado a cada nodo, cuyo valor depende de la longitud del camino desde la raiz al nodo específico.
- Altura o profundidad: número máximo de nodos de una rama; es decir, el nivel más alto de los nodos más uno.
- Peso: número de nodos terminales.


Ejemplo de un árbol:

                                       RAIZ
                                        /  \
                                     N    H
                                   /  \
                                N     N
                               /  \     /  \
                             N  H H   N
                           /  \             /  \
                         H   H         H  N
                                              /   \
                                           H     H

N: Nodo
H: Hoja

Si se trata de una estructura de un solo campo, entonces es un árbol con un vértice o nodo pero sin bordes: H = N

Un ejemplo de estructura en forma de árbol es la organización de la información en el disco duro en una jerarquía de raíz, diractorios, sudirectorios y archivos. Aquí los archivos serían las hojas del árbol, sus nodos terminales:

                                       (C): <- RATZ
                                        /  \
                                     N     N <-
Directorios
                                    /  \     /  \
                                  N  H H   N <-
Subdirectorios 1
                                /  \             /  \
                              H   H         H  N<-
Subdirectorios 2
                                                   /   \
                                                 H     H <-
Subdirectorios3 o Archivos


La raíz y las ramas del árbol

La raiz del directorio de recursos está ocupada por una estructura llamada IMAGE_RESOURCE_DIRECTORY (directorio de recursos). En lenguaje ensamblador:

IMAGE_RESOURCE_DIRECTORY struct  
Characteristics DWORD 
?
TimeDateStamp DWORD
?
MajorVersion WORD 
?
MinorVersion WORD 
?
NumberOfNamedEntries WORD 
?
NumberOfIdEntries WORD 
?
  ends  

IMAGE_RESOURCE_DIRECTORY TYPEDEF PTR IMAGE_RESOURCE_DIRECTORY

 

Los campos NumberOfNamedEntries y NumberOfIdEntries de esta estructura, cuando se trata de la raíz del árbol de recursos, dicen los tipos de recursos identificables por nombres o por IDs, respectivamente, que hay en el archivo. Me explico.

Para ubicar los datos brutos que conforman cualquier recurso, entre todos los campos de la estructura IMAGE_RESOURCE_DIRECTORY los más importantes son NumberOfNamedEntries y NumberOfIdEntries. Cada uno de estos campos informa el número de ramas en las que se despliega la raíz del árbol. Es decir, el directorio de recursos informa, a través de los campos NumberOfNamedEntries y NumberOfIdEntries, el número de subdirectorios de recursos. Cada uno de estos subdirectorios puede ser considerado luego como
la raiz de un subárbol de un "tipo de recurso". Después veremos estos subárboles.

La estructura IMAGE_RESOURCE_DIRECTORY, que forma la raiz del árbol de recursos, es seguida de un arreglo (array) constituido por entradas de dos campos cada una. Llamamos este arreglo: IMAGE_RESOURCE_DIRECTORY_ENTRY, es decir, directorio de entradas de recursos. Cada entrada de este último directorio tiene la siguiente estructura:

IMAGE_RESOURCE_DIRECTORY_ENTRY struct  
Name    DWORD 
?
OffsetToData DWORD
?
  ends 

PIMAGE_RESOURCE_DIRECTORY_ENTRY TYOPEDEF PTR IMAGE_RESOURCE_DIRECTORY_ENTRY

El campo NumberOfNameEntries de la estructura IMAGE_RESOURCE_DIRECTORY,
que como vimos es la raíz del árbol de recursos, contiene el número de entradas al comienzo del arreglo (array) del directorio de entradas (IMAGE_RESOURCE_DIRECTORY_ENTRY) que tienen las cadenas actuales asociadas con ellas.

El campo NumberOfIdEntries de IMAGE_RESOURCE_DIRECTORY indica el número
de entradas del arreglo del directorio de entradas con números enteros como identificadores (IDs).

El campo Name de la estructura IMAGE_RESOURCE_DIRECTORY_ENTRY,
correspondiente a una entrada del arreglo que sigue a la estructura IMAGE_RESOURCE_DIRECTORY, si se trata de una entrada correspondiente al nombre de un recurso, contiene la dirección virtual relativa a la base de la imagen donde se encuentra una estructura _IMAGE_RESOURCE_DIR_STRING_U con el nombre del recurso y que explicaremos luego. Si este valor comienza con un 8h, que significa que el bit 32 -el más alto- está puesto en 1, quiere decir que no se debe considerar el siguiente miembro de la estructura. Si se trata de una entrada correspondiente a un ID de recurso, entonces contiene un número entero que identifica un recurso. Si la entrada del directorio de recursos actual sigue a la raíz, el número entero del campo Name indica un tipo de recurso según las siguientes equivalencias, que indican los tipos básicos de recursos identificados en el archivo WINUSER.H:

WINUSER.H

#define MAKEINTRESOURCEA(i) (LPSTR) ( (DWORD) ( (WORD)(i) ) )
#define DIFFERENCE 11

/* * Predefined Resource Types */
#define RT_CURSOR MAKEINTRESOURCE(1)
#define RT_BITMAP MAKEINTRESOURCE(2)
#define RT_ICON MAKEINTRESOURCE(3)
#define RT_MENU MAKEINTRESOURCE(4)
#define RT_DIALOG MAKEINTRESOURCE(5)
#define RT_STRING MAKEINTRESOURCE(6)
#define RT_FONTDIR MAKEINTRESOURCE(7)
#define RT_FONT MAKEINTRESOURCE(8)
#define RT_ACCELERATOR MAKEINTRESOURCE(9)
#define RT_RCDATA MAKEINTRESOURCE(10)
#define RT_MESSAGETABLE MAKEINTRESOURCE (11)
#define RT_GROUP_CURSOR MAKEINTRESOURCE ((DWORD)RT_CURSOR + DIFFERENCE)
#define RT_GROUP_ICON MAKEINTRESOURCE ((DWORD)RT_ICON + DIFFERENCE)
#define RT_VERSION MAKEINTRESOURCE (16)
#define RT_DLGINCLUDE MAKEINTRESOURCE (17)
#define RT_PLUGPLAY MAKEINTRESOURCE (19)
#define RT_VXD MAKEINTRESOURCE (20)
#define RT_ANICURSOR MAKEINTRESOURCE (21)
#define RT_ANIICON MAKEINTRESOURCE (22)

En lenguaje ensamblador:

DIFFERENCE EQU 11
     
RT_CURSOR  EQU    1
RT_BITMAP  EQU    2
RT_ICON   EQU    3
RT_MENU  EQU    4
RT_DIALOG     EQU    5
RT_STRING   EQU    6
RT_FONTDIR  EQU    7
RT_FONT  EQU    8
RT_ACCELERATOR  EQU    9
RT_RCDATA  EQU    10
RT_MESSAGETABLE   EQU    11
RT_GROUP_CURSOR  EQU    RT_CURSOR + DIFFERENCE
RT_GROUP_ICON  EQU    RT_ICON + DIFFERENCE
RT_VERSION  EQU    16
RT_DLGINCLUDE  EQU    17
RT_PLUGPLAY  EQU    19
RT_VXD  EQU    20
RT_ANICURSOR  EQU    21
RT_ANIICON  EQU   22


Como ya se ha dicho, la raiz del árbol de recursos, el primer directorio, es seguida por un arreglo (array) que tiene una entrada para cada tipo de recurso que contiene el archivo, no importa cuántos tipos. Cada entrada del arreglo que sigue a la raíz del directorio de recursos sería una rama principal del árbol.

En el nivel más alto del árbol, los valores MAKEINTRESOURCE de la lista de arriba, son colocados en el campo Name de cada entrada del arreglo que constituye las entradas del directorio de recursos, identificando los diferentes recursos por tipo.

El campo OffsetToData de IMAGE_RESOURCE_DIRECTORY_ENTRY, contiene un
desplazamiento respecto al comienzo de la sección de recursos (que también es el inicio del directorio de recursos) donde se encuentra un subdirectorio de recursos o ya los datos brutos que forman un recurso.

-Cómo saber si el campo OffsetToData de una entrada del directorio de entradas apunta a un subdirectorio o a datos brutos? Esto lo señala el bit más alto (el bit 31) del campo de 32 bits que forma el campo OffsetToData. Por ejemplo, si el valor de este campo es 8000 0101h,
entonces el campo apunta al desplazamiento 0101h, respecto del comienzo de la sección de recursos y, como el bit más alto es 1 (8000h), apunta a un subdirectorio. Si el valor fuera 0000 0101h, el campo no apuntaría a un subdirectorio de recursos sino a una estructura
IMAGE_RESOURCE_DATA_ENTRY con información sobre la ubicación exacta de los datos brutos que forman un recurso del archivo PE.

Cada una de las entradas en el directorio de entradas raíz generalmente se puede considerar como el "vértice" que apunta a un nodo en el segundo nivel del árbol. Estos nodos son como subdirectorios del árbol de recursos. También estos nodos, que llamamos subdirectorios de recursos, tienen estructura IMAGE_RESOURCE_DIRECTORY con sus propias entradas cada
uno. En este segundo nivel, los directorios son usados para identificar el nombre o ID de cada recurso para un tipo de recurso dado. Si se tuvieran múltiples menúes definidos en la aplicación, habría una entrada para cada uno aquí en el segundo nivel del árbol.


LAS HOJAS

Si el campo OffsetToData apunta a un nodo terminal u hoja, entonces este nodo terminal tiene la siguiente estructura:

IMAGE_RESOURCE_DATA_ENTRY struct  
OffsetToData DWORD 
?
Size DWORD
?
CodePage DWORD 
?
Reserved DWORD 
?
  ends  

PIMAGE_RESOURCE_DATA_ENTRY TYPEDEF PTR IMAGE_RESOURCE_DATA_ENTRY

 

El campo OffsetToData indica la dirección virtual del desplazamiento desde la base de la imagen donde están los datos brutos que conforman el recurso y el campo Size el tamaño de los datos actuales del recurso.

Puesto que esta información es usada por funciones una vez que la aplicación ha sido cargada, tiene más sentido hacer que el campo OffsetToData sea una dirección virtual relativa, lo cual es el caso. Los demás desplazamientos, tales como punteros desde las entradas a otros directorios, son
desplazamientos relativos a la localización del nodo raiz.

Si el recurso es una cadena de caracteres, entonces esta cadena está en formato UNICODE y tiene la estructura:

WINNT.H

typedef struct   _IMAGE_RESOURCE_DIR_STRING_U
{
  USHORT   Length;
  WCHAR    NameString[ 1 ];
}
IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U ;


En lenguaje ensamblador:

IMAGE_RESOURCE_DIR_STRING_U struct  
Name    WORD 
?
OffsetToData WORD
?
  ends 

Los recursos tipo cadenas son almacenados después del Directorio de Entradas del Recurso e inmediatamente antes del primer objeto de datos del recurso. La longitud necesita ser alineada en el límite de un dato WORD.

Debajo de este nivel de la jerarquía de subdirectorios, el árbol de recursos ya no tiene más ramas.


Resumiendo

Navegar por la jerarquía del directorio de recursos es como navegar por un disco duro. Hay un directorio maestro (el directorio raiz), el cual tiene subdirectorios. Los subdirectorios tiene sus propios subdirectorios que poueden apuntar a los datos de recursos brutos para cosas como plantillas de diálogo.

En el formato PE, el directorio raiz de la jerarquía del directorio de recursos y de todos sus directorios son estructuras del tipo IMAGE_RESOURCE_DIRECTORY. El campo NumberOfNamedEntries y el campo NumberOfIdEntries de la estructura IMAGE_RESOURCE_DIRECTORY que forma la raíz del árbol de recursos indican el número de entradas que conforman un arreglo que sigue inmediatamente después de la raíz. Cada entrada de este arreglo tiene la estructura IMAGE_RESOURCE_DIRECTORY_ENTRY.

Cada entrada del arreglo raíz del árbol de recursos tiene dos campos. El campo Name de la estructura IMAGE_RESOURCE_DIRECTORY_ENTRY, indica un tipo de recurso segon las equivalencias, que indican los tipos básicos de recursos identificados en el archivo WINUSER.H.

El campo OffsetToData de IMAGE_RESOURCE_DIRECTORY_ENTRY, contiene un
desplazamiento respecto al comienzo de la sección de recursos (que también es el inicio del directorio de recursos) donde se encuentra un subdirectorio de recursos o ya los datos brutos que forman un recurso. Para saber si se trata de un subdirectorio más o de los datos brutos del recurso, hay que veriricar si este valor tiene la forma 8xxx xxxx h; un 8 en el comienzo de este valor indica que el bit 31 de la expresión es 1 y que el campo apunta a un subdirectorio; si no es 8, entonces
apunta ya a los datos brutos del recurso.

Los subdiectorios o nodos del árbol de recurso tienen la misma estructura que la raíz, es decir, IMAGE_RESOURCE_DIRECTORY y también son seguidos de un arreglo del tipo IMAGE_RESOURCE_DIRECTORY_ENTRY. La diferencia, es que los subdirectorios ya no tratan de tipos de recursos sino del número de recursos de un mismo tipo en el archivo
PE. Entonces, los campos del arreglo IMAGE_RESOURCE_DIRECTORY_ENTRY
que sigue a cada subdirectorio de recursos, identifican los recursos particulares de un mismo tipo y señalan dónde se ubican.

Cuando el valor del campo OffsetToData de una entrada con la estructura IMAGE_RESOURCE_DIRECTORY_ENTRY no comienza con un 8, entonces apunta
a un nodo terminal u hoja del árbol de recursos. Si este nodo terminal no es un recurso tipo cadena, entonces tiene estructura IMAGE_RESOURCE_DATA_ENTRY. Los dos primeros campos de esta estructura, OffsetToData y Size, señalan respectivamente la ubicación (desde la base de la imagen) y tamaño (en bytes) del recurso.

Si el campo OffsetToData de una entrada IMAGE_RESOURCE_DIRECTORY_ENTRY
apunta a un nodo terminal de tipo cadena, entonces este nodo terminal tiene la forma IMAGE_RESOURCE_DIR_STRING_U.


============================================================
             Identificación y localización de los recursos en NOTEPAD.EXE
============================================================

A continuación muestro el volcado que hice manualmente del árbol de recursos del archivo notepad.exe que viene como ejemplo junto con este trabajo.

He colocado los enlaces (links) que corresponden a cada puntero en los campos pertinentes para que pueda seguirse con comodidad.

Recomiendo que sean verificados los campos abriendo una vista de notepad.exe con un editor hexadecimal.

 

Ubicación de la sección de recursos:

Antes que nada hay que averiguar dónde está la sección de recursos. Una manera intuitiva de hacerlo es abrir el target con un editor hexadecimal. Luego emprender una bosqueda de la cadena ".rsrc". Con HexWorkshop el comando es "Edit/Fin", se coloca ".rsrc" en la ventana "Find" y se presiona el botón "Find Next". Donde el editor se detenga, ahí inicia la entrada, en el directorio
de secciones del archivo PE, con información correspondiente a la sección de recursos.

El editor se detendrá en el desplazamiento 0218h. Es el comienzo de la tabla de sección que corresponde al árbol de recursos. Esta entrada tiene la estructura.

_IMAGE_SECTION_HEADER struct    
Name DB 8 DUP  
( ? ) ;
Cadena con el nombre de la sección
VirtualSize DD
?
VirtualAddress DD 
?
SizeOfRawData DD 
?
PointerToRawData DD 
?
NumberOfIdEntries DD 
?
PointerToRelocations DD
?
 
PointerToLinenumbers DD
?
 
NumberOfRelocations DW
?
 
NumberOfLinenumbers DW
?
 
Characteristics DD
?
 
  ends    

El campo PointerToRawData indica el desplazamiento del archivo donde comienza la sección de recursos. Ese campo está en 0218h (inicio de la estructura) + 14h bytes = 022Ch. El contenido de este campo es 0000 5000 h (invertido).

En notepad.exe, la entrada de la sección de recursos, en el directorio de datos del archivo, empieza en 0218h. La información correspondiente al campo PointerToRawData de esta entrada estariá en 0218h + 14h = 022Ch, cuyo valor es 0000 5000 h. Entonces, la sección de recursos .rsrc de notepad.exe está en el desplazamiento 0000 5000 h.

A partir de aquí, considerando que esta sección comienza con el directorio raiz del árbol de recursos; que este directorio tiene la estructura IMAGE_RESOURCE_DIRECTORY; y que este directorio es seguido por un arreglo de entradas con la estructura
IMAGE_RESOURCE_DIRECTORY_ENTRY; considerando todo esto podemos ya volcar manualmente el árbol de recursos del archivo. Es engorroso hacerlo y puede parecer inecesario, pero si lo hacemos al menos una vez, podemos estar seguro que podremos hacer un programa que lo haga por nosotros o que podemos ubicar ciertos datos que nos interesen y alterarlos manualmente cuándo y cómo se nos antoje.

A continuación el volcado.

desplazamiento 0000 5000h

Desplazamiento     Datos

5000
00000000
00000000
00000000   00070000   (7 tipos de datos)
5010
 00000003
80000048
(Tipo 3 (icono), en 5000+48h)
5018
00000004
80000078
(Tipo 4 (menú), en 5000+78h)
5020
00000005
80000090
(Tipo 5 (diálogo), en 5000+90h)
5028
00000006
800000B0
(Tipo 6 (cadena), en 5000+0B0h)
5030
00000009
800000E0
(Tipo 9 (acelerador), en 5000+0E0h)
5038
0000000E
80000100
(Tipo 0Eh (icono de grupo), en 5000+100h)
5040
00000010
80000120
(Tipo 10h (info de versión), en 5000+120h)

 

5048
00000000
00000000
00000000 00040000 (4 iconos)
5058 
  00000001
80000138
(Nombre ID 1, en 5000+0138h)
5060 
00000002
80000150
(Nombre ID 2, en 5000+0150h)
5068
00000003
80000168
 (Nombre ID 3, en 5000+0168h))
5070
00000004
80000180
(Nombre ID 4, en 5000+0180h)
       
5078
00000000
00000000
00000000 00010000 (1 menú)
5088
00000001
80000198
(Nombre ID 1, en 5000+0198h)
       
5090
 00000000
 00000000
00000000 00020000 (2 diálogos)
50A0 
00000004
800001B0
(Nombre ID 0Ch, en 5000+01B0h)
50A8
00000004
800001C8
(Nombre ID 0Eh, en 5000+01C8h)
       
50B0
00000000
00000000
00000000 00040000 (4 cadenas)
50C0
00000001
800001E0
(Nombre ID 1, en 5000+01E0h)
50C8
00000002
800001F8
(Nombre ID 2, en 5000+01F8h)
50D0 
00000003
80000201
(Nombre ID 3, en 5000+0210h)
50D8
00000004
80000228
(Nombre ID 4, en 5000+0228h)
       
50E0  00000000  00000000 00000000 00040000 (2 tablas de acelers.)
50F0  00000001 80000240 (Nombre ID 1, en 5000+0240h)
50F8 00000002 80000248 (Nombre ID 2, en 5000+0248h)
       
5100 00000000 00000000 00000000 00040000 (2 entradas)
5110 00000001 80000270 (Nombre ID 1, en 5000+0270h)
5118 00000002 80000280 (Nombre ID 2, en 5000+0280h)
       
5120 00000000 00000000 00000000 00010000 (1 entrada)
5130 00000001 800002A0 (Nombre ID 1, en 5000+02A0h)
       
5138 000000000 000000000 00000000 00010000 (1 entrada)
5148 00000040A 000002B8

(Nombre ID 040Ah, en 5000+02B8h)

       
5150 000000000 000000000 00000000 00010000 (1 entrada)
5160 00000040A 000002C8 (Nombre ID 040Ah, en 5000+02C8h)
       
5168 000000000 000000000 00000000 00010000 (1 entrada)
5178 00000040A 000002D8 (Nombre ID 040Ah, en 5000+02D8h)
       
5180  000000000 000000000 00000000 00010000 (1 entrada)
5190 00000040A 000002E8 (Nombre ID 040Ah, en 5000+02E8h)
       
5198 000000000 000000000 00000000 00010000 (1 entrada)
51A8 00000040A 000002F8 (Nombre ID 040Ah, en 5000+02F8h)
       
51B0 000000000 000000000 00000000 00010000 (1 entrada)
51C0 00000040A 00000308 (Nombre ID 040Ah, en 5000+0308h)
       
51C8 000000000 000000000 00000000 00010000 (1 entrada)
51D8 00000040A 00000318 (Nombre ID 040Ah, en 5000+0318h)
       
51E0 00000000 00000000 00000000 00010000 (1 entrada)
51F0 00000040A 00000328 (Nombre ID 040Ah, en 5000+0328h)
       
51F8  00000000 00000000 00000000 00010000 (1 entrada)
5208  00000040A 00000338 (Nombre ID 040Ah, en 5000+0338h)
       
5210 000000000 000000000 00000000 00010000 (1 entrada)
5220 00000040A 00000348 (Nombre ID 040Ah, en 5000+0348h)
       
5228 000000000 000000000 00000000 00010000 (1 entrada)
5238 00000040A 00000358 (Nombre ID 040Ah, en 5000+0358h)
       
5240 000000000 000000000 00000000 00010000 (1 entrada)
5248 00000040A 00000368 (Nombre ID 040Ah, en 5000+0368h)
       
5258 000000000 000000000 00000000 00010000 (1 entrada)
5268 00000040A 00000378 (Nombre ID 040Ah, en 5000+0378h)
       
5270 000000000 000000000 00000000 00010000 (1 entrada)
5280 00000040A 00000388 (Nombre ID 040Ah, en 5000+0388h)
       
5288 000000000 000000000 00000000 00010000 (1 entrada)
5298  00000040A 00000398 (Nombre ID 040Ah, en 5000+0398h)
       
52A0 000000000 000000000 00000000 00010000 (1 entrada)
52B0 00000040A 000003A8 (Nombre ID 040Ah, en 5000+03A8h)

* Aquí indica el desplazamiento desde ImageBase donde está el icono 1
52B8           000083B8 Desp (VO)
83B8h, Tipo 3, NombreID 1, LengID 0
                    00000128 Tamaño: 0128h bytes
                    000004E4
                    00000000

* Aquí indica el desplazamiento desde ImageBase donde está el 2
52C8           000084E0 Desp (VO)
84E0h, Tipo 3, NombreID 2, LengID 0
                    000002E8 Tamaño: 02E8h bytes
                    000004E4
                    00000000

* Aquí indica el desplazamiento desde ImageBase donde está el icono 3
52D8         000087C8 Desp (VO) 87C8h, Tipo 3, NombreID 3, LengID 0
                    000002E8 Tamaño: 02E8h bytes
                    000004E4
                    00000000

* Aquí indica el desplazamiento desde ImageBase donde está el icono 4
52E8           00008AB0 Desp (VO) 8AB0h, Tipo 3, NombreID 4, LengID 0
                    00000128 Tamaño: 0128h bytes
                    000004E4
                    00000000

* Aquí indica el desplazamiento desde ImageBase donde está el menú
52F8           00008BD8 Desp (VO) 8BD8h, Tipo 4, NombreID 1, LengID 0
                    000002F0 Tamaño: 02F0h bytes
                    000004E4
                    00000000

* Aquí indica el desplazamiento desde ImageBase donde está el diálogo 0Ch
5308           00008EC8 Desp (VO) 8EC8h, Tipo 5, NombreID 0Ch, LengID 0
                    000000B8 Tamaño: 0B8h bytes
                    000004E4
                    00000000

* Aquí indica el desplazamiento desde ImageBase donde está el diálogo 0Eh
5318           00008F80 Desp (VO) 8F80h, Tipo 5, NombreID 0Eh, LengI
                    000002E8 Tamaño: 02E8h bytes
                    000004E4
                    00000000

* Aquí indica el desplazamiento desde ImageBase donde están cadenas
5328           00009418 Desp (VO) 9418h, Tipo 6, NombreID 0Eh, LengI
                    00000258 Tamaño:
                    000004E4
                    00000000

5338            00009670
                    00000BBC Tamaño:
                    000004E4
                    00000000

5348            0000A22C
                    000002EC Tamaño:
                    000004E4
                    00000000

5358            0000A518
                    00000628 Tamaño:
                    000004E4
                    00000000

* Desplazamiento de la tabla de aceleradores 1
5368            0000AB40 Desp (VO) AB40h, Tipo 9, NombreID 01, LengID 0
                    00000068 Tamaño:
                    000004E4
                    00000000

* Desplazamiento de la tabla de aceleradores 2
5378            0000AB88 Desp (VO) AB40h, Tipo 9, NombreID 01, LengID 0
                     00000022 Tamaño:
                    000004E4
                    00000000

* Desplazamiento del icono de grupo
5388            0000ABF0 Desp (VO) ABF0h, Tipo 0Eh, NombreID 01, LengID 0
                    00000022 Tamaño:
                    000004E4
                    00000000

* Aquí indica el desplazamiento desde ImageBase donde está el
5398            0000AC14 Desp (VO) AC14h, Tipo 0Eh, NombreID 01, LengID 0
                    000002E8 Tamaño:
                    000004E4
                    00000000

* Desplazamiento desde ImageBase donde está la información de la versión
53A8            000084E0 Desp (VO) 84E0h, Tipo 10h, NombreID 01, LengID 0
                    000002E8 Tamaño:
                    000004E4
                    00000000

 

Desplazamiento desde ImageBase donde está la información de la versión
53B8            000084E0 Desp (VO) 84E0h, Tipo 10h, NombreID 01, LengID 0
                    000002E8 Tamaño:
                    000004E4
                    00000000

 

                    Campos del encabezado de un archivo .ico
ico 1:
53B8            00000028
                    00000010
                    00000020
                    0001
                    0004
                    00000000
                    00000080
                    00000000
                    00000000
                    00000000
                    00000000

ico 2:
54E0            00000028
                    00000020
                    00000040
                    0001
                    0004
                    00000000
                    00000020
                    00000000
                    00000000
                    00000000
                    00000000

menu:
5BD8            00000000
                    0010 Nº de caracteres: 10h = 16d

diálogo 0Ch:
5EC8            90C800C0
                    00000000

diálogo 0Eh:
5F80            90C820C4
                    00000000
cadena 1:
6418            0000005A Nº de caracteres:

acelerador 1:
7B40            0009
                    002D
                    00000301

acelerador 2:
7B88            0009
                    002D
                    00000301

##########################################################################

Volcar recursos.

Con estos datos podríamos volcar cualquier recurso que nos interese de notepad.exe. Una manera de hacerlo, es desplegar notepad.exe en SoftIce y, si se cuenta con IceDump, volcar los datos que conforman un recurso al disco duro.

Para facilitar esto, he escrito una pequeña utilidad que permite extraer de archivos PE datos con un tamaño indicado, desde un desplazamiento específico. Lo he llamado KrackPE, la primera
parte de un ambiente para parchar archivos PE y volcar su contenido.

Bien. Abramos notepad.exe con KrackPE (File/Open File o Select a file). Ahora indicamos en el campo Offset = 53B8, que es el desplazamiento donde hemos localizado el icono 1 de notepad.
En el campo Size colocaremos 0128, el tamaño del ícono. Luego pulsamos Save as, elegimos un nombre como np1.ico y guardamos en disco duro.

Hay programas que hacen esto de manera más sencilla. Pero KrackPE es una herramienta que nos permite extraer otros datos binarios que especifiquemos, siempre que tengamos su desplazamiento y
su tamaño. Además, como muchas de las direcciones que obtenemos no son desplazamientos sino direcciones virtuales, he agregado un comando que permite convertir un tipo de valor en otro, y viceversa.

Aún no estoy conforme con esta herramienta. Pronto agregaré funciones importantes. Entre ellas, algunas que he implementado en otros programas.

Podemos seguir con el procedimiento empleado y seguir volcando los demás iconos, ya que notepad tiene cuatro iconos, como nos informa el desplazamiento 5048h.

 


Agregar un item al menú

Vamos a volcar ahora el menú. Siguiendo los enlaces desde el desplazamiento 5048h (el subdirectorio con información sobre los menúes en notepad) llegamos hasta el desplazamiento 5BD8h, donde está la data que forma el menú. El desplazamiento 52F8h está ocupado por una estructura IMAGE_RESOURCE_DATA_ENTRY que apunta al nodo terminal, la hoja ocupada por los datos del menú, en la dirección virtual relativa (RVA) 8BD8h. El desplazamiento 52FCh corresponde al campo Size de esta estructura, indica el espacio ocupado por la data que corresponde al menú: 02F0h = 652 bytes.

Volquemos el menú a disco duro. Para ello necesitamos el desplazamiento donde se encuentra. Sabemos su dirección virtual relativa (RVA = 8BD8h). Para hallar el desplazamiento con KrackPE necesitamos la dirección virtual absoluta: ImageBase + RVA = 400000 + 8BD8h = 408BD8h. Ahora, en KrackPE, escribimos en el campo Virtual Address de la izquierda 408BD8A, y pulsamos el botón GetOffset. Ya tenemos el desplazamiento en el campo GetOffset de la izquierda; ahí se encuentra el menú de notepad.exe.Tenemos ahora Offset = 5BD8, y ponemos Snippet Size = 02F0. Volcamos el recorte a disco duro pulsando el botón Save as. Llamémosle al volcado "mnp.bin". Abrámosle con Hexworkshop. Lo que vemos en la ventana de Hexworkshop es una vista de la data que forma el menú de notepad.exe.

Vamos a agregar un item al final del menú. Agregaremos un item que diga "Explorar". Para ello debemos aumentar el tamaño de mnp.bin. En la vista que nos muestra Hexworkshop de mnp.bin, ponemos el cursor al final del archivo y ejecutamos el comando Edit / Insert; colocamos en el campo "Number of bytes" 48, así que cuando pulsemos OK mnp.bin aumentará 48 bytes. Yo pongo 48, pero pueden ser menos o más.

En el desplazamiento 027C, que contiene 9000h (fin del menú), cambiémosle por 1000h (MENUITEM).

En el desplazamiento 02ECh, escribamos 8000h. Inmediatamente después, en 02EEh, escribimos el identificador de item: CC. Luego en el desplazamiento 02F0h escribiremos en formato unicode  

E & x p l o r a r

El formato unicode separa cada caracter por un cero:

45 00 26 00 78 00 70 00 6C 00 6F 00 72 00 61 00 72 00 00 00

  E       &        x        p        l         o         r        a         r                 

Los últimos cuatro ceros son importantes, porque señalan el final de la cadena.

Ahora el tamaño del menú es 0304h. Así que eliminemos los bytes que nos sobran: marcamos desde 0304h (= 768 bytes) hasta 0338h y pulsamos la tecla delete. Guardamos (File \ Save), seleccionamos todo (Edit / Select All) y copiamos (CTRL+C).

Ahora buscamos un espacio vacío en notepad para el recorte. Para esto basta sólo pulsar el botón Get greatest space de KrackPe. Encontramos que en el desplazamiento 4016h hay un espacio de 01EAh = 490 bytes. la VA correspondiente a este desplazamiento es : 406216h. Lamentablemente, este espacio es menor que el requerido, ya que el menú es de 768 bytes.

Lo que podemos hacer entonces es colocar el nuevo menú al final del archivo. Revisemos el final de notepad.exe con Hexworkshop. Hay también un espacio amplio, aunque todavía más pequeño que nuestro menú. Así que agreguemos 500 bytes: Edit / Insert, en Hexworkshop; colocamos en el campo "Number of bytes" 500; marcamos toda el area libre desde el desplazamiento 8810h, pegamos el menú (CTRL+V) y salvamos con respaldo.

Hemos pegado nuestro menú en 8810h. KrackPE nos dice que la dirección virtual que corresponde a este desplazameinto es 0040B810h.

Ahora, en el desplazamiento 52F8h, que contiene la dirección virtual (VA) donde se encuentra el menú escribamos 10B8, la dirección virtual (menos la base de la imagen) invertida donde colocamos el nuevo menú. En el desplazamiento 52FCh, que corresponde al campo Size de esta estructura IMAGE_RESOURCE_DATA_ENTRY del menú, coloquemos el tamaño del nuevo menú, 0403 (invertido es 0304h).

El incremento de tamaño del archivo nos obliga a corregir el encabezado PE, ya que el cragador del sistema revisa la información en el encabezado para reservar espacio antes de cargar el programa en memoria. Esto lo podemos hacer con Snippet Creator. Corremos Snippet Creator y abrimos nuestro notepad.exe (Action / New Target - Browse - elegimos notepad.exe - Save). Luego revisamos la información sobre el encabezado PE y sobre las secciones. Ejecutamos PE Info\View PE Header. Anotamos:

Size of Image: 00C000h = 49152 bytes
Section Alignment 001000h = 4096 bytes
File Alignment: 200h = 512 bytes

Cerramos esta ventana (Cancel) y ejecutamos (PE Info \ View Section Info). Hemos agregado nuestro nuevo menú en la última sección, que en notepad, como podemos ver, es .reloc. Anotamos los valores de esta sección:

Virtual Size: 091Eh
Raw Size: 0A00h

Hemos aumentado la última sección en 768 - 490 = 278 = 116h bytes. Hay bastante espacio de memoria reservada para notepad.exe (Size of Image: 00C000h = 49152 bytes como para tener que incrementar el valor de Size of Image. Así que sólo tenemos que incrementar el espacio ocupado por la data (Raw Size) y el tamaño virtual (virtual size) de .reloc (Virtual Size). El tamaño de la data hay que incrementarlo en múltiplos del valor del alineamiento de archivo (File Alignment). El tamño virtual hay que agregar el tamaño de la data que injertamos:

Virtual Size: 091Eh + 0116h = 0A34h
Raw Size: 0A00h + 0200h (File Alignment) = 0C00h

Para hacer efectivos estos cambios, cerramos nuestra vista de notepad. exe en Hexworkshop, abrimos de nuevo notepad.exe con Snippet Creator y ejecutamos de nuevo PE Info \ View Section Info. Hacemos click con el botón derecho del ratón sobre ".reloc". En el menú emergente que se despliega elegiremos "Edit Section". Escribimos en Virtual Size = 0A34; Save - Cancel.

Ahora, para evitar problemas, corregimos la suma de chequeo. Esto es sencillo hacerlo con KrackPE. Abrimos nuestro notepad.exe con KrackPE. Veremos que de inmediato se activa el botón "Correct Sum". Lo pulsamos y automáticamente se corregirá este valor.

Corramos notepad. ¿Cómo se ve el menú?

Por supuesto este nuevo item del menú no hace nada. Todavía tendríamos que injertar código en sitios estratégicos para ello.

Injertar código

Para injertar código en un PE tenemos que encontrar primero un espacio para él. Aprovechamos que tenemos notepad.exe cargado en KrackPE y pulsamos el botón Get greatest space. KrackPE nos dice que hay un espacio disponible de 01EAh= 490 bytes en la sección .data, en el desplazamiento 4016h. Conviene redondear el desplazamiento hasta 4020h. A la dirección virtual correspondiente lo llamamos VA1 = 00406220h, la dirección virtual de nuestro recorte.

Otra dirección que tenemos que determinar, quizás la más importante, es el punto propicio para entregar el control a nuestro recorte. Tenemos que encontrar la rutina que es activada cuando el usuario elige algunos de los items del menú. Para esto tenemos que utilizar un depurador como SoftIce o un desensamblador de programas de Windows como W32dasm de URSoftware CO.

Si consigues este programa, abres con él nuestro notepad.exe e inicias una búsqueda (Search\FindText) de las cadenas de los items del menú hasta dar con un fragmento de código interesante. Cuando se busca, por ejemplo, la cadena "cortar" llegamos al siguiente pasaje:

* Referenced by a (U)nconditional or (C)onditional Jump at Address: |
:0040119E(C)
|

* Possible Ref to Menu: MenuID_0001, Item: "Cortar CTRL+X"
                                                                                  |
:004011BA   3D00030000                   cmp eax, 00000300
:004011BF    7C21                               jl 004011E2

¿Qué significa este código? ¿qué nos dice el desensamblador? Nos dice que 00000300 es una constante que equivale al identificador del item de menú "Cortar CTRL+X". El código en cuestión revisa si en eax (que seguramente tendrá el valor wParam del mensaje con el item del menú seleccionado por el usuario) hay un valor menor a 0304h, si ese es el caso, salta a la dirección virtual 4011E2h.

Si revisamos un poco más arriba nos toparemos con el siguiente código:

:00401195    0FB7750C                      movzx esi, word ptr [ebp+0C]
:00401199    83FE20                           cmp esi, 00000020
:0040119C    8BC6                             mov eax, esi
:0040119E    7F1A                              jg 004011BA
:004011A0    0F84C1030000              je 00401567

Es el código donde se mueve a eax el valor que será revisado en 004011BF. Vemos que si wParam es mayor que 20h, entonces llegaremos a 4011BA. Esto quiere decir que el identificador de nuestro item de menú debe ser mayor que 20h. Habíamos elegido al azar 0CCh, que debería funcionar, porque es mayor a 20h.

La dirección virtual VA0 = 004011BA es la propicia para entregar el control a nuestro recorte en VA1= 00406220h.

En VA0 = 004011BAh que equivale al desplazamiento Offset0 = 05BAh, debemos colocar la siguiente instrucción:

4011BA:   6820624000     push   00406220h
4011BF:    C3                     ret
4011C0:    90                     nop

Esta instrucción entregará el control a nuestro recorte en VA1 = 406220h que equivale al desplazamiento Offset1 = 4020h.

En código octal, la nueva instrucción en el desplazamiento 05BAh tiene la forma 68 20 62 40 00 C3, donde 68 = push, 20624000 es el valor que metemos en la pila (al revés = 00406220) y C3 = ret, es decir, saltar a la dirección a la que apunta el puntero de pila (ESP).

La labor de escribir la nueva instrucción podemos dejársela a Snippet Creator.Lo corremos y abrimos un proyecto nuevo (Edit \ New Project); seleccionamos nuestro notepad.exe y ponemos el nombre de nuestro nuevo projecto: NP.

Ahora elegimos las siguientes opciones generales (Action \ Options). Las siguientes opciones suponen que usamos TASM v5.0 y lo tenemos instalado, con las variables de entorno correctas, en el directorio C:\TASM. También suponen que tenemos Snippet Creator en el dierctorio C:\TASM\TOOLS\ICZ.

Assembler: TASM32 /ml %1
Linker: TLINK32 /Tpe /c /aa /V4.0 %1,,,C:\TASM\LIB\import32.lib
Project Directory: C:\TASM\TOOLS\ICZ\

Luego establecemos las opciones del proyecto (Action \ Project Options).

SnippetVa: 406220 (VA1)
Patch Into Existing Section
Redirect Control From Code Section
Virtual Address:
4011BA (VA0)
Don't Return Control To Program

Al elegir Redirect Control From Code Section y colocar en la venta correspondiente Virtual Address = 4011BA (VA0), nos liberamos del trabajo de escribir maualmente el salto que entrega el control a nuestro recorte.

Luego escribimos en la ventana de edición el código:

; ---------------------------------- np.as ---------------------------------------------

extrn ShellExecuteA

jmp init:

str db 'explorer.exe',0

init:
pushad
cmp ax,00CCh       ; ¿Se ha seleccionado el item "explorar"?
jne a1                     ; Si no se ha seleccionado continuar con las rutinas originales
lea esi,str
call ShellExecuteA,0,0,esi,0,5    ; Ejecutar explorer.exe
a0:
popad
push 4011C1h       ; Devolver el control a 4011C1h
ret

a1:
cmp ax,0300h        ; ¿Se ha elegido el item "Cortar"?
jge a0                     ; Si se ha elegido "Cortar" u otro con ID mayor, ir a 4011C1h
popad
push 4011E2h       ; De lo contrario, ir a 4011E2h
ret

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

Ahora ensamblamos (pulsamos el botón Assemble o ejecutamos en el menú el comando Action \ Assemble) y parchamos a notepad.exe: Action \ Patch Target.

Verifiquemos ejecutando notepad.exe seleccionando el nuevo item "Explorar" o tecleando ALT+X..

Podemos cambiar nuestro recorte y el nombre de nuestro item de menú para que al seleccionar este item se active otro programa que podríamos poner en el mismo directorio que notepad.exe. También podríamos hacer muchas cosas más. Queda a la imaginación y destreza del programador lo que podría hacerse.

 

Palabras finales

Quisiera haber terminado este escrito con más aplicaciones prácticas de esta investigación, pero si así lo hiciera, la presentación del trabajo se atrasaría demasiado. Así que prefiero dejar esto para subsecuentes trabajos y proyectos en los que trabajo actualmente. Para estar informado sobre la culminación de alguno de estos proyectos, pueden visitar mensualmente la página "nuMIT_or's Programming Page" en http://members.xoom.com/numit_or/asm.html. Intento en lo posible hacer actualizaciones mensuales. Por lo pronto me concentraré en un buen tutorial sobre la programación en lenguaje ensamblador para Win32. También tengo unas ideas interesantes en el área de la Inteligencia Artificial que me encantaría compartir.

Y bien, con esto culmino la serie de tres trabajos que había prometido realizar sobre los archivos PE. Cómo se puede observar, ha habido algunas secciones típicas que no he analizado. La razón de ello es que todavía no me he visto en la necesidad de ello. De todas maneras, si en cualquier momento me encuentro en esta necesidad, realizaré la investigación conveniente y lo haré público.

Espero que este trabajo sea de tanta utilidad para los lectores como ha sido para mí escribirlo.


Comentarios, observaciones: nuMIT_or@iname.com
Marzo del 2000.