3
votes

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.

2
Yeah, this doesn't work. The linker treats the @ character special since it is also used to give the ordinal. Same as this question. No known workaround, there is no quoting option. Just declare the functions with their original name so they get mangled the same and have them call whatever replacement function you want.Hans Passant
@HansPassant thanks so much for that comment. I feel stupid for not trying to use the original signature for my replacements... Will try and see if that works as expected.Till

2 Answers

3
votes

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
1
votes

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);