Int 1的实现过程 (一)

  闲话少说,直奔主题,首先OD载入一个程序,然后执行一下单步(调试器会将TF置1)

  此时,CPU会在基于当前线程上下文的环境中,进入int 1的中断门,也就是KiTrap01

kd> !idt -a

Dumping IDT:

00:	805431a0 nt!KiTrap00
01:	8054331c nt!KiTrap01     // int 1会转到这个函数执行
02:	Task Selector = 0x0058   // NMI  从这里可以看到NMI是通过任务门执行的
03:	80543730 nt!KiTrap03
04:	805438b0 nt!KiTrap04
05:	80543a10 nt!KiTrap05
06:	80543b84 nt!KiTrap06
07:	805441fc nt!KiTrap07
08:	Task Selector = 0x0050  // DF 因为发生DF时,寄存器可能已经出错了,所以需要任务门,加载新的环境
09:	80544600 nt!KiTrap09

  然后我们看下windbg,首先在KiTrap01下断,其次通过OD的单步后,确实已经中断到windbg的KiTrap01处了,其环境也是被调试线程的环境

    看下TSS中ESP0的值

kd> r tr
tr=00000028
kd> dg 28
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0028 80042000 000020ab TSS32 Busy 0 Nb By P  Nl 0000008b
kd> dd 80042000
80042000  0c458b24 b23b9de0 8b080010 758b0855  //b23b9de0就是ESP0的值 

        intel手册中说到,在转移到中断例程入口前,会先将SS  ESP  EFLAGS  CS  EIP  ErrorCode压入内核栈,所以我们看看ESP0中的值

kd> dd esp
b23b9dcc 00421480 0000001b 00000346 0012ffc0
b23b9ddc 00000023                  //从这里可以得知   SS:23    ESP:12FFC0    EFLAGS:346    CS:1b    EIP:421480

    好了,可以进入正题了,先看下KiTrap01的执行过程

nt!KiTrap01:
8054331c 6a00            push    0                 // ErrorCode
8054331e 66c74424020000  mov     word ptr [esp+2],0
80543325 55              push    ebp
80543326 53              push    ebx
80543327 56              push    esi
80543328 57              push    edi
80543329 0fa0            push    fs
8054332b bb30000000      mov     ebx,30h
80543330 668ee3          mov     fs,bx
80543333 648b1d00000000  mov     ebx,dword ptr fs:[0]
8054333a 53              push    ebx                // ExceptionList
8054333b 83ec04          sub     esp,4               // PreviousPreviousMode
8054333e 50              push    eax
8054333f 51              push    ecx
80543340 52              push    edx
80543341 1e              push    ds
80543342 06              push    es
80543343 0fa8            push    gs
80543345 66b82300        mov     ax,23h
80543349 83ec30          sub     esp,30h             // 到这里_KTRAP_FRAME已经填充一半了,还差30h
8054334c 668ed8          mov     ds,ax
8054334f 668ec0          mov     es,ax
80543352 8bec            mov     ebp,esp
80543354 f744247000000200 test    dword ptr [esp+70h],20000h  // 检测是否是虚拟8086模式
8054335c 7596            jne     nt!V86_kit1_a (805432f4)      // 是虚拟8086模式的话,就发生跳转,这里我们应该继续执行
8054335e fc              cld
8054335f 8b5d60          mov     ebx,dword ptr [ebp+60h]      // 被压入栈的EBP寄存器的值,这里是12FFF0
80543362 8b7d68          mov     edi,dword ptr [ebp+68h]      // 被压入栈中EIP寄存器的值,这里是421480
80543365 89550c          mov     dword ptr [ebp+0Ch],edx    // KiFastSystemCall
80543368 c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h // DbgArgMark
8054336f 895d00          mov     dword ptr [ebp],ebx      // DbgEbp
80543372 897d04          mov     dword ptr [ebp+4],edi     // DbgEip

/**************************************************************************************************************************************
/*****                  执行完这几个栈操作后,我们看下栈中的值                                   *****/**************************************************************************************************************************************
  kd> dd ebp  其实这里都是KTRAP_FTAME的值
  b23b9d64 0012fff0  00421480  badb0d00  7c92e4f4
  b23b9d74 7c930208  ffffffff   ffffffff  0012fff0
  b23b9d84 00000000  80543349   00000008  00000196
  b23b9d94 00000000  00000023   00000023  7c92e4f4
  b23b9da4 0012ffb0   00000000  00000146  ffffffff
  b23b9db4 00000030  7c930208   ffffffff  7ffd8000
  b23b9dc4 0012fff0  00000000    00421480 0000001b
  b23b9dd4 00000346  0012ffc0   00000023  805470de

80543375 64f60550000000ff test    byte ptr fs:[50h],0FFh    // 测试DebugActive
8054337d 0f85edfeffff     jne     nt!Dr_kit1_a (80543270)
80543383 64833d5400000000 cmp     dword ptr fs:[54h],0
8054338b 7561             jne     nt!KiTrap01+0xd2 (805433ee)
8054338d 8b4d68           mov     ecx,dword ptr [ebp+68h]
80543390 81f920255480     cmp     ecx,offset nt!KiFastCallEntry (80542520)  
80543396 0f84c4feffff     je      nt!KiTrap00+0xc0 (80543260) // 测试是不是sysenter指令,如果是的话,不能步过
8054339c f7457000000200   test    dword ptr [ebp+70h],20000h
805433a3 7524         jne    nt!KiTrap01+0xad (805433c9) // 检测虚拟8086模式
805433a5 66f7456c0100     test   word ptr [ebp+6Ch],1    
805433ab 7408         je     nt!KiTrap01+0x99 (805433b5)   // 检测当前CPL,如果是R3的话,就不发生跳转
805433ad 66837d6c1b     cmp    word ptr [ebp+6Ch],1Bh
805433b2 7515         jne    nt!KiTrap01+0xad (805433c9)     // 如果是R3的话,CS应该是1B,如果不是的话,发生跳转
805433b4 fb         sti
805433b5 816570fffeffff  and    dword ptr [ebp+70h],0FFFFFEFFh   //EFLAGS的TF位清零
805433bc 8b5d68          mov    ebx,dword ptr [ebp+68h]     // (ebx)-> faulting instruction
805433bf b804000080      mov    eax,80000004h         // TF异常的错误码
805433c4 e94efcffff      jmp    nt!KiExceptionExit+0x16b (80543017)

 

80543017 33c9            xor     ecx,ecx
80543019 e81a000000      call    nt!CommonDispatchException (80543038)  // Never return

接下来看看CommonDispatchException内部都做了些什么

