Today, we are going to look at how malware evasion works.
Since during analysis the malware will run on a virtual machine, it seems obvious that in order to make the analyst’s job complicated, it has to find a way to understand when it is in a virtual machine.
There are various techniques to do this (red pill).
And in this article, we will analyse and implement some very simple ones, but at the same time quite effective in the case of superficial dynamic analysis.
This way we will be able to recognise them in case of need without being unprepared.
So let’s focus on these two techniques:
- Checking secondary memory space (A VM usually has smaller HD/SSDs)
- Searching for a registry entry (VMs contain particular registry entries)
Malware evasion techniques that look at disk size
But now let’s stop talking and start looking at the code step by step, and the first block will be the imports that don’t need comments.
#include <Windows.h>
#include <tchar.h>
#include <stdlib.h>
#include <processthreadsapi.h>
Now let’s have a look at the first function, where we will check if the secondary memory space is less than 80GB
#define DISK_SIZE_IN_GB 80ULL
BOOL checkDiskSize()
{
ULONGLONG min_disk_size = (DISK_SIZE_IN_GB * (1024ULL * (1024ULL * (1024ULL))));
ULARGE_INTEGER total_bytes;
BOOL disk_free_space = GetDiskFreeSpaceEx(
NULL,
NULL,
&total_bytes,
NULL
);
if(disk_free_space)
if ( total_bytes.QuadPart > min_disk_size)
return FALSE;
return TRUE;
}
The function used to recognise the VM is GetDiskFreeSpace, and if we look at the documentation we can see that the third parameter is:
PULARGE_INTEGER lpTotalNumberOfBytes
So it is a pointer to a variable whose content will be set to the value of the total bytes of secondary memory.
The next step is to compare the value obtained with the minimum value of bytes that we have decided beforehand.
At that point, if it is lower, it will most likely be in a VM and will therefore return TRUE, otherwise, it will return FALSE.
Malware evasion techniques that look at registry
Having seen the first technique,
let’s move on to the second, in which we will search the machine for a specific VirtualBox registry key.
BOOL checkRegistry()
{
TCHAR vbox_registry[] = _T("HARDWARE\\ACPI\\DSDT\\VBOX__");
HKEY hkResult = NULL;
TCHAR lpData[1024] = { 0 };
DWORD cbData = MAX_PATH;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, vbox_registry, NULL, KEY_READ, &hkResult) == ERROR_SUCCESS)
{
RegCloseKey(hkResult);
return TRUE;
}
return FALSE;
}
In this case, we have RegOpenKeyEx to help us.
As we can see in the documentation, it tries to open a registry key whose path is specified by the first two parameters.
If successful, the return value is ERROR_SUCCESS.
So the function can return :
- TRUE because if the key exists and we are on VirtualBox.
- FALSE otherwise
The main
And now let’s write a simple main that launches a calculator in case it is not in a virtual machine.
int main()
{
if (!checkDiskSize() && !checkRegistry())
{
BOOL stats = 0;
PROCESS_INFORMATION processInfo;
STARTUPINFO startinfo = { sizeof(startinfo) };
BOOL result = CreateProcess("C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL,
&startinfo, &processInfo);
printf("%d", result);
}
return 0;
}
Now call the file “evasion.cpp” and compile it with the following line by placing native tools in the working directory.
cl.exe /W0 /Tcevasion.cpp /link /DEFAULTLIB:advapi32.lib /OUT:evasion.exe
Put all together
And finally, the complete code:
#include <Windows.h>
#include <tchar.h>
#include <stdlib.h>
#include <processthreadsapi.h>
#define DISK_SIZE_IN_GB 80ULL
BOOL checkDiskSize()
{
ULONGLONG min_disk_size = (DISK_SIZE_IN_GB * (1024ULL * (1024ULL * (1024ULL))));
ULARGE_INTEGER total_bytes;
BOOL disk_free_space = GetDiskFreeSpaceEx(
NULL,
NULL,
&total_bytes,
NULL
);
if(disk_free_space)
if ( total_bytes.QuadPart > min_disk_size)
return FALSE;
return TRUE;
}
BOOL checkRegistry()
{
TCHAR vbox_registry[] = _T("HARDWARE\\ACPI\\DSDT\\VBOX__");
HKEY hkResult = NULL;
TCHAR lpData[1024] = { 0 };
DWORD cbData = MAX_PATH;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, vbox_registry, NULL, KEY_READ, &hkResult) == ERROR_SUCCESS)
{
RegCloseKey(hkResult);
return TRUE;
}
return FALSE;
}
int main()
{
if (checkDiskSize() && checkRegistry())
{
BOOL stats = 0;
PROCESS_INFORMATION processInfo;
STARTUPINFO startinfo = { sizeof(startinfo) };
BOOL result = CreateProcess("C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL,
&startinfo, &processInfo);
printf("%d", result);
}
return 0;
}
Conclusion
In conclusion, during your analysis, always be paranoid and accurate, because often limit ourselves to a superficial dynamic analysis and relying only on tools such as :
- process explorer
- process monitor
- regshot
- etc
may lead us to some dangerous errors.
I also provide some links that may be useful for further study:
- VMCloack (useful for neutralising the less sophisticated evasion techniques)
- Al-khaser (Interesting open source tool that allows us to examine numerous techniques of evasion)
Further readings
If you found it interesting to read, I recommend the following articles.