Log in

View Full Version : Ring3 debugger detection stuph (was Please test a program)


Limee AKA Lamer
August 29th, 2004, 11:35
this Program can test the present of OD
Please get rid of the plugin of isdebuggerPresent

esther
August 29th, 2004, 11:55
PAY US THEN WE TEST FOR JO

hobgoblin
August 29th, 2004, 13:54
I don't get it. When I run this program in Olly, I get the message "Debugger NOT present". (This happens when I have the Hide debugger plugin activated, and it happens when I run Olly without).
What's the purpose with this?

Limee AKA Lamer
August 29th, 2004, 14:12
Do you run the program on Win9x?
The Program is available on Win2000 or XP.
Win9x does not take effect.

and it happens when I run Olly without).
~~~~~~~
What happens

Quote:
[Originally Posted by hobgoblin]I don't get it. When I run this program in Olly, I get the message "Debugger NOT present". (This happens when I have the Hide debugger plugin activated, and it happens when I run Olly without).

What's the purpose with this?

r4g3
August 29th, 2004, 14:12
hey hey if anyone else wants test this piece of ... can do that now by proof reading the source code of it, i did great work to rewrite it in C, please value my effort ;\

#include <windows.h>
#pragma comment ( linker, "/ENTRY:main" )
#pragma comment ( linker, "/NODEFAULTLIB:libc" )

int __stdcall main ( int hmod, int reason, int reserved )
{
if ( IsDebuggerPresent () )
MessageBox ( 0, "Test", "Debugger present", MB_OK );
else
MessageBox ( 0, "Test", "Debugger NOT present", MB_OK );
ExitProcess ( 0 );
}

Neitsa
August 29th, 2004, 14:38
Well, This is maybe a joke, but...anyway

I'm sure Limee, that almost 99% of people interested in reverse engineering or cracking know this API...

There's numerous anti-debug tricks. If you want to go a little bit deeper, just rip (i mean Copy/paste) the function code in you program:

Code:

MOV EAX,DWORD PTR FS:[18] ;TEB self pointer
MOV EAX,DWORD PTR DS:[EAX+30] ;PEB pointer
MOVZX EAX,BYTE PTR DS:[EAX+2] ;IsDebuggerPresent Flag


you can also separate each line of the above code in your program and set them appart, in this way, this code is less easy to discover (a "middle" cracker will see it very easily).

Code:

MOV EAX,DWORD PTR FS:[18]
mov AntiDebug1,eax

....;lot of code

mov eax,AntiDebug1
MOV EAX,DWORD PTR DS:[EAX+30]
mov AntiDebug2,eax

...;lot of code
mov eax,AntiDebug2
MOVZX EAX,BYTE PTR DS:[EAX+2]
mov AntiDebug3,eax

;if eax==1 then a Debugger is here
;just test it far away with some complicated way


You can also remove the access by Self pointer and access directly to PEB

This trick is useless with the Olly "Isdebbuggerpresent" plug since it's testing the above flag.

There's numerous way to deect a debbuger, just do a little google/forum search, you'll find a lot of hits.

Regards, Neitsa.

Kayaker
August 29th, 2004, 14:44
Ahh, don't pay any attention, we test free of charge here

OK, since the IsDebuggerPresent check is easily fooled as noted, how about something a bit more challenging? Though I'm sure it's been done, I haven't seen EPROCESS.DebugPort detection implemented yet, nor subverted...

When a ring3 debugger is attached, this field contains a pointer to the LPC (Local Procedure Call) port used by the debugger. Interestingly this is the same pointer as the ExceptionPort entry of EPROCESS which is always present.

To see the DebugPort field in action, create an exception which will be handled by the debugger, such as calling CloseHandle twice on the same handle (invalid handle exception). Trace into the second CloseHandle -> _NtClose and find the test for a valid DebugPort. If a debugger is attached KeRaiseUserException is called, bypassed if not.

This is a simple driver-based debugger check unrelated to IsDebuggerPresent. It seems to me that subversion (i.e. Olly plugin) might be a bit more difficult since you can't simply zero out the DebugPort field without affecting the debugger operation.


Kayaker

Neitsa
August 29th, 2004, 14:48
Hi Kayaker,

That's trully interesesting, thanks for sharing !

Regards, Neitsa.

hobgoblin
August 29th, 2004, 16:14
Agree. Really interesting stuff, Kayaker.

to Limee: I tried out your proggie on XP with SP1.

nikolatesla20
August 29th, 2004, 22:27
Kayaker, are you talking about the typical Process Debug Port? I use this code:

