Click here to Skip to main content
15,880,364 members
Articles / Desktop Programming / Win32

Unmangle Mangled Libraries

Rate me:
Please Sign up or sign in to vote.
4.95/5 (9 votes)
2 Feb 2017CPOL4 min read 12.8K   165   13   1
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:

C++
// 
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:

C++
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:

C++
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);
    }
}
We are using the 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:

C++
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:

C++
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:

C++
#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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
eslipak6-Feb-17 8:58
professionaleslipak6-Feb-17 8:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.