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 |
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 * Aquí indica el desplazamiento desde ImageBase donde está el icono 3 * Aquí indica el desplazamiento desde ImageBase donde está el icono 4 * Aquí indica el desplazamiento desde ImageBase donde está el menú * Aquí indica el desplazamiento desde ImageBase donde está el diálogo 0Ch * Aquí indica el desplazamiento desde ImageBase donde está el diálogo 0Eh * Aquí indica el desplazamiento desde ImageBase donde están cadenas 5338 00009670 5348 0000A22C 5358 0000A518 * Desplazamiento de la tabla de aceleradores 1 * Desplazamiento de la tabla de aceleradores 2 * Desplazamiento del icono de grupo * Aquí indica el desplazamiento desde ImageBase donde está el * Desplazamiento desde ImageBase donde está la información de la versión
52C8 000084E0
Desp (VO) 84E0h,
Tipo 3, NombreID 2, LengID 0
000002E8
Tamaño: 02E8h bytes
000004E4
00000000
52D8 000087C8
Desp (VO) 87C8h, Tipo 3, NombreID 3, LengID 0
000002E8
Tamaño: 02E8h bytes
000004E4
00000000
52E8 00008AB0
Desp (VO) 8AB0h, Tipo 3, NombreID 4, LengID 0
00000128
Tamaño: 0128h bytes
000004E4
00000000
52F8 00008BD8
Desp (VO) 8BD8h, Tipo 4, NombreID 1, LengID 0
000002F0
Tamaño: 02F0h bytes
000004E4
00000000
5308 00008EC8
Desp (VO) 8EC8h, Tipo 5, NombreID 0Ch, LengID 0
000000B8
Tamaño: 0B8h bytes
000004E4
00000000
5318 00008F80
Desp (VO) 8F80h, Tipo 5, NombreID 0Eh, LengI
000002E8
Tamaño: 02E8h bytes
000004E4
00000000
5328 00009418
Desp (VO) 9418h, Tipo 6, NombreID 0Eh, LengI
00000258
Tamaño:
000004E4
00000000
00000BBC
Tamaño:
000004E4
00000000
000002EC
Tamaño:
000004E4
00000000
00000628
Tamaño:
000004E4
00000000
5368 0000AB40
Desp (VO) AB40h, Tipo 9, NombreID 01, LengID 0
00000068
Tamaño:
000004E4
00000000
5378 0000AB88
Desp (VO) AB40h, Tipo 9, NombreID 01, LengID 0
00000022 Tamaño:
000004E4
00000000
5388 0000ABF0
Desp (VO) ABF0h, Tipo 0Eh, NombreID 01, LengID 0
00000022
Tamaño:
000004E4
00000000
5398 0000AC14
Desp (VO) AC14h, Tipo 0Eh, NombreID 01, LengID 0
000002E8
Tamaño:
000004E4
00000000
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.