Examining Smokeloader’s Anti Hooking technique

Hooking is a technique to intercept function calls/messages or events passed between software, or in this case malware. The technique can be used for malicious, as well as defensive cases.

Rootkits for example can hook API calls to make themselves invisible from analysis tools, while we as defenders can use hooking to gain more knowledge of malware or build detection mechanisms to protect customers.

Cybersecurity continues to be a game of cat and mouses, and while we try to build protections, blackhats will always try to bypass these protection mechanisms. Today I want to show you how SmokeLoader bypasses hooks on ntdll.dll and how Frida can be used to hook library functions.

The bypass was also already explained in a blog article from Checkpoint[1] written by Israel Gubi. It also covers a lot more than I do regarding Smokeloader, so it is definitely worth reading too.

Hooking with Frida

If you’ve read my previous blog articles about QBot, you are familiar with the process iteration and AV detection[3]. It iterates over processes and compares the process name with entries in a black list containing process names of common AV products. If one process name matches with an entry, QBot quits its execution.

Frida is a Dynamic Instrumentation Toolkit which can be used to write dynamic analysis scripts in high level languages, in this case JavaScript. If you want to know more about this technology, I advice you to read to visit this website[4] and read its documentation.

We can write a small Frida script to hook the lstrcmpiA function in order to investigate which process names are in the black list.

def main():
    """Main."""
    # argv[1] is our malware sample
    pid = frida.spawn(sys.argv[1])
    sess = frida.attach(pid)
    script = sess.create_script("""
        console.log("[+] Starting Frida script")
        var lstrcmpiA = ptr("0x76B43E8E")
        console.log("[+] Hooking lstrcmpiA at " + lstrcmpiA)
        Interceptor.attach(lstrcmpiA, {
            onEnter: function(args) {
                console.log("[+][+] Called strcmpiA");
                console.log("[+][+] Arg1Addr = " + args[0]);
                console.log("[+][+] Buffer");
                pretty_print(args[0], 0x30);
                console.log("[+][+] Arg2Addr = " + args[1]);
                console.log("[+][+] Buffer");
                pretty_print(args[1], 0x30);
            },
            onLeave: function(retval) {
                console.log("[+][+] Returned from strcmpiA")
            }
        });

        function pretty_print(addr, sz) {
            var bufptr = ptr(addr);
            var bytearr = Memory.readByteArray(bufptr, sz);
            console.log(bytearr);
        };

        """)
    script.load()
    frida.resume(pid)
    sys.stdin.read()
    sess.detach()

We attach to the malicious process and hook the lstrcmpiA function at static address. When analysing malware, we have (most of the time) the privilege to control and adjust our environment as much as we want. If you turn off ASLR and use snapshots, using Frida with static pointers is pretty convenient, because most functions will always have the same address. However, it’s also possible to calculate the addresses dynamically. lstrcmpiA has 2 arguments, which are both pointers of type LPSTR. So we just resolve the pointers, fill 0x30 bytes starting at pointer address into a ByteArray and print it.

Result of Frida Script

Smokeloader’s Anti Hooking technique

So how does Smokeloader bypass hooks? Well it can do it atleast for the ntdll.dll library. During execution Smokeloader retrieves the Temp folder path and generates a random name. If a file with the generated name already exists in the temp folder, it is deleted with DeleteFileW.

drltrace output DeleteFileW call, deleting 9A26.tmp in Temp Folder

Next the original ntdll.dll file is copied from system32 to the temp folder with the exact name it just generated. This leads to a copy of this mentioned library being placed in the temp directory.

Meta data of disguised ntdll.dll
Export functions of the disguised ntdll file

Instead of loading the real ntdll.dll file, the copy is loaded into memory by calling LdrLoadDll.

9A26.tmp as ntdll.dll

Most AV vendors, as well as analysts probably implemented their hooks on ntdll.dll, so the references to the copied ntdll.dll file will be missed.

Smokeloader continues to call functions from this copied DLL, using for example function calls like NtQueryInformationProcess to detect wether a debugger is attached to it.

Final Words

While analysing SmokeLoader at work, I stumbled across this AntiHook mechanism, which I haven’t seen before, so I wanted to share it here :-).


I’ve also only scratched on the surface of what Frida is capable of. I might work on something more complex next time.

Scroll to Top