Tuesday, May 6, 2025

Uroboros Revisited: Tracing PatchGuard-Evasive Techniques Beyond SSDT Hooking

 



Uroboros Revisited: Tracing PatchGuard-Evasive Techniques Beyond SSDT Hooking

A status-based anti-analysis case study on NtProtectVirtualMemory interception in x64 Windows

 

 

 

“To understand the immeasurable, the mind must be extraordinarily quiet, still.”
— Jiddu Krishnamurti

 

By Seeker(李标明) China

Independent Malware Analyst & Researcher

Download the Full Report (PDF)


Poem: It is one way I talk to myself

Paper Boat
Recalling my childhood,
A small paper boat folded on a rainy day,
Sinking as it drifted down the ditch.
From the little wooden door, it floated
Toward the foot of the mountain.
Again and again, I folded those paper boats —
Just I and the boat.

By Seeker(李标明), 2025.4.1


Prologue: The Temple and the Kernel

Recently, I still climbed a mountain and visited the temple again and again, as you know from my last report, the SSDT hooking report mentioned. Here I keep moving to find more.

 

After discovering the SSDT hooking, I have recovered from a bad cold, and I’m happy and very eager for knowledge and doing deeper research. And it was a nice day, and I was sitting in a place in the temple and doing internal observation; the lovely dogs were sitting around me silently; the feeling was just the feeling.


Sample choice: Uroboros

Yeah, it’s still the nation-state group APT called Turla.

 

Sample md5: ed785bbd156b61553aaf78b6f71fb37b                          


Status-Based Anti-Analysis: High-Risk and Rare APIs

In fact, to face the unknown things, I have no solid solution but try to do more trying from different paths, especially on the background without enough understanding. I picked some high-risk APIs to test one by one, such as NtLoadDriver, NtWriteVirtualMemory, and NtQuerySystemInformation. When I debug the API NtProtectVirtualMemory, it seems very suspicious.

 

What’s the kernel API NtProtectVirtualMemory, and what’s the purpose?

NtProtectVirtualMemory is an internal Windows system call (syscall) that modifies the memory protection flags of a specified region in a process's virtual address space. One of its primary purposes is to allow modifying permissions (read/write/execute) for memory pages. Malware often uses it to bypass user-mode API monitoring.

 

disassemble NtProtectVirtualMemory with L15, These strings “::NNGAKEGL::“ took my attention again; very possibly compiler-generated strings, it seems not a normal syscall layout.

Fig.1 the suspicious strings

 

 

It is highly possible about potential kernel manipulation. The unrelated error codes 0C00000F1h that Should Not Return This Status, the debug breakpoints “int 3” that are not present in clean window kernels, and the cross-function jump to NtCreateThreadEx are very highly suspicious error handling and critical red flags. And especially comparison with the standard and clean NtProtectVirtualMemory, it highly confident this potential inline hooks or patches.

 

Normally, NtProtectVirtualMemory only returns:

0x00000000 (STATUS_SUCCESS)

0xC0000045 (STATUS_INVALID_PAGE_PROTECTION)

0xC0000005 (STATUS_ACCESS_VIOLATION)

STATUS_SECTION_NOT_EXTENDED (0xC00000F1) is related to memory section objects, not memory protection.

 

The above unexpected NTSTATUS value—0xC00000F1 (STATUS_SECTION_NOT_EXTENDED)—was observed. This status code is conventionally tied to NtExtendSection, where it signals a failure to extend a section object. Legitimate use of STATUS_SECTION_NOT_EXTENDED should only occur inside NtExtendSection and closely related memory expansion scenarios.

 

So we are highly confident this is a deception trick. Its value is valid, not custom-defined; it is a legitimacy disguise. It passes if the lazy scanner checks the error code. Fake a failure that causes tools relying on NtProtectVirtualMemory to misbehave even to break, which the primary purpose is to anti-debug and anti-EDR; some EDRs think it just failed, not that it was tampered with.

 

Totally, it is unexpected NTSTATUS codes, and it's advanced, elegant, even artistic. I respect the kind of understanding and thinking. It’s amazing. It’s deception artistic. I temporarily called this being “Status-Based Anti-Analysis,” and it's clearly crafted to mislead sandboxes or researchers.

 

I have to make clear the concept of “status-based anti-analysis.” I’m not sure if it is widely accepted in the cybersecurity field, but it is widely recognized and studied by security researchers, reverse engineers, and defenders as a common malware tactic.

 

So we can understand “Status-Based Anti-Analysis” like this:

A stealth technique where malware deliberately returns valid but misleading NTSTATUS values to trick sandboxes, EDRs, or analysts into believing an operation failed or behaved normally — even when it actually succeeded or did something suspicious.

Fig.2 rare statue error code and debug breakpoints

 

 

