一种让Ring 3程序断下来的方法
背景
解决一个bug**的过程中使用windbg但是断不下来,因为每次符号信息没来得及加载系统就蹦了。so想到之前驱动调试的 int 3但程序是64位的不好嵌入汇编想到DbgBreakPoint(),遂有下面的方法
在应用层中直接调用ntdll的DbgBreakPoint。
大概就像下面这个样子:
1 | typedef void (WINAPI *PDbgBreakPoint)(); |
在逆向分析的时候不好找attach时机的时候都可以先设置实时调试器,然后通过在程序或则dll入口点加入it 3断点自动将程序挂在调试器上
顺带把崩溃堆栈列出来吧,还没找到问题所在。。。。。。。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25Child-SP RetAddr : Args to Child : Call Site
0000005a`21aad850 00007ff9`cd99cfc6 : 0000005a`21aada10 00007ff9`d01dd140 00000000`00000000 0000005a`210117e8 : ntdll!RtlUnhandledExceptionFilter2+0x340
0000005a`21aad8c0 00007ff9`d01e6b14 : 00007ff9`00000000 00007ff9`d0250524 00000000`00000000 00000000`00000000 : KERNELBASE!UnhandledExceptionFilter+0x196
0000005a`21aad9b0 00007ff9`d01c831f : 00007ff9`cd9b26ec 0000005a`21aae8c0 00007ff9`cd9a1ca0 0000005a`21aada20 : ntdll!RtlUserThreadStart$filt$0+0x34
0000005a`21aad9e0 00007ff9`d01d921d : 00000000`00000000 0000005a`21aadb80 0000005a`21aae930 0000005a`21aae930 : ntdll!_C_specific_handler+0x87
0000005a`21aada50 00007ff9`d0195b5b : 00000000`00000001 00007ff9`d0140000 00000000`00000000 00007ff9`d02754c8 : ntdll!RtlpExecuteHandlerForException+0xd
0000005a`21aada80 00007ff9`d0197f98 : 00000000`000006c4 00007ff9`cc611159 0000005a`21aae930 00000000`00000020 : ntdll!RtlDispatchException+0x19b
0000005a`21aae150 00007ff9`d01d845a : 00000000`00000000 0000005a`21aaea50 00000000`00000038 00000000`000006c4 : ntdll!RtlRaiseException+0xf0
0000005a`21aae910 00007ff9`cd8f9973 : 00000000`00000000 00000000`00000002 0000005a`21010000 00000000`00000000 : ntdll!KiRaiseUserExceptionDispatcher+0x3a
0000005a`21aae9e0 00007ff9`cdde30f0 : 00000000`0000000c 00000000`00000000 00007ff9`cc611159 00000000`0001001c : KERNELBASE!GetHandleInformation+0x3f
0000005a`21aaea20 00007ff9`cdddba00 : 00000000`00000000 00000000`00000000 00000000`00000038 00000000`6d03cb87 : WS2_32!DSOCKET::FindIFSSocket+0x20
0000005a`21aaea50 00007ff9`cc6147ae : 00000000`00000000 00000000`00000000 00000000`00000000 00007ff9`00000000 : WS2_32!Prolog_Detached+0x1dd1
0000005a`21aaeaf0 00000000`00000000 : 00000000`00000000 00000000`00000000 00007ff9`00000000 0000005a`21aaebb0 : 0x00007ff9`cc6147ae
RetAddr : Args to Child : Call Site
fffff803`7774f6ba : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt! ?? ::FNODOBFM::`string'+0x4fa13
fffff803`775d5ef5 : 00000000`00000000 fffff803`00000000 00000000`00000000 00000000`00000001 : nt! ?? ::NNGAKEGL::`string'+0x1baca
fffff803`7763ce30 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!ObReferenceObjectByHandle+0x25
fffff803`773634b3 : ffffe000`023285c0 000000f7`da93e878 ffffd000`238bbaa8 08421084`21084211 : nt!NtQueryObject+0xc0
00007ffe`2850667a : 00007ffe`25c29973 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13
00007ffe`25c29973 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!NtQueryObject+0xa
00007ffe`264230f0 : 00000000`00140012 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!GetHandleInformation+0x3f
00007ffe`2641b6d9 : 00007ffe`249e98e8 00000000`00000000 00007ffe`249d1c95 00007ffe`25bfaac0 : WS2_32!DSOCKET::FindIFSSocket+0x20
00007ffe`249d1cb3 : 00000000`00000000 00007ffe`00000000 00000000`00000000 00000000`00000000 : WS2_32!Prolog_Detached+0x15d7
问题解决
1.由上面的堆栈找到异常为0xc0000008
fffff803773b7b9e b9080000c0 mov ecx,0C0000008h
fffff803
773b7ba3 e8b0a30200 call nt!KeRaiseUserException (fffff803`773e1f58)
2.解决方案
1 | LONG MyFilter ( DWORD dwExceptionCode ) |
3.反思:
在异常发生后就想到了异常处理不过当时使用的是 try catch,使用后没有任何效果就否决了这种方案,主要还是对Windows SEH学习理解不够。但是在不断F10、F11也学到很多,也重新学习了Windows SEH
4.参考
xfocus上一篇文章Windows异常处理流程
重点:1
2
3
4
5内核模式下:
KiDispatchException->(第一次机会)KiDebugRoutine->RtlDispatchException在内核堆栈寻找SEH(VEH)->(第二次机会)KiDebugRoutine->KeBugCheckEx
用户模式下:
KiDispatchException->KiDebugRoutine->(第一次机会)发送消息到进程调试端口->RtlDispatchException在用户堆栈寻找SEH(VEH)->ZwRaiseException回到KiDispatchException->(第二次机会)发送消息到进程调试或异常端口->KeBugCheckEx。
附加一点关于断点的知识:
- 软件断点:重写断点处代码,在代码中插入INT 3(cc)指令是程序异常,让调试器获取程序控制权限,插入代码后会导致程序的循环冗余码校验和(CRC)改变,很容易被检测到,但是软件断点一般没有数量限制,可以设置很多个。 ollydbg实现软件断点的主要流程:检查INT 3断点是否记录在断点信息表中———->将INT 3断点信息记录到表中—–>记录INT 3断点处的机器码信息—->将INT 3断点处的机器码修改为0xCC—->设置断点信息表
- 硬件断点:硬件断点的可行性依赖于CPU的物理支持,在x86处理器中依赖Dr0-Dr7寄存器,其中Dr0~Dr3四个寄存器用来存放中断地址,Dr4、Dr5保留不使用,Dr6、Dr7用来记录Dr0~Dr3的属性(如读,写还是执行,单位是字节,字还是双字),所以硬件断点一般数量有限。和软件断点不同,硬件断点不是用 INT3 中断,而是用 INT1(1 号中断).INT1 负责硬件中断和步进事件。步进( Single-step)意味着一步一步的执行指令,从而精确的观察关键。代码以便监视数据的变化。在CPU每次执行代码之前,都会先确认当前将执行的代码的地址是否是硬件断点的地址,同时也要确认是否有代码要访问被设置了硬件断点的内存区域。 如果任何储存在 DR0-DR3 中的地址所指向的区域被访问了,就会触发 INT1 中断,同时暂 停 CPU。如果没有,CPU 执行代码,到下一行代码时,CPU 继续重复上面的检查。ollydbg实现:首先通过GetThreadContext获取当前线程中寄存器的信息,再通过SetThreadContext设置当前线程中的寄存器的信息完成对调试寄存器的修改。
- 内存断点:内存断点其实不是真正的断点。当一个调试器设置了一个内存断点的时候,它其实是改变了内存中某个块或者页的权限。一个内存页是操作系统处理的最小的内存单位。一个内存页被申请成功以后,就拥有了一个权限集,它决定了内存该如何被访问。任何对保护页的访问都会引发异常,之后页面恢复访问前的状态,当触发异常后程序的控制权限就交由调试器了,达到了断点的目的。ollydbg实现:通过VirtualQuery获取原内存属性,以便于还原,通过VirtualProtectEx修改内存属性,制造内存访问异常。
看到一篇文章mark一下
让程序等待调试器附加