Nim - Hide the exported NimMain in a DLL

As @byt3bl33d3r describes in the OPSEC Considerations part in the OffensiveNim repository, one thing to consider when using DLLs written in Nim is that the function NimMain is an exported function for DLLs under Windows.

The following short description will show an easy method of removing that export during the compilation process.

Creating a DLL

The following code is a minimal working WIndows DLL in Nim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import winim/lean

proc NimMain() {.nimcall, importc.}

proc DllMain(hinstDLL: HINSTANCE, fdwReason: DWORD, lpvReserved: LPVOID) : BOOL {.stdcall, exportc, dynlib.} =
  NimMain()

  if fdwReason == DLL_PROCESS_ATTACH:
    MessageBox(0, "Hello, world !", "Nim is Powerful", 0)

  return true

This was taken from the OffensiveNim Repository.

This can be compiled with:

nim c -d=mingw --app=lib --nomain --cpu=amd64 --nimcache=dllcache lib.nim

But as @byt3bl33d3r points out, this will result in two exported functions: DllMain and NimMain as seen in the following screenshot:

I haven’t found any way of removing the export of NimMain from within the Nim code, but it can be changed by fiddeling with the resulting C-Code that the Nim compiler generates.

The Nim Compilation Process

The Nim compiler creates all intermediate files (.c files), inlcuding the gcc compiler and linker commands in the nimcache directory. Per default, this directory is under $HOME/.cache/nim/<project>. Nim generates a really convenient JSON file that contains the compiler commands, linker commands and C-files in a structured form.

This can be abused to fiddle with the C-Files and recompile them conveniently to a functioning executable. As doing these kind of things by hand, i wrote a small tool: compFiddler.

Info: The Nim compiler supports the option to supply an arbitrary nimcache directory via --nimcache=customfolder/.

Changing the Exports

The function NimMain is located in the file @mlib.nim.c and looks like that:

1
2
3
4
5
6
7
N_LIB_EXPORT N_CDECL(void, NimMain)(void) {
	void (*volatile inner)(void);
	PreMain();
	inner = NimMainInner;
	initStackBottomWith((void *)&inner);
	(*inner)();
}

By changing N_LIB_EXPORT to N_LIB_PRIVATE and recompiling the DLL with compFiddler --json dllcache/lib.json the NimMain function is not exported anymore.

The exports after the change:

And the DLL still executes as expected: