Marshal and Native API bridging on Microsoft Windows (NT)

| No Comments | No TrackBacks
Marshal and Native API bridging on Microsoft Windows (NT)

The .NET framework provides a Marshal class from its Runtime.InteropServices namespace which helps interfacing native and unmanaged data with managed code. The easy path for most of these cases is to simply use unsafe blocks and cast a pointer, but you end up losing references to allocated structures, leaking memory and likely leaving some funny exploitable condition in your unmanaged code bridge. Those pesky dangling pointers...

The function below calls an internal method to retrieve the list of loaded kernel modules from userland. It depends on NtQuerySystemInformation() and requires a heap-allocated structure array. Interfacing this with a C# managed class will require another exported function to call HeapFree() and release the allocated memory.
Using such an approach is certainly not recommended but it will cut you some hassle:

extern "C" __declspec(dllexport) PSYSTEM_MODULE_INFORMATION GetKernelModules(void)
{
	HANDLE tmpHeap = GetProcessHeap();
	PSYSTEM_MODULE_INFORMATION modList = NULL;
	
	LoadFunctionPointers();
	_getSysModules(&modList, tmpHeap);

	return modList;
}

extern "C" __declspec(dllexport) void MyFreeHeap(LPVOID ptrToFree)
{
	HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, ptrToFree);
}

On the C# side, we will be using a Marshal structure declaration in order to be able to use the PtrToStructure method, which allows us to copy memory from unmanaged space into our managed class, and then we can release whatever memory was allocated for the native API.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
public struct SYSTEM_MODULE_INFORMATION
{
    [MarshalAs(UnmanagedType.U4)] public UInt32 Reserved1;
    [MarshalAs(UnmanagedType.U4)] public UInt32 Reserved2;
    ...
}

int curOffset = (int)(i * Marshal.SizeOf(Modules[i]));
IntPtr curPtr = new IntPtr(ModulesListPtr.ToInt32() + curOffset);

Modules[i] = (SYSTEM_MODULE_INFORMATION) Marshal.PtrToStructure(curPtr,
                   typeof(SYSTEM_MODULE_INFORMATION));

[DllImport("mylib.dll", CharSet = CharSet.Unicode)]
        private extern static void MyFreeHeap(IntPtr ptr);

Depending on your target platform, you might want to adjust CharSet since Unicode is the default on NT based systems (that is, all modern versions of Windows, excluding 9x/ME if you consider them modern... although in terms of security, it seems like Windows 98 is safer, after all, most malware doesn't work on it anymore). Packing is also important, since it means how your structure is actually stored on memory. Values of 1-2 are safe, just verify the alignment of the variables within the structure you are trying to use.
Some suggestions:

  • In your bridge DLL, don't resort to typical pointer overwrites to return or store information to the calling function. Using GCHandles to deal with such functions will cause a great deal of stupid exceptions about incorrect references in the internal GC object tracking. Let's say that .NET GC does not like to release handle with wrong pinned object addresses. Save you some debugging time, and in the meanwhile, do proper unmanaged programming.
  • Your bridge DLL should allocate heap memory and set or return a pointer to that location. Then your managed class should call a pInvoke or similar method that itself works with HeapFree within your DLL bridge library. You could also use Marshal.AllocHCGlobal
  • Use a GCHandle when you need to write data from your unmanaged code directly, and remember to release it once you are done with it. But never overwrite the address of managed object or you will end up hitting an invalid free whenever the GC attempts to release your now corrupted object. And that might happen in a manner that makes debugging a pain in the ass. Better go clubbing than waste your time debugging that.

No TrackBacks

TrackBack URL: http://www.subreption.com/mt/mt-tb.fcgi/98

Leave a comment

About this Entry

This page contains a single entry by Subreption LLC published on September 11, 2008 3:44 PM.

Pyblosxom and mod_wsgi benchmark was the previous entry in this blog.

Custom shellcode and return-to-libc on Mac OS X is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.