PDA

View Full Version : Debugger not receiving breakpoint/singlestep exceptions


leakymingebox
February 22nd, 2010, 16:51
Hi all,

I'm trying to examine an app packed with a recent packer. I've stopped the debugattach protection and can attach with ollydbg but I cannot get breakpoints (sw or hw) or single-step to work. The debugger never receives the first-chance exceptions (0x80000003/4) - instead the app just displays a msgbox with the exception code.

It seems to be a per-thread protection - if I inject a thread of my own into the app process, I can single-step and breakpoint that no problem. Currently I am having to do the *incredibly* bodgy method and set my injected thread's context to be similar to the app's thread and do the single-stepping with that one instead

Does anyone have any insights into how the app is preventing the debugger from receiving the first-chance exceptions? I thought they fired even before vectored, so I guess there must be ring0 involved?

Thanks in advance.

disavowed
February 22nd, 2010, 18:35
If doing a regular single-step on a regular instruction isn't even working, then the app is likely mucking with your kernel.
Otherwise, it's likely just detecting and removing your breakpoints.

FrankRizzo
February 22nd, 2010, 19:10
Is this by chance some form of SEH? I'm seeing the same thing, and that's what I attributed it to. (Being an SEH newbie and all).

disavowed
February 22nd, 2010, 19:14
Nope, debugger receives exceptions before the SEH chain does.

disavowed
February 22nd, 2010, 19:14
... and because I'm clairvoyant and can predict the next question... the debugger receives exceptions before the VEH chain as well.

FrankRizzo
February 22nd, 2010, 22:43
Is this ANY debugger, or only sub ring-3 ones?

disavowed
February 23rd, 2010, 01:22
Any.

jstorme
February 23rd, 2010, 01:37
Sounds like it used ZwSetInformationThread with ThreadHideFromDebugger.

leakymingebox
February 23rd, 2010, 16:17
Quote:
[Originally Posted by jstorme;85411]Sounds like it used ZwSetInformationThread with ThreadHideFromDebugger.


You are absolutely right jstorme, thanks! I had to fiddle with patching ZwSetInformationThread a little (looks like the app also scrubs the memory used by system dlls) but I am now able to debug this fine

Indy
February 23rd, 2010, 18:03
leakymingebox
Quote:
Does anyone have any insights into how the app is preventing the debugger from receiving the first-chance exceptions?

NtRemoveProcessDebug:
Code:
; (c) Indy
OS_VERSION_ID_2000 equ 0
OS_VERSION_ID_XP equ 1
OS_VERSION_ID_2003 equ 2
OS_VERSION_ID_VISTA equ 3
OS_VERSION_ID_7 equ 4

jmp OutsideOfDebugger
; +
; Возвращает индекс для SERVICE_TABLE. В случае успеха ZF = 1.
;
QueryVersion proc C
push eax
push edx
assume fs:nothing
mov ecx,fs:[TEB.Peb]
assume ecx:PPEB
mov eax,[ecx].NtMajorVersion
mov edx,[ecx].NtMinorVersion
cmp eax,5
je v_5_x_
cmp eax,6
jne err_ver_
; 6.X
test edx,edx
mov ecx,OS_VERSION_ID_VISTA
jz end_ver_ ; 6.0
inc ecx ; OS_VERSION_ID_7
dec edx
jz end_ver_ ; 6.1
jmp err_ver_
v_5_x_:
xor ecx,ecx ; OS_VERSION_ID_2000
test edx,edx
jz end_ver_ ; 5.0
inc ecx ; OS_VERSION_ID_XP
dec edx
jz end_ver_ ; 5.1
inc ecx ; OS_VERSION_ID_2003
dec edx
jz end_ver_
err_ver_:
mov ecx,0
end_ver_:
pop edx
pop eax
ret
QueryVersion endp

