.NETZ, .NET and the Native DLL Files
There are two aspects with regard to the native DLLs: (a) compressing, and (b) packing them to a single EXE. .NETZ supports native DLLs compressed with other tools, but cannot pack native DLLs.
Compressing
This is not a problem. .NETZ does not compress native DLL files, but other free tools, such as, UPX, can be used to compress native DLLs. The compressed native DLLs can be used in the same way the normal uncompressed native DLLs with the .NET programs. The .NET programs that use native DLLs can be compressed with .NETZ.
For example, create a simple testdll.c file containing:
#include
#include
#define DllExport __declspec( dllexport )
DllExport BOOL WINAPI DllMain (
HANDLE h,
DWORD d,
LPVOID r){
return TRUE;
}
DllExport int test()
{
return 3;
}
Create a DLL testdll.dll from the code above, and call the test() function from a test C# program:
using System;
using System.Runtime.InteropServices;
public class Test
{
[DllImport("testdll")]
public static extern int test();
public static void Main(string[] args)
{
try
{
Console.WriteLine(test());
}
catch(Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
}
Place both the test.exe and the testdll.dll in the same folder and test it. Then compress testdll.dll using UPX:
Run test.exe again to verify that it works. Then compress test.exe to test with .NETZ:
Copy the compressed testdll.dll in the same folder as the compressed test.exe and run test.exe. Again, test.exe can use the compressed native DLL.
Packing
.NETZ cannot handle packing native DLLs into a single EXE. There are two ways to use DLLs in a native (unmanaged) application: either link them statically (load-time dynamic linking) using a *.lib file, or load them dynamically (run-time dynamic linking) by using the ::LoadLibrary(Ex) API function(s) (refer to MSDN for more information).
The .NET CLR does only run-time dynamic linking and relies also on the LoadLibrary(Ex) function to load native DLLs. When a method decorated with the System. Runtime. InteropServices. DllImportAttribute is found in code, the .NET compiler generates a special empty IL method body to hint the .NET run-time where to find the DLL and what method to call:
.method public hidebysig static pinvokeimpl("testdll" winapi)
int32 test() cil managed preservesig {}
When a .NET program is executed and a native method is called for the first time, the CLR loads the corresponding DLL using ::LoadLibrary(Ex) and then locates the test() function using ::GetProcAddress. The documentation of ::LoadLibrary(Ex) contains details where it looks for the DLL file. It is unclear when CLR calls ::FreeLibrary, to remove the DLL from the process space, if it does it ever, but it is possible that it uses some heuristics at run-time to do so.
Because of the ::LoadLibrary(Ex) is it not possible to pack the native DLL with .NETZ. The ::LoadLibrary(Ex) cannot use a HANDLE to an embedded resource, or a pointer to a memory location to load a DLL. All other functions that use DLLs, such as, ::GetModuleHandleEx, rely that that DLL has been properly loaded into the process at some point by using ::LoadLibrary(Ex). If a native DLL is packed, it has to be written at some point into the file system before it can be loaded. .NETZ does not consider creating temporary files in the file system for several side-effects ranging from permissions, space, and read-only volumes, code to clean up the temporary file in case of crash, being only a few. Because of this, .NETZ does not pack native DLLs.
There are two main ways to trick the OS to load packed DLLs from in-memory data (thanks to Adam Byrne, abyrne [at] inpute.com, for mentioning some tools that rely on such techniques - Thinstall, Molebox, WinLicense's XBundler plugin):
- Create and install a virtual file system driver that could map some region of RAM and present it as a logical volume.
- Instrument an EXE to redirect ::LoadLibrary(Ex) calls to some special crafted run-time and/or write an low-level OS driver that processes these calls (for .NET, the CLR run-time needs to be instrumented).
Both these options require installing native system drivers in the user machine, just to run an application, and are not suitable for .NETZ.
If temporary native DLLs file are ok, then the .NET CLR can be tricked to load a DLL at a given point of time of choice, by calling explicitly the ::LoadLibrary(Ex). For example, lets put the testdll.dll not in same folder as the text.exe .NET program, but in a subfolder named .\test\. When test.exe is run, the CLR will call ::LoadLibrary(Ex), which has no way to know that testdll.dll is in the .\test\ subfolder, so it will fail, and a System. DllNotFoundException will be thrown by the CLR:
Unable to load DLL (testdll).
The ::LoadLibrary(Ex) loads a DLL only once in the process memory. Calling ::LoadLibrary(Ex) a second time will only increment the call count. This behavior can be used to load the native testdll.dll at a point before the test() function is called, usually at the very start up. The test.cs example has been modified to do this:
using System;
using System.Runtime.InteropServices;
public class Test
{
[DllImport("testdll")]
public static extern int test();
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string lpFileName);
public static void Main(string[] args)
{
try
{
LoadLibrary(@"test\testdll");
Console.WriteLine(test());
}
catch(Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
}
This way the testdll.dll is loaded explicitly by using LoadLibrary(@"test\testdll"). After this point when CLR ::LoadLibrary(Ex) checks for testdll.dll, it is already loaded and it will not attempt to reload it. This technique can be used to preload the native DLLs from arbitrary file system locations, but in terms of packing without creating file system files it is not useful, and .NETZ does not use it.