Get on-the-go access to the latest insights featured on our Trustworthy Computing blogs.
The Windows heap memory is a rich source of anti-debugging techniques. It can be altered in numerous ways to achieve interesting effects, such as the execution of arbitrary code in particular circumstances. It can also be used in indirect ways, since many APIs allocate and/or free memory as part of their standard behaviour. What follows is a description of some of the ones that we might see in malware samples -- to raise awareness among the good guys and remove the element of surprise for the bad guys.
For instance, a corrupted heap can result in a call to the DbgPrint() function, if the appropriate flag is set in the heap flags. This happens by default when a debugger is present. When the DbgPrint() function is called, it raises a special exception, which in turn changes the returned error code. This change can be detected by an application, and it might behave differently as a result.
The most obvious API that produces this effect is the HeapFree() function. Two other common APIs are the DeleteFiber() and the FindVolumeClose() functions. There is a special case in the ConvertFiberToThread() function that can also result in this behaviour. More recent versions of Windows have introduced some new APIs which also demonstrate this behaviour. In Windows Vista and later, there is the BasepFreeAppCompatData() function. In Windows 7, there are the BasepFreeActivationContextActivationBlock() and the SortCloseHandle() functions. The FindVolumeMountPointClose() function exists in all platforms, but the heap-related behaviour exists only on Windows 7. However, that API also calls the CloseHandle() function, and the CloseHandle() detection technique(*) is possible in all versions of Windows.
This kind of behaviour can also be used to call APIs indirectly, which can make static analysis a bit more "interesting". Need to free memory? The FindVolumeClose() function can do that. Open a process and read its memory? The Toolhelp32ReadProcessMemory() function can do that. Call a user-defined function in a sparse structure? The RtlProcessFlsData() function can do that.
Now we know those tricks, we won't be fooled – and when we see the tricks being used, we will know why. That knowledge might even help us to categorise samples immediately, since it seems unlikely that a legitimate piece of software would use indirect APIs to perform its tasks.
-- Peter Ferrie
* By passing an invalid handle to the API, an invalid handle exception is raised, which can reveal the presence of a debugger.