无聊之中拿起情景分析书看看....顺便做下笔记加深下印象
1. 从用户态跳转到内核态一般有三种方式:中断,异常,和自陷,除了这些还有调用门啦,快速系统调用啦什么的
2. 传统的windows是调用int 0x2e来切换到内核态的。从TSS中装入本线程的系统空间堆栈寄存器SS和堆栈指针ESP,在把用户空间的堆栈寄存器SS,堆栈指针ESP,标志位寄存器EFLAGS,代码段寄存器CS,EIP压入用户系统空间的堆栈;然后从IDT中以0x2e为索引找到相应的程序入口开始执行内核程序。返回就是上述逆过程
3. 快速系统调用sysenter指令实现。配套了3个寄存器。SYSENTER_CS_MSR,SYSENTER_EIP_MSR,SYSENTER_ESP_MSR。
SYSENTER_CS_MSR内容复制到段寄存器CS中
SYSENTER_EIP_MSR内容复制到寄存器EIP中
SYSENTER_CS_MSR内容+8写入堆栈段寄存器SS
SYSENTER_ESP_MSR的内容复制到堆栈指针ESP中
这3个寄存器的内容是在内核初始化的时候赋值的
SYSENTER_CS_MSR :Ke386Wrmsr(0x174,KGDT_R0_CODE,0);
SYSENTER_ESP_MSR:Ke386Wrmsr(0x175,(ULONG)KeGetCurrentPrcb->DpcStack,0);
SYSENTER_EIP_MSR:Ke386Wrmsr(0x176,(ULONG)KiFastCallEntry,0);
第3个参数为0,是意味着这3个寄存器为32位的。SYSENTER_CS_MSR被设置成KGDT_R0_CODE即0x8,设置堆栈段寄存器ss的就是0x10,就是KGDT_R0_DATA。
SYSENTER_ESP_MSR设置成PRCB中指针DpcStack的内容.这个是指向一个中立的不属于任何一个线程的堆栈,供内核执行DPC函数的使用的. SYSENTER_EIP_MSR设置成指向KiFastCallEntry,这是系统空间中快速调用的总入口
4. 执行sysexit退出时:
将CS设置成SYSENTER_CS_MSR的内容+16,就是KGDT_R3_CODE
把寄存器EDX的内容复制到EIP
把SS设置成SYSENTER_CS_MSR的内容+24,就是KGDT_R3_DATA
把寄存器ECX的内容复制到ESP
以ZwReadFile为例
nt!ZwReadFile:
804ffbf4 b8b7000000 mov eax,0B7h//功能号
804ffbf9 8d542404 lea edx,[esp+4]//参数的开始位置
804ffbfd 9c pushfd//标志寄存器
804ffbfe 6a08 push 8//KGDT_R0_CODE
804ffc00 e88ce80300 call nt!KiSystemService (8053e491)
804ffc05 c22400 ret 24h//9个参数,4*9
Call 8053e491进入这个函数后
Cpu自动压入以下信息:
用户空间的堆栈位置,包括堆栈段寄存器ss和堆栈指针esp的内容
Cpu中的标志寄存器EFLAGS的内容
用户空间的指令位置,包括代码段寄存器cs和指令指针eip的内容
当用户空间进入到系统空间时,cpu自动根据TR的指引从TSS中获取当前进程的SS和ESP两个寄存器的值,根据TR的指引从TSS获取当前线程的系统空间堆栈,再把好几个寄存器的内容压入这个堆栈,并且根据IDTR的指引从中断向量表中获取CS和EIP的值。
以ZwCreateFile为例 kd> t nt!KiSystemService: 8053e491 6a00 push 0 //这个PUSH 0只是在堆栈中占一个位置,返回用户空间时其内容将被设置成寄存器eax中作为系统调用的返回值,就是出错代码。先设置成0,假设正常,还有一个方面是为了保持自陷和异常进入内核时的框架是一样的 kd> t nt!KiSystemService+0x2: 8053e493 55 push ebp kd> r eax=00000025 ebx=e108b890 ecx=0000002e edx=f66b4bbc esi=00000000 edi=f66b4c70 eip=8053e493 esp=f66b4ba8 ebp=f66b4c20 iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246 nt!KiSystemService+0x2: 8053e493 55 push ebp kd> t nt!KiSystemService+0x3: 8053e494 53 push ebx kd> t nt!KiSystemService+0x4: 8053e495 56 push esi kd> t nt!KiSystemService+0x5: 8053e496 57 push edi kd> t nt!KiSystemService+0x6: 8053e497 0fa0 push fs//保存寄存器 kd> t nt!KiSystemService+0x8: 8053e499 bb30000000 mov ebx,30h//KGDT_R0_PCR=0x30 //windows内核有个特殊的要求,只要cpu在内核中运行,段寄存器fs就必须指向一个KPCR的结构。kd> t nt!KiSystemService+0xd: 8053e49e 668ee3 mov fs,bx kd> r eax=00000025 ebx=00000030 ecx=0000002e edx=f66b4bbc esi=00000000 edi=f66b4c70 eip=8053e49e esp=f66b4b94 ebp=f66b4c20 iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246 nt!KiSystemService+0xd: 8053e49e 668ee3 mov fs,bx//是FS段的起点与KPCR数据结构对齐 kd> t nt!KiSystemService+0x10: 8053e4a1 ff3500f0dfff push dword ptr ds:[0FFDFF000h]//保存当前ECCEPTIONLIST kd> t nt!KiSystemService+0x16: 8053e4a7 c70500f0dfffffffffff mov dword ptr ds:[0FFDFF000h],0FFFFFFFFh//新的EXCEPTIONLIST为空白 nt!KiSystemService+0x20: 8053e4b1 8b3524f1dfff mov esi,dword ptr ds:[0FFDFF124h]//使ESI指向当前进程的KTHREAD结构 kd> t nt!KiSystemService+0x26: 8053e4b7 ffb640010000 push dword ptr [esi+140h]//保存老的先前模式,可能是用户模式或者内核模式 kd> t nt!KiSystemService+0x2c: 8053e4bd 83ec48 sub esp,48h//跳过其他寄存器,目的是为包括寄存器其他段寄存器暂存在空间kd> t nt!KiSystemService+0x2f: 8053e4c0 8b5c246c mov ebx,dword ptr [esp+6Ch]// kd> r eax=00000025 ebx=00000030 ecx=0000002e edx=f66b4bbc esi=823c3da8 edi=f66b4c70 eip=8053e4c0 esp=f66b4b44 ebp=f66b4c20 iopl=0 nv up ei ng nz na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286 nt!KiSystemService+0x2f: 8053e4c0 8b5c246c mov ebx,dword ptr [esp+6Ch] ss:0010:f66b4bb0=00000008//[esp+6Ch]就是进入这个call之前的cs值 nt!KiSystemService+0x33: 8053e4c4 83e301 and ebx,1//为1001了,0环的最低位为0,3环的为1 nt!KiSystemService+0x36: 8053e4c7 889e40010000 mov byte ptr [esi+140h],bl//设置新的模式,此时为r3模式 nt!KiSystemService+0x3c: 8053e4cd 8bec mov ebp,esp kd> t nt!KiSystemService+0x3e: 8053e4cf 8b9e34010000 mov ebx,dword ptr [esi+134h]//KTHREAD结构中的指针TrapFrame kd> t nt!KiSystemService+0x44: 8053e4d5 895d3c mov dword ptr [ebp+3Ch],ebx。//暂时保存下 nt!KiSystemService+0x47: 8053e4d8 89ae34010000 mov dword ptr [esi+134h],ebp//设置新的TrapFrame,指向堆栈上的框架 kd> t nt!KiSystemService+0x4d: 8053e4de fc cld kd> t nt!KiSystemService+0x4e: 8053e4df 8b5d60 mov ebx,dword ptr [ebp+60h] nt!KiSystemService+0x51: 8053e4e2 8b7d68 mov edi,dword ptr [ebp+68h] kd> t nt!KiSystemService+0x54: 8053e4e5 89550c mov dword ptr [ebp+0Ch],edx//edx中时参数的位置kd> t nt!KiSystemService+0x57: 8053e4e8 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h kd> t nt!KiSystemService+0x5e: 8053e4ef 895d00 mov dword ptr [ebp],ebx kd> t nt!KiSystemService+0x61: 8053e4f2 897d04 mov dword ptr [ebp+4],edi kd> t nt!KiSystemService+0x64: 8053e4f5 f6462cff test byte ptr [esi+2Ch],0FFh//检查线程是否被调试kd> t nt!KiSystemService+0x68: 8053e4f9 0f858dfeffff jne nt!Dr_kss_a (8053e38c) kd> t nt!KiSystemService+0x6e: 8053e4ff fb sti//开中断 nt!KiSystemService+0x6f: 8053e500 e9d8000000 jmp nt!KiFastCallEntry+0x8d (8053e5dd) ……………………….. 系统调用函数的跳转kd> t nt!KiFastCallEntry+0x8d: 8053e5dd 8bf8 mov edi,eax//eax为系统调用号 kd> r eax=00000025 ebx=f66b4c20 ecx=0000002e edx=f66b4bbc esi=823c3da8 edi=804ff09d eip=8053e5dd esp=f66b4b44 ebp=f66b4b44 iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246 nt!KiFastCallEntry+0x8d: 8053e5dd 8bf8 mov edi,eax kd> p nt!KiFastCallEntry+0x8f: 8053e5df c1ef08 shr edi,8//SERVICE_TABLE_SHIFT,除以256 kd> p nt!KiFastCallEntry+0x92: 8053e5e2 83e730 and edi,30h//SERVICE_TABLE_MASK kd> p nt!KiFastCallEntry+0x95: 8053e5e5 8bcf mov ecx,edi//下面将比较ecx kd> p nt!KiFastCallEntry+0x97: 8053e5e7 03bee0000000 add edi,dword ptr [esi+0E0h]//该线程的系统调用表 kd> p nt!KiFastCallEntry+0x9d: 8053e5ed 8bd8 mov ebx,eax//eax系统调用号 kd> p nt!KiFastCallEntry+0x9f: 8053e5ef 25ff0f0000 and eax,0FFFh// kd> p nt!KiFastCallEntry+0xa4: 8053e5f4 3b4708 cmp eax,dword ptr [edi+8]//检查系统调用号是否越界 kd> p nt!KiFastCallEntry+0xa7: 8053e5f7 0f8345fdffff jae nt!KiBBTUnexpectedRange (8053e342)//大于越界尝试去加载shadow 表 kd> p nt!KiFastCallEntry+0xad: 8053e5fd 83f910 cmp ecx,10h//检查是否是win32表 kd> p nt!KiFastCallEntry+0xb0: 8053e600 751a jne nt!KiFastCallEntry+0xcc (8053e61c)//不是的 //这一段是使用win32k系统的调用表的 8053e602 8b0d18f0dfff mov ecx,dword ptr ds:[0FFDFF018h] 8053e608 33db xor ebx,ebx 8053e60a 0b99700f0000 or ebx,dword ptr [ecx+0F70h] 8053e610 740a je nt!KiFastCallEntry+0xcc (8053e61c) 8053e612 52 push edx 8053e613 50 push eax 8053e614 ff1564405580 call dword ptr [nt!KeGdiFlushUserBatch (80554064)] 8053e61a 58 pop eax 8053e61b 5a pop edx // kd> p nt!KiFastCallEntry+0xcc: 8053e61c ff0538f6dfff inc dword ptr ds:[0FFDFF638h]//增加syscall的数目(系统调用计数) kd> p nt!KiFastCallEntry+0xd2: 8053e622 8bf2 mov esi,edx//esi指向用户空间堆栈上的参数块 kd> p nt!KiFastCallEntry+0xd4: 8053e624 8b5f0c mov ebx,dword ptr [edi+0Ch]//参数表 kd> p nt!KiFastCallEntry+0xd7: 8053e627 33c9 xor ecx,ecx kd> p nt!KiFastCallEntry+0xd9: 8053e629 8a0c18 mov cl,byte ptr [eax+ebx] kd> p nt!KiFastCallEntry+0xdc: 8053e62c 8b3f mov edi,dword ptr [edi]//edi指向系统调用表 kd> p nt!KiFastCallEntry+0xde: 8053e62e 8b1c87 mov ebx,dword ptr [edi+eax*4]//定位到具体的调用函数 kd> p nt!KiFastCallEntry+0xe1: 8053e631 2be1 sub esp,ecx //分配系统空间堆栈 kd> p nt!KiFastCallEntry+0xe3: 8053e633 c1e902 shr ecx,2//除以4,参数个数 kd> p nt!KiFastCallEntry+0xe6: 8053e636 8bfc mov edi,esp kd> p nt!KiFastCallEntry+0xe8: 8053e638 3b35549a5580 cmp esi,dword ptr [nt!MmUserProbeAddress (80559a54)]//是否在用户模式地址上 kd> p nt!KiFastCallEntry+0xee: 8053e63e 0f83a8010000 jae nt!KiSystemCallExit2+0x9f (8053e7ec)//大于的话进行访问检查 8053e644 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]////拷贝参数 8053e646 ffd3 call ebx //call这个目标函数//下面的是开始系统调用的返回过程了 8053e648 8be5 mov esp,ebp//回到系统调用的框架 8053e64a 8b0d24f1dfff mov ecx,dword ptr ds:[0FFDFF124h]//ecx指向当前线程的kthread 8053e650 8b553c mov edx,dword ptr [ebp+3Ch]//取出保存着的框架指针 8053e653 899134010000 mov dword ptr [ecx+134h],edx//恢复kthread结构中的框架指针 nt!KiServiceExit: 8053e659 fa cli//关中断呗 8053e65a f7457000000200 test dword ptr [ebp+70h],20000h//检查是否有apc请求 8053e661 7506 jne nt!KiServiceExit+0x10 (8053e669),有的话就去处理apc请求 8053e663 f6456c01 test byte ptr [ebp+6Ch],1//如果先前模式是内核模式的话就不递交apc 8053e667 7457 je nt!KiServiceExit+0x67 (8053e6c0) //这一段式处理apc的过程 8053e669 8b1d24f1dfff mov ebx,dword ptr ds:[0FFDFF124h]是ebx指向当前线程的kthread结构 8053e66f c6432e00 mov byte ptr [ebx+2Eh],0 8053e673 807b4a00 cmp byte ptr [ebx+4Ah],0 8053e677 7447 je nt!KiServiceExit+0x67 (8053e6c0) 8053e679 8bdd mov ebx,ebp 8053e67b 894344 mov dword ptr [ebx+44h],eax 8053e67e c743503b000000 mov dword ptr [ebx+50h],3Bh 8053e685 c7433823000000 mov dword ptr [ebx+38h],23h 8053e68c c7433423000000 mov dword ptr [ebx+34h],23h 8053e693 c7433000000000 mov dword ptr [ebx+30h],0 8053e69a b901000000 mov ecx,1 8053e69f ff15f4864d80 call dword ptr [nt!_imp_KfRaiseIrql (804d86f4)] 8053e6a5 50 push eax 8053e6a6 fb sti 8053e6a7 53 push ebx 8053e6a8 6a00 push 0 8053e6aa 6a01 push 1 8053e6ac e88d03fcff call nt!KiDeliverApc (804fea3e) 8053e6b1 59 pop ecx 8053e6b2 ff151c874d80 call dword ptr [nt!_imp_KfLowerIrql (804d871c)] 8053e6b8 8b4344 mov eax,dword ptr [ebx+44h] 8053e6bb fa cli 8053e6bc ebab jmp nt!KiServiceExit+0x10 (8053e669) 8053e6be 8bff mov edi,edi :::::::::::::::::::::::::::::::::::::: 8053e6c0 8b54244c mov edx,dword ptr [esp+4Ch] 8053e6c4 648b1d50000000 mov ebx,dword ptr fs:[50h] 8053e6cb 64891500000000 mov dword ptr fs:[0],edx 8053e6d2 8b4c2448 mov ecx,dword ptr [esp+48h] 8053e6d6 648b3524010000 mov esi,dword ptr fs:[124h] 8053e6dd 888e40010000 mov byte ptr [esi+140h],cl 8053e6e3 f7c3ff000000 test ebx,0FFh 8053e6e9 7579 jne nt!KiSystemCallExit2+0x17 (8053e764) 8053e6eb f744247000000200 test dword ptr [esp+70h],20000h 8053e6f3 0f85eb080000 jne nt!KiExceptionExit+0x12c (8053efe4) 8053e6f9 66f744246cf8ff test word ptr [esp+6Ch],0FFF8h 8053e700 0f84b4000000 je nt!KiSystemCallExit2+0x6d (8053e7ba) 8053e706 66837c246c1b cmp word ptr [esp+6Ch],1Bh 8053e70c 660fba64246c00 bt word ptr [esp+6Ch],0 8053e713 f5 cmc 8053e714 0f878e000000 ja nt!KiSystemCallExit2+0x5b (8053e7a8) 8053e71a 66837d6c08 cmp word ptr [ebp+6Ch],8 8053e71f 7405 je nt!KiServiceExit+0xcd (8053e726) 8053e721 8d6550 lea esp,[ebp+50h] 8053e724 0fa1 pop fs 8053e726 8d6554 lea esp,[ebp+54h] 8053e729 5f pop edi 8053e72a 5e pop esi 8053e72b 5b pop ebx 8053e72c 5d pop ebp 8053e72d 66817c24088000 cmp word ptr [esp+8],80h 8053e734 0f87c6080000 ja nt!KiExceptionExit+0x148 (8053f000) 8053e73a 83c404 add esp,4 8053e73d f744240401000000 test dword ptr [esp+4],1 nt!KiSystemCallExitBranch: 8053e745 7506 jne nt!KiSystemCallExit2 (8053e74d)//从用户空间的系统调用,使用KiSystemCallExit2 8053e747 5a pop edx//从内核空间发起的调用 8053e748 59 pop ecx 8053e749 9d popfd 8053e74a ffe2 jmp edx//跳转到调用KsystemServices()时的返回地址 nt!KiSystemCallExit: 8053e74c cf iretd nt!KiSystemCallExit2: 8053e74d f644240901 test byte ptr [esp+9],1//检查是否是通过自陷指令进入内核的 8053e752 75f8 jne nt!KiSystemCallExit (8053e74c) //通过自陷指令进入的,调用sysexit离开 8053e754 5a pop edx//新的EIP 8053e755 83c404 add esp,4//跳过r3 DS 8053e758 80642401fd and byte ptr [esp+1],0FDh恢复EFLAGS_INTERRUPT_MASK 8053e75d 9d popfd 8053e75e 59 pop ecx //R3下SS:ESP 8053e75f fb sti 8053e760 0f35 sysexit 8053e762 cf iretd


