As we saw previously in the antivirus and EDR modules, API monitoring
is usually performed by injecting a DLL inside newly‑created
processes to hook various APIs and notify the antivirus to enable
heuristic analysis of the process at runtime. This DLL is injected
inside our stager's process.
And since we have full control over our processes' virtual address space,
we can tamper with this DLL to avoid detection when we
call the monitored API functions.
We have many options at our disposal when it comes to evading API hooks.
Usually, to hook an API, the entry point of the function you want to
hook is patched to redirect code execution into the injected DLL, which
will proxy the call to spy on it.
This means that, in some cases, removing the hook can consist of restoring
the original bytes at the entry point of the function,
which will effectively remove the hook.
Alternatively, you could read the DLL file, which contains the API you want
to use and overwrite the entire code section of the DLL in memory to remove
the hooks for all of its exported functions.
Both of these techniques could be detected by the antivirus if it
checks the integrity of the hooks at runtime.
In such cases, it might be better to simply avoid them completely.
This can be done by copying the DLL,
which contains the APIs you want to call and renaming it so
that it can be reloaded using LoadLibrary.
You can then use GetProcAddress to obtain function pointers on the APIs you
want to call, as we did previously in our custom stager.
Finally,
you can, of course, avoid using the hooked APIs and perform
syscalls without relying on the APIs to perform them.
This will, however,
impact portability since syscalls may change between operating system versions.
As a side note,
some of these techniques will also blind certain ETW providers. Bear in mind
that this is not an exhaustive list of all possible bypasses,
but it is a good starting point.
Let's take a look at how we could remove API hooks,
You may recall that in the antivirus and EDR modules,
we saw how a driver could inject a monitoring DLL inside our
shell code runner to monitor API calls.
You may also recall that we were able to spy on calls
to VirtualAlloc and LoadLibrary.
I have made some adjustments to the custom stager we wrote previously by
adding two new functions, unhook_kernel32 and unhook_ntdll.
The former will use one of the techniques we talked about previously
to overwrite the kernel32 module's code section in memory by reading
a fresh copy of the module on disk.
This has the effect of removing all hooks, which the API monitoring DLL
may have placed in memory for the kernel32 module.
The unhook _ntdll function uses the same technique to
remove the hooks from the ntdll module.
Our stager makes use of the LoadLibrary API to load the WinINet module. And
if you recall, the payload's reflective loader also makes use of the
VirtualAlloc and LoadLibrary APIs. We can launch the driver and start
capturing debug output in DebugView. We can then run our modified stager. We
can stop the driver to avoid capturing too much information and then take a
look at DebugView and process Explorer to see what the monitoring DLL was
able to record at runtime.
As you can see, the driver did inject the monitoring DLL inside
our stager's process, and the DLL was able to hook the LoadLibrary
and VirtualAlloc APIs successfully,
as it did previously when I demonstrated API monitoring
in the antivirus and EDR modules.
This time, however,
you will notice that it did not log the calls to LoadLibrary or VirtualAlloc.
The reason being that we removed the hooks, which the DLL had placed in memory.