Skip to content

Commit

Permalink
Merge pull request #16 from israelgu/master
Browse files Browse the repository at this point in the history
Added new techniques related to Raspberry Robin
  • Loading branch information
chkp-alexanderc committed May 31, 2024
2 parents 639b468 + 5b19ce1 commit dc3e3a7
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 4 deletions.
44 changes: 44 additions & 0 deletions _src/Anti-Debug/techniques/assembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ tags: assembly
* [6. Instruction Counting](#instruction-counting)
* [7. POPF and Trap Flag](#popf_and_trap_flag)
* [8. Instruction Prefixes](#instruction_prefixes)
* [9. POPF and CPUID](#popf_and_cpuid)
* [Mitigations](#mitigations)
<br />

Expand Down Expand Up @@ -404,6 +405,49 @@ bool IsDebugged()

<hr class="space">


<br />
<h3><a class="a-dummy" name="popf_and_cpuid">9. POPF and CPUID</a></h3>

This technique is similar to [7. POPF and Trap Flag](#popf_and_trap_flag).
To detect the use of a VM in a sandbox, malware could check the behavior of the CPU after the trap flag is set.
The trap flag is a flag bit in the processor's flags register that is used for debugging purposes.
When the Trap Flag is set, the processor enters a single-step mode, which causes it to execute only one instruction at a time and then generate a debug exception.

For example, The <tt>popf</tt> instruction pops the top value from the stack and loads it into the flags register. Based on the value on the stack that has the Trap Flag bit set, the processor enters a single-step mode (SINGLE_STEP_EXCEPTION) after executing the next instruction.

But the next instruction is <tt>cpuid</tt> which behaves differently in VM. When in a physical machine, this exception stops the CPU execution to allow the contents of the registers and memory location to be examined by the exception handler after thecpuid instruction which moves away the instruction pointer from the next bytes. In a VM, executing cpuid will result in a VM exit. During the VM exit the hypervisor will carry out its usual tasks of emulating the behaviors of the cpuid instruction which will make the Trap Flag be delayed and the code execution will continue to the next instruction with the C7 B2 bytes. This results in an exception because of an illegal instruction exception.

<b>C/C++ Code</b>
<p></p>

{% highlight c %}

bool IsDebugged()
{
__try
{
__asm
{
pushfd
popfd
cpuid
C7 B2
}
return true;
}
__except(GetExceptionCode() == EXCEPTION_SINGLE_STEP
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_EXECUTION)
{
return false;
}
}

{% endhighlight %}

<hr class="space">

<br />
<h3><a class="a-dummy" name="mitigations">Mitigations</a></h3>
* During debugging:
Expand Down
74 changes: 72 additions & 2 deletions _src/Anti-Debug/techniques/interactive.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ tags: interactive
* [5. EnumWindows() and SuspendThread()](#suspendthread)
* [6. SwitchDesktop()](#switchdesktop)
* [7. OutputDebugString()](#outputdebugstring)
* [8. Process Suspension Detection](#processsuspensiondetection)
* [Mitigations](#mitigations)
<br />

Expand Down Expand Up @@ -47,7 +48,7 @@ In the example below, we run the second instance of our process which tries to a
#define EVENT_SELFDBG_EVENT_NAME L"SelfDebugging"

bool IsDebugged()
{
{
WCHAR wszFilePath[MAX_PATH], wszCmdLine[MAX_PATH];
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
Expand Down Expand Up @@ -430,6 +431,74 @@ bool IsDebugged()
<hr class="space">

<br />

<hr class="space">

<br />

<h3><a class="a-dummy" name="processsuspensiondetection">8. Process Suspension Detection</a></h3>

This evasion depends on having the thread creation flag <tt>THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE</tt> that Microsoft added into Windows 10, version 1903 (19H1). This flag makes the thread ignore any <tt>PsSuspendProcess</tt> API being called.

Then an attacker can create two threads with this flag, one of which keeps suspending the other one until the suspend counter limit which is 127 is reached (suspend count is a signed 8-bit value).

When you get to the limit, every call for <tt>PsSuspendProcess</tt> doesn’t increment the suspend counter and returns <tt>STATUS_SUSPEND_COUNT_EXCEEDED</tt>. But what happens if someone calls <tt>NtResumeProcess</tt>? It decrements the suspend count!
So when someone decides to suspend and resume the thread, they’ll leave the count in a state it wasn’t previously in.

<b>C/C++ Code</b>
<p></p>

{% highlight c %}

// Maximum suspend count before STATUS_SUSPEND_COUNT_EXCEEDED error is returned
#define MAX_SUSPEND_COUNT 127

// Function to spawn the thread that suspends the other thread
DWORD WINAPI SuspendThreadFunction(LPVOID lpParam) {
HANDLE hThread = (HANDLE)lpParam;
while (true) {
// Suspend the other thread
SuspendThread(hThread);
// Sleep for some time before resuming the other thread
Sleep(1000);
}
return 0;
}

int main() {
// Create two threads
HANDLE hThread1 = NULL, hThread2 = NULL;
hThread1 = CreateThread(NULL, 0, SuspendThreadFunction, &hThread2, THREAD_CREATE_FLAGS_BYPASS_PROCESS_SUSPEND, NULL);
hThread2 = CreateThread(NULL, 0, SuspendThreadFunction, &hThread1, THREAD_CREATE_FLAGS_BYPASS_PROCESS_SUSPEND, NULL);

// Loop to periodically suspend the second thread and check if it's externally suspended
while (true) {
// Suspend the second thread
SuspendThread(hThread2);
// Check if the last error is STATUS_SUSPEND_COUNT_EXCEEDED
DWORD lastError = GetLastError();
if (lastError != STATUS_SUSPEND_COUNT_EXCEEDED) {
printf("Thread is externally suspended!\n");
// Terminate the process
TerminateProcess(GetCurrentProcess(), 0);
}
// Sleep for some time before the next check
Sleep(2000);
}

// Close thread handles
CloseHandle(hThread1);
CloseHandle(hThread2);

return 0;
}


{% endhighlight %}


<br />

<h3><a class="a-dummy" name="mitigations">Mitigations</a></h3>
During debugging, it is better to skip suspicious function calls (e.g. fill them with <tt>NOP</tt>s).

Expand All @@ -446,4 +515,5 @@ If you write an anti-anti-debug solution, all the following functions can be hoo
* <tt>user32!SwitchDesktop</tt>
* <tt>kernel32!OutputDebugStringW</tt>

Hooked functions can check input arguments and modify the original function behavior.
Hooked functions can check input arguments and modify the original function behavior.
To circumvent the Process Suspension detection evasion, you can hook <tt>NtCreateThread</tt> to omit the <tt>THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE</tt> flag.
61 changes: 61 additions & 0 deletions _src/Anti-Debug/techniques/misc.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ tags: misc
* [5. DbgSetDebugFilterState()](#dbgsetdebugfilterstate)
* [6. NtYieldExecution() / SwitchToThread()](#switchtothread)
* [7. VirtualAlloc() / GetWriteWatch()](#getwritewatch)
* [8. IFEO removal](#ifeo-removal)
* [Mitigations](#mitigations)
<br />

Expand Down Expand Up @@ -486,6 +487,66 @@ bool Generic::CheckWrittenPages2() const {

<hr class="space">

<br />

<h3><a class="a-dummy" name="ifeo-removal">8. IFEO removal</a></h3>

This technique involves modifying the Image File Execution Options (IFEO) registry key, which is used by the Windows operating system to set debugging options for executable files.
When an executable file is launched, the operating system checks the corresponding IFEO registry key for any specified debugging options. If the key exists, the operating system launches the specified debugger instead of the executable file.
Removing these entries further complicates analysis efforts by eliminating one potential avenue for researchers to attach debuggers to the malware process.

<table style="width:100%">
<tr>
<td colspan="2">Check if the following process names are being removed (also check if the current process name is being removed)</td>
</tr>
<tr>
<th style="text-align:center">Path</th>
<th style="text-align:center">value</th>
</tr>
<tr>
<th rowspan="6">HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options</th>
<td>rundll32.exe</td>
</tr>
<tr>
<td>regsvr32.exe</td>
</tr>
<tr>
<td>dllhost.exe</td>
</tr>
<tr>
<td>msiexec.exe</td>
</tr>
<tr>
<td>explorer.exe</td>
</tr>
<tr>
<td>odbcconf.exe</td>
</tr>
</table>

<b>C/C++ Code</b>
<p></p>

{% highlight c %}

int main() {
// Define the registry key path for explorer.exe under IFEO
LPCWSTR keyPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\explorer.exe";

// Attempt to open the registry key
LONG result = RegDeleteKey(HKEY_LOCAL_MACHINE, keyPath);

if (result == ERROR_SUCCESS) {
std::cout << "IFEO entry for explorer.exe successfully deleted." << std::endl;
} else {
std::cerr << "Failed to delete IFEO entry for explorer.exe. Error code: " << result << std::endl;
}

return 0;
}

{% endhighlight %}

<br />
<h3><a class="a-dummy" name="mitigations">Mitigations</a></h3>
During debugging: Fill anti-debug pr anti-traced checks with <tt>NOP</tt>s.
Expand Down
20 changes: 20 additions & 0 deletions _src/Evasions/techniques/generic-os-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ Function used:
</ul>

Besides this function numbers of processors can be obtained from PEB, via either asm inline or intrinsic function, see code samples below.
It can be also obtained (ActiveProcessorCount flag) from the KUSER_SHARED_DATA structure.

<hr class="space">

Expand Down Expand Up @@ -436,10 +437,29 @@ int gensandbox_one_cpu_GetSystemInfo() {

{% endhighlight %}


<i>Credits for this code sample: <a href="https://github.com/a0rtega/pafish">pafish project</a> </i>

<hr class="space">

<b>Code sample (variant 4)</b>
<p></p>

{% highlight c %}

__declspec(naked)
DWORD get_number_of_active_processors() {
__asm {
mov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure fixed address
mov eax, byte ptr [eax+0x3c0] ; checking ActiveProcessorCount
retn ; return from function
}
}

{% endhighlight %}

<hr class="space">

<b>Countermeasures</b>
<p></p>

Expand Down
60 changes: 60 additions & 0 deletions _src/Evasions/techniques/os-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ tags: os-features
[1. Checking debug privileges](#checking-debug-privileges)
<br />
[2. Using unbalanced stack](#using-unbalanced-stack)
<br />
[3. Detect Wine](#detect-wine)
<br />
[Countermeasures](#countermeasures)
<br />
Expand Down Expand Up @@ -95,6 +97,7 @@ BOOL CanOpenCsrss()

<hr class="space">


<b>Signature recommendations</b>
<p></p>
If <tt>OpenProcess</tt> requests all the possible rights when opening one of the critical system processes — it's a strong indicator of malware trying to apply this evasion technique.
Expand Down Expand Up @@ -204,12 +207,69 @@ bool Cuckoo::CheckUnbalancedStack() const {
<p></p>
Signature recommendations are not provided as it's pretty tricky to track such a behavior on malware side.

<br />

<h3><a class="a-dummy" name="detect-wine">3. Detect Wine</a></h3>

<hr class="space">

The <tt>MulDiv</tt> [API](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-muldiv) is being called with specific arguments (<tt>`MulDiv(1, 0x80000000, 0x80000000)`</tt>) which should logically return 1 - however, due to a bug with the ancient implementation on Windows, it returns 2.

There are more known evasion methods to detect Wine like the good old check of searching for the existence of one of Wine’s exclusive APIs such as <tt>`kernel32.dll!wine_get_unix_file_name`</tt> or <tt>`ntdll.dll!wine_get_host_version`</tt>) as also mentioned in <a href="https://evasions.checkpoint.com/src/Evasions/techniques/processes.html#check-if-specific-functions-are-present-in-specific-libraries
">Processes evasion techniques</a>.

<b>Code sample</b>
<p></p>

<hr class="space">

{% highlight c %}

int Check_MulDiv_1() {
// Call MulDiv with specific arguments
int result = MulDiv(1, 0x80000000, 0x80000000);

// Check if the result matches the expected value
if (result != 2) {
std::cout << "MulDiv evasion method detected: Wine environment." << std::endl;
} else {
std::cout << "MulDiv evasion method not detected." << std::endl;
}

return 0;
}

int Check_MulDiv_2() {
// Check for the existence of Wine's exclusive APIs
HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
FARPROC wineGetUnixFileName = GetProcAddress(hKernel32, "wine_get_unix_file_name");
HMODULE hNtdll = GetModuleHandle("ntdll.dll");
FARPROC wineGetHostVersion = GetProcAddress(hNtdll, "wine_get_host_version");

if (wineGetUnixFileName || wineGetHostVersion) {
std::cout << "Wine's exclusive APIs detected: Wine environment." << std::endl;
} else {
std::cout << "Wine's exclusive APIs not detected." << std::endl;
}

return 0;
}

{% endhighlight %}

<hr class="space">

<b>Signature recommendations</b>
<p></p>
Check if <tt>`MulDiv(1, 0x80000000, 0x80000000)`</tt> is being called.

<br />
<h3><a class="a-dummy" name="countermeasures">Countermeasures</a></h3>

<ul>
<li><tt>versus checking debug privileges:</tt> hook <tt>OpenProcess</tt> and track for critical system processes PIDs — then return an error.</li>
<li><tt>versus using unbalanced stack:</tt> 1) stack adjusting before function call; 2) kernel-mode hooking.</li>
<li><tt>versus Detect Wine:</tt> If Using Wine, hook MulDiv to return 2 or modify the implementation as it works in Windows. </li>
</ul>

<br />
Expand Down
7 changes: 5 additions & 2 deletions _src/Evasions/techniques/processes.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,17 @@ then it's an indication of application trying to use this evasion technique.
<th style="text-align:center">Function</th>
</tr>
<tr>
<th rowspan="2">Wine</th>
<th rowspan="3">Wine</th>
<td>kernel32.dll</td>
<td>wine_get_unix_file_name</td>
</tr>
<tr>
<td>ntdll.dll</td>
<td rowspan="2">ntdll.dll</td>
<td>wine_get_version</td>
</tr>
<tr>
<td>wine_get_host_version</td>
</tr>
</table>

<br />
Expand Down
Loading

0 comments on commit dc3e3a7

Please sign in to comment.