Skip to main content
  1. Posts/

Anti-VM, Anti-Debug & Anti-* Bypass Part - 1

·1339 words·7 mins·
TheDeadThinker
Author
TheDeadThinker

Overview
#

Malware is a piece of software which is created by some smart developers who think out of the box and this piece of software is more complex to reverse engineer because they implemented a lot of Anti-Analysis, Anti-Bypass, Anti-VM and all other Anti stuff. Therefore as reverse Engineers and Malware analysts, we should also be aware of how to bypass all the anti.

[!NOTE] : I’m not an expert, but I’m keen on improvement. If you spot any mistakes in my blog, please DM me on any platform you like for constructive feedback.

The sample I have taken from the Minerva CTF

Techniques Covered
#

  • IsDebuggerPresent
  • CPUID Check
  • peb->IsBeingDebugged
  • VMChecks by Name
  • HiddenThread

1. IsDebuggerPresent
#

Understanding Implementation
#

This is a winapi which can be used by the application to check if the application is being debugged by user mode debugger. The API will be called and the result can be compared if it is zero or non-zero. If it is non-zero, it means the program is being debugged and the program will exit.

Assembly & Disassembled Code
#

How to Bypass
#

  • Set the breakpoint on the IsDebuggerPresent API [ in x32 dbg use: bp IsDebuggerPresent ]
  • Execute till the program hits the API breakpoint and then execute till the return
  • On return, the eax register value will be 1 [ non-zero ]
  • Change the value of the eax register to zero, save and continue the execution

2. CPUID Instruction
#

Understanding Implementation
#

This instruction is executed with EAX=1 as input, the return value describes the processor’s features. The 31st bit of ECX on a physical machine will be equal to 0. On a guest VM it will equal to 1. [ Shamelessly taken from cyberbit ]

Assembly & Disassembled Code
#

How to Bypass
#

Method 1
#

  • Execute step by step to find the cpuid instruction
  • Execute till return of the function
  • Update the eax register 31st byte to 0, save and continue the execution

Method 2
#

  • If using the VmWare, update the vmx file
  • Add the following to the bottom of the vmx file
cpuid.1.ecx="0---:----:----:----:----:----:----:----"
  • No need to wait or find the cpuid instruction, It will automatically be handled by the vmx configuration

3. peb->IsBeingDebugged
#

Understanding PEB
#

PEB [ Process Environment Block ] is a critical structure in the Windows OS, most of its fields are not intended to be used by other than the operating system. It contains data structures that apply across a whole process and is stored in user-mode memory, which makes it accessible for the corresponding process [ Taken From Void-Stack Blog ]. The structure contains valuable information about the running process, including:

  • whether the process is being debugged or not
  • which modules are loaded into memory
  • the command line used to invoke the process

Understanding Implementation
#

This structure can be used to find the information if the program is being debugged or not. The below image is from the Microsoft Documents. It shows that the second member of the _ PEB structure is BeingDebugged which we can use to find if a debugger is attached or not.

But when we used the WinDBG to check the _ PEB of a debugger attached process, we found BeingDebugged is not the second member it is the third member [ index 0x02 ]

Checking the implementation of this anti-debug technique in malware we found this is similar to the IsDebugPreesent API.

  • Both first get the address of the PEB Structure
  • Then check the value present at the eax+2 [ Location of the BeingDebugged Member ].
  • If it compares to zero then the program continues otherwise exits.

Assembly & Disassembled Code
#

How to Bypass
#

Method 1
#

  • Execute step-by-step to find where this check implemented

  • Execute till we reach the next instruction of where the program is moving the byte from the PEB to a variable [ mov [ebp+var-6D], al ]

  • Change the value of the al to zero and continue the execution

Method 2
#

  • Execute step-by-step to find where this check implemented
  • Select the jne operation and fill with nops [ 0x90 ] and continue the execution

4. VMChecks by Name
#

Understanding Implementation
#

There can be multiple ways to check the virtualization software used by registry keys, files, directories etc. Example:

