Here is the next post that I promissed to create a simple list of the export function manually with C code.
Why parsing manually of function is good to understand because most of the malware nowadays is using manual dll loading to evade the static and dynamic analysis. The malware will use encrypted fucntion name which will be matched later with the exported function to gain the memory of the function for invocation
Following our code in the previous post that we have been able to extract the export directory RVA and the Size. So in order to really able to read the EXPORT directory we need to be able to calculate the RVA to actual file offset. RVA is the relative virtual address which we cannot reference directly as file offset because RVA is usefull when the PE has been loaded into the memory.
Below is a simple RVA to file raw offset
int Rva2Offset(int noOfSection, unsigned int rva) {
for (int i = 0; i < noOfSection; i++) {
unsigned int x = sections[i].VirtualAddress + sections[i].SizeOfRawData;
//To check which section the RVA falls into
if (x >= rva) {
//calculate the file offset
return rva - sections[i].VirtualAddress + sections[1].PointerToRawData;
}
}
return -1;
}
There are two operation to be done in the above function, The function will determine what PE Section the RVA falls into, it checks for every section of virtual address + size of virtual raw data of each section that must be greater than the supplied RVA. The second operation is to calculate the actual file offset with this formula, FIleOffset = RVA – section Virtual address + section Pointer to raw data;
We can use the result of the above calculation as file offset which we can supply to fseek function.
int export_offset = Rva2Offset(numOfSection, exportDirectoryRVA);
//Get the RVA of the EXPORT Array of API
DWORD pointer_to_exportName_RVA;
fseek(file, export_offset + 0x20, SEEK_SET);
fread(&pointer_to_exportName_RVA, 4, 1, file);
printf("pointer_to_exportName_RVA = %x\n", pointer_to_exportName_RVA);

Below is the code output.

Again, we need to remember that the A3074 is pointer an address that leads us to function name array. We must convert it again into a file offset using the function Rva2Offset.
//Get the RVA of the EXPORT Array of API
DWORD pointer_to_exportName_RVA;
fseek(file, export_offset + 0x20, SEEK_SET);
fread(&pointer_to_exportName_RVA, 4, 1, file);
printf("pointer_to_exportName_RVA = %x\n", pointer_to_exportName_RVA);
//Get the file offset from the RVA of EXPORT array pointer
int export_name_array_offset = Rva2Offset(numOfSection, pointer_to_exportName_RVA);
fseek(file, export_name_array_offset, SEEK_SET);
//Read the RVA to Array
DWORD pointerToArrayRVA;
fread(&pointerToArrayRVA, 4, 1, file);
printf("pointerToArrayRVA = %x\n", pointerToArrayRVA);

We can follow the A4829 RVA to lead us to the function name array. But dont forget to convert the RVA back to file offset again
//Read the Actual file offset of the array
int offsetToFirstAddressOfAPIArray = Rva2Offset(numOfSection, pointerToArrayRVA);
fseek(file, offsetToFirstAddressOfAPIArray, SEEK_SET);

After we get the file offset (A3629) then we can use fseek to guide us to array sequence as below

Below is the code on how I enumerate all the function name in the export list
int arrayOffset = 0;
for (int ii = 1; ii <= numberOfAPIName; ii++) {
int szNameLen = 0;
//Counting the number of char of the API Name
char c = '0';
while (c != '\0') {
fread(&c, 1, 1, file);
szNameLen++;
}
//Revert the file offset as much as the length of API name for API name read with fread
fseek(file, -szNameLen, SEEK_CUR);
char* szName = (char*)calloc(szNameLen + 1, 1);
//Read the API name
fread(szName, szNameLen, 1, file);
printf("%d API name : %s\n",ii, szName);
//set the file offset to jump
arrayOffset += szNameLen;
fseek(file, offsetToFirstAddressOfAPIArray + arrayOffset, SEEK_SET);
}

