Log in

View Full Version : Strange things with debug registers and SEHs?


cyberheg
July 6th, 2002, 20:44
More and more protections these days are using SEH's to do various things amoung cleaning debug registers. One of these is Asprotect but I am not limiting myself to this.

I thought I'd try to write a little program which generates a exception and reads out the values of the registers.

The code at the buttom of this post shows the code I used (you can compile it with masm32).
What it does is basicly put the content of the registers dr7, dr1 and dr2 into edx which can be read out with softice if you single step through it.
Now the problem is that it the values it reads out of the registers are not the one softice reports me nor they are correct.
Bpm type of breakpoints should directly be using these registers so I did the following tests under both win98 and win2k.
With both having a bpm breakpoint set in dr1 and also without I got the followin results:
dr7 = 0 under both OS's
dr1 = 0 under win98 and 0xbe7cbd94 under win2k
dr2 = 0 under win98 and 0x7ffd0010 under win2k

So whats the deal with this? At the same time I also used the "CPU" command in softice which also showed different results from the real ones and also different results from mine.
I am thinking it has something to do with Ring0/3 but would it help to get access to the registers under ring0?
I know cdilla uses a sys driver under winNT/2k/XP and in past I read that it uses DR7 to do checking with but I am not sure if thats the reason why a driver was provided.
For these tests I used Softice v4.05 under both OS's.

// CyberHeg
---------------------------------------
Content of seh.asm:
.386
.model flat, stdcall
option casemap:none

include p:\progs\masm32\include\windows.inc
include p:\progs\masm32\include\kernel32.inc

.data

.code
main:

int 3

push Int3Handler
ASSUME FS:nothing
push fs:[0]
mov fs:[0], esp
int 3
nop
pop fs:[0]
add esp, 4

invoke ExitProcess, 0

Int3Handler:
mov eax, [esp+4]
cmp [eax.EXCEPTION_RECORD.ExceptionCode], EXCEPTION_BREAKPOINT
je HandleException
xor eax, eax
inc eax
ret
HandleException:
mov eax, [esp+12]
add [eax.CONTEXT.regEip], 1
mov edx, [eax.CONTEXT.iDr7]
mov edx, [eax.CONTEXT.iDr1]
mov edx, [eax.CONTEXT.iDr2]
xor eax, eax
ret

end main
end

nikolatesla20
July 7th, 2002, 05:33
COol idea to look at, gives you more insight into how the system operates.


I'm confused on one small aspect tho. I'm looking at the "structured exception handling in win32asm" tutorial on Izcelion's site, and then looking at your code, I wonder about the CONTEXT structure you are trying to reference.

This is what you have:

Code:


At this point, we have a ret address pushed on the stack, add 4 to all stack ops to jump over it.....

Int3Handler:
mov eax, [esp+4] ; <--------- This is OK
cmp [eax.EXCEPTION_RECORD.ExceptionCode], EXCEPTION_BREAKPOINT
je HandleException
xor eax, eax
inc eax
ret

HandleException:

mov eax, [esp+12] ; <----------------------R U sure?

add [eax.CONTEXT.regEip], 1 ; <-------Using CONTEXT structure.
mov edx, [eax.CONTEXT.iDr7]
mov edx, [eax.CONTEXT.iDr1]
mov edx, [eax.CONTEXT.iDr2]
xor eax, eax
ret


What I don't understand is that [esp+12] - um that doesn't seem right ?? The documentation says the exception handler gets one parameter, the pointer to the EXCEPTION POINTERS structure, ok, and that EXCEPTION_POINTERS + 0 is the EXCEPTION_RECORD. You have [esp+4], that is fine, because this is a function call and we have the RET address above us, so we add 4 to get to EXCEPTION_POINTERS+0. BUT ! Now you are trying to use [esp+12] to get to CONTEXT, where the docs say CONTEXT is at EXCEPTION_POINTERS + 4, and so this would be [esp+8] wouldn't it (jump 4 over ret address, another 4 for the exception_record entry, you get context at esp+8). It looks like you are trying to get [esp+12] as the CONTEXT structure in eax, and then using traditional win32asm structure calls on it, correct?

