Introduction

Consider the situation where the native, Win32 DLL is unknown at compile time.
Perhaps its name or location is stored in the Windows Registry, or is selected by
the user from a FileOpen dialog. How can we call a function exported from this library, but
only resolved at runtime? The prescribed way to call native functions from the CLR is through
PInvoke, using the DllImport attribute, but this must be declared at compile time or,
at the very least, generated on the fly using Reflection.Emit. This article will
show an alternative way which requires the use of a little x86 assembler to meet our goal.

LoadLibrary

Windows provides two ways to load DLLs into the process of an executable. Either, the DLL
can be specified in the imports table and the Windows Loader will map the DLL automatically or the
LoadLibrary()
Win32 API call can be used. These are called implicit linking and explicit linking respectively. Both
these types can be seen in action using Dependency Walker available from
http://www.dependencywalker.com/. The CLR, Visual BASIC 6
and the /delayload feature of MSVC6 all use explicit linking to call DLL functions.

We can quite happily call

LoadLibrary()

from C# to load a DLL into our address space. The problem comes when we try to call a function in the DLL.
Win32 provides the GetProcAddress() function to return the memory address of a function exported from the given DLL and
we can easily obtain this memory address, but we can do nothing with it. It is simply an integer. The CLR provides
no way to jump to this location in memory, passing appropriate parameters too.

The CLR does allow us to do the reverse and pass a pointer to a managed function to a DLL using the delegate keyword, but there
is no way to specify that a value returned from an unmanaged API call should be treated as a delegate.
Perhaps we may see this in .Net version 2, but for now we need to find another way to call the function.

Going low-level

One solution would be to write a small C++ DLL which merely forwards the call on. In other words, the C++ DLL
is acting as a proxy for our intended function. The downside is that a new C++ DLL would have to be created
every time a different DLL function needs to be called. The proxy function needs the exact number of parameters
that the real function takes. Every C# programmer needs to know C++ to be able to do this.

A much better solution is to write a small, reusable DLL in x86 assembly language which can forward function calls
to any location. This is trivial to write if we know a bit about how Win32 DLLs are called. All DLLs are called
using the stdcall calling convention. This means that parameters are pushed onto the stack beginning at the right-most
parameter. Thus, the first in the parameter list will be at the top of the stack. The return address is then placed
on the stack and control is transferred to the callee. It is the callee's responsibility to pop all the parameters off
the stack and not fiddle with more registers than absolutely necessary.

It is implemented in a DLL called Invoke.dll and has the export name InvokeFunc.
It also takes five parameters, of which the last four are the exact parameters taken by the MessageBox() function.
The first parameter is an address of a function. We will leave the implementation of
InvokeFunc for now and look
at code which can call this.

This code loads the DLL into our process space, finds the address of a function we wish to call, then uses our special

InvokeFunc()

function to call a function through a function pointer.

In the screenshot above, notice how GetProcAddress() is being used to find the address of
GetProcAddress()! This is because
PInvoke uses GetProcAddress to find the address of any function specified by the
DllImport attribute.

InvokeFunc Implementation

As we discussed earlier, the stdcall calling conventions places parameters onto the stack in reverse order. Thus, our
function pointer will be at the top of the stack because it is first in the parameter list. If we can take
this parameter off the stack, then jump to that location in memory, it would be the equivalent of calling that function
without the intermediate proxy function.

Because we've used a jmp instruction rather than a call instruction, control will be
transferred directly
from the called function back to the CLR, passing any return value directly back.

Conclusion

Everything needed to compile and run this code is included with Visual Studio
.Net. The x86 DLL built can be
reused for calling any function pointer, not just a function with a specific signature and is only 2,560 bytes.

It turns out that if
you are not running .Net on Windows XP, there is a DLL with an equivalent proxy function to the one we built.
That DLL is msjava.dll, which of course is missing from Windows XP due to the Microsoft-Sun
agreement on Java technology. msjava.dll provides an export with the name call()
which duplicates this functionality.

License

This article, along with any associated source code and files, is licensed under The BSD License

Share

About the Author

Richard Birkby is a software engineer from London, UK, specializing in .Net. Richard has coded for many different sized companies from small venture-capital funded start-ups, to multi-national corporations (ie Microsoft). When he's not programming, he enjoys driving his sports car or eating curry (although never at the same time!).

Richard helps run CurryPages.com and has several other covert ventures in development. Stay tuned!