Log in

View Full Version : Debug registers and w2k3


reverser
November 1st, 2004, 21:53
I've been investigating a program, but I have a problem: it doesn't run under w2k3 (unexpected error). After some looking around it turns out that an exception happens in a dll. The code there is actively using Get/SetThreadContext, DR registers and some SEH. The weird thing is, the program works perfectly under 2000 so it looks like something was changed in w2k3. I tried to bypass the anti-debugging code, but I'm not very familiar with such tricks so I failed. Maybe someone knows why it works differently under w2k3?

I'll try to identify the exact place it crashes in w2k3 and post it later.

JMI
November 2nd, 2004, 12:23
Did you start your quest for knowledge by attempting to help yourself? Did you try putting "anti-debug and win2k" (without the quotes) in the Search button here? I got four hits, including one titled "In Win2K how to defeat the anti-debug trick of ASProtect?" which might have relevant information. There is also lots of information on general "anti-debug" tricks and SEH here which you can find and review by a general search using the same Search button. Using "anti-debug* and win2k" (again without the quotes and notice the "*" I got 147 hits in google

Since you report that your are "not very familiar with such tricks" you really should start with attempting to broaden your knowledge base, rather than ask someone else to solve this problem for you with a particular software. I understand you REALLY want to defeat THIS target, but simply telling you what is needed HERE doesn't actually help you very much in acquiring the skillset to do such things on your own. What you need is some patient study on the concepts used for such tricks and they you will begin to notice how they have been applied as you look at your code.

This is what "learning" is all about. The rest is just "solving" your immediate quest. Think about the difference.

Regards,

reverser
November 2nd, 2004, 13:24
Well I don't really need to "defeat" it, I just want to make it run on w2k3. I did do some search on google, but didn't find anything on differences between 2k and 2k3. I just thought maybe someone here had encountered the same problem, so I can fix it and actually use the program. While "defeating" it might be useful, it's not really what I need right now. And BTW it's not ASProtect or some other packer, the dll is not packed or crypted, just obfuscated in some places. It looks like the protection is custom-made.
Sorry if I sounded arrogant, but I did spent some time researching before posting here.

Now I'll post some snippets from the program along with my findings.

Code:

.text:100352A6 push 540h ; size_t
.text:100352AD push offset AntiDebug3 ; void *
;[calculate a random (but valid) pointer in eax]
.text:100352FB push eax ; void *
.text:100352FC mov [esp+5B8h+Context2.Dr3], eax
.text:10035303 call ds:memmove ;copy AntiDebug3 function
.text:10035309 mov eax, [esp+5B8h+Context2.Dr3]
.text:10035310 mov esi, ds:SetThreadContext
.text:10035316 xor eax, 7FFFFFFFh
.text:1003531B add esp, 0Ch
.text:1003531E mov [esp+5ACh+Context2.Dr3], eax //save new address xored with 7FFFFFFF
.text:10035325 lea eax, [esp+5ACh+Context2]
.text:1003532C push eax ; lpContext
.text:1003532D push 0FFFFFFFEh ; hThread
.text:1003532F mov [esp+5B4h+Context2.Dr7], 11110455h
.text:1003533A call esi ; SetThreadContext
.text:1003533C lea ecx, [esp+5ACh+Context2]
.text:10035343 mov edi, 11110400h
.text:10035348 push ecx ; lpContext
.text:10035349 push 0FFFFFFFEh ; hThread
.text:1003534B mov [esp+5B4h+Context2.Dr7], edi
.text:10035352 call esi ; SetThreadContext
.text:10035354 lea edx, [esp+5ACh+Context]
.text:10035358 mov [esp+5ACh+Context.ContextFlags], ebp
.text:1003535C push edx ; lpContext
.text:1003535D push 0FFFFFFFEh ; hThread
.text:1003535F call ebx ; GetThreadContext
.text:10035361 xor eax, eax
.text:10035363 mov [esp+5ACh+Context.Dr1], 1
.text:1003536B mov [esp+5ACh+Context.Dr0], eax
.text:1003536F mov [esp+5ACh+Context.Dr2], eax
.text:10035373 lea eax, [esp+5ACh+Context]
.text:10035377 mov [esp+5ACh+Context.Dr7], 11110455h
.text:1003537F push eax ; lpContext
.text:10035380 push 0FFFFFFFEh ; hThread
.text:10035382 call esi ; SetThreadContext
.text:10035384 lea ecx, [esp+5ACh+Context]
.text:10035388 mov [esp+5ACh+Context.Dr7], edi
.text:1003538C push ecx ; lpContext
.text:1003538D push 0FFFFFFFEh ; hThread
.text:1003538F call esi ; SetThreadContext
.text:10035391 mov edx, [esp+5ACh+Context.Dr3] //get back the address of the copy of AntiDebug3
.text:10035395 push offset AntiDebug5
.text:1003539A xor edx, 7FFFFFFFh //xor it back
.text:100353A0 call edx //call it, param 1 = address of next function to copy&call
.text:100353A2 add esp, 4

This one is pretty straightforward, it copies the function to a new address, then calls it using DR3 for extra protection. There is about 20 such functions, copying and calling each other in chain, and they set slightly different values in dr0-dr2. First I though that maybe 2k3 doesn't like invalid pointer in dr3 (because of xor with 7FFFFFFF), so I patched them all xors to be like "xor edx, 0". This didn't help. Next I made a hook dll, which hooked Get/SetThreadContext and just kept the DR values in memory, instead of setting real values. It still crashed. So I thought before trying to unwind it all by hand, to try and ask a question here.

doug
November 2nd, 2004, 15:46
- What kind of exception are you getting.
- Where does the exception occur & what code is there
- What are the appropriate register values there.

JMI
November 2nd, 2004, 18:03
reverser:

You didn't sound arrogant, you simply ignored one of the Rules, which is, if you have already searched for the answer for issue, you should say so. Then no one needs remind you of the need to search first.

But it is clear that you did not understand the import of my comments. I did not referrer you to articles about ASPR because I believed you were dealing with ASPR, but because these articles, and others I referred you to deal with anti-debug code and Win2k. You stated "the code there is actively using Get/SetThreadContext, DR registers and some SEH." These are not only somewhat common issues for "anti-debug" defense, but are much discussed here and elsewhere.

You do, however demonistrate the other point I was making. You actually are more interested in making THIS piece of software work for you (i.e., "defeating" it) than you actually are in attempting to learn about the general subject of antidebugging, SEH and so forth. That's OK. It simply doesn't hold much prospect of advancing your knowledge base for next time and is a product of the general impatience of youth. As long as you abide by the Rules, you are entitled to proceed anyway you wish. I was attempting only to steer you towards a path of higher learning. The choice was always yours.

Regards,

seapagan
November 3rd, 2004, 02:24
Y'know,

I doesent actually seem to me that he wants to 'defeat' the application - just to get it running on windows 2003, and asking if there were any major SEH related changes under 2003 as opposed to 2000. All the search tips JCM listed point to 2k - which he already said works fine. The rationale of checking aspr info etc is however very valid, lots of examples on understanding and bypassing SEH.
Checking google however, there is an interesting article by Matt Pietrek on MSDN Magazine about new exception handling in W2k3 (Vectored exception handling) which is supposed to co-exist with SEH - but i'm sure that it is possible the complicated SEH tricks used in protection may cause conflicts .... however, i believe that VEH is supported on XP also - does the prog crash on XP? There seems to have been a lot of changes to the debugging api's also in 2k3.
Basically, 2k3 has quite a few changes in the low level area and screws quite a few programs I have tried. I Stopped using it as a server and went back to 2k myself, and would not really consider using it as a workstation.
One other option is checking the website of whatever the program is - you are probably not the first to try to run it under 2k3, and there may be a patch or work-around avaliable there. This is assuming ofcourse that it crashes when run from explorer and not only under a debugger ....

As for
Quote:
the general impatience of youth
not sure where this came from? He seems to have done his searching / homework, and he may be older than all of us!

just my 0.02 euro's, and first post after a long lurk

SP

reverser
November 3rd, 2004, 21:55
Sorry for the late reply, been kinda busy.
Now for some answers.
Quote:

- What kind of exception are you getting.
- Where does the exception occur & what code is there
- What are the appropriate register values there.

I'm getting an AV, trying to execute code at 7FFFFFFFh. It's not really clear where it happens as the stack is pretty much screwed up, and also lots of program code is copied into allocated buffers before execution, so you don't know anymore which function it comes from. I suspect it comes from the code like this, which is present in plenty:
Code:

mov edx, [esp+5ACh+Context.Dr3] //get the address of the copied function
push offset AntiDebug5 //next function to copy&execute
xor edx, 7FFFFFFFh //xor address back
call edx //call it
add esp, 4

If the value of DR3 is 0, then we get an exception.

I had some progress on the program. I was able to run it by patching it so it doesn't call the anti-debugging code (I tried that before but skipped too much). While it seems to work, there are some strange errors, so maybe I will need to "break" the code after all, but for now I have a working program.
I will, however, post some snippets that use SEH, it might be interesting for someone.

Code:

AntiDebug3_1 proc near

pRegist = dword ptr -1Ch
save_esp = dword ptr -18h
pExceptionPointers= dword ptr -14h
prevreg = dword ptr -10h
handler = dword ptr -0Ch
scopetable = dword ptr -8
_trylevel = dword ptr -4
_ebp = dword ptr 0
retaddr = dword ptr 4
dst = dword ptr 8
src = dword ptr 0Ch
count = dword ptr 10h

push ebp
mov ebp, esp
push 0FFFFFFFFh
push offset Antidebug3_scopetable
push offset _except_handler3
mov eax, large fs:0
push eax
mov large fs:0, esp
sub esp, 0Ch
push ebx
push esi
push edi
mov [ebp+save_esp], esp

;[...]
;do some work
;[...]

mov [ebp+_trylevel], 0
mov eax, large fs:0
mov [ebp+pRegist], eax
mov eax, [ebp+pRegist]
mov dword ptr [eax+4], offset return_seh
pushfw
pop eax
or eax, 100h ;set trace flag -> call return_seh
push eax
popfw
jmp short @@handled_by_debugger
; トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト

Antidebug3_filter:
mov eax, 1
retn
; トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト

Antidebug3_handler:
mov esp, [ebp+save_esp]

@@handled_by_debugger:
mov [ebp+_trylevel], 0FFFFFFFFh
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
mov ecx, [ebp+prevreg]
mov large fs:0, ecx
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
retn
AntiDebug3_1 endp


The function sets up its own SEH handler, and tries to invoke it by setting the single step flag. If it's being debugged, then (I guess) the exception gets ignored and excecution continues "normally". I can't see it doing anything nasty to the debugger, unless it can't handle such stuff.

The handler looks like this:
Code:

return_seh proc near

pContext = dword ptr 0Ch

mov eax, [esp+pContext]
push ebx
mov ecx, [eax+CONTEXT.Esp]
mov edx, [ecx]
add ecx, 4
mov [eax+CONTEXT.Edi], edx ; pop edi
mov edx, [ecx]
mov [eax+CONTEXT.Esi], edx ; pop esi
mov ecx, [ecx+4]
mov [eax+CONTEXT.Ebx], ecx ; pop ebx
mov ecx, [eax+CONTEXT.Ebp]
mov edx, [ecx]
add ecx, 4
mov [eax+CONTEXT.Ebp], edx ; ebp = [ebp+0] = saved ebp
mov edx, [ecx]
add ecx, 4
mov [eax+CONTEXT.Eip], edx ; eip = [ebp+4] = retaddr
mov [eax+CONTEXT.Esp], ecx ; esp = ebp+8, basically effect of mov esp, ebp; pop ebp; retn
mov [eax+CONTEXT.Dr7], 400h
push eax
push ebx
mov eax, large fs:0
mov ebx, [eax] ;restore SEH handler
mov ebx, [ebx]
mov [eax], ebx
pop ebx
pop eax
xor eax, eax
pop ebx
retn
return_seh endp

So basically it does what the epilogue code should do, and sets up context to return to the caller.
There is another handler used in a bunch of other places that does about the same, only returns to DR2, not return address in the stack:
Code:

ret_to_dr2 proc near

pContext = dword ptr 18h

push ebx
push eax
push ebx
mov eax, large fs:0
mov ebx, [eax]
mov ebx, [ebx]
mov [eax], ebx
pop ebx
pop eax
mov eax, [esp-8+pContext]
pop ebx
mov ecx, [eax+CONTEXT.Esp]
mov edx, [ecx]
add ecx, 4
mov [eax+CONTEXT.Edi], edx ; pop edi
mov edx, [ecx]
mov [eax+CONTEXT.Esi], edx ; pop esi
mov ecx, [ecx+4]
mov [eax+CONTEXT.Ebx], ecx ; pop ebx
mov ecx, [eax+CONTEXT.Ebp]
mov edx, [ecx]
add ecx, 8
mov [eax+CONTEXT.Esp], ecx ; esp = ebp+8 (move esp, ebp; pop ebp; retn)
mov ecx, [eax+CONTEXT.Dr2]
xor ecx, 7FFFFFFFh
mov [eax+CONTEXT.Ebp], edx ; ebp = [ebp+0] = saved ebp
mov [eax+CONTEXT.Dr7], 400h
mov [eax+CONTEXT.Eip], ecx ; eip = dr2 ^ 0x7ffffff
xor eax, eax
retn
ret_to_dr2 endp


The rest is pretty much moving code back and forth with little pieces of real work done in between. It's quite a pain to read, and I've achieved my goal for now, so I'm putting it aside.
Thanks for reading and sorry for wasting your time.