Error Handler at 'fffff80002da15f0', that appears to be a shared error-handling stub for NtProtectVirtualMemory. It loads different NTSTATUS error codes into EAX before jumping to a common cleanup routine (NtProtectVirtualMemory+0x183). and Cleanup Routine at fffff80002cfa303, it is a standard function epilogue, and there are no signs of tampering here.

Fig.3 No signs of tampering here

 

 

PatchGuard: Partial Visibility in Windows 7 x64

 

Although this suspicious clue was discovered by now, we still need to know more and confirm it again. So I went back to the early part of the flow, which is from user mode to kernel mode.  

 

NtProtectVirtualMemory is the kernel-mode counterpart to the Win32 API VirtualProtect and is called via ZwProtectVirtualMemory. The real execution flow is:

Fig.4 NtProtectVirtualMemory execution flow

 

 

This means NtProtectVirtualMemory is entry 0x4D in the SSDT. The syscall number (0x4D) is passed to KiSystemServiceStart; here it is normal.

Fig.5 snippet nt!ZwProtectVirtualMemory

 

 

 

Deep dive into the API KiSystemServiceStart, and we have found the same base address that was mentioned in the last report. As you see, this instruction loads the address of KeServiceDescriptorTable into register r10. The table KeServiceDescriptorTable holds the service descriptor table (SDT) for system calls.

Fig.6 snippet nt!KiSystemServiceStart

 

 

Following the base address to explore the structure of KeServiceDescriptorTable and The highlighted values do not point to valid kernel code addresses, the detail please view the last report.

Fig.7 Some SSDT entries’s addresses are suspicious in windows7 x64

 

 

Here, it’s important to make the SSDT clear, as you see in Fig. 7. Yeah, in the last report, I talked about the “invalid kernel code addresses,” which strongly seems to imply that some SSDT entries have been hooked or tampered with. Later on, I continue to do research and think of the two things as follows:

1.      NtProtectVirtualMemory was intercepted; it’s highly confident.

2.      SSDT hooked? Why can some entries not be accessed, and how to jump to NtProtectVirtualMemory?

 

With those questions I continued to deep dive into more detail about the kernel. Many days gone I had found nothing. One night, I walked down the street and went across different bridges to do this thing to have a rest and let me have a good pause. And I also listened to music…

 

 

On that day’s second night, a flashing thought emerged: why not pick the clean kernel to watch?

So I choose a clean snapshot without the malware to do it again. Likewise, they still have the same SSDT entries that, like the showing in Fig. 7, seem to be the design of the system; the SSDT entries are not directly accessible or are "intentionally" protected. But it is very obvious that some part of the SSDT entries can still be visible and readable or accessible with kernel addresses and kernel symbols in Windows 7 x64. Suddenly I understand it is a design thinking or mechanism, but without full control, I’m not sure if it is a temporary plan.

 

I did not stop here but chose Windows 7 x86 to do it again. I found it is very different; all SSDT entries are readable or accessible with kernel addresses and kernel symbols.

Fig.8 Some SSDT entries’s addresses in windows7 x86

 

 

I started to ask myself, what’s the design or mechanism added from Windows 7 x86 to Windows 7 ×64, and finally it told me that it is PatchGuard. PatchGuard is designed to monitor critical kernel structures and detect unauthorized modifications, but it is not a perfect solution. And about the PatchGuard, I still need to learn more.

 

PatchGuard makes it difficult for malware to directly modify the SSDT, but here, combining the NtProtectVirtualMemory intercepted without touching the SSDT, it is very obvious. Turla still has abilities to bypass PatchGuard. It is very high skill and beyond the SSDT hooking.

 

But, my friend, here I really seriously ask myself, is it true? 100% confirmed?

When I compared it to the clean system again. Oh, my god!
It’s still the same; things happened again. the Unusual Behavior in NtProtectVirtualMemory, In a clean system, NtProtectVirtualMemory should not have such “debug breakpoints and NTSTATUS error codes” unless some other protective measure (like PatchGuard) is in place.

 

 

Conclusion: It is a malicious driver that has not overwritten some SSDT entries but added a limited protection mechanism called PatchGuard.

 

 

 

In my previous report, I explored the foundational aspects of SSDT hooking and found “invalid kernel code addresses”, but it is the design thinking or protection mechanism called PatchGuard in Windows 7 x64. And the strategic manipulation of NtProtectVirtualMemory and unexpected NTSTATUS returns, likely used to evade EDR hooks and mimic legitimate kernel behavior. And it is a more advanced and stealthy technique than classic SSDT hooking and evasion tech, but it’s still the protective measure PatchGuard.

 

Epilogue: What the Kernel Taught Me

1.      There’s no other best solution but to understand what it is.

2.      The process matters as much as the results.

3.      Scientific research must be serious.



“Do or do not, there is no try.”
Master Yoda

End of Report

Labels: , , , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home