Also, if you are talking ring0 than a driver is needed, and most drivers aren't encrypted (dangerous to encrypt ring 0 code..) so no problem to bypass then.


Code:


// Override QueryInformationProcess to return a zero value, so the program
// does not detect our debugger process.
NtQueryInformationAddress = LookupAPIAddress("NtQueryInformationProcess","ntdll.dll";
PlaceBreakPoint(hProcess,NtQueryInformationAddress,QueryInformationHandler);


......

BOOL QueryInformationHandler(HANDLE hProcess, HANDLE hThread)
{


CONTEXT TempContext;
ZeroMemory(&TempContext,sizeof(TempContext));
TempContext.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread,&TempContext);

DWORD ProcessInfoArg = 0;

// get the return address
ReadProcessMemory(hProcess,(LPVOID)TempContext.Esp,&NtQueryInformationReturn,4,NULL);

ReadProcessMemory(hProcess,(LPVOID)((DWORD)TempContext.Esp +8),&ProcessInfoArg,4,NULL);

if (ProcessInfoArg == 0x07)
{
//ShowString("Called ProcessDebugPort";

PlaceBreakPoint(hProcess,NtQueryInformationReturn,QueryInformationReturnHandler);

return TRUE;
} else
return FALSE;
}


......

BOOL QueryInformationReturnHandler(HANDLE hProcess, HANDLE hThread)
{

CONTEXT TempContext;
ZeroMemory(&TempContext,sizeof(TempContext));
TempContext.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread,&TempContext);

// have to edit the value at [esp+8] to zero
DWORD EspValue =0;

//ReadProcessMemory(hProcess,(LPVOID)((DWORD)TempContext.Esp+8),&EspValue,4,NULL);
WriteProcessMemory(hProcess,(LPVOID)((DWORD)TempContext.Esp+8),&EspValue,4,NULL);
return TRUE;
}





This code is part of my tools..it overwrites the PROCESS_DEBUG_PORT call to return false.




-niko20

Limee AKA Lamer
August 29th, 2004, 23:30
which system does ur program run on?
win 9X or win 2000?

Quote:
[Originally Posted by hobgoblin]I don't get it. When I run this program in Olly, I get the message "Debugger NOT present". (This happens when I have the Hide debugger plugin activated, and it happens when I run Olly without).
What's the purpose with this?

esther
August 29th, 2004, 23:46
>Ahh, don't pay any attention, we test free of charge here

Aww he paid you under the table huh? LOL


>which system does ur program run on?
win 9X or win 2000?


hobgoblin Agree. Really interesting stuff, Kayaker.

to Limee: I tried out your proggie on XP with SP1.

Didn't you see it?

Kayaker
August 30th, 2004, 02:37
Hi nikolatesla,

Yes, I believe it's one and the same DebugPort being checked. The difference is that using NtQueryInformationProcess with the ProcessDebugPort information class 7 only returns a boolean value indicating whether or not a debug port has been set. I still need to trace NtQueryInformationProcess+ProcessDebugPort to see how it's done internally, my static analysis wasn't conclusive.

The ring0 method illustrated in the NtClose code is like this (Win2Ksp3):

Code:

:0044F691 xor esi, esi
...
:0044F8BB mov eax, large fs:124h ; KTHREAD
:0044F8C1 mov [ebp+var_88], eax
:0044F8C7 mov eax, [eax+44h] ; [KTHREAD+44h]
:0044F8CA cmp [eax+120h], esi ; DebugPort
:0044F8D0 jz short loc_44F924
:0044F8D2 loc_44F8D2: ; CODE XREF: NtClose+25Dj
:0044F8D2 push 0C0000008h ; Invalid Handle error
:0044F8D7 call KeRaiseUserException


Now here's something peculiar. [KTHREAD+44] is reported to be a pointer to a KPROCESS structure (part of _KAPC_STATE).
Code:

0: kd> !kthread
struct _KTHREAD (sizeof=432)
+000 struct _DISPATCHER_HEADER Header
...
+034 struct _KAPC_STATE ApcState
+034 struct _LIST_ENTRY ApcListHead[2]
struct _LIST_ENTRY *Flink
struct _LIST_ENTRY *Blink
+044 struct _KPROCESS *Process
...


If this is the case, the next line in the code would reference [KPROCESS+120h], which doesn't make sense because the sizeof KPROCESS is only 6Ch. Instead, [EPROCESS+120h] *does* exist and makes sense in the context of what can be seen tracing the code. I hope someone can confirm or not this analysis.

The possible driver implemented debugger check code then would be:
Code:

mov eax, large fs:124h ; KTHREAD
mov eax, [eax+44h] ; KTHREAD.KAPC_STATE.Process -> PTR EPROCESS
cmp [eax+120h], 0 ; EPROCESS.DebugPort (LPC_PORT_OBJECT)
jnz debugger attached


>dangerous to encrypt ring 0 code..
I think I remember someone mentioning that before, why would that be the case? As long as you know you're not paged out... code is code is code n'est pas?

>Aww he paid you under the table huh?
Sshhh...

nikolatesla20
August 30th, 2004, 07:26
Quote:
[Originally Posted by Kayaker]

>dangerous to encrypt ring 0 code..
I think I remember someone mentioning that before, why would that be the case? As long as you know you're not paged out... code is code is code n'est pas?



Well, with the new AMD and/or XP SP2 a driver probably wouldn't be allowed to modify its own code because of memory protection enforcement now. But that remains to be seen.

-nt20

Kayaker
August 30th, 2004, 10:33
Ah, omega_red just set me straight on my confusion re KPROCESS+120 vs EPROCESS+120. As it turns out it comes to the same thing. Thanks for that

There are just too damn many substructures involved and it depends on which structure definition you happen to be looking at at 2am...

Here is the gist of omega-red's explanation resurrected (but using my structure output). Don't know why you deleted your post though.

Cheers,
Kayaker


EPROCESS is an extension of KPROCESS, maybe this will explain it.

Code:

struct _EPROCESS (sizeof=648)
+000 struct _KPROCESS Pcb
+000 struct _DISPATCHER_HEADER Header
+000 byte Type
...
+06a byte Spare[2]
--------------------------------> KPROCESS ends
+06c int32 ExitStatus
+070 struct _KEVENT LockEvent
+070 struct _DISPATCHER_HEADER Header
...
+120 void *DebugPort
+124 void *ExceptionPort
...

0rp
August 30th, 2004, 10:55
hi,

Quote:
[Originally Posted by Kayaker]
Code:

mov eax, large fs:124h ; KTHREAD
mov eax, [eax+44h] ; KTHREAD.KAPC_STATE.Process -> PTR EPROCESS
cmp [eax+120h], 0 ; EPROCESS.DebugPort (LPC_PORT_OBJECT)
jnz debugger attached



where did you found this code?

btw, this is funny to:
0:001> dt _ethread $thread -vb
Code:

ntdll!_ETHREAD
struct _ETHREAD, 54 elements, 0x258 bytes
+0x000 Tcb : struct _KTHREAD, 73 elements, 0x1c0 bytes
+0x000 Header : struct _DISPATCHER_HEADER, 6 elements, 0x10 bytes
.....
+0x033 Priority : 127 ''
+0x034 ApcState : struct _KAPC_STATE, 5 elements, 0x18 bytes
+0x000 ApcListHead : (2 elements)
[00] struct _LIST_ENTRY, 2 elements, 0x8 bytes
[ 0x0 - 0x0 ]
+0x000 Flink : (null)
+0x004 Blink : (null)
[01] struct _LIST_ENTRY, 2 elements, 0x8 bytes
[ 0x0 - 0x0 ]
+0x000 Flink : (null)
+0x004 Blink : (null)
+0x010 Process : (null)
+0x014 KernelApcInProgress : 0 ''
....

Kayaker
August 30th, 2004, 15:48
Quote:
[Originally Posted by 0rp]
where did you found this code?

Micro$oft? ;-)

Hi,
Really, it's right there in the NtClose code I showed. If you want to follow it exactly you need to trace an *invalid* CloseHandle which will generate an exception. A valid CloseHandle call follows the same path, just branches a bit earlier.
If Olly is attached it will handle the exception by pausing until you press F7/F8/F9 again, then the invalid CloseHandle call is returned from and execution continues.
It doesn't really matter what structure elements you use to get there, it's just the idea of physically checking EPROCESS.DebugPort.


Not sure why you're getting a null EPROCESS/KPROCESS pointer.

Kayaker
September 1st, 2004, 00:30
Hi All,

Well, just carrying on with this, a little Ring 3 Debugger detection idea in the spirit of RCE...


Straight from the PSDK description of the CloseHandle API:
"Closing an invalid handle raises an exception when the application is running
under a debugger. This includes closing a handle twice, and using CloseHandle
on a handle returned by the FindFirstFile function."


