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!
Muy bueno! que alegria al ver el blog con tanto material
ResponderEliminar