; +
;
SystemCall proc C
; [Esp]:
; + 00 Eax
; + 04 Return1 <- Esp
; + 08 ServiceList1
; + 0C ServiceList2
; + 10 ServiceList3
; + 14 NumberParameters
; + 18 Return2
; + 1C Parameter1
; + 20 ParameterN <- Return
; + XX ...
Call QueryVersion
.if Zero?
lea edx,[esp + 14H]
movzx eax,word ptr [esp + ecx*2 + 4]
.else
mov eax,esp
.endif
; Если выполняется трассировка сервиса NtRemoveProcessDebug,
; то при возврате из прерывания отладочный орт будет отключе
; н, но TF взведён, что приведёт к генерации трассировочного
; исключения, которое необходимо обработать, либо вызвать пр
; ерывание со сброшенным TF, загрузя заранее свормированный
; контекст в процессор посредством сервиса NtContinue(исполь
; зовать для этой цели диспетчер исключений недопустимо, на
; отладочный порт будет доставлено сообщение об исключении и
; диспетчер может трассироваться).
mov ecx,esp
sub esp,300H ; sizeof(CONTEXT)
assume ecx:PCONTEXT
mov CONTEXT.ContextFlags[esp],CONTEXT_CONTROL or CONTEXT_INTEGER or CONTEXT_DEBUG_REGISTERS
mov CONTEXT.regEsp[esp],ecx
mov CONTEXT.regEFlags[esp],EFLAGS_IF or 2
mov word ptr CONTEXT.regSegSs[esp],ss
mov word ptr CONTEXT.regSegCs[esp],cs
mov CONTEXT.regDr7[esp],0
mov CONTEXT.regEbp[esp],ebp
mov CONTEXT.regEax[esp],eax
mov CONTEXT.regEdx[esp],edx
mov CONTEXT.regEbx[esp],ebx
mov CONTEXT.regEsi[esp],esi
mov CONTEXT.regEdi[esp],edi
Call GetGraphEntry
mov CONTEXT.regEip[esp],ecx
push FALSE
push esp
push dword ptr 3CH ; Aligned.
add dword ptr [esp + 4],4
push 3722201CH
Call QueryVersion
jnz @f
lea edx,[esp + 8]
movzx eax,byte ptr [esp + ecx]
Int 2EH
DB 0CCH ; Int3
@@:
add esp,308H
jmp CallService
GetGraphEntry:
Call GetGraphReturn
CallService:
Int 2EH
ServiceExit:
push eax
mov eax,dword ptr [esp + 14H]
push dword ptr [esp + 4]
lea eax,[eax*4 + esp + 18H]
pop dword ptr [eax]
mov dword ptr [esp + 4],eax
pop eax
pop esp
inc dword ptr [esp]
ret
GetGraphReturn:
pop ecx
ret
SystemCall endp

ProcessDebugObjectHandle equ 1EH
STATUS_PORT_NOT_SET equ 0C0000353H

; +
;
OutsideOfDebugger proc C
push esp
mov eax,esp
push NULL
push sizeof(HANDLE)
push eax
push ProcessDebugObjectHandle ;0x1E
push NtCurrentProcess
push 5
push 000000EAH
push 00E400A1H
push 009A0086H
Call SystemCall ; NtQueryInformationProcess(ProcessDebugObjectHandle)
nop
test eax,eax
jnz @f
push dword ptr [esp]
push NtCurrentProcess
push 2
push 00000121H
push 010A00C7H
push 00BFFFFFH
Call SystemCall ; NtRemoveProcessDebug
nop
push eax
push dword ptr [esp + 4]
push 1
push 00000032H
push 0030001BH
push 00190018H
Call SystemCall ; NtClose
nop
pop eax
@@:
add esp,sizeof(HANDLE)
inc dword ptr [esp] ; test.
ret
OutsideOfDebugger endp

