En el post del otro día mostré una función de C para transformar de una dirección RVA a un offset del fichero en disco. Está función nos será útil para el objetivo de hoy: recorrer la Import Table de un ejecutable.
Ya lo hicimos manualmente es estos dos post: http://el-blog-de-thor.blogspot.com/2011/03/localizar-direcciones-en-la-iat-mano-12.html http://el-blog-de-thor.blogspot.com/2011/03/localizar-direcciones-en-la-iat-mano-22.html
Resumiendo:
- Debemos buscar la tabla de importaciones en el directorio de datos (PE+0x80), transformarla de RVA a Offset.
- Al inicio de la IT leeremos las distintas estructuras “IMAGE IMPORT DESCRIPTOR” hasta encontrar una vacía y en cada una de ellas:
- Leemos el nombre de la dll a la que corresponde
- Recorremos la lista de punteros u ordinales apuntados por OriginalFirstThunk o si esta estuviera vacía la de FirstThunk.
- Si es un ordinal, empieza por 0x8…, le mostramos
- Si no, es una RVA de una estructura “IMAGE_IMPORT_BY_NAME” que contiene el nombre de la función importada, mostramos dicho valor.
-
Os dejo el código:
#include <stdio.h>
#include <windows.h>
DWORD RVAToOffset(byte *buf, DWORD RVA);
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Pasale un parametro !");
return EXIT_FAILURE;
}
HANDLE fichero = CreateFile((LPCTSTR)argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(fichero == INVALID_HANDLE_VALUE)
{
printf("Error abriendo el fichero");
return EXIT_FAILURE;
}
printf("IAT de %s\n", argv[1]);
DWORD size = GetFileSize(fichero, NULL);
byte *buf = (byte *)malloc(size);
DWORD bytesleidos;
ReadFile(fichero, buf, size, &bytesleidos, NULL);
CloseHandle(fichero);
//Si se consigue leer en el buffer todo el fichero continuar
if(size == bytesleidos)
{
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)buf;
if(pIDH->e_magic == IMAGE_DOS_SIGNATURE) //MZ
{
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)&buf[pIDH->e_lfanew];
//Dirección de la Import Table
DWORD ITdir = pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
//El primer Image Import Descriptor está al principio de la IT
PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)&buf[RVAToOffset(buf, ITdir)];
while(pIID->Name != 0)
{
printf("%s:\n", &buf[RVAToOffset(buf, pIID->Name)]);
PIMAGE_THUNK_DATA pITD;
//Se lee la INT (Import Name Table), si existe, sino la IAT
if(pIID->OriginalFirstThunk != 0) //En los exe's de borland OFT es 0, o no existe INT
pITD = (PIMAGE_THUNK_DATA)&buf[RVAToOffset(buf, pIID->OriginalFirstThunk)];
else
pITD = (PIMAGE_THUNK_DATA)&buf[RVAToOffset(buf, pIID->FirstThunk)];
while(pITD->u1.AddressOfData != NULL){
//Por ordinal o por nombre?
if((DWORD)pITD->u1.AddressOfData & IMAGE_ORDINAL_FLAG32) //0x80000000
printf(" Ord: %d\n", (DWORD)pITD->u1.AddressOfData & 0x7FFFFFFF);
else{
PIMAGE_IMPORT_BY_NAME pIIBN = (PIMAGE_IMPORT_BY_NAME)&buf[RVAToOffset(buf, pITD->u1.AddressOfData)];
printf(" %s\n", pIIBN->Name);
}
pITD++;
}
pIID++;
}
}
}
free(buf);
return EXIT_SUCCESS;
}
//Dada una dirección relativa virtual, RVA, la transforma a una posición exacta en el fichero
DWORD RVAToOffset(byte *buf, DWORD RVA)
{
//Se lee la cabecera DOS que está al principio
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)buf;
//Se lee la cabecera PE, el campo e_lfanew nos indica donde se encuentra dentro del fichero
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)&buf[pIDH->e_lfanew];
//Buscamos a que sección pertenece la RVA para hacer los calculos correctos
for(DWORD i = 0; i < pINH->FileHeader.NumberOfSections; i++){
//Nos vamos desplazando por las secciones
PIMAGE_SECTION_HEADER pISH = (PIMAGE_SECTION_HEADER)&buf[pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS) + i*sizeof(IMAGE_SECTION_HEADER)];
//Si la RVA está dentro del rango
if (pISH->VirtualAddress <= RVA && pISH->VirtualAddress + pISH->SizeOfRawData > RVA){
//Se realizan los cálculos
return RVA - pISH->VirtualAddress + pISH->PointerToRawData;
}
}
//Si no encuentra en ninguna sección retornamos -1
return -1;
}
Se admiten críticas al código, un saludo!