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.
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
.
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.
Instead of loading the real ntdll.dll
file, the copy is loaded into memory by calling LdrLoadDll
.
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.