Andrew

September 8, 2008

Generating .DLL Wrappers

Filed under: cplusplus,Programming,Win32 — floodyberry @ 5:25 am
Tags: , ,

A while ago I came across Create your Proxy DLLs automatically by Michael Chourdakis. I thought it was a good idea, but had some room for improvement:

  • Having to use an external .exe (dumpbin/tdump) was an unnecessary step, all the information you need is in the PE header!
  • He did not handle wrapping mangled names or forwarding forwards.
  • Generating an actual project instead of a command line compile call would be a lot more useful considering you will want to do some actual coding instead of generating an empty wrapper.
  • His coding style was somewhat awkward and not easy to modify.

With this in mind, I set about writing my own version. Dumping the export information from the PE header was the first step and relatively straightforward as the PE header is not complicated. I did, however, need to steal GetPtrFromRVA and GetSectionFromRVA from Matt Pietrek because the RVAs (Relative Virtual Addresses) are only relative when the sections are properly mapped out in memory, not while in the PE container! Those two functions also happen to be used for game hacks when the cheat-maker want to bypass any LoadLibrary & EnumProcessModules hooks by manually mapping a .DLL in to memory.

Handling export forwards is tricky if you don’t know what they are and simple once you find out.  Since there are no flags to indicate if an export is a forward, and an export name can contain any non-zero character, the system needs a way to tell if “NTDLL.RtlAllocHeap” is a forward or not. The linker solves this by pointing the exported function address at the name of the export in the export table, so you merely need to check if the exported function address is within the export table or not.

Proxying name mangling is unfortunately not as simple. Say a .DLL exports

void __stdcall SimpleExport( void ) {
}

as a C++ function, resulting in the mangled export of “?SimpleExport@@YGXXZ”. Now say your proxy .DLL implements a stub and attempts to forward it (the @1 at the end of the export is telling the linker which ordinal to assign the export to):

.cpp file:

int __stdcall SimpleExport__YGXXZ() {
	...
}

 

.def file

?SimpleExport@@YGXXZ=SimpleExport__YGXXZ @1

When you compile your .DLL, you’ll be surprised to find that it is exporting “SimpleExport”, not “?SimpleExport@@YGXXZ”! What is going on?! It turns out the Microsoft linker checks the target function for name mangling (an @ symbol), and if the target function name isn’t mangled, formats the exported name as a C function. The only way that I’ve found to export a mangled name is to point it at a mangled name:

.def file v2

?SimpleExport@@YGXXZ=?SimpleExport__YGXXZ@@YGXH@Z @1

The good news is that this works great. The bad news is that if you alter your proxy function to match the source function, the name mangling changes and your .def file will need to be updated with the new name (not a trivial thing!). The “at least it works” solution I came up with is to have proxy functions for your proxy functions, i.e. functions whose name mangling will never change and which jmp to the real proxy function:

.cpp file:

int __stdcall SimpleExport__YGXXZ() {
	...
}

naked void __stdcall decorated1() {
	__asm {
		jmp SimpleExport__YGXXZ
	}
}

 

.def file:

?SimpleExport@@YGXXZ=?decorated1@YGXXZ @1

Now you can alter the calling conventions, parameters, and return type of the actual proxy function as much as you like and it won’t affect the linking.

A benefit the trouble mangled names produce is that, with the UnDecorateSymbolName DbgHelp API call, it is trivial to annotate the return type, calling conventions, and parameters of the proxied function; e.g. you can now see “?func1@a@@AAEXH@Z” actually means “private: void __thiscall a::func1(int)”.

My Version

There are a lot of ways to generate the wrapper and I thought Michael’s generated source also had room for improvement. For example, he doesn’t name any of the generated functions, and calling through to the original function requires a typedef (doesn’t work with intellisense), a non-obvious index in to the imported function array, and a cast. Extending the code so any function can call through to any original function would require the typedef to be global (which still won’t work with intellisense).

The main points I wanted to hit were:

  1. Allow the original functions to be easily callable from anywhere
  2. Require as few changes as possible to if you want to convert a stub to a proxied function
  3. Intellisense has to work!

#1 meant creating dummy functions which jmp to their target proc. By creating them as:

inline __declspec(naked) int call_AcceptEx() {
	__asm {
		jmp dword ptr [ mProcs + 0 * 4 ]
	}
}

I’m not only able to stuff them in the header (keeping the source file clean), but naked means you can alter their parameters and return type as much as you like and don’t need to worry about how to use the FARPROC entry or which index to use.

To make one of the original functions globally callable, you only need to change the function signature in two places: The header stub, and the proxy function in the .cpp file. I would have loved to only have a single signature to change but wasn’t able to come up with a clean method. It’s “possible” with some god-awful macro magic, but I don’t even have a need for a wrapper at this point and the macros really obfuscate the code.

That is about it! More work could be done, but it’s decently functional now and I think I’ve been sitting on it for long enough now. My version generates projects for both Visual Studio 2003 & 2005 and attempts to load the original .DLL from the Windows system directory by default (fairly arbitrary, easy to change). There’s also a sample .DLL (dll.dll) with odd exports (a forwarded function, a mangled class function, a mangled C++ global function, and a __stdcall C function) so you can make sure any changes you make still compile fine.

Now I just need to find something to wrap with it..

Downloads

About these ads

9 Comments »

  1. I tried your gen wrapper on a different dll it crashed at line 272.
    Because the function in line 271 returned null.

    Disassembling the dll , the header tables looked ok.
    lines 271-272.
    PIMAGE_SECTION_HEADER section = getsectionfromrva( rva );
    return (type *)( mBase + section->PointerToRawData + ( rva – section->VirtualAddress ) );

    Comment by Martin Horowitz — November 19, 2008 @ 5:56 pm | Reply

  2. I’d have to see the .dll you used to see what is going wrong. It’s possible the .dll is doing something odd that I didn’t encounter in testing, I haven’t done that much work with .dll’s other than this.

    Comment by floodyberry — November 19, 2008 @ 7:26 pm | Reply

  3. Thanks man, made my day )

    Comment by Spider — April 26, 2011 @ 9:47 am | Reply

  4. Really great! Thanks for making this public.
    Might be a silly question but would you be so kind and add a short example on how to actually get/manipulate the parameters of a “wrapped” function call?
    For example, how would you modify this part in order to access the first parameter of a::func1(int)?

    // private: void __thiscall a::func1(int)
    int __stdcall __func1_a__AAEXH_Z() {
    return call__func1_a__AAEXH_Z();
    }

    Comment by unikopie — May 16, 2012 @ 3:12 pm | Reply

  5. Wow, really great article. Made my day, too.
    Would you please be so kind and give us a simple example on how to “access” the parameters of a function call (or better said: how to “repair” the stack before passing the control to the underlying original dll after accessing the parameters).

    Comment by unikopie — May 17, 2012 @ 8:43 am | Reply

  6. Nice post, teach me a lot! Thank you for sharing!

    Comment by m4v3n — August 6, 2012 @ 1:01 am | Reply

  7. Reblogged this on m4v3n and commented:
    This post is very helpful for making a dll wrapper

    Comment by m4v3n — August 6, 2012 @ 1:02 am | Reply

  8. Hi, I think there is something wrong in your code, you have two proxy functions, I think each should be naked, else there will be one extra pair of prolog and epilog generated, and the behaviour is changed.
    I’ve been confused about this, and I checked Michael Chourdakis’s work, and I think you need to also make the proxy of proxy naked.

    Comment by m4v3n — August 6, 2012 @ 5:56 am | Reply

  9. I also made one, 64 bit support added.http://linmin.me/wordpress/?p=25

    Comment by m4v3n — August 13, 2012 @ 12:10 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Rubric Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: