In short words, I am working on a tool which can generate signatures (which can be used in a Disasm) for all Nim stdlib functions. The goal is to have binary signatures for Nim stdlib funcs/procs so a reverse engineer analysing a Nim malware binary can focus on the procedures written by the malware author. This means I need the function name and the compiled machine code (generated by different compiler flags). I can change the code under libpure e.g. in math.nim e.g. the 'binom' func from 'func binom*(n, k: int): int =' to 'func binom*(n, k: int): int {.stdcall,exportc,dynlib.} =' and compile it as staticlib or DLL. (the 2nd stage tool I am using can read object files in static libs or DLLs and can generate the signatures from there). Doing these changes manually, of course doesn't scale. It's more complex than I can describe in three sentences here, but hope that gives you some background about the question I have:
So the question: Is there any way that I can tell the compiler to built code and symbols for all funcs/procs in a source file (e.g. math.nim) ? Resulting file should be either a static lib or a DLL which includes the symbols for all the function and the compiled machine code of all the function in the stdlibs e.g. math.nim etc.
I am super new to Nim, but I guess there must be at least a method to produce DLLs for all the stdlibs, doesn't it ?
You can compile "nimrtl.dll" but it's usually not done and the malware author is unlikely to use it. Now, to make matters worse or better if the malware cares about code size it was compiled with -d:release or -d:danger. If so then Nim optimized out unused functions whether in the standard library or elsewhere.
To make matters even worse, the produced machine code for the standard library is different and depends on the used mm switch. Likely the malware defaults to mm:refc but it could also be any of arc, orc, none, markAndSweep. Then there is the used exception handling strategy and the nim c vs nim cpp switches. And which C++ compiler was used. That's roughly 5*2*3*2 = 60 different combinations, some more likely than others.
Now, who am I to explain your job, but you should disassemble the malware and your detector should focus on Windows API calls as these will always end up doing the harmful work. os.nim and osproc.nim likely encapsulated these so you need to build a call graph and the functions which end up calling into os.nim might contain the harmful malware logic.
Can you share a disassembling of the malware with us? Then I might be able to help you more effectively.
Long story short, my problem is that the Nim compiler doesn't inlcude the functions (of the common Nim libs like math.nim) if the function is not used in the code or it has the {.stdcall,exportc,dynlib.} pragma. Which makes totally sense to keep the binary smaller for normal compiler cases, but not for mine. If I built a lib for math.nim for example:
nim c -d:release --opt:size --app:staticlib -o:".mathtest-rel-size.lib" math.nim
it would not include the code and symbol for 'binom', because the binom function is not used anywhere and not defined with the {.stdcall,exportc,dynlib.} pragma.
func binom*(n, k: int): int =
I can manually change it to
func binom*(n, k: int): int {.stdcall,exportc,dynlib.} =
... but as I mentioned earlier, doing this manually, doesn't scale for an automated process.
if there would be a "global" pragma or compiler flag in Nim which I could use to tell the compiler to include all functions in the source code in the compiled binary, it would solve my problem.
There is
nim c -d:release lib/nimrtl.nim
which builds nimrtl.dll but it has the same problem: Procs that are not annotated with rtl will be missing. But rtl does cover most things from os.nim so it is a starting point.
Something I do to convince the compiler to export (non generic) procs is, as you mentioned, add {.dynlib.}
Maybe you don't know, you can use the {.push.} pragma to apply a pragma to all following procedures. So
{.push,dynlib.}
proc foo()
proc bar()
...
#optional {.pop.}
To instantiate generic procs you could use a macro that iterates over all methods in a file and instantiate them for your choice of type