/**********************************************************************************************
/*****          Set up exception record for raising exception            *****
/**********************************************************************************************
首先来看看EXCEPTION_RECORD
  kd> dt _exception_record
  nt!_EXCEPTION_RECORD
  +0x000 ExceptionCode : Int4B
  +0x004 ExceptionFlags : Uint4B
  +0x008 ExceptionRecord : Ptr32 _EXCEPTION_RECORD
  +0x00c ExceptionAddress : Ptr32 Void
  +0x010 NumberParameters : Uint4B
  +0x014 ExceptionInformation : [15] Uint4B

nt!CommonDispatchException:
80543038 83ec50          sub     esp,50h            // ExceptionRecordLength, allocate exception record
8054303b 890424          mov     dword ptr [esp],eax     // set ExceptionCode           其实也就是上文的80000004h
8054303e 33c0            xor     eax,eax
80543040 89442404        mov     dword ptr [esp+4],eax    // set ExceptionFlags         这里为0
80543044 89442408        mov     dword ptr [esp+8],eax    //  set associated ExceptionRecord 这里为0
80543048 895c240c        mov     dword ptr [esp+0Ch],ebx  //  set ExceptionAddress       其实也就是上文的421480
8054304c 894c2410        mov     dword ptr [esp+10h],ecx  //  set NumberParameters       这里为0
80543050 83f900          cmp     ecx,0
80543053 740c            je      nt!CommonDispatchException+0x29 (80543061)  

//跳到了这里 set up arguments and call _KiDispatchException
80543061 8bcc       mov ecx,esp               //  (ecx)->exception record
80543063 f7457000000200 test dword ptr [ebp+70h],20000h
8054306a 7407       je nt!CommonDispatchException+0x3b (80543073)  // 因为不是虚拟8086模式,所以会发生跳转

//跳转到这里后,就该调用KiDispatchException了
stdCall _KiDispatchException,<ecx, 0, ebp, eax, 1>      //  汇编中的原型
KiDispatchException (                       //  API中的原型
IN PEXCEPTION_RECORD ExceptionRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
IN KPROCESSOR_MODE PreviousMode,
IN BOOLEAN FirstChance
)

80543073 8b456c      mov   eax,dword ptr [ebp+6Ch]   
80543076 83e001      and   eax,1           //  通过CS段选择子,得到被调试程序的CPL, 通过与运算后,得到后面的PreviousMode
80543079 6a01       push  1              //  1 - first chance TRUE
8054307b 50        push  eax            //  eax - PreviousMode
8054307c 55         push  ebp            //  TrapFrame
8054307d 6a00        push  0             //   ExceptionFrame
8054307f 51        push  ecx            //   ExceptionRecord
80543080 e8e1c3fbff    call   nt!KiDispatchException (804ff466)

  

这里再看看我们的KTRAP_FRAME的具体值,这些值能很详细的说明故障信息

kd> dt _ktrap_frame b2bf3d64
nt!_KTRAP_FRAME
   +0x000 DbgEbp           : 0x12fff0
   +0x004 DbgEip           : 0x421480
   +0x008 DbgArgMark       : 0xbadb0d00
   +0x00c DbgArgPointer    : 0x7c92e4f4
   +0x010 TempSegCs        : 0x7c930208
   +0x014 TempEsp          : 0xffffffff
   +0x018 Dr0              : 0xffffffff
   +0x01c Dr1              : 0x12fff0
   +0x020 Dr2              : 0
   +0x024 Dr3              : 0x80543349
   +0x028 Dr6              : 8
   +0x02c Dr7              : 0x196
   +0x030 SegGs            : 0
   +0x034 SegEs            : 0x23
   +0x038 SegDs            : 0x23
   +0x03c Edx              : 0x7c92e4f4
   +0x040 Ecx              : 0x12ffb0
   +0x044 Eax              : 0
   +0x048 PreviousPreviousMode : 0x146
   +0x04c ExceptionList    : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
   +0x050 SegFs            : 0x30
   +0x054 Edi              : 0x7c930208
   +0x058 Esi              : 0xffffffff
   +0x05c Ebx              : 0x7ffd8000
   +0x060 Ebp              : 0x12fff0
   +0x064 ErrCode          : 0
   +0x068 Eip              : 0x421480
   +0x06c SegCs            : 0x1b
   +0x070 EFlags           : 0x246
   +0x074 HardwareEsp      : 0x12ffc0
   +0x078 HardwareSegSs    : 0x23
   +0x07c V86Es            : 0
   +0x080 V86Ds            : 0
   +0x084 V86Fs            : 0
   +0x088 V86Gs            : 0

  

  好了,先分析到这吧,明天分析KiDispatchException这个函数......困了!

                                                              01:08:21

  

  

时间: 2024-10-13 23:29:23

Int 1的实现过程 (一)的相关文章

mysql 存贮过程,触发器,游标示例

create PROCEDURE  result(in  n int,out total int) #执行过程开始begin#存贮过程中变量的定义 declare num int default 0; set total :=0; while num<n do set num :=num+1; set tatal :=total+num; end while; end

【系统篇】从int 3探索Windows应用程序调试原理

探索调试器下断点的原理 在Windows上做开发的程序猿们都知道,x86架构处理器有一条特殊的指令——int 3,也就是机器码0xCC,用于调试所用,当程序执行到int 3的时候会中断到调试器,如果程序不处于调试状态则会弹出一个错误信息,之后程序就结束.使用VC开发程序时,在Debug版本的程序中,编译器会向函数栈帧中填充大量的0xCC,用于调试使用.因此,经常我们的程序发生缓冲区溢出时,会看到大量的“烫烫烫…”,这是因为“烫”的编码正是两个0xCC. 那么?为什么int 3可以让程序中断到调试

操作系统运行环境与运行机制(系统调用篇)

系统调用: 用户在编程是可以调用的操作系统功能(使CPU可以从用户态陷入内核态)  应用程序,C函数,API,和内核函数关系 系统调用机制的设计 ①中断/异常机制 支持系统调用服务的实现:选择一条陷入指令(访管指令)即可 ②选择一条特殊指令:陷入指令(亦称访管指令) 引发异常完成用户态到内核态的切换 ③系统调用号和参数: 每个系统调用都实现给定一个编号(功能号) ④系统调用表: 存放系统调用服务例程入口地址 参数传递问题(怎样实现用户程序的参数(存在于用户栈)传递给内核(存在于内核栈)?): ①

scala习题精选100道

1.**[primary]**关于和Scala进行交互的基本方式REPL说法错误的是? C A.R 读取(read)B.E 求值(evaluate)C.P 解析(Parse)D.L 循环(Loop) 2.**[primary]**不属于Scala7种数值类型的是? D A.CharB.IntC.FloatD.LongLong 3.**[primary]**如果需要使用scala库中的sqrt函数,如sqrt(2),下列引用包的方式错误的是? B A.import scala.math._B.im

golang 数据一 &nbsp; (字符串、数组和数组指针)

从如下几个方面介绍GO语言的数据 1. 字符串 2. 数组 3. 切片 4. 字典 5. 结构 字符串 Go语言中的字符串是由一组不可变的字节(byte)序列组成从源码文件中看出其本身是一个复合结构 string.go  type stringStruct struct {     str unsafe.Pointer         len int } 字符串中的每个字节都是以UTF-8编码存储的Unicode字符字符串的头部指针指向字节数组的开始但是没有NULL或'\0'结尾标志. 表示方式

php的mysql\mysqli\PDO(二)mysqli

原文链接:http://www.orlion.ga/1147/ mysqli有面向对象风格和面向过程风格,个人感觉还是用面向对象风格比较好(毕竟是面向对象) 1.mysqli::_construct() mysqli::__construct ([ string $host = ini_get("mysqli.default_host") [, string $username = ini_get("mysqli.default_user") [, string $

第17课 ++和--操作符分析

1. ++和—操作符对应的两条汇编指令 (1)前置++或--:变量自增(减)1,然后取变量值 (2)后置++或--:取变量值,然后变量自增(减)1 [实例分析]一对令人头疼的兄弟 #include <stdio.h> int main() { int i = 0; int r = 0; r = (i++) + (i++) + (i++); printf("i = %d\n", i); //i等于3 printf("r = %d\n", r); //vc下

Listview实现不同类型的布局

打开各种客户端发现 Listview的布局多种多样,在我以前的认知中listview不是只能放一种item布局嘛,我就震惊了,现在我自己的项目上要用到这种方式那么就去做下 原理是listview 的adapter中 有这两个方法 // 返回当前布局的样式type @Override public int getItemViewType(int position) { return listDatas.get(position).type; } // 返回你有多少个不同的类型的数量 @Overri

优先队列ADT_PrioQueue

如果定义最小值为最高优先权, 使用最小堆为例. 每次入队新元素都要向上调整, 同理, 弹出优先权最高的元素时要向下调整, 使之成为堆. 将新元素插入p[j]后的调整工作由AdjustUp()函数完成, 该函数按照与函数AdjustDown()相反的方向比较路径, 由下向上, 与双亲结点进 行比较. 若双亲结点的元素值比孩子结点元素值大, 则调整之, 直到或者其双亲不大于待插入元素, 或者以及到达堆顶. 程序中首先将新元素插在q[j]处, 令tmp元素等于新元素q[j], 从i = j开始, 将t