<jupyter_start><jupyter_text>Nim Malware Analysis From the malware analysis perspective, Nim has some fascinating characteristics. Let's focus on one for this talk (I am happy to talk about this for hours if you want!) All Nim Windows Binaries Dynamically Resolve Their API CallsLet's perform an experiement.In the `samples` directory, I have a number of malware samples. We will examine a program written in Nim and a program written in a different language and compare them.The first sample is a simple, four line C++ program that calls the ShellExecute API. Let's examine the code:<jupyter_code>!cat ../samples/src/shellexec/cpp_shellexec/cpp_shellexec/cpp_shellexec.cpp<jupyter_output><empty_output><jupyter_text>The code is incredibly simple, but something interesting happens when this code is compiled. The compiler identifies the ShellExecute API in the code and links the required DLL and API into the binary's Import Address Table (IAT). The IAT acts as a phone book for the API calls that a binary uses during execution.When this C++ program is compiled and examined, its IAT includes the ShellExec API call:![image.png](attachment:e074e84a-74be-41c9-b155-63c797869635.png)This can inform malware analysts very quickly about the existence of malicious code in a binary.In the `src` directory of this notebook, I've included a copy of `MalAPIReader`, a collaboration between myself, [@SquiblydooBlog](https://twitter.com/SquiblydooBlog) and [mrd0x](https://twitter.com/mrd0x). This program is a Python script that pulls the Import Address Table out from a portable executable and checks each Windows API call against https://malapi.io. This helps to find API calls that are commonly used by malware.We will use `MalAPIReader` with two different PEs in this directory and compare the difference. First, let's examine this C++ program.---<jupyter_code>!python3 ../src/MalAPIReader/MalAPIReader.py --pe ../samples/shellexec/cpp_shellexec.exe<jupyter_output><empty_output><jupyter_text>---The MalAPIReader script should have identified `ShellExecute`, a Win API that is commonly used to interact with files. We can then examine this API call and its parameters to identify what the API call is doing:<jupyter_code>import pefile

pe =  pefile.PE("../samples/shellexec/cpp_shellexec.exe")

for entry in pe.DIRECTORY_ENTRY_IMPORT:
    if "SHELL32.dll" in str(entry.dll):
        for imp in entry.imports:
            print('[*] API Located at: \t', hex(imp.address), imp.name)<jupyter_output><empty_output><jupyter_text>If we open this PE in something like Cutter, we can locate the function by cross-referencing the ShellExecW string:![image.png](attachment:0c01f92b-432b-4aaa-abea-0b13e5523f74.png)... and we see the API call. We can then examine its parameters.This call simply opens notepad.exe:![image.png](attachment:d8fef16c-3ce4-45be-b2ea-1d0ae1bd4fde.png) ---Now, let's do the same thing for a program that does the exact same thing, but is written in Nim.<jupyter_code>!cat ../samples/src/shellexec/nim_shellexec/shellexec.nim<jupyter_output><empty_output><jupyter_text>The Nim POC makes use of the `winim` library, which allows for direct access to Win API calls.Let's run MalAPIReader against this sample:<jupyter_code>!python3 ../src/MalAPIReader/MalAPIReader.py --pe ../samples/shellexec/nim_shellexec.exe<jupyter_output><empty_output><jupyter_text>---We do get some hits for potentially malicious API calls, and we even get a few that were not in the C++ example, but there is one noteable omission.**ShellExecuteA** is not in the output.This is because when Nim seeks to link API calls within a Windows binary, it does not add them to the IAT. Instead, it utilizes two API calls, **LoadLibraryA** and **GetProcAddress**, to resolve these API calls dynamically when the binary is executed.What this looks like at the ASM level:![image.png](attachment:cd63b9d2-57ce-41fa-bd3c-f0d467779f89.png)There are interesting OPSEC implications for this method of API resolution. Consider that you could fill a Nim binary with many different API calls that are normally flagged as malicious and static detection mechanisms will not identify them. That does not mean they are undetectable, however! An EDR platform can still identify all sus API calls and load their EDR hooks into each one to inspect the API's functionality.The Nim binary's IAT:![image.png](attachment:3d6b22fb-851d-4b56-b02f-19169e604786.png)These API calls are common to most Nim binaries and always include GetProcAddress and LoadLibrary.<jupyter_code>import pefile

pe =  pefile.PE("../samples/shellexec/nim_shellexec.exe")

for entry in pe.DIRECTORY_ENTRY_IMPORT:
    print("[*] DLL: " + str(entry.dll))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.07788624787775891, 'fraction_numerical': 0.021434634974533108, 'mean_word_length': 5.225891677675033, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '540836', 'license': 'MIT', 'n_tokens_mistral': 1453, 'n_tokens_neox': 1371, 'n_words': 566, 'path': 'notebooks/MalwareAnalysis.ipynb', 'repo_name': 'HuskyHacks/the-crown-defcon615', 'size': '4712'}