; Dump.
; 0x178/0x17B/0x17C
DB 0E9H, 013H, 001H, 000H, 000H, 050H, 052H, 064H, 08BH
DB 00DH, 030H, 000H, 000H, 000H, 08BH, 081H, 0A4H, 000H
DB 000H, 000H, 08BH, 091H, 0A8H, 000H, 000H, 000H, 083H
DB 0F8H, 005H, 074H, 014H, 083H, 0F8H, 006H, 075H, 01DH
DB 085H, 0D2H, 0B9H, 003H, 000H, 000H, 000H, 074H, 019H
DB 041H, 04AH, 074H, 015H, 0EBH, 00EH, 033H, 0C9H, 085H
DB 0D2H, 074H, 00DH, 041H, 04AH, 074H, 009H, 041H, 04AH
DB 074H, 005H, 0B9H, 000H, 000H, 000H, 000H, 05AH, 058H
DB 0C3H, 0E8H, 0B7H, 0FFH, 0FFH, 0FFH, 075H, 00BH, 08DH
DB 054H, 024H, 014H, 00FH, 0B7H, 044H, 04CH, 004H, 0EBH
DB 002H, 08BH, 0C4H, 08BH, 0CCH, 081H, 0ECH, 000H, 003H
DB 000H, 000H, 0C7H, 004H, 024H, 013H, 000H, 001H, 000H
DB 089H, 08CH, 024H, 0C4H, 000H, 000H, 000H, 0C7H, 084H
DB 024H, 0C0H, 000H, 000H, 000H, 002H, 002H, 000H, 000H
DB 08CH, 094H, 024H, 0C8H, 000H, 000H, 000H, 08CH, 08CH
DB 024H, 0BCH, 000H, 000H, 000H, 0C7H, 044H, 024H, 018H
DB 000H, 000H, 000H, 000H, 089H, 0ACH, 024H, 0B4H, 000H
DB 000H, 000H, 089H, 084H, 024H, 0B0H, 000H, 000H, 000H
DB 089H, 094H, 024H, 0A8H, 000H, 000H, 000H, 089H, 09CH
DB 024H, 0A4H, 000H, 000H, 000H, 089H, 0B4H, 024H, 0A0H
DB 000H, 000H, 000H, 089H, 0BCH, 024H, 09CH, 000H, 000H
DB 000H, 0E8H, 033H, 000H, 000H, 000H, 089H, 08CH, 024H
DB 0B8H, 000H, 000H, 000H, 06AH, 000H, 054H, 068H, 03CH
DB 000H, 000H, 000H, 083H, 044H, 024H, 004H, 004H, 068H
DB 01CH, 020H, 022H, 037H, 0E8H, 024H, 0FFH, 0FFH, 0FFH
DB 075H, 00BH, 08DH, 054H, 024H, 008H, 00FH, 0B6H, 004H
DB 00CH, 0CDH, 02EH, 0CCH, 081H, 0C4H, 008H, 003H, 000H
DB 000H, 0EBH, 005H, 0E8H, 01BH, 000H, 000H, 000H, 0CDH
DB 02EH, 050H, 08BH, 044H, 024H, 014H, 0FFH, 074H, 024H
DB 004H, 08DH, 044H, 084H, 018H, 08FH, 000H, 089H, 044H
DB 024H, 004H, 058H, 05CH, 0FFH, 004H, 024H, 0C3H, 059H
DB 0C3H, 054H, 08BH, 0C4H, 06AH, 000H, 06AH, 004H, 050H
DB 06AH, 01EH, 06AH, 0FFH, 06AH, 005H, 068H, 0EAH, 000H
DB 000H, 000H, 068H, 0A1H, 000H, 0E4H, 000H, 068H, 086H
DB 000H, 09AH, 000H, 0E8H, 00FH, 0FFH, 0FFH, 0FFH, 090H
DB 085H, 0C0H, 075H, 036H, 0FFH, 034H, 024H, 06AH, 0FFH
DB 06AH, 002H, 068H, 021H, 001H, 000H, 000H, 068H, 0C7H
DB 000H, 00AH, 001H, 068H, 0FFH, 0FFH, 0BFH, 000H, 0E8H
DB 0EFH, 0FEH, 0FFH, 0FFH, 090H, 050H, 0FFH, 074H, 024H
DB 004H, 06AH, 001H, 06AH, 032H, 068H, 01BH, 000H, 030H
DB 000H, 068H, 018H, 000H, 019H, 000H, 0E8H, 0D6H, 0FEH
DB 0FFH, 0FFH, 090H, 058H, 083H, 0C4H, 004H
DB 0FFH, 004H, 024H ; inc dword ptr ss:[esp]
DB 0C3H ; ret


