Reputation: 27587
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
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
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:
??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
)
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