Dir: C:\Program Files\VMware
Filename: vmtoolsd.exe

But this malware used the Win API keys to find the name and compare, if the name matches, the malware stops the execution and exits the process.

API Keys Used:

  • SetupDiGetClassDevsW : It is used in Windows programming to retrieve a handle to a device information set that contains information about a specified class of devices.
HDEVINFO SetupDiGetClassDevsW( 
const GUID *ClassGuid, 
PCWSTR Enumerator, 
HWND hwndParent, 
DWORD Flags 
);
  • SetupDiEnumDeviceInfo : This is used to retrieve a specified Plug and Play (PnP) device property for a device instance.
BOOL SetupDiEnumDeviceInfo( 
HDEVINFO DeviceInfoSet, 
DWORD MemberIndex, 
PSP_DEVINFO_DATA DeviceInfoData 
);
  • SetupDiGetDeviceRegistryPropertyW : This API retrieves a specified Plug and Play (PnP) device property for a device instance. This function is often used in conjunction with SetupDiGetClassDevsW and SetupDiEnumDeviceInfo to obtain detailed information about a device.
    • The constant 0xCU is typically used for the property SPDRP_CHARACTERISTICS. The SPDRP_CHARACTERISTICS property represents the characteristics of a Plug and Play (PnP) device.
BOOL SetupDiGetDeviceRegistryPropertyW( 
HDEVINFO DeviceInfoSet, 
PSP_DEVINFO_DATA DeviceInfoData, 
DWORD Property, 
PDWORD PropertyRegDataType, 
PBYTE PropertyBuffer, 
DWORD PropertyBufferSize,
PDWORD RequiredSize 
);

Assembly & Disassembled Code
#

How to Bypass
#

  • Set the breakpoint for the API to find when the execution hit the anti-VM checks

  • In the below image it can be seen that SetupDiGetDeviceRegistryPropertyW found about our disk i.e, VMWARE VIRTUAL NVME DISK

  • The First check is for the VBOX Keyword and then VMWARE keyword is compared.

  • Malware in our case update the al value in both cases VBOX or VMWARE
    • In VBOX case after comparing it follow the jne and then mov 1 to al and ret
    • In VMWARE same is done compare and then comparison, it moves the 1 to al but this time it uses setne
  • Update the al value to 0, because if the function returns 1 then it will exit after few instructions as shown in the below image

5. HiddenThread
#

Understanding Implementation
#

In the Assembly & Disassembled Code image, we can identify that there are two API calls are used:

  • NtCreateThreadEx
  • NtSetInformationThread

NtCreateThreadEx
#

This is an undocumented API which can be used to create Threads.

Function definition is taken from : https://ntdoc.m417z.com/

NtSetInformationThread
#

This API can be used to update the Thread information and malware can also use this API to hide threads.

But why to hide threads ?

The threads will be hidden from the debugger and instructions executed by this hidden thread will not be monitored by the debugger even if we already set the breakpoint on API or instructions.

How this can be achieved ?

Check the 2nd red box in the below image, we have provided the 0x11 as input to the NtSetInformationThread API which will be used to make the thread hidden.

Assembly & Disassembled Code
#

How to Bypass
#

  • The malware is dynamically resolving the address of WIN API: NtCreateThreadEx & NtSetInformationThread, so we can’t directly put the breakpoint and wait, Reason if you setup the breakpoint on the two APIs in my case ZwSetInformationThread is hit but before I reach to the user code and update the value the API is already called and program exit.
  • We can find this either through manual one-step execution[ which is not a good idea ], or the second way by putting the breakpoint on GetProcAddress after you reach the entrypoint of the binary.
  • Once you reach the breakpoint click on the button run to user code as shown in the below image and check if you reach the correct GetProcAddress.
  • After a few false hits we reach the NtSetInformationThread API and found that 11 is pushed on the stack and then call eax instruction (eax stores address of NtSetInformationThread)
  • Change the 0x11 to 0x00 and continue the execution