Calling CloseHandle with an invalid handle forces the debugger, and the user, to handle the ensuing
exception. The *time* taken for CloseHandle to return therefore takes magnitudes of time longer than if a debugger is *not* present, on the order of seconds or milliseconds rather than nanoseconds. By clocking the single API call (using QueryPerformanceCounter) we can then make a *subjective* guess whether or not a debugger is attached.

Here is a rough sketch of what occurs when CloseHandle is called:

Kernel32!CloseHandle ->
ntdll!_ZwClose (INT 2E/SYSENTER) ->
ntoskrnl!_KiSystemService -> _NtClose ->


The first thing done in NtClose is, apparently, to disable kernel APC's by setting
KernelApcDisable to -1. The field is reset to 0 just before returning from NtClose.
This is interesting in terms of the possible reliability of using an API timing check
of any sort. While there is no direct IRQL level raising going on to prevent system
interruption of the code flow, setting this flag may serve a similar purpose, giving
a greater chance of us calculating accurate execution times.

Code:

0044F685 mov eax, large fs:124h ; KTHREAD
0044F68B dec dword ptr [eax+0D0h] ; KernelApcDisable
; [KTHREAD+D0h] uint32 KernelApcDisable


Next, ntoskrnl!ExMapHandleToPointer is called, in turn calling ExpLookupHandleTableEntry
and ExLockHandleTableEntry. If a valid handle was passed, a pointer to the HandleTable entry is returned and ExDestroyHandle is called to clear the entry, then returns from CloseHandle directly.

If an *invalid* handle was passed to CloseHandle, the code first tests if a debugger is attached by checking the EPROCESS.DebugPort field, as we noted above.

Code:

:0044F691 xor esi, esi
...
:0044F8BB mov eax, large fs:124h ; KTHREAD
:0044F8C1 mov [ebp+var_88], eax
:0044F8C7 mov eax, [eax+44h] ; [KTHREAD+44h] *Process
:0044F8CA cmp [eax+120h], esi ; [EPROCESS+120] *DebugPort
:0044F8D0 jz short loc_44F924
:0044F8D2 loc_44F8D2: ; CODE XREF: NtClose+25Dj
:0044F8D2 push 0C0000008h ; STATUS_INVALID_HANDLE
:0044F8D7 call KeRaiseUserException

; [KTHREAD+44h] is a pointer to the EPROCESS/KPROCESS block for the running application
; +044 struct _KPROCESS *Process

; [EPROCESS+120] is a pointer to EPROCESS.DebugPort (LPC_PORT_OBJECT)
; +120 void *DebugPort


If the DebugPort field is empty, the code simply resets the KernelApcDisable flag and returns STATUS_INVALID_HANDLE back to the user. If not, then KeRaiseUserException is called and the
debugger is forced to handle the exception, and more importantly the *user* is forced to
handle the debugger. This is what takes a lot of time and is of course the basis for the method of detection.

Without a debugger attached, the invalid CloseHandle call in the following code takes
on the order of 10-2 to 10-3 milliseconds on my 2.8GHz machine running Win2Ksp3.
The fastest time I could record using OllyDbg to "Beat the Clock", by having to press
F9 to resume execution after the exception error pause, was around 300 milliseconds.


I make no judgements on how effective this method of debugger detection might be,
just that it exists. Normally timing checks might be considered unreliable, but in
this case the code flow is relatively straightforward, and the KernelApcDisable flag
in NtClose may prevent the system from grabbing any CPU cycles, limiting false readings.

Prevention? Assuming the code is hidden well enough to avoid direct patching, then you're
probably looking at a monitoring hook of some sort on CloseHandle. Either that, or design
(modify?) a debugger which ignores such an exception and returns directly without
requiring user intervention. This may make the timing values too close to be a reliable
indicator. Source attached and follows.

Cheers,
Kayaker

Code:

.586
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

include \masm32\include\masm32.inc ; for FPU routine
includelib \masm32\lib\masm32.lib

CTEXT MACRO text:VARARG
LOCAL TxtName
.data
TxtName BYTE text, 0
.code
EXITM <ADDR TxtName>
ENDM

;======
.DATA?
;======

;---------------------
hInstance HINSTANCE ?
hFile HANDLE ?
StringBuff db 64 dup(?)
FloatBuff db 32 dup(?)

lpPerformanceFrequency LARGE_INTEGER <>
CountsPerSec QWORD ?
lpPerformanceCount LARGE_INTEGER <>
StartCount QWORD ?
EndCount QWORD ?
ExecutionTime QWORD ?
msec DWORD ?

;##################################################

;======
.CODE
;======
start:
invoke GetModuleHandle, NULL
mov hInstance,eax

; ==================================================
; Create a dummy file in the current directory
; ==================================================

