Very nice work, thanks for sharing! I've been looking for some existing code to use to obtain a list of exports from a target and couldn't find any simple and clean code to copy over into a project. I have imports done, but I wrote it many years ago and forgot the logic of how it works so I'd have to rewrite it all to understand it again.
This works out very nicely and has saved quite a bit of time. I was getting ready to just port Iczelion's Tutorial 7 from ASM to C++, but now I don't have to!
I also noticed a few things I did not know before. For example, the whole "DbgHelp" header, I was not aware of and have never used it before in my projects. It has some very useful functions, some of which I was coding by hand that should help greatly.
However, I noticed your code has a few logical errors and does not handle all exports correctly. Here is a small rewrite of the exports gathering code from a base address that should be correct and handle all types of exports (there are two!).
Code:
#include <windows.h>
#include <stdio.h>
#define MakePtr(cast, ptr, addValue) (cast)((DWORD)(ptr)+(DWORD)(addValue))
void GetAllExports(void * moduleBase)
{
PIMAGE_DOS_HEADER dosHeader = 0;
PIMAGE_NT_HEADERS peHeader = 0;
IMAGE_DATA_DIRECTORY * dataDirectory = 0;
DWORD dwModuleBase = 0;
DWORD * ptrFuncs = 0;
DWORD * ptrNames = 0;
DWORD * funcIndexTable = 0;
DWORD funcsSize = 0;
DWORD namesSize = 0;
_IMAGE_EXPORT_DIRECTORY * expDescBuf = 0;
dwModuleBase = PtrToUlong(moduleBase);
dosHeader = (PIMAGE_DOS_HEADER)moduleBase;
peHeader = MakePtr(PIMAGE_NT_HEADERS, dosHeader, dosHeader->e_lfanew);
dataDirectory = &peHeader->OptionalHeader.DataDirectory[0];
expDescBuf = (_IMAGE_EXPORT_DIRECTORY*)(dataDirectory->VirtualAddress + dwModuleBase);
ptrFuncs = (DWORD*)expDescBuf->AddressOfFunctions;
ptrNames = (DWORD*)expDescBuf->AddressOfNames;
funcIndexTable = (DWORD*)(expDescBuf->AddressOfNameOrdinals);
ptrNames = (DWORD*)((DWORD)ptrNames + dwModuleBase);
ptrFuncs = (DWORD*)((DWORD)ptrFuncs + dwModuleBase);
funcIndexTable = (DWORD*)((DWORD)funcIndexTable + dwModuleBase);
funcsSize = expDescBuf->NumberOfFunctions;
namesSize = expDescBuf->NumberOfNames;
// Not sure how this would happen, maybe corruption or intention?
if(funcsSize != namesSize)
{
printf("[%s] WARNING: funcsSize(%i) != namesSize(%i)", funcsSize, namesSize);
}
// Loop through all of the named functions
for(DWORD index = 0; index < namesSize; ++index)
{
DWORD ptrFuncAddr = 0;
char * name = 0;
unsigned long addr = 0;
// Handle the funcsSize != namesSize case to prevent
// logic errors.
if(index < funcsSize)
{
ptrFuncAddr = (unsigned long)ptrFuncs[index];
addr = ptrFuncAddr + dwModuleBase;
}
name = (char*)(ptrNames[index] + dwModuleBase);
// If this is true, the function is forwarded
if( ptrFuncAddr >= dataDirectory->VirtualAddress &&
ptrFuncAddr < dataDirectory->VirtualAddress + dataDirectory->Size)
{
char * forwardExport = (char * )UlongToPtr(addr);
size_t len = strlen(forwardExport);
char * moduleName = new char[len];
char * moduleFunc = new char[len];
memset(moduleName, 0, len);
memset(moduleFunc, 0, len);
for(size_t x = 0; x < len; ++x)
{
if(forwardExport[x] == '.')
{
memcpy(moduleName, forwardExport, x);
memcpy(moduleFunc, forwardExport + x + 1, len - x - 1);
break;
}
}
HMODULE hNewDLL = GetModuleHandleA(moduleName);
FARPROC fp = GetProcAddress(hNewDLL, moduleFunc);
delete [] moduleName;
delete [] moduleFunc;
printf("* %s -> %s (%X)\n", name, UlongToPtr(addr), PtrToUlong(fp));
}
// Otherwise, a normal export, or something gone wrong if addr == 0
else
{
printf("%s @ %X\n", name, addr);
}
}
}
int main(int argc, char * argv[])
{
HMODULE hDLL = LoadLibraryA("FwdDLL.dll");
if(hDLL == 0)
{
printf("Could not get a handle to the DLL.\n");
return 0;
}
GetAllExports(hDLL);
FreeLibrary(hDLL);
}
Most notably, your code does not handle 'Forwarded Exports', so that is something I added. Next, the address calculation code for your exported functions is not right either, it has to do with how you are using a WORD when a DWORD should be used instead for the funcIndexTable. You can open a DLL in Olly and check the output of your code that way as well, it's what I first did to notice the problem.
If you want to make your own DLL that forwards some exports, you can use the following simple code and def file and create an empty DLL project in Visual Studio and compile. If all works out well, you should be able to use Dependency Walker to verify the forward exported function as well as using my code to see them.
FwdDLL.cpp
Code:
extern "C" __declspec(dllexport) void Test1()
{
}
FwdDLL.def
Code:
LIBRARY FwdDLL
EXPORTS
Test1
Test2 = Kernel32.HeapAlloc
To link the DEF with the project (in case someone else reading this does not know how) simply go to Project->Properties->Configuration Properties->Linker->Input and type "FwdDLL.def" into the "Module Definition File" edit box. You should then be able to compile and load the DLL into Dependency Walker as previously mentioned.
Finally, here's an updated version of your code that should be correct. I did simple testing and it worked out well, but nothing too extensive, there might be other issues that have to be worked around, but this is just a start down to having a more flexible function:
Code:
unsigned long cAddressTable::SearchEATForAddress(char* expSymbolName, AddressTable* adrTable)
{
AddressTable* selTable = (adrTable == 0 ? &m_addrTable : adrTable);
DWORD* ptrFuncs;
DWORD* ptrNames;
DWORD* funcIndexTable;
DWORD funcsSize;
DWORD namesSize;
_IMAGE_EXPORT_DIRECTORY* expDescBuf = (_IMAGE_EXPORT_DIRECTORY*)(selTable->tableOffset + selTable->moduleBase);
ptrFuncs = (DWORD*)expDescBuf->AddressOfFunctions;
ptrNames = (DWORD*)expDescBuf->AddressOfNames;
funcIndexTable = (DWORD*) expDescBuf->AddressOfNameOrdinals;
ptrNames = (DWORD*)((DWORD)ptrNames + selTable->moduleBase);
ptrFuncs = (DWORD*)((DWORD)ptrFuncs + selTable->moduleBase);
funcIndexTable = (DWORD*)((DWORD)funcIndexTable + selTable->moduleBase);
funcsSize = expDescBuf->NumberOfFunctions;
namesSize = expDescBuf->NumberOfNames;
for(DWORD i = 0; (i < namesSize && i < funcsSize); i++)
{
if(!strcmp(expSymbolName, (char*)(ptrNames[i] + selTable->moduleBase)))
{
if((unsigned long)ptrFuncs[i] >= adrTable->tableOffset && (unsigned long)ptrFuncs[i] <= adrTable->tableOffset + adrTable->size)
{
char * forwardExport = (char * )UlongToPtr(ptrFuncs[i] + selTable->moduleBase);
size_t len = strlen(forwardExport);
char * moduleName = new char[len];
char * moduleFunc = new char[len];
memset(moduleName, 0, len);
memset(moduleFunc, 0, len);
for(size_t x = 0; x < len; ++x)
{
if(forwardExport[x] == '.')
{
memcpy(moduleName, forwardExport, x);
memcpy(moduleFunc, forwardExport + x + 1, len - x - 1);
break;
}
}
HMODULE hNewDLL = GetModuleHandleA(moduleName);
FARPROC fp = GetProcAddress(hNewDLL, moduleFunc);
delete [] moduleName;
delete [] moduleFunc;
return PtrToUlong(fp);
}
else
{
return ptrFuncs[i] + selTable->moduleBase;
}
}
}
return 0;
}
You should be able to run that on any function and check the address in OllyDbg and it should be correct! Thanks for sharing the original code!
[Edit] Noticed a small off by one bug:
Code:
if( ptrFuncAddr >= dataDirectory->VirtualAddress && ptrFuncAddr <= dataDirectory->VirtualAddress + dataDirectory->Size)
should be:
Code:
if( ptrFuncAddr >= dataDirectory->VirtualAddress && ptrFuncAddr < dataDirectory->VirtualAddress + dataDirectory->Size)
and has been fixed in the post.