Bonus Code
Below is the complete code that I develop to parse the PE header manually and especially to read the export function of PE.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <Windows.h>
typedef struct {
unsigned char Name[8];
unsigned int VirtualSize;
unsigned int VirtualAddress;
unsigned int SizeOfRawData;
unsigned int PointerToRawData;
unsigned int PointerToRelocations;
unsigned int PointerToLineNumbers;
unsigned short NumberOfRelocations;
unsigned short NumberOfLineNumbers;
unsigned int Characteristics;
} sectionHeader;
sectionHeader* sections;
int Rva2Offset(int noOfSection, unsigned int rva) {
for (int i = 0; i < noOfSection; i++) {
unsigned int x = sections[i].VirtualAddress + sections[i].SizeOfRawData;
//To check which section the RVA falls into
if (x >= rva) {
//calculate the file offset
return rva - sections[i].VirtualAddress + sections[1].PointerToRawData;
}
}
return -1;
}
void main()
{
FILE* file = fopen("C:\\Users\\pentest\\Desktop\\Work\\user32.dll", "rb");
long peheaderoffset;
// read the offset of the PE header which is located at offset 0x3c
fseek(file, 0x3c, SEEK_SET);
fread(&peheaderoffset, 4, 1, file); //read the offset --> e_lfanew
char PEHeader[4]; // PE header: contains normally 'P','E',0,0
fseek(file, peheaderoffset, SEEK_SET);
fread(&PEHeader, 4, 1, file);
short machine_rio;
fseek(file, peheaderoffset + 4, SEEK_SET);
fread(&machine_rio, 2, 1, file);
printf("machine_rio = %x\n", machine_rio);
short numOfSection;
fseek(file, peheaderoffset + 4 + 2 , SEEK_SET);
fread(&numOfSection, 2, 1, file);
printf("numOfSection = %x\n", numOfSection);
long timedatastamp;
fseek(file, peheaderoffset + 4 + 4, SEEK_SET);
fread(&timedatastamp, 4, 1, file);
printf("timedatastamp = %x\n", timedatastamp);
long pointerToSymbolTable;
fseek(file, peheaderoffset + 4 + 8, SEEK_SET);
fread(&pointerToSymbolTable, 4, 1, file);
printf("pointerToSymbolTable = %x\n", pointerToSymbolTable);
long numberOfSymbols;
fseek(file, peheaderoffset + 4 + 0x0c, SEEK_SET);
fread(&numberOfSymbols, 4, 1, file);
printf("numberOfSymbols = %x\n", pointerToSymbolTable);
//Get sizeofOptionalHeader from File Header
short sizeofOptionalHeader;
fseek(file, peheaderoffset + 4 + 16, SEEK_SET);
fread(&sizeofOptionalHeader, 2, 1, file);
printf("sizeofOptionalHeader = %x\n", sizeofOptionalHeader);
//Get Characteristic from File Header
short characteristic;
fseek(file, peheaderoffset + 4 + 18, SEEK_SET);
fread(&characteristic, 2, 1, file);
printf("characteristic = %x\n", characteristic);
//Get The magic from optional hdr
short magic;
fseek(file, peheaderoffset + 0x18, SEEK_SET);
fread(&magic, 2, 1, file);
printf("magic = %x\n", magic);
//Get the Export Directory RVA
DWORD exportDirectoryRVA;
fseek(file, peheaderoffset + 0x18 + 0x70, SEEK_SET);
fread(&exportDirectoryRVA, 4, 1, file);
printf("exportDirectoryRVA = %x\n", exportDirectoryRVA);
DWORD exportDirectorySize;
fseek(file, peheaderoffset + 0x18 + 0x70 + 4, SEEK_SET);
fread(&exportDirectorySize, 4, 1, file);
printf("exportPointerSize = %x\n", exportDirectorySize);
//120 is offset from last read exportPointerSize
fseek(file, 120, SEEK_CUR);
sections = (sectionHeader*)malloc(numOfSection * sizeof(sectionHeader));
//Collecting the details of section Header
for (int i = 0; i < numOfSection; i++) {
fread(sections[i].Name, 8, 1, file); //Read Image section name
fread(§ions[i].VirtualSize, 4, 1, file);
fread(§ions[i].VirtualAddress, 4, 1, file);
fread(§ions[i].SizeOfRawData, 4, 1, file);
fread(§ions[i].PointerToRawData, 4, 1, file);
fread(§ions[i].PointerToRelocations, 4, 1, file);
fread(§ions[i].PointerToLineNumbers, 4, 1, file);
fread(§ions[i].NumberOfRelocations, 2, 1, file);
fread(§ions[i].NumberOfLineNumbers, 2, 1, file);
fread(§ions[i].Characteristics, 4, 1, file);
}
int export_offset = Rva2Offset(numOfSection, exportDirectoryRVA);
//Getting number of exported API
unsigned int numberOfAPIName;
fseek(file, export_offset + 0x18, SEEK_SET);
fread(&numberOfAPIName, 4, 1, file);
printf("numberOfAPIName = %x\n", numberOfAPIName);
//Get the RVA of the EXPORT Array of API
DWORD pointer_to_exportName_RVA;
fseek(file, export_offset + 0x20, SEEK_SET);
fread(&pointer_to_exportName_RVA, 4, 1, file);
printf("pointer_to_exportName_RVA = %x\n", pointer_to_exportName_RVA);
//Get the file offset from the RVA of EXPORT array pointer
int export_name_array_offset = Rva2Offset(numOfSection, pointer_to_exportName_RVA);
fseek(file, export_name_array_offset, SEEK_SET);
//Read the RVA to Array
DWORD pointerToArrayRVA;
fread(&pointerToArrayRVA, 4, 1, file);
printf("pointerToArrayRVA = %x\n", pointerToArrayRVA);
//Read the Actual file offset of the array
int offsetToFirstAddressOfAPIArray = Rva2Offset(numOfSection, pointerToArrayRVA);
fseek(file, offsetToFirstAddressOfAPIArray, SEEK_SET);
int arrayOffset = 0;
for (int ii = 1; ii <= numberOfAPIName; ii++) {
int szNameLen = 0;
//Counting the number of char of the API Name
char c = '0';
while (c != '\0') {
fread(&c, 1, 1, file);
szNameLen++;
}
//Revert the file offset as much as the length of API name for API name read with fread
fseek(file, -szNameLen, SEEK_CUR);
char* szName = (char*)calloc(szNameLen + 1, 1);
//Read the API name
fread(szName, szNameLen, 1, file);
printf("%d API name : %s\n",ii, szName);
//set the file offset to jump
arrayOffset += szNameLen;
fseek(file, offsetToFirstAddressOfAPIArray + arrayOffset, SEEK_SET);
}
}