Could you pleaseexplain that [esp+12] for me? I mean that seems like a really high number out on the stack, it just doesn't seem right.

-nt20

cyberheg
July 7th, 2002, 07:49
In Jeremy Gordons essay about the subject availble at http://www.jorgon.freeserve.co.uk/ExceptFrame.htm it says the following:

The information sent to the per-thread handlers

At the time of the call to the per-thread handler, ESP points to three structures as follows:
ESP+4 Pointer to structure: EXCEPTION_RECORD
ESP+8 Pointer to own ERR structure
ESP+C Pointer to structure: CONTEXT record

Hopefully this answers your question.

In the meantime I found this example which I yet have to try written in C++: http://www.morearty.com/code/breakpoint/

This uses Get/Set context api though so it doesn't use the method I use however if thats what needed to get it to work I'd go for it.

// CyberHeg

+SplAj
July 7th, 2002, 08:03
nt20

I am completely baffled by your number of 'ret' explanation for adding esp+??. These are the DWORD pointerers to the structure, not counters.

Here is a snippet of code form our well known T**** boyz for their lame attempt to clear BPM etc....

:00553FF0 55 push ebp
:00553FF1 8BEC mov ebp, esp
:00553FF3 83C4E4 add esp, FFFFFFE4
:00553FF6 53 push ebx
:00553FF7 56 push esi
:00553FF8 894DF8 mov dword ptr [ebp-08], ecx
:00553FFB 8945FC mov dword ptr [ebp-04], eax
:00553FFE E825000000 call 00554028 <-set SEH get all infos/offsets of stack frame
:00554003 8B44240C mov eax, dword ptr [esp+0C]
:00554007 8380B800000002 add dword ptr [eax+000000B8], 00000002 <- change ret to a safe step over exception
:0055400E 51 push ecx
:0055400F 31C9 xor ecx, ecx
:00554011 894804 mov dword ptr [eax+04], ecx <-clear pointers for DR0-3
:00554014 894808 mov dword ptr [eax+08], ecx
:00554017 89480C mov dword ptr [eax+0C], ecx
:0055401A 894810 mov dword ptr [eax+10], ecx
:0055401D C7401855010000 mov [eax+18], 00000155 <-also set DR7 register to stop BPM
:00554024 59 pop ecx
(hex 155 = binary 101010101)
:00554025 31C0 xor eax, eax
:00554027 C3 ret <- ret into safe exception

* Referenced by a CALL at Address:
|:00553FFE
|
:00554028 31C0 xor eax, eax
:0055402A 64FF30 push dword ptr fs:[eax]
:0055402D 648920 mov dword ptr fs:[eax], esp <-set new SEH
:00554030 3100 xor dword ptr [eax], eax
:00554032 648F0500000000 pop dword ptr fs:[00000000]

So they say the structure starts at
:00554003 8B44240C mov eax, dword ptr [esp+0C]


Spl/\j

nikolatesla20
July 7th, 2002, 14:52
LOL Cyberheg, I read the same article - but I didn't scroll down far enough oops ...


Spl/\j, Nah, that's just my way of looking at things - when you go into a function call the processor pushes the return address on the stack, so you have to add a DWORD to all stack ops to get over it. That's what I was talking about. Um guess its just how I have learned to understand it


P.S.
Finally tried *IDA Pro* this weekend. WOW. Nice program. I like that you can rename stuff on the fly, etc. Really helpful. (and FLIRT!!)

-nt20

SiNTAX
October 8th, 2002, 08:36
Quote:
Originally posted by cyberheg

