Unmangle Mangled Libraries






4.95/5 (9 votes)
Dynamically load a DLL with a name mangled interface, and use the undecorated interface.
Introduction
A generic C/C++ code to allow you to dynamically load a DLL, without worrying if the DLL has a name mangled interface.
Background
Recently, I had to include a DLL into my project, I used what I would generally - PDll.h developed by MicHael Galkovsky and available in the Windows SDK. However, I soon ran into the problem of the DLL loading but none of the interfaces being called. Checking the Dependancy Walker did not show any problems, then I realized that the option "Undecorate C++ Functions" in the View menu was checked. As soon as I unchecked the option, I saw my problem, the DLL exported name mangled interfaces.
In case you want to use PDll.h, the beginner explanation can be found on MSDN Forum, it is a nice way to keep the code for loading clean:
I use PDll.h for all my dynamic loading of DLLs - and there are a LOT of DLLs that my project attaches to, it is complex and out of scope to discuss it. However, I did not want a new set of sequence for loading mangled DLLs, so I started looking for a common implementation that can be used generically in my project. The following is an example of how to use it.
Using the Code
The code just calls a dummy DLL, FooDll.dll, to use a name mangled interface to add two values passed. The DLL exports a mangled interface, and the project PdllLoad
uses unmangled interface names to call the mangled interface.
This is a brief description of how to use the PDll.h to dynamically load DLLs. The way it is used does not change, the changes I made were just modifications to the PDll.h, and what I added to improve on it for mangled interfaces.
PDll.h allows you to create a class for the DLL you want to load:
//
class FooDll : public PDLL
{
public:
DECLARE_FUNCTION1(INT, FooExport1, INT)
DECLARE_FUNCTION2(INT, FooExport2, INT, INT)
DECLARE_FUNCTION3(INT, FooExport3, INT, INT, INT)
DECLARE_FUNCTION4(INT, FooExport4, INT, INT, INT, INT)
private:
DECLARE_CLASS(FooDll)
};
//
Where "FooDll
" is the DLL you want to load, and FooExport1
,...FooExport4
are the interfaces of the DLL. Now you have a simple C++ class for each of your DLLs!
Create an instance of the class, and use it like a regular Object:
FooDll* foo;
foo = new FooDll("FooDll.dll");
if(foo->IsLoaded())
{
returncode = foo->FooExport1(aValue);
}
So, it is fairly easy to use and read, the macros behind the class are already explained in multiple articles. I will keep this one brief and talk about how to map mangled names to unmangled names. I am assuming you know the unmangled interface names and have an idea of using the interface.
Find the Name Mangled Interfaces
First of all, you need to find out what the actual names of the Interfaces exported by the DLL are. You can use the following function to get the list of interfaces from the DLL:
void ListDllInterfaces(string sADllName, vector<string>& slListOfDllFunctions)
{
DWORD *dNameRVAs(0);
IMAGE_EXPORT_DIRECTORY *ImageExportDirectory;
unsigned long cDirSize;
_LOADED_IMAGE LoadedImage;
string sName;
slListOfDllFunctions.clear();
if (MapAndLoad(sADllName.c_str(), NULL, &LoadedImage, TRUE, TRUE))
{
ImageExportDirectory =
(IMAGE_EXPORT_DIRECTORY*)ImageDirectoryEntryToData(LoadedImage.MappedAddress,
false,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&cDirSize);
if (ImageExportDirectory != NULL)
{
dNameRVAs = (DWORD *)ImageRvaToVa(LoadedImage.FileHeader,
LoadedImage.MappedAddress,
ImageExportDirectory->AddressOfNames,
NULL);
for (size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
{
sName = (char *)ImageRvaToVa(LoadedImage.FileHeader,
LoadedImage.MappedAddress,
dNameRVAs[i],
NULL);
slListOfDllFunctions.push_back(sName);
}
}
UnMapAndLoad(&LoadedImage);
}
}
MapAndLoad
API to load the DLL, and preload its data. Use ImageDirectoryEntryToData
to get the relative virtual addresses to the exported interfaces and the ImageExportdirectory
structure, and use it in ImageRvaToVa
to locate, first, the Address of Names of the Interfaces. And then to get the actual names of the interfaces.Add It to the PDll.h
This needs to read into the PDll
class, when the DLL is loaded, so add it to the DECLARE_CLASS
member that will do the Dynamic Loading:
void LoadDll(char* name, short showMsg = 0)
{
if (name)
{
SetDllName(name);
}
//try to load
m_dllHandle = LoadLibraryA(m_dllName);
ListDllInterfaces(name, MangledNames);
}
Map Known Interface Name to the Name Mangled Interface
Now we have the Mangled interface names from the DLL, we have to map it to the unmangled names that we know, simple task of finding the umangled name string in the mangled names:
bool isNameCharacter(string c)
{
string cc = c.length()>0 ? c.substr(0,1) : '\0';
return (cc.compare("a")>=0 && cc.compare("z")<=0)
|| (cc.compare("A")>=0 && cc.compare("Z")<=0)
|| (cc.compare("0")>=0 && cc.compare("9")<=0)
|| (cc.compare("_")==0);
}
void GetMangledNameFromUnmangledName
(vector<string> MangledNames, string unmangledName, string &MangledName)
{
string pMangled;
for (unsigned int i= 0; i < MangledNames.size(); i++)
{
pMangled = MangledNames.at(i);
size_t Found = pMangled.find(unmangledName);
if (Found != string::npos)
{
if (Found + unmangledName.length() < pMangled.length()
&& !isNameCharacter(pMangled.substr(Found+unmangledName.length(),1)))
{
if (Found > 0 && !isNameCharacter(pMangled.substr(Found-1,1)))
{
MangledName = pMangled;
return;
}
}
}
}
return;
}
Add to the DECLARE_FUNCTIONX Macro
So now you have the interfaces mapped to their mangled names, in the PDll.h, the macro to call the function should now call the mangled name when you pass in the unmangled name:
#define DECLARE_FUNCTION1(retVal, FuncName, Param1) \
typedef retVal (WINAPIV* TYPE_##FuncName)(Param1); \
TYPE_##FuncName m_##FuncName; \
short m_is##FuncName;\
retVal FuncName(Param1 p1) \
{ \
if (m_dllHandle) \
{ \
if (FUNC_LOADED != m_is##FuncName) \
{\
m_##FuncName = NULL; \
string MangledName;\
GetMangledNameFromUnmangledName(MangledNames, #FuncName, MangledName);\
m_##FuncName =
(TYPE_##FuncName)GetProcAddress(m_dllHandle, MangledName.c_str()); \
m_is##FuncName = FUNC_LOADED;\
}\
if (NULL != m_##FuncName) \
return m_##FuncName(p1); \
else \
return (retVal)NULL; \
} \
else \
return (retVal)NULL; \
}
And you are now name mangled DLL safe! Use it as you have been using it before!
Points of Interest
It takes a LONG time to update a macro if you are not used to it. It can be quite challenging, but with the above setup, attaching to DLLs becomes a breeze, and the DLLs can be instantiated like an object!
History
- First draft... comments or improvements are welcome!