首页
论坛
课程
招聘
int3流程简单分析
2020-11-21 20:33 930

int3流程简单分析

2020-11-21 20:33
930


主要看了这篇文章https://bbs.pediy.com/thread-259971.htm

就想分析一下int3。

下面主要讲用户模式的int3

 

int3发生的时候,切换内核堆栈,堆栈中依次压入eipint 3 下一条指令地址),csrflagsespss

 

进入KiBreakpointTrap

这个函数就是在堆栈上建立trapframe结构体

.text:FFFFF80003ED4AD5 loc_FFFFF80003ED4AD5:                   ; CODE XREF: KiBreakpointTrap+D2↑j

.text:FFFFF80003ED4AD5                 mov     ecx, 80000003h

.text:FFFFF80003ED4ADA                 mov     edx, 1

.text:FFFFF80003ED4ADF                 mov     r8, [rbp+0E8h]  ; trapframerip

.text:FFFFF80003ED4AE6                 dec     r8              ; 重新指向那条int n指令,注意并没有改trapframe

.text:FFFFF80003ED4AE9                 mov     r9d, 0

.text:FFFFF80003ED4AEF                 call    KiExceptionDispatch ; rcx 为异常码

.text:FFFFF80003ED4AF4                 nop

.text:FFFFF80003ED4AF4 KiBreakpointTrap endp ; sp-analysis failed

 

KiExceptionDispatch

sub     rsp, 1D8h

.text:FFFFF80003ED6C07                 lea     rax, [rsp+(_KEXCEPTION_FRAME+100h)] ; rbx rsp->exceptionframe

.text:FFFFF80003ED6C0F                 movaps  [rsp+1D8h+var_1A8], xmm6 ; struct _KEXCEPTION_FRAME

.text:FFFFF80003ED6C0F                                         ; /*0x030*/     struct _M128A Xmm6;

.text:FFFFF80003ED6C0F                                         ; 推出rsp_KEXCEPTION_FRAME结构体头部指针

.text:FFFFF80003ED6C14                 movaps  xmmword ptr [rsp+(_KEXCEPTION_FRAME+40h)], xmm7

.text:FFFFF80003ED6C19                 movaps  xmmword ptr [rsp+(_KEXCEPTION_FRAME+50h)], xmm8

.text:FFFFF80003ED6C1F                 movaps  xmmword ptr [rsp+(_KEXCEPTION_FRAME+60h)], xmm9

.text:FFFFF80003ED6C25                 movaps  xmmword ptr [rsp+(_KEXCEPTION_FRAME+70h)], xmm10

 

 

mov     [rax+_EXCEPTION_RECORD64.ExceptionFlags], ecx

.text:FFFFF80003ED6C8C                 mov     [rax+_EXCEPTION_RECORD64.ExceptionRecord], rcx

.text:FFFFF80003ED6C90                 mov     [rax+_EXCEPTION_RECORD64.ExceptionAddress], r8(由于传参进来的时候减了一,此时是指向int3指令地址的)

.text:FFFFF80003ED6C94                 mov     [rax+_EXCEPTION_RECORD64.NumberParameters], edx

.text:FFFFF80003ED6C97                 mov     [rax+_EXCEPTION_RECORD64.ExceptionInformation], r9

.text:FFFFF80003ED6C9B                 mov     [rax+(_EXCEPTION_RECORD64.ExceptionInformation+8)], r10

.text:FFFFF80003ED6C9F                 mov     [rax+(_EXCEPTION_RECORD64.ExceptionInformation+10h)], r11

 

然后进入KiDispatchException

首先第一次分发,内核调试器会有接手int3的机会,如果没有或者不处理再交还给三环调试器。

 

此时当前线程仍然运行在内核中,这时候od或者x64dbg接手后,你在EXCEPTION_DEBUG_EVENT里用GetThreadContext的话,返回给你的Rip肯定还是指向int3的下一条指令的RipGetThreadContext的原理就是插入一个apc到你要查的线程中,从那个线程的ethread->trapframe把寄存器拷贝出来转化为context返回给你)如果三环调试器不处理,然后会把trapframe中的rip改成KeUserExceptionDispatcher,然后此函数返回,由KiExceptionDispatch恢复当初进中断的环境,iretq弹出trapframe中的rip,执行ntdll!KiUserExceptionDispatcher如果此时调试器处理了,也就是ContinueDebugEvent最后一个参数DBG_CONTINUE,那么iretq弹出riptrapframe中的rip,也就是int3下一条指令地址)继续运行,所以调试器如果自己要下断点的话要Rip--,再次执行原来的指令。

 

.text:0000000078EA124A KiUserExceptionDispatcher proc near     ; DATA XREF: .rdata:0000000078F54BB0o

.text:0000000078EA124A                                         ; .rdata:off_78F56298o ...

.text:0000000078EA124A                 cld

.text:0000000078EA124B                 mov     rax, cs:Wow64PrepareForException

.text:0000000078EA1252                 test    rax, rax

.text:0000000078EA1255                 jz      short loc_78EA1266 ; rcx exceptionrecord

.text:0000000078EA1257                 mov     rcx, rsp

.text:0000000078EA125A                 add     rcx, 4F0h

.text:0000000078EA1261                 mov     rdx, rsp

.text:0000000078EA1264                 call    rax ; Wow64PrepareForException

.text:0000000078EA1266