I know cdilla uses a sys driver under winNT/2k/XP and in past I read that it uses DR7 to do checking with but I am not sure if thats the reason why a driver was provided.


The device driver is needed because a normal program can't access Ring-0. The secdrv.sys driver does indeed read out DR7.


to nikolatesla20: hence the name 'Interactive DisAssembler'

evaluator
October 10th, 2002, 20:20
cyberheg!

I think, I know why you have strange garbage in SEH

Because you use INT3 for exception!

So you should try anouther kind exception.

OK!?

^DAEMON^
October 11th, 2002, 14:34
heh... what a shame ))

debug vwin32_set/getthreadcontext to understand why it is 0
@ startup
+ go to w95 and debug it there too...

^DAEMON^

evaluator
October 11th, 2002, 15:53
OK, I here uploaded puzzy-prog for checking DRx_context.

So what is required for resolving DRx's in context ON W98se.

1. Firstly must "activated" DR7_context!

This means, when we first time make exception, DRx_context WILL clear anyway if DRx are full.
So on first SEH we will put in DR7_context value 00000155h (this means "activation"

2. Now we need 2nd exception & in 2nd SEH we will have full DRx_context.

**

Heh! But when we first time "activated" DR7_context, inside kernel it causes to clear DRx,
because we already had clear DR0-3_context.
So for testing purposes we need to set DRx's in SICE after 1st SEH & before 2nd.
Or another suggestion: make BPX between SEH's & on break SICE again activates DRx's.

(manual)

Load my prog in SICE & when it breaks on progs start, set four BPM's & one BPX:

BPM 401022 x
BPM 401024 x
BPM 401029 x
BPM 401048 x

BPX 401016

First time SICE breaks on BPM 401048 x inside SEH. Tape "d EBP" & you see context (& it is clear!).
Now handler will put in DR7_context value 00000155h.
Press F5 & SICE breaks on BPX 401016 before 2nd UD2. On break SICE restores DRx.
Now again F5 & second time SICE breaks on BPM 401048 x inside SEH.
But now DRx_context is full with OUR BPMx's
Congratz!

For XP bud news!
On clear system DRx_context is clear, with loaded NTice ALWAYS dirty!
My prog reported this fact
cyberheg! So I made mistrake about INT3

SINTAX! This is Job for you. Find & patch NTice(or NToSKRNL?) for avoid this fact. Then report here!

cyberheg
October 12th, 2002, 07:41
Thanks for the information (even though this thread is a bit old).

So basicly if you need to read/write the debug registers you need to do it in 2 steps... interesting.

I know with Get/SetThreadContext it is possible to do, but it's not very efficient.

Evaluator, are you saying this method for reading out the registers isn't working on WinXP?
I tried your program on win2k which seems to have read out the values fine.
As for overwriting as you do on address 4010b0 softice still breaks on the last 3 bpm's:
BPM 401022 x
BPM 401024 x
BPM 401029 x

I don't think that was the purpose?

// CyberHeg

SiNTAX
October 12th, 2002, 12:30
Quote:
Originally posted by evaluator

SINTAX! This is Job for you. Find & patch NTice(or NToSKRNL?) for avoid this fact. Then report here!


Aha.. I love a good challenge :-) I will have a look at it.

SiNTAX
October 12th, 2002, 12:58
Quote:
Originally posted by evaluator
For XP bud news!
On clear system DRx_context is clear, with loaded NTice ALWAYS dirty!
My prog reported this fact
cyberheg! So I made mistrake about INT3

SINTAX! This is Job for you. Find & patch NTice(or NToSKRNL?) for avoid this fact. Then report here!


Hmm eval.. I just tried it on my XP.. if I start it without settings any breakpoints I get the message that the DRx_context is clear. (even with NTice loaded).

If I set the breakpoints, then it does indeed detect this.

If I load SuperBPM, then the DR7 is always clear so it skips the EIP 2x, so I only get the finished dialog.


