Buenas noches! Es hora de comenzar a programar un poquillo con esto del PE.
Algo que seguro habéis leído en casi todos los documentos que he ido poniendo como referencia es cómo convertir de una dirección relativa virtual, RVA, a una posición física en el fichero, llamémoslo offset.
Primero manualmente
Lo primero que debemos hacer es encontrar a que sección del ejecutable pertenece esa RVA, para ello en cada sección nos fijamos en los valores VirtualAddress y VirtualSize. Una vez localizada la sección restamos a la RVA la VirtualAddress para saber el desplazamiento dentro de la sección y por último sumamos el PointerToRawData para localizar ese desplazamiento en disco.
Veámoslo con un ejemplo que sino no se entiende nada. Tomemos como ejemplo este post: http://el-blog-de-thor.blogspot.com/2011/03/localizar-direcciones-en-la-iat-mano-12.html
En él buscábamos la IT en disco, mediante un editor hexadecimal vimos que esta se encontraba en la RVA 0x7604. Mirando la secciones nos encontramos con la primera, .text con los siguientes valores:
Name: .text VirtualSize: 0x7748 VirtualAddress: 0x1000 SizeOfRawData: 0x7800 PointerToRawData: 0x400
La sección empieza en 0x1000 y acaba en 0x8748 (0x1000+0x7748), por lo que la RVA 0x7604 se encuentra dentro de ella.
Ahora debemos hacer los cálculos. RVA – comienzo de la sección(VirtualAddress) + comienzo de la sección en disco (PointerToRawData). 0x7604 – 0x1000 + 0x400 = 0x6A04
Y ahí está:
A programarlo
Vamos a programarlo en C, al programa se le pasaran 2 parámetros, el primero el nombre del ejecutable y el segundo la RVA en hexadecimal. Para pasar de una cadena en hexadecimal a un número usaremos la función strtoul.
Así que verificamos que lo que nos pasan como argumentos es lo que esperamos:
if(argc != 3 || strtoul(argv[2], NULL, 16) == 0) { printf("Se esperaban 2 parametros, el primero un fichero y el segundo una RVA en hexadecimal\n"); printf("Ej: %s notepad.exe 3A03\n", argv[0]); return EXIT_FAILURE; }
Después leemos la RVA recibida, abrimos el fichero y lo leemos entero en un buffer:
//Convierte la cadena que contiene el numero en hexadecimal a DWORD DWORD RVA = strtoul(argv[2], NULL, 16); HANDLE fichero = CreateFile((LPCTSTR)argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(fichero == INVALID_HANDLE_VALUE) { printf("Error abriendo el fichero\n"); return EXIT_FAILURE; } //Se lee todo el fichero en buf DWORD size = GetFileSize(fichero, NULL); byte *buf = (byte *)malloc(size); DWORD bytesleidos; ReadFile(fichero, buf, size, &bytesleidos, NULL); CloseHandle(fichero); if (bytesleidos != size) { printf("Error leyendo el fichero\n"); return EXIT_FAILURE; }
Invocamos a la función mágica que convertirá de RVA a Offset, su contenido después.
DWORD Offset = RVAToOffset(buf, RVA); if (Offset == -1) { printf("No se encontró el offset correspondiente a dicha RVA\n"); return EXIT_FAILURE; } //Se imprime el resultado printf("RVA: 0x%08x\n", RVA); printf("Offset: 0x%08x\n", Offset); free(buf); return EXIT_SUCCESS; }
Y ahora la función mágica la que tiene todo lo importante.
//Dada un 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 cálculos 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; }
Admito que así visto el código asusta un poco, con Visual Studio se ve mas bonito:
Y así se usa:
Cuelgo aquí el .c y el compilado para que lo podáis ver bien, compilar, probar o incluso usar.
Realmente simple pero contundente, y para practica mejor, como te dije no conocía esa funcion de hex, algo más que se aprende.
ResponderEliminarSaludos y un placer leerlo.