Till
Till

Reputation: 27587

DLL export symbol mangling via DEF file seems to get modified

I created a wrapper DLL using a DEF file for describing its EXPORTS.

From the original DLL's exports (generated using dumpbin):

EXPORTS
??0FooBar@@QAE@ABV0@@Z @1

From the created DEF file for my wrapper DLL:

EXPORTS
??0FooBar@@QAE@ABV0@@Z=__E__0__ @1

When checking the resulting wrapper DLL using OllyDbg, I can see that the export actually gets changed towards:

Names in FooBarDLL, item 0
 Address=0F085685
 Section=.text
 Type=Export
 Name=??0FooBar

As you see, it is missing that additional garbledegock (@@QAE@ABV0@@Z) usually generated by Microsoft Visual C++ for this type of a class/function/parameter combination.

To make sure that this is not a user error on the usage of OllyDbg, I checked the original DLL's exports as well and to no surprise those looked just like the stuff I was expecting:

Names in FooBarDLLOriginal, item 1
 Address=1003A800
 Section=.text
 Type=Export
 Name=??0FooBar@@QAE@ABV0@@Z

As I need my wrapper DLL to look exactly like the original DLL, this result is obviously no good.

How can I prevent Visual C++ from ignoring fragments of my DEF exports definitions?

I have tried to play with many compiler and linker options but utterly failed in getting any closer to my goal.

Upvotes: 3

Views: 2378

Answers (2)

Graeme Gill
Graeme Gill

Reputation: 645

This question (and others like it) relate to creating proxy DLLs automatically using the Code Project WRAPPIT tool. The solution using the observation from Till, is to:

Always declare the wrapper function to be __stdcall so that it gets decorated with a leading _ and trailing @0, and then use that in the .def file so that the original function decoration (if any) is preserved.

(When/if you replace the wrapper with a real function, you need to remember to change the call convention from __stdcall to the one needed, as well as remove __declspec(naked), add argument declarations, change or remove the .def file declaration to match etc.)

Wrapper .cpp snipet:

// _OriginalFunction@12
extern "C" __declspec(naked) void __stdcall __E__0__()
    {
    __asm
        {
        jmp p[0*4];
        }
    }

.def file:

EXPORTS
_OriginalFunction@12=___E__0__@0 @1
etc.

I modified my version of the WRAPPIT tool to do this automatically:

165c165
<                       fprintf(fdef,"%s=%s @%u\r\n",v[i].en,v[i].in,v[i].o);
---
>                       fprintf(fdef,"%s=_%s@0 @%u\r\n",v[i].en,v[i].in,v[i].o);
167c167
<                       fprintf(fdef,"%s=%s @%u NONAME\r\n",v[i].en,v[i].in,v[i].o);
---
>                       fprintf(fdef,"%s=_%s@0 @%u NONAME\r\n",v[i].en,v[i].in,v[i].o);
225c225
<               fprintf(fcpp,"// %s\r\nextern \"C\" __declspec(naked) void %s %s()\r\n",v[i].en,argv[3],v[i].in);
---
>               fprintf(fcpp,"// %s\r\nextern \"C\" __declspec(naked) void %s %s()\r\n",v[i].en,"__stdcall",v[i].in);

Upvotes: 1

Till
Till

Reputation: 27587

I finally got it working.

As Hans Passant noted, the linker somehow does handle @-signs within export definitions in a way that I found hard to grasp...


My observations:

  • Mangled C++ function (??0FooBar@@QAE@ABV0@@Z) forwarded to an unmangled (extern "C") function (__E__0__)
  • => C++ function mangling gets cropped at the first @-sign (??0FooBar)

  • Mangled C++ function (??0FooBar@@QAE@ABV0@@Z) forwarded to a mangled (extern "C" __declspec(dllexport)) function (___E__0__@4)

  • => C++ function mangling stays intact

This would be my initial test-case, now being close enough to my initial goal.

From the DEF file of the wrapper:

EXPORTS
??0FooBar@@QAE@ABV0@@Z=___E__0__@4 @1

From the wrapper DLL's exports (generated using dumpbin):

ordinal hint RVA      name

   1    0 00001000 ??0FooBar@@QAE@ABV0@@Z = ___E__0__@4
   2    1 00001000 ___E__0__@4 = ___E__0__@4

What really astonished me is that the export from ordinal 1 remains invisible when using OllyDbg to read the exports table. Still, when loading the DLL (LoadLibrary) and resolving the address of that function using GetProcAddress(hDLL, "??0FooBar@@QAE@ABV0@@Z"), things work just fine.


So I now have a DLL that wraps the original one, using a functionally identical exports table. It now shows an additional export of my proxy implementation, but that is no problem for my purposes. That way it is possible to create a proxy DLL that intercepts and, if needed, enhances the original implementation even when C++ mangling is involved. I somehow feel that I overcomplicated things a little but hey, it works.

For functions that I entirely pass through, I simply create a forwarding export in my DEF file, looking like this:

??BarFoo@WhoCares@@@Z = OriginalDllName.??BarFoo@WhoCares@@@Z

Upvotes: 3

Related Questions