Without NTice loaded it produces random data.. sometimes it gives dirty, sometime clear. so this

'
For XP bud news!
On clear system DRx_context is clear,
'

Is not correct

Neither is this:

'
with loaded NTice ALWAYS dirty!
'

(this is only true, if you have active BPM's)



Oh.. and the clear of the BPMs has no effect either (NOTE: am using DS2.7 here)

evaluator
October 12th, 2002, 14:19
Hi, ciberheg!

For _setting DRx via SEH you NOT need 2 attemp. For read you need..


Hi, SiNTAX!

BE more trickiE!
(you remember, I not did easy help for you

OK, special edition for you uploaded here.
It only will look if DRx_context is clear.
On my clear XP I many time run it & always reported "clear",
then I start NTice(2.6) & it always reports "DIRTY".

BTW, test it on many other nt-OS'es you have

why you load SUPERbpm?????? somewhere I wrote about!?

evaluator
October 12th, 2002, 15:45
yep!

I also catch case on clear XP, when prog reports DIRTY!
(2 from ~100)

OK, so "BAD news" correctly killed by SiNTAX

"Enemies" will not able use this fact!?

But this happens (1 from ~100).
& DIRTY is 98 form 100

???

SiNTAX
October 12th, 2002, 15:55
Quote:
Originally posted by evaluator
yep!

I also catch case on clear XP, when prog reports DIRTY!
(2 from ~100)

OK, so "BAD news" correctly killed by SiNTAX

"Enemies" will not able use this fact!?

But this happens (1 from ~100).
& DIRTY is 98 form 100

???


The memory is probably uninitialized.. so that's why it sometimes works and sometimes not.. anyway like you said.. the enemy won't be able to use it :-)

But there's enough other ways left to detect SI

Just found another one: scanning the first byte of exported functions of KERNEL32.. when SI is loaded, you will find some INT3's there (saw it being done in SD) -- this one can be done from Ring-3.. so no device driver needed.

nikolatesla20
October 28th, 2002, 16:32
Concerning "CC" byte at API (int 3).

This is why "mature" SI user knows to

"bpmb GetVersion X" rather than "bpx", and also then use SuperBPM or DS 2.7

-nt20

SiNTAX
October 28th, 2002, 16:53
Quote:
Originally posted by nikolatesla20
Concerning "CC" byte at API (int 3).

This is why "mature" SI user knows to

"bpmb GetVersion X" rather than "bpx", and also then use SuperBPM or DS 2.7

-nt20


Ok I'll bite

I always use BPM's, but still SD was able to find a CC on a KERNEL import. So it seems that DS 2.7 sets some INT3's all by itself.

It had me puzzled a bit when playing with SD.. as I set a 'BPM xx X' on the code path right after the CC-found check and when it went into SI there was no CC at all.. so SI already replaced it with the original byte.

All this was without any BPX's set, only BPM's.


This was on WinXP + DS2.7

tgodd
October 28th, 2002, 19:44
One of the things that I always do, is to have a dummy Driver which allocates plenty of space.

If I need to write some code on the side I do it in the space provided by the bummy device driver.
I go into softice record my EIP.
Set my EIP to the code I just created, and single step my code to get the result I want.
Then I set EIP back to what it was when I first went into Softice.

I have created many patches and decrypted many code sections using just this technique.

Why write Utils which are so simple it hurts.

It's like writing a reboot program.
A waste of time.



Regards,

TGODD

evaluator
October 28th, 2002, 21:09
I can confirm:

DS2.6 + XP
After loading NTice in Kernel32.dll found one CC.
Export Name: UnhandledExceptionFilter


EnljoE

nikolatesla20
October 28th, 2002, 21:16
This is no doubt the reason for the patch of "Unhandled Exception Filter" string in ntice.exe to be performed according to +Splaj's walkthru.

If you change the name, SI cant put the CC there, hence no more detect.

-nt20