FrankRizzo
February 24th, 2010, 21:57
AND, FWIW, that was what my issue was as well. 1 down, 99 to go.

Indy
February 24th, 2010, 22:13
FrankRizzo
The kernel knows nothing about VEH. In the core are totally different principles.

omega_red
February 25th, 2010, 13:20
OK, I may as well write here instead of starting another thread.

My issue is similar, but wit a kernel debugger (windbg over serial to vmware guest - xp 32bit). Target application is a pair of exes debugging themselves (all user mode). Issue: kernel debugger *does not* get exceptions if the relevant process has a user mode debugger attached. Even MSDN says that. Very strange design decision imo. Is there a way to force windbg to get exceptions first without modifying the kernel exception dispatchers and/or debug routines?

INT3s are a bit different, they can be redirected to windbg, but then after the target breaks, it seems OS/windbg generates a single step event at the address of instruction immediately following INT3 breakpoint, and this single step event does get forwarded to target process' debugger and/or VEH/SEH.

I've tried different configurations of windbg's event filters (example, no way to catch privileged instruction exception/c0000096 on any event filter settings, it just goes to the usermode debugger straight away), gflags (stop on exceptions doesn't seem to change anything). I've written a very simple pair of mutual-debugging exes for testing and they behave as above. Any ideas?

EDIT:
Well, I solved the problem by patching KiDispatchException. Following excerpt is from 32bit XP, but is similar on other OSes.
Code:
; === USER MODE ===
804fcaba 807d1801 cmp byte ptr [ebp+18h],1 ; FIRST CHANCE
804fcabe 0f85fe010000 jne nt!KiDispatchException+0x374 (804fccc2)
; --- FIRST CHANCE ---
804fcac4 393d842f5580 cmp dword ptr [nt!KiDebugRoutine (80552f84)],edi
804fcaca 746d je nt!KiDispatchException+0x1eb (804fcb39) ; no kd attached, forward to user mode
804fcacc 64a124010000 mov eax,dword ptr fs:[00000124h] ; current KTHREAD (we're in r0, so fs = KPCR)
804fcad2 8b4044 mov eax,dword ptr [eax+44h] ; K/EPROCESS
804fcad5 39b8bc000000 cmp dword ptr [eax+0BCh],edi ; DebugPort
; !!! patch following jump to always occur
804fcadb 7413 je nt!KiDispatchException+0x1a2 (804fcaf0) ; process has no debug port, call kd handler
804fcadd 6a01 push 1 ; user mode
804fcadf 8d8518fdffff lea eax,[ebp-2E8h]
804fcae5 50 push eax ; context frame
804fcae6 56 push esi ; exception record
804fcae7 e8b2401600 call nt!KdIsThisAKdTrap (80660b9e) ; should we call kd
804fcaec 84c0 test al,al
804fcaee 7449 je nt!KiDispatchException+0x1eb (804fcb39) ; no, forward to user mode
; call kd handler
804fcaf0 57 push edi ; 0 = first chance
804fcaf1 ff7514 push dword ptr [ebp+14h] ; previous mode
804fcaf4 8d8518fdffff lea eax,[ebp-2E8h]
804fcafa 50 push eax ; context frame
804fcafb 56 push esi ; exception record
804fcafc ffb510fdffff push dword ptr [ebp-2F0h] ; exception frame
804fcb02 53 push ebx ; trap frame
804fcb03 ff15842f5580 call dword ptr [nt!KiDebugRoutine (80552f84)] ; call kd