.text:0000000078EA1266 loc_78EA1266:                           ; CODE XREF: KiUserExceptionDispatcher+Bj

.text:0000000078EA1266                 mov     rcx, rsp        ; rcx exceptionrecord

.text:0000000078EA1269                 add     rcx, 4F0h

.text:0000000078EA1270                 mov     rdx, rsp        ; rdx contextrecord

.text:0000000078EA1273                 call    RtlDispatchException ; veh seh处理异常

 

如果RtlDispatchException处理不了异常的话,调用ZwRaiseException 进行第二次分发。

 

.text:0000000078EA1288 loc_78EA1288:                           ; CODE XREF: KiUserExceptionDispatcher+30j

.text:0000000078EA1288                 mov     rcx, rsp        ; 进行第二次分发

.text:0000000078EA128B                 add     rcx, 4F0h

.text:0000000078EA1292                 mov     rdx, rsp

.text:0000000078EA1295                 xor     r8b, r8b        ; firstChance=false

.text:0000000078EA1298                 call    ZwRaiseException ; 异常重新被提交到内核

 

KiUserExceptionDispatcher调用的时候堆栈上会有2个结构体(KiDispatchException),上面的是ContextFrame,下面的是ExceptionRecord。而ContextFrame大小为0x4d0个字节,rsp+0x4F0刚好指向ExceptionAddress(指向int3那条指令的地址),然后用syscall进内核。

 

.text:FFFFF80003ED6640 KiSystemCall64  proc near               ; DATA XREF: .pdata:FFFFF800040DE11C↓o

.text:FFFFF80003ED6640                                         ; KiInitializeBootStructures+26E↓o

.text:FFFFF80003ED6640

.text:FFFFF80003ED6640 var_58          = byte ptr -58h

.text:FFFFF80003ED6640 var_30          = qword ptr -30h

.text:FFFFF80003ED6640 var_28          = qword ptr -28h

.text:FFFFF80003ED6640 var_20          = qword ptr -20h

.text:FFFFF80003ED6640 var_18          = qword ptr -18h

.text:FFFFF80003ED6640 var_10          = qword ptr -10h

.text:FFFFF80003ED6640 arg_78          = byte ptr  80h

.text:FFFFF80003ED6640 arg_F8          = qword ptr  100h

.text:FFFFF80003ED6640

.text:FFFFF80003ED6640                 swapgs                  ; MSR(C0000101) <-> MSR(C0000102)

.text:FFFFF80003ED6643                 mov     gs:_KPCR._union_1._struc_3.UserRsp, rsp

.text:FFFFF80003ED664C                 mov     rsp, gs:_KPCR.Prcb.RspBase

.text:FFFFF80003ED6655                 push    2Bh             ; ss

.text:FFFFF80003ED6657                 push    gs:_KPCR._union_1._struc_3.UserRsp ; rsp

.text:FFFFF80003ED665F                 push    r11             ; rflags

.text:FFFFF80003ED6661                 push    33h             ; cs

.text:FFFFF80003ED6663                 push    rcx             ; 下一条指令的Rip

.text:FFFFF80003ED6664                 mov     rcx, r10        ; 把系统调用的第一个参数重新赋给ecx

.text:FFFFF80003ED6667                 sub     rsp, 8

.text:FFFFF80003ED666B                 push    rbp

.text:FFFFF80003ED666C                 sub     rsp, 158h       ; rsp指向trapframe

 

此时trapframe中的Rip就指向了int3那条指令,然后进入内核再次调用KiDispatchException

KiDispatchException会再给三环调试器一个机会,

//给调试器分发异常

        if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {

            goto Handled2;

 

//给异常端口分发异常

        } else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {

            goto Handled2;

 

        } else {

            ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);

            KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,

                         ExceptionRecord->ExceptionCode,

                         (ULONG_PTR)ExceptionRecord->ExceptionAddress,

                         ExceptionRecord->ExceptionInformation[0],

                         ExceptionRecord->ExceptionInformation[1]);

 

        }

 

 

此时再次调用GetContextThread获取的Rip当然是指向int3这条指令的Rip,如果调试器不处理,那么线程被结束,如果调试器处理(ContinueDebugEvent里写DBG_CONTINUE,假设调试器本身不做任何eip修改),然后sysret的时候弹出的Rip就是trapframe中的Rip,就是指向int3这条指令的。所以会发生调试器第一次接手异常和第二次接手异常的Rip会差了1

(这个现象困惑了我很久)。。

 

Ps:我是菜鸟,一口气打完的字,可能逻辑不大流畅,分析的不一定对,大家看个乐呵哈哈

附件为笔记,没什么价值,也发出来吧。



[培训]12月3日2020京麒网络安全大会《物联网安全攻防实战》训练营,正在火热报名中!地点:北京 · 新云南皇冠假日酒店

上传的附件:
收藏
点赞3
打赏
分享
最新回复 (2)
雪    币: 1197
活跃值: 活跃值 (65)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
木羊 活跃值 2020-11-23 09:01
2
0
感谢分享。应该多写一句这是windows下的int3流程。

int3是系统中断,对中断的handle各家可以不同
雪    币: 6617
活跃值: 活跃值 (279)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 活跃值 2020-11-23 09:19
3
0
2个不同的概念,RIP要执行指令的地址;RIP-1: int3异常指令地址(ExceptionAddress)。如果是其它指令异常,那么应该是RIP-x,x等于异常指令的长度。
游客
登录 | 注册 方可回帖
返回