martes, 22 de marzo de 2011

Recorrer la Import Table en C

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:

  1. Debemos buscar la tabla de importaciones en el directorio de datos (PE+0x80), transformarla de RVA a Offset.
  2. Al inicio de la IT leeremos las distintas estructuras “IMAGE IMPORT DESCRIPTOR” hasta encontrar una vacía y en cada una de ellas:
    1. Leemos el nombre de la dll a la que corresponde
    2. Recorremos la lista de punteros u ordinales apuntados por OriginalFirstThunk o si esta estuviera vacía la de FirstThunk.
    3. Si es un ordinal, empieza por 0x8…, le mostramos
    4. 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!

1 comentario:

  1. Muy bueno! que alegria al ver el blog con tanto material

    ResponderEliminar