问题
I want to print information about IMAGE_EXPORT_DIRECTORY
in the following form:
<Name1>,<Ordinal1>,<FileAddress1>
I know that this IMAGE consists of 3 arrays:
- AddressOfFunctions - (Export address table, each element is an RVA)
- AddressOfNames - (Export Name Pointer table, also each element is an RVA - ordered ?)
- AddressOfNameOrdinals (where the element of the array - Base represents an ordinal in the EAT)
But how can i access those tables in order to print information for each element in one line? Also, what would happen when NumberOfNames < NumberOfFunctions ?
I already know how to get to the IMAGE_EXPORT_DIRECTORY
PIMAGE_DATA_DIRECTORY pFirstDir = &(pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
if (pFirstDir->Size > 0)
{
PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE *)pDosHeader + ConvertRvaToOffset(pFirstDir->VirtualAddress, pNTHeaders));
}
If someone could explain how those arrays actually work or an algorithm of how to access them it would be greatly appreciated.
回答1:
You can use the following pseudo-C-code:
UINT32 *export_addr_table = (UINT32 *) MAP(pExportDir->AddressOfFunctions);
UINT32 *export_nameptr_table = (UINT32 *) MAP(pExportDir->AddressOfNames);
UINT16 *export_ordinal_table = (UINT16 *) MAP(pExportDir->AddressOfNameOrdinals);
for (SIZE_T i = 0; i < pExportDir->NumberOfFunctions; i++)
{
UINT32 ordinal = pExportDir->Base + i;
UINT32 export_rva = export_addr_table[i];
if (is_forwarder_rva(export_rva))
{
// TODO: special care must be taken here - we cannot resolve directly to a VA unless target module is memory mapped
}
else
{
BOOL found_symname = FALSE;
char symname[MAX_SYMNAME_LEN];
// Loop through all exported names
for (SIZE_T j = 0; j < pExportDir->NumberOfNames; j++)
{
if (export_ordinal_table[j] == i)
{
UINT32 export_symname_rva = export_nameptr_table[j];
const char *export_symname = (const char *) MAP(export_symname_rva);
found_symname = TRUE;
// Copy export_symname into symname (i.e. using strncat or similar)
}
}
if (!found_symname)
{
snprintf(symname, MAX_SYMNAME_LEN, "#%"PRIu32, ordinal);
}
// Print symname, ordinal, address
}
}
Here, I use MAP()
as representing the operation of resolving an RVA to a dereferencable virtual address (similar to what you did using ConvertRvaToOffset
).
Explanation:
All functions that are exported are present in the Export Address Table, but as you already have pointed out, only named functions are present in the Export Name Pointer Table and Export Ordinal Table (which are parallel tables).
The entries in the Export Name Pointer Table are ordered lexically, to facilitate quick resolution from symbol names of exported functions to their "unbiased" ordinals (i.e. indexes into the Export Address Table). So to go the other way, and find the exported name (if any) for an exported function, the only solution is really to iterate through every exported name and try to match the RVA.
However, some entries in the Export Address Tables represent so called "forwarder RVA" entries, which redirect the export to a symbol in another DLL module (see PE/COFF 6.3.2). These cannot really be printed in the format you wanted, so I just added a // TODO
in the pseudo-code above for that case.
In addition, note that for every exported function (both exported by name, and by ordinal only), a valid symbol name (w.r.t to resolving Import Lookup Table entries) for this function is #OrdinalNumber
, where OrdinalNumber
is replaced with the actual ordinal (see the description for Forwarder RVA in PE/COFF 6.3.2), so this could make sense to list as the name of exported functions that are not exported by name.
来源:https://stackoverflow.com/questions/49705700/show-info-about-image-export-directory