invoke CreateFile, CTEXT("dummy.txt", GENERIC_READ, FILE_SHARE_READ, NULL,\
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL

.if (!eax)
invoke MessageBox, NULL, CTEXT("Error", CTEXT("Can't create the file",\
MB_OK+MB_ICONERROR+MB_TASKMODAL+MB_TOPMOST
invoke ExitProcess, 0
.endif

mov hFile, eax

invoke CloseHandle, hFile ; the valid CloseHandle call


; ==================================================
; Initialize our timing check
; ==================================================

finit ; initialize FPU

; -------------------------------------------------------------
; Get the frequency of the high-resolution performance counter
; -------------------------------------------------------------

invoke QueryPerformanceFrequency, ADDR lpPerformanceFrequency

fld qword ptr [lpPerformanceFrequency] ; push result onto top of FPU stack st(0)
fstp qword ptr [CountsPerSec] ; store value in st(0) into variable

; -------------------------------------------------------------
; Get the starting tick value
; -------------------------------------------------------------

invoke QueryPerformanceCounter, ADDR lpPerformanceCount

fld qword ptr [lpPerformanceCount]
fstp qword ptr [StartCount]


; ==================================================
; Call CloseHandle a second time using the same file handle
; Will generate STATUS_INVALID_HANDLE and force
; KeRaiseUserException if debugger attached
; ==================================================

invoke CloseHandle, hFile


; -------------------------------------------------------------
; Get the ending tick value
; -------------------------------------------------------------

invoke QueryPerformanceCounter, ADDR lpPerformanceCount

fld qword ptr [lpPerformanceCount]
fstp qword ptr [EndCount]

; -------------------------------------------------------------
; The time in seconds between the two QueryPerformanceCounter calls is
; (EndCount - StartCount) / CountsPerSec
; -------------------------------------------------------------

fld qword ptr [EndCount] ; load EndCount to st(0)
fsub qword ptr [StartCount] ; sub StartCount from st(0)
fdiv qword ptr [CountsPerSec] ; divide result by CountsPerSec
mov msec, 1000 ; shift decimal place to milliseconds
fimul msec ; multiply result in st(0) by 1000
fstp qword ptr [ExecutionTime] ; store result in st(0) to variable

; -------------------------------------------------------------
; Convert floating point result to a string using the
; masm32lib function FloatToStr
; -------------------------------------------------------------

invoke FloatToStr, qword ptr [ExecutionTime], ADDR FloatBuff

;===============================================

invoke wsprintf, offset StringBuff,\
CTEXT("%s is the elapsed time for CloseHandle",\
offset FloatBuff

; A direct check of the elapsed time is probably better here, say
; ExecutionTime > 10 milliseconds might be considered a
; positive test for a debugger
;
; Instead, this is a lame check of where the decimal point is in the string
; Since we are forcing FloatToStr to convert milliseconds, anything less
; than 1 ms will be expressed as i.e. "1.0 x 10-3"
; and the decimal point will be the second character

lea eax, offset StringBuff

.if byte ptr [eax+1] != '.'

invoke MessageBox, NULL, offset StringBuff,\
CTEXT("Wise choice, you're using a debugger...",\
MB_OK+MB_ICONWARNING+MB_TASKMODAL+MB_TOPMOST

.else

invoke MessageBox, NULL, offset StringBuff,\
CTEXT("No debugger detected. Have you tried Olly?",\
MB_OK+MB_ICONINFORMATION+MB_TASKMODAL+MB_TOPMOST
.endif

invoke ExitProcess, 0

end start

omega_red
September 1st, 2004, 09:22
Doh, after posting I thought that I am wrong and deleted that instead of waiting for other opinions. Had to be really late night

dELTA
September 1st, 2004, 11:25
Hehe Kayaker, cool.

Neitsa
September 1st, 2004, 16:19
Hello Coders,

Just a question about the subject which currenty intresting us...

Does someone noticed this field in the PEB struct:

Code:

//PEB offset
+010 struct _RTL_USER_PROCESS_PARAMETERS *ProcessParameters


Now in the _RTL_USER_PROCESS_PARAMETERS struct :

Code:

typedef struct _RTL_USER_PROCESS_PARAMETERS {
// (sizeof=656)
+000 uint32 MaximumLength
+004 uint32 Length
+008 uint32 Flags
+00c uint32 DebugFlags <= here !
+010 void *ConsoleHandle
//...[cut]


I've already tested this field with a debugger, but I can't see any change...Maybe someone is aware of what it could be...

Thank you !

Regards, Neitsa.