本篇文章为个人学习笔记,可能不适合正常人看
持续更新中···
基于滴水逆向中级班补充了部分64位的内容
很多数据结构源于win10 1511
保护模式 段寄存器结构 1 mov dword ptr ds:[0x1234],eax
真正读写的地址是:ds.base + 0x1234
段寄存器共有8个:ES CS SS DS FS GS LDTR TR
段寄存器共有96位,可见部分有前16位
用结构体描述:
1 2 3 4 5 6 struct SegMent { WORD Seletor; WORD Atrributes; DWORD Base; DWORD Limit; }
段描述符与段选择子 段描述符
P:P = 1 段描述符有效,反之无效,当我们通过指令将一个段描述符加载到段寄存器的时候,CPU第一件事情就是检查它的P位,若P无效则不会去做后续的其他检查
Atrributes:从第8字节Type到第23字节
Base:16-31 + 32-39 + 56-63
Limit:0-15 + 48-51 如果G位为1则单位为4K
S:S = 1 代码段或者数据段描述符,S = 0 系统段描述符
Type:当S = 1时:11位标志代码还是数据,A是否已经被访问,W是否可写,E是否向上拓展,R是否可读,C是否是一致代码段
Type:当S = 0时:
DB:影响CS段寻址,SS段ESP or SP,向下拓展的数据段的段上限4GB or 64KB
查看gdt表与gdt表长度(字节为单位):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 kd> r gdtr gdtr=fffff80082b0b000 kd> r gdtl gdtl=006f kd> dq fffff80082b0b000 fffff800`82b0b000 00000000`00000000 00000000`00000000 fffff800`82b0b010 00209b00`00000000 00409300`00000000 fffff800`82b0b020 00cffb00`0000ffff 00cff300`0000ffff fffff800`82b0b030 0020fb00`00000000 00000000`00000000 fffff800`82b0b040 82008bb0`c0700067 00000000`fffff800 fffff800`82b0b050 5c40f321`30003c00 00000000`00000000 fffff800`82b0b060 00000000`00000000 00000000`00000000 fffff800`82b0b070 807c8e00`00102500 00000000`fffff800 kd> r idtr idtr=fffff80082b0b070
可以看到fffff800`82b0b070开始就是idt表的内容,在x64中gdt表的作用并没有那么多了,最多就是在进入内核态时将cs寄存器的权限改为0
段选择子
RPL:请求特权级别
TI:TI=0 查GDT表;TI=1 查LDT表(windows下不使用LDT)
Index:GDT表索引
奇怪的指令 除了mov指令,我们还可以使用LES、LSS、LDS、LFS、LGS修改寄存器
CS不能通过上述的指令进行修改,CS位代码段,CS的改变会导致EIP的改变
1 2 3 4 char buffer [6 ];__asm{ les eccx,fword ptr ds:[buffer ] }
注意:RPL <= DPL(在数值上)
段权限检查 CPL CS、SS段选择子的后两位比较特殊称为:CPL(Current Privilege Level),标志当前CPU的特权等级
DPL DPL(Descriptor Privilege Level 描述符特权等级):规定了访问该段所需要的特权级别是什么,如果你要访问我,你应具备什么样的特权。
比如:mov DS, AX ,如果AX指向的段DPL为0,那么当程序的CPL为3时这行指令是不会成功的。
RPL RPL(Request Privilege Level 请求特权级别):RPL针对段选择子而言的,每个段的选择子都有自己的RPL
比如:mov ax,8 与 mov ax,b ,指向同一个段描述符,但RPL是不一样的
比如:当前程序处于0环,即CPL = 0
1 2 mov ax,000b //1011 RPL = 3 mov ds,ax // ax 指向的段描述符的DPL = 0
数据段(代码段和系统段并不相同)的权限检查:CPL <= DPL 并且 RPL <= DPL
为什么要有RPL?
我们本可以用rw的权限去打开一个文件,但为了避免出错,有些时候我们使用只读的权限去打开。
思考SMEP、SMAP的实现
代码跨段跳转 举例JMP 0x20:0x004183d7
流程如下:
段选择子拆分,RPL = 00,TI = 0,Index = 4
查询GDT,因为TI = 0 所以查GDT表,Index = 4 找到对应的段描述符,四种情况可以跳转:代码段、调用门、TSS任务段、任务门(后三类都是系统段描述符)
权限检查,如果是非一致代码段,要求CPL == DPL 并且 RPL <= DPL;如果是一致代码段,要求CPL >= DPL
加载段描述符,通过以上检查后,CPU会将段描述符加载到CS段寄存器中
代码执行,CPU将CS.Base + Offset 的值写入EIP,然后执行CS:EIP处的代码,段间跳转结束
一致代码段:内核态和用户态共享的代码段,允许低特权访问高特权的代码
非一致代码段:只允许同级访问
长调用 JMP FAR 只能跳转到同级非一直代码段,但CALL FAR 可以通过调用门提权,提升CPL的权限。
跨段不提权 指令格式:CALL CS:EIP(EIP是废弃的,此处CS必须对应一个调用门段描述符)
使用RETF
返回
跨段并提权 指令格式:CALL CS:EIP(EIP是废弃的)
1 2 3 4 5 //切换0环栈 push SS push esp push cs push eip
调用门
现代操作系统大都是分页的,所以这玩意估计也被时代抛弃了,但还是要简单记录一下(windows没有使用调用门!! )
执行流程
根据CS的值查GDT表,找到对应的段描述符,这个描述符是一个调用门
在调用门描述符中储存另一个代码段的段选择子
选择子指向的段的Base + 调用门中的段中偏移值 就是真正要执行的地址
翻墙理论 去政府部门办事,进去的时候如果翻墙进去就会被KO,如果从大门进去把身份证件压在门卫那里就没问题;出来的时候不从大门出去而是翻墙出去一般是没人管你的XD
中断门
系统调用(现代CPU一般都支持快速系统调用而不是使用中断)
调试
查看IDT 1 2 3 4 5 6 7 8 9 10 11 12 13 kd> r idtr idtr=fffff8007830b070 kd> r idtl idtl=0fff kd> dq fffff8007830b070 fffff800`7830b070 75fd8e00`00104500 00000000`fffff800 fffff800`7830b080 75fd8e00`00104600 00000000`fffff800 fffff800`7830b090 75fd8e03`001047c0 00000000`fffff800 fffff800`7830b0a0 75fdee00`00104b40 00000000`fffff800 fffff800`7830b0b0 75fdee00`00104c40 00000000`fffff800 fffff800`7830b0c0 75fd8e00`00104d40 00000000`fffff800 fffff800`7830b0d0 75fd8e00`00104fc0 00000000`fffff800 fffff800`7830b0e0 75fd8e00`00105200 00000000`fffff800
IDT表的构成
64位是16字节,最高4位保留,次4位为偏移高4位
陷阱门 与中断门的区别 中断门执行时,会将IF位清零(存于EFLAGS中),但陷阱门不会。
如果IF为0意味着不再接受可屏蔽中断
任务段 任务状态段(Task-state segment, TSS) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 typedef struct TSS { DWORD link; DWORD esp0; DWORD ss0; DWORD esp1; DWORD ss1; DWORD esp2; DWORD ss2; DWORD cr3; DWORD eip; DWORD eflags; DWORD eax; DWORD ecx; DWORD edx; DWORD ebx; DWORD esp; DWORD ebp; DWORD esi; DWORD edi; DWORD es; DWORD cs; DWORD ss; DWORD ds; DWORD fs; DWORD gs; DWORD ldt; DWORD io_map; } TSS;
TR寄存器 系统启动时会将GDT中TSS段描述符的数据加载到TR寄存器中
TR寄存器本质上是一个段寄存器
TR.Base指定TSS的基址
TR.Limit指定TSS的大小
任务门 虽然intel设计TSS旨用于多任务切换,但由于效率太低所以大多情况下只用于中断权限切换时更换esp和ss寄存器
任务门描述符:
任务门执行过程
INT N
查IDT表,找到中断门描述符
通过中断门描述符,找到TSS段选择子
查GDT表,找到任务段描述符并加载到TR寄存器中
使用TR寄存器指向的TSS段中的值修改相应寄存器
IRETD返回
分页
Bit
Display when set
Display when clear
Meaning
0x200
C
-
Copy on write.
0x100
G
-
Global.
0x80
L
-
Large page. This only occurs in PDEs, never in PTEs.
0x40
D
-
Dirty.
0x20
A
-
Accessed.
0x10
N
-
Cache disabled.
0x8
T
-
Write-through.
0x4
U
K
Owner (user mode or kernel mode).
0x2
W
R
Writeable or read-only. Only on multiprocessor computers and any computer running Windows Vista or later.
0x1
V
Valid.
E
-
Executable page. For platforms that do not support a hardware execute/noexecute bit, including many x86 systems, the E is always displayed.
虚拟地址转物理地址 以 9-9-9-9-12 分页举例:
先将要转化的地址比如fffff803a8f0a000后48位转化为二进制:
1 2 3 4 5 6 11111000 00000011 10101000 11110000 10100000 00000000 以9-9-9-9-12进行分割 111110000 1f0 000001110 0e 101000111 147 100001010 10a 000000000000 0
在cr3中取出顶级页表物理基址:
1 2 kd> r cr3 cr3=00000000238c1000
找各级页表的索引:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 kd> !dq 00000000238c1000 + 1f0 * 8 #238c1f80 00000000`00784063 00000000`00000000 #238c1f90 00000000`31d14863 00000000`00000000 #238c1fa0 00000000`00000000 00000000`3fedf863 #238c1fb0 80000000`00203863 80000000`00203863 #238c1fc0 80000000`00203863 80000000`00203863 #238c1fd0 00000000`3fedd863 00000000`3fedb863 #238c1fe0 00000000`00000000 00000000`00000000 #238c1ff0 00000000`00000000 00000000`007a4063 kd> !dq 00000000`00784000 + e * 8 # 784070 00000000`00785063 00000000`00000000 # 784080 00000000`00000000 00000000`00000000 # 784090 00000000`00000000 00000000`00000000 # 7840a0 00000000`00000000 00000000`00000000 # 7840b0 00000000`00000000 00000000`00000000 # 7840c0 00000000`00000000 00000000`00000000 # 7840d0 00000000`00000000 00000000`00000000 # 7840e0 00000000`00000000 00000000`00000000 kd> !dq 00000000`00785000 + 147 * 8 # 785a38 00000000`007a2063 00000000`00000000 # 785a48 00000000`00000000 00000000`00000000 # 785a58 00000000`00000000 00000000`00000000 # 785a68 00000000`00000000 00000000`00000000 # 785a78 00000000`00000000 00000000`00000000 # 785a88 00000000`00000000 00000000`00000000 # 785a98 00000000`00000000 00000000`00000000 # 785aa8 00000000`00000000 00000000`00000000 kd> !dq 00000000`007a2000 + 10a * 8 # 7a2850 00000000`0930a963 00000000`0930b963 # 7a2860 00000000`00000000 80000000`0930d963 # 7a2870 80000000`0930e963 80000000`0930f963 # 7a2880 80000000`09310963 80000000`09311963 # 7a2890 80000000`09312963 80000000`09313963 # 7a28a0 00000000`00000000 80000000`09315963 # 7a28b0 80000000`09316963 80000000`09317963 # 7a28c0 80000000`09318963 80000000`09319963
验证一下是否转化成功:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 kd> !dq 00000000`0930a000 # 930a000 00000000`00000000 00000000`00000000 # 930a010 00209b00`00000000 00409300`00000000 # 930a020 00cffb00`0000ffff 00cff300`0000ffff # 930a030 0020fb00`00000000 00000000`00000000 # 930a040 a8008bf0`b0700067 00000000`fffff803 # 930a050 3540f3bc`30003c00 00000000`00000000 # 930a060 00000000`00000000 00000000`00000000 # 930a070 a6bd8e00`00108500 00000000`fffff803 kd> dq fffff803a8f0a000 fffff803`a8f0a000 00000000`00000000 00000000`00000000 fffff803`a8f0a010 00209b00`00000000 00409300`00000000 fffff803`a8f0a020 00cffb00`0000ffff 00cff300`0000ffff fffff803`a8f0a030 0020fb00`00000000 00000000`00000000 fffff803`a8f0a040 a8008bf0`b0700067 00000000`fffff803 fffff803`a8f0a050 3540f3bc`30003c00 00000000`00000000 fffff803`a8f0a060 00000000`00000000 00000000`00000000 fffff803`a8f0a070 a6bd8e00`00108500 00000000`fffff803
页表虚拟地址计算 想要改写页表或者查看页表肯定要知道页表的虚拟地址
在xp中pte虚拟基址固定为:0xc0000000
在win10中pte虚拟基址储存在nt中是随机的,在windbg中查看如下:
1 2 3 4 5 6 kd> !pte 0 VA 0000000000000000 PXE at FFFFF6FB7DBED000 PPE at FFFFF6FB7DA00000 PDE at FFFFF6FB40000000 PTE at FFFFF68000000000 contains 0470000039122867 contains 0000000000000000 pfn 39122 ---DA--UWEV contains 0000000000000000 not valid
举个例子,计算gdt表所在页的pte虚拟地址
1 2 kd> r gdtr gdtr=fffff803a8f0a000
取后48位有效位,因为每页4k所以左移12位,然后因为pte中每项为8字节所以左移3位,最后加上pte虚拟基址
1 2 f803a8f0a000 >> 12 << 3 = 7C01D47850 7C01D47850 + FFFFF68000000000 = FFFFF6FC01D47850
在windbg中验证一下
1 2 3 4 5 kd> !pte fffff803a8f0a000 VA fffff803a8f0a000 PXE at FFFFF6FB7DBEDF80 PPE at FFFFF6FB7DBF0070 PDE at FFFFF6FB7E00EA38 PTE at FFFFF6FC01D47850 contains 0000000000784063 contains 0000000000785063 contains 00000000007A2063 contains 000000000930A963 pfn 784 ---DA--KWEV pfn 785 ---DA--KWEV pfn 7a2 ---DA--KWEV pfn 930a -G-DA--KWEV
pde的虚拟地址是pte表虚拟地址的pte,即对pte的虚拟地址做以上运算,同理pxe和ppe
TLB(Translation Lookaside Buffer)
LA(线性地址)
PA(物理地址)
ATTR(属性)
LRU(统计)
属性为各级页表and的结果
统计记录该页是否常用
不同的CPU表的大小不一样
只要cr3改变,TLB立即刷新,一个CPU核心一个TLB
页表G位与TLB是否刷新有关
INVLPG指令可删除某些映射关系
TLB种类 根据页表的种类分为以下4组TLB:
第一组:缓存一般页表(4K字节页面)的指令页表缓存(Instruction-TLB)
第二组:缓存一般页表(4K字节页面)的数据页表缓存(Data-TLB)
第三组:缓存大尺寸页表(2M/4M字节页面)的指令页表缓存(Instruction-TLB)
第四组:缓存大尺寸页表(2M/4M字节页面)的数据页表缓存(Data-TLB)
利用1、2组表缓存不同步绕过crc校验?
CPU缓存 CPU缓存是位于CPU与物理内存之间的临时存储器,它的容量比内存小的多,但是交换速度却比内存要快得多。
CPU缓存与TLB的区别:
TLB:线性地址<—>物理地址
CPU缓存:物理地址<—>具体内容
关于PWT/PCD
PWT = 1 时,写Cache的时候也要将数据写入内存中;反之只写入cache
PCD = 1 时,禁止该页写入缓存,直接写内存。比如,做页表用的页,已经存储在TLB中了,可能不需要再缓存在CPU中
中断与异常
中断的本质是改变CPU执行的路线
中断通常是由CPU外部的输入输出设备(硬件)所触发的,供外部设备通知CPU“有事情需要处理”,因此又叫中断请求
中断请求的目的是虚妄CPU暂时停止执行当前正在执行的程序,转去执行中断请求所对应的中断处理例程(由IDT表决定)
80x86有两条中断请求线:
非屏蔽中断线,称为NMI
可屏蔽中断线,称为INTR
非可屏蔽中断 当非可屏蔽中断产生时,CPU在执行完当前指令后会进入中断处理程序
非可屏蔽中断不受EFLAG寄存器中IF位的影响,以但发生,CPU必须处理非可屏蔽中断处理程序位于IDT表中的2号位置(80x86中固定为0x2)
可屏蔽中断 在硬件级,可屏蔽中断是有一块专门的芯片来管理的,通常称为中断控制器。它负责分配中断资源和管理各个中断源发出的中断请求。为了便于标识各个中断请求,中断管理器通常用IRQ(Interrupt Request)后面加上数字来表示不同的中断。
比如:在windows中,时钟中断的IRQ编号为0,也就是IRQ0(在 设备管理器-> 属性 -> 资源 中查看)
如何处理可屏蔽中断:
IDT中断号
IRQ
说明
0x30
IRQ0
时钟中断
0x31-0x3f
IRQ1-IRQ15
其他硬件设备的中断
如果自己的程序执行时不希望CPU去处理中断,可以使用CLI
指令清空EFLAG寄存器的IF位;用STI
指令设置EFLAG寄存器中的IF位
硬件中断与IDT表中的对应关系并非固定不变,详细参考:APIC(高级可编程中断控制器)
异常 异常通常是CPU在执行指令时检测到的某些错误,比如除零、访问无效页面。
中断与异常的区别:
中断来自于外部设备,是中断源(比如键盘)发起的,CPU是被动的
异常来自于CPU本身,是CPU主动产生的
INT N虽然被称为软件中断,但其本质是异常。EFLAG的IF位对INT N无效
异常处理 无论是由硬件设备触发的中断还是CPU产生的异常,处理程序都在IDT表中
常见的异常处理程序:
错误类型
IDT中断号
页错误
0xE
段错误
0xD
除零错误
0x0
双重错误
0x8
双重错误是指,在某错误的异常处理程序中再次触发异常,即为双重错误
缺页异常 缺页异常的产生:
一旦发生缺页异常,CPU会执行IDT表中的0xE号中断处理程序,由操作系统来接管
例如:当物理页不够用时,操作系统会将物理页存在文件里并且将页表P位置零;若对该物理页进行访问时,操作系统发现P 为0则会触发缺页异常处理程序,再对页表中11(转移位)位、10(原型位)位进行检查,如果都是0则意味着该物理页被存在索引为PFN(1-4位)的页面文件里,那么此时就会恢复物理页并把P位置1
控制寄存器 控制寄存器用于控制和确定CPU的操作模式
Cr1 保留
Cr3 页目录表基址 (不同分页形式结构不一样)
Cr0
Bit
Name
Full Name
Description
0
PE
Protected Mode Enable
If 1, system is in protected mode , else system is in real mode
1
MP
Monitor co-processor
Controls interaction of WAIT/FWAIT instructions with TS flag in CR0
2
EM
Emulation
If set, no x87 floating-point unit present, if clear, x87 FPU present
3
TS
Task switched
Allows saving x87 task context upon a task switch only after x87 instruction used
4
ET
Extension type
On the 386, it allowed to specify whether the external math coprocessor was an 80287 or 80387
5
NE
Numeric error
Enable internal x87 floating point error reporting when set, else enables PC style x87 error detection
16
WP
Write protect
When set, the CPU can’t write to read-only pages when privilege level is 0
18
AM
Alignment mask
Alignment check enabled if AM set, AC flag (in EFLAGS register) set, and privilege level is 3
29
NW
Not-write through
Globally enables/disable write-through caching
30
CD
Cache disable
Globally enables/disable the memory cache
31
PG
Paging
If 1, enable paging and use the § CR3 register, else disable paging.
PE = 0 PG = 0 实地址模式
PE = 1 PG = 0 进入保护模式,开启分段机制
PE = 0 PG = 1 不存在这种情况
PE = 1 PG = 1 开启分页机制
Cr2 Contains a value called Page Fault Linear Address (PFLA). When a page fault occurs, the address the program attempted to access is stored in the CR2 register.
Cr3 Used when virtual addressing is enabled, hence when the PG bit is set in CR0. CR3 enables the processor to translate linear addresses into physical addresses by locating the page directory and page tables for the current task. Typically, the upper 20 bits of CR3 become the page directory base register (PDBR), which stores the physical address of the first page directory entry. If the PCIDE bit in CR4 is set, the lowest 12 bits are used for the process-context identifier (PCID).[1]
Cr4
Bit
Name
Full Name
Description
0
VME
Virtual 8086 Mode Extensions
If set, enables support for the virtual interrupt flag (VIF) in virtual-8086 mode.
1
PVI
Protected-mode Virtual Interrupts
If set, enables support for the virtual interrupt flag (VIF) in protected mode.
2
TSD
Time Stamp Disable
If set, RDTSC instruction can only be executed when in ring 0 , otherwise RDTSC can be used at any privilege level.
3
DE
Debugging Extensions
If set, enables debug register based breaks on I/O space access.
4
PSE
Page Size Extension
If unset, page size is 4 KiB, else page size is increased to 4 MiBIf PAE is enabled or the processor is in x86-64 long mode this bit is ignored.[2]
5
PAE
Physical Address Extension
If set, changes page table layout to translate 32-bit virtual addresses into extended 36-bit physical addresses.
6
MCE
Machine Check Exception
If set, enables machine check interrupts to occur.
7
PGE
Page Global Enabled
If set, address translations (PDE or PTE records) may be shared between address spaces.
8
PCE
Performance-Monitoring Counter enable
If set, RDPMC can be executed at any privilege level, else RDPMC can only be used in ring 0.
9
OSFXSR
Operating system support for FXSAVE and FXRSTOR instructions
If set, enables Streaming SIMD Extensions (SSE) instructions and fast FPU save & restore.
10
OSXMMEXCPT
Operating System Support for Unmasked SIMD Floating-Point Exceptions
If set, enables unmasked SSE exceptions.
11
UMIP
User-Mode Instruction Prevention
If set, the SGDT, SIDT, SLDT, SMSW and STR instructions cannot be executed if CPL > 0.[1]
12
LA57
57-Bit Linear Addresses
If set, enables 5-Level Paging.[3] [4] : 2–18
13
VMXE
Virtual Machine Extensions Enable
see Intel VT-x x86 virtualization .
14
SMXE
Safer Mode Extensions Enable
see Trusted Execution Technology (TXT)
16
FSGSBASE
Enables the instructions RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE.
17
PCIDE
PCID Enable
If set, enables process-context identifiers (PCIDs).
18
OSXSAVE
XSAVE and Processor Extended States Enable
20
SMEP[5]
Supervisor Mode Execution Protection Enable
If set, execution of code in a higher ring generates a fault .
21
SMAP
Supervisor Mode Access Prevention Enable
If set, access of data in a higher ring generates a fault .[6]
22
PKE
Protection Key Enable
See Intel 64 and IA-32 Architectures Software Developer’s Manual.
23
CET
Control-flow Enforcement Technology
If set, enables control-flow enforcement technology.[4] : 2–19
24
PKS
Enable Protection Keys for Supervisor-Mode Pages
If set, each supervisor-mode linear address is associated with a protection key when 4-level or 5-level paging is in use.[4] : 2–19
系统调用 API 3环进0环 _KUSER_SHARED_DATA
1 2 3 4 5 6 7 8 9 10 11 12 13 .text:000000018009CE20 4C 8B D1 mov r10, rcx ; NtReadFile .text:000000018009CE23 B8 06 00 00 00 mov eax, 6 .text:000000018009CE28 F6 04 25 08 03 FE+ test byte ptr ds:7FFE0308h, 1 ; 判断CPU是否支持快速系统调用 .text:000000018009CE28 7F 01 .text:000000018009CE30 75 03 jnz short loc_18009CE35 ; Jump if Not Zero (ZF=0) .text:000000018009CE32 0F 05 syscall ; Low latency system call .text:000000018009CE34 C3 retn ; Return Near from Procedure .text:000000018009CE35 ; --------------------------------------------------------------------------- .text:000000018009CE35 .text:000000018009CE35 loc_18009CE35: ; CODE XREF: NtReadFile+10↑j .text:000000018009CE35 CD 2E int 2Eh ; DOS 2+ internal - EXECUTE COMMAND .text:000000018009CE35 ; DS:SI -> counted CR-terminated command string .text:000000018009CE37 C3 retn ; Return Near from Procedure
windbg中查看:
1 2 3 2:002> dt _KUSER_SHARED_DATA 7FFE0000 SystemCall ntdll!_KUSER_SHARED_DATA +0x308 SystemCall : 0
修改寄存器
cs的权限从3变为0
ss与cs权限永远一致,即ss权限也变为0
权限发生切换,栈也一定需要切换,需要新的esp
进0环后代码的位置,需要新的eip
中断进0环
固定中断号为0x2e
cs eip 由IDT提供,esp ss 由TSS提供
进入0环后执行的内核函数:nt!KiSystemService
sysenter进0环
cs esp eip由MSR寄存器提供(ss为cs + 8)
进入0环后执行的内核函数:nt!KiFastCallEntry
sysenter 和 中断 进0环的区别 相同点:
目的都是找到内核态需要的cs ss eip esp
sysenter/快速调用的优点:
中断需要的cs eip在IDT表中,需要查内存(ss与esp由TSS提供),速度慢
如果cpu支持快速系统调用,则会在启动时提前把内核态的cs ss esp eip储存到MSR寄存器中,sysenter执行时,直接交换寄存器的值,速度相对快
64位syscall
STAR (0xC0000081) - Ring 0 and Ring 3 Segment bases, as well as SYSCALL EIP. Low 32 bits = SYSCALL EIP, bits 32-47 are kernel segment base, bits 48-63 are user segment base.
LSTAR (0xC0000082) - The kernel’s RIP SYSCALL entry for 64 bit software.
CSTAR (0xC0000083) - The kernel’s RIP for SYSCALL in compatibility mode.
SFMASK (0xC0000084) - The low 32 bits are the SYSCALL flag mask. If a bit in this is set, the corresponding bit in rFLAGS is cleared.
1 2 3 4 5 6 7 8 9 10 11 12 kd> rdmsr c0000082 msr[c0000082] = fffff803`18159d00 kd> u fffff803`18159d00 nt!KiSystemCall64: fffff803`18159d00 0f01f8 swapgs fffff803`18159d03 654889242510000000 mov qword ptr gs:[10h],rsp fffff803`18159d0c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h] fffff803`18159d15 6a2b push 2Bh fffff803`18159d17 65ff342510000000 push qword ptr gs:[10h] fffff803`18159d1f 4153 push r11 fffff803`18159d21 6a33 push 33h fffff803`18159d23 51 push rcx
保存现场 _Ktrap_frame 进入0环后,存入所有用户态的寄存器
位于kthread中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 0:000> dt _Ktrap_frame ntdll!_KTRAP_FRAME +0x000 DbgEbp : Uint4B +0x004 DbgEip : Uint4B +0x008 DbgArgMark : Uint4B +0x00c TempSegCs : Uint2B +0x00e Logging : UChar +0x00f FrameType : UChar +0x010 TempEsp : Uint4B +0x014 Dr0 : Uint4B +0x018 Dr1 : Uint4B +0x01c Dr2 : Uint4B +0x020 Dr3 : Uint4B +0x024 Dr6 : Uint4B +0x028 Dr7 : Uint4B +0x02c SegGs : Uint4B +0x030 SegEs : Uint4B +0x034 SegDs : Uint4B +0x038 Edx : Uint4B +0x03c Ecx : Uint4B +0x040 Eax : Uint4B +0x044 PreviousPreviousMode : UChar +0x045 EntropyQueueDpc : UChar +0x046 NmiMsrIbrs : UChar +0x046 Reserved1 : UChar +0x047 PreviousIrql : UChar +0x048 MxCsr : Uint4B +0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD +0x050 SegFs : Uint4B +0x054 Edi : Uint4B +0x058 Esi : Uint4B +0x05c Ebx : Uint4B +0x060 Ebp : Uint4B +0x064 ErrCode : Uint4B +0x068 Eip : Uint4B +0x06c SegCs : Uint4B +0x070 EFlags : Uint4B +0x074 HardwareEsp : Uint4B +0x078 HardwareSegSs : Uint4B +0x07c V86Es : Uint4B +0x080 V86Ds : Uint4B +0x084 V86Fs : Uint4B +0x088 V86Gs : Uint4B
ETHREAD 描述线程相关状态,其中_KTHREAD相对重要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 0:000> dt _ETHREAD ntdll!_ETHREAD +0x000 Tcb : _KTHREAD +0x280 CreateTime : _LARGE_INTEGER +0x288 ExitTime : _LARGE_INTEGER +0x288 KeyedWaitChain : _LIST_ENTRY +0x290 ChargeOnlySession : Ptr32 Void +0x294 PostBlockList : _LIST_ENTRY +0x294 ForwardLinkShadow : Ptr32 Void +0x298 StartAddress : Ptr32 Void +0x29c TerminationPort : Ptr32 _TERMINATION_PORT +0x29c ReaperLink : Ptr32 _ETHREAD +0x29c KeyedWaitValue : Ptr32 Void +0x2a0 ActiveTimerListLock : Uint4B +0x2a4 ActiveTimerListHead : _LIST_ENTRY +0x2ac Cid : _CLIENT_ID · · ·
KPCR(processor control region) CPU控制区,描述当前CPU的状态
查看CPU数量:
查看其他CPU的KPCR: (此处显示结尾地址,应减去相应KPCR结构大小)
KiSystemCall64 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 .text:000000014014CD00 KiSystemCall64 proc near ; DATA XREF: .pdata:0000000140344818↓o .text:000000014014CD00 ; KiInitializeBootStructures+12E↓o .text:000000014014CD00 .text:000000014014CD00 var_58 = byte ptr -58h .text:000000014014CD00 var_30 = qword ptr -30h .text:000000014014CD00 var_28 = qword ptr -28h .text:000000014014CD00 var_20 = qword ptr -20h .text:000000014014CD00 var_18 = qword ptr -18h .text:000000014014CD00 var_10 = qword ptr -10h .text:000000014014CD00 arg_78 = byte ptr 80h .text:000000014014CD00 arg_F8 = qword ptr 100h .text:000000014014CD00 .text:000000014014CD00 ; __unwind { // KiSystemServiceHandler .text:000000014014CD00 0F 01 F8 swapgs ; Exchange GS base with KernelGSBase MSR .text:000000014014CD03 65 48 89 24 25 10+ mov gs:10h, rsp .text:000000014014CD03 00 00 00 .text:000000014014CD0C 65 48 8B 24 25 A8+ mov rsp, gs:1A8h .text:000000014014CD0C 01 00 00 .text:000000014014CD15 6A 2B push 2Bh ; '+' ; ss .text:000000014014CD17 65 FF 34 25 10 00+ push qword ptr gs:10h ; rsp .text:000000014014CD17 00 00 .text:000000014014CD1F 41 53 push r11 ; eflags .text:000000014014CD21 6A 33 push 33h ; '3' ; cs .text:000000014014CD23 51 push rcx ; rip .text:000000014014CD24 49 8B CA mov rcx, r10 .text:000000014014CD27 48 83 EC 08 sub rsp, 8 ; Integer Subtraction .text:000000014014CD2B 55 push rbp ; rbp .text:000000014014CD2C 48 81 EC 58 01 00+ sub rsp, 158h ; rsp = _KTRAP_FRAME .text:000000014014CD2C 00 .text:000000014014CD33 48 8D AC 24 80 00+ lea rbp, [rsp+arg_78] ; rbp = _KTRAP_FRAME + 0x80 .text:000000014014CD33 00 00 .text:000000014014CD3B 48 89 9D C0 00 00+ mov [rbp+0C0h], rbx ; rbx .text:000000014014CD3B 00 .text:000000014014CD42 48 89 BD C8 00 00+ mov [rbp+0C8h], rdi ; rdi .text:000000014014CD42 00 .text:000000014014CD49 48 89 B5 D0 00 00+ mov [rbp+0D0h], rsi ; rsi .text:000000014014CD49 00 .text:000000014014CD50 .text:000000014014CD50 KiSystemServiceUser: ; DATA XREF: KiSystemService+2F↑o .text:000000014014CD50 C6 45 AB 02 mov byte ptr [rbp-55h], 2 ; ExceptionActive = 2
KiSystemService KiSystemCall64的阉割版
ss eflags cs rip 已经在中断时保存,rsp通过TSS取得
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 .text:000000014014CA00 KiSystemService proc near ; DATA XREF: .pdata:00000001403447E8↓o .text:000000014014CA00 ; INITDATA:0000000140716308↓o .text:000000014014CA00 .text:000000014014CA00 var_E8 = byte ptr -0E8h .text:000000014014CA00 .text:000000014014CA00 0F 01 F8 swapgs ; Exchange GS base with KernelGSBase MSR .text:000000014014CA03 49 8B CA mov rcx, r10 .text:000000014014CA06 48 83 EC 08 sub rsp, 8 ; Integer Subtraction .text:000000014014CA0A 55 push rbp .text:000000014014CA0B 48 81 EC 58 01 00+ sub rsp, 158h ; Integer Subtraction .text:000000014014CA0B 00 .text:000000014014CA12 48 8D AC 24 80 00+ lea rbp, [rsp+168h+var_E8] ; 80h .text:000000014014CA12 00 00 .text:000000014014CA1A 48 89 9D C0 00 00+ mov [rbp+0C0h], rbx .text:000000014014CA1A 00 .text:000000014014CA21 48 89 BD C8 00 00+ mov [rbp+0C8h], rdi .text:000000014014CA21 00 .text:000000014014CA28 48 89 B5 D0 00 00+ mov [rbp+0D0h], rsi .text:000000014014CA28 00 .text:000000014014CA2F 4C 8D 1D 1A 03 00+ lea r11, KiSystemServiceUser ; ExceptionActive = 2 .text:000000014014CA2F 00 .text:000000014014CA36 41 FF E3 jmp r11 ; Indirect Near Jump .text:000000014014CA39 ; --------------------------------------------------------------------------- .text:000000014014CA39 C3 retn ; Return Near from Procedure .text:000000014014CA39 KiSystemService endp
关于后面的分析需要提前知道下面两个东西
系统服务表 1 2 3 4 5 6 typedef struct _SYSTEM_SERVICE_TABLE { PVOID ServiceTableBase; PVOID ServiceCounterTableBase; ULONGLONG NumberOfServices; PVOID ParamTableBase; } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;
SSDT SSDT全称为System Service Descriptor Table,系统服务描述表
1 2 3 4 5 6 7 8 9 10 11 kd> dq nt!KeServiceDescriptorTable fffff802`6f3fe780 fffff802`6f341150 00000000`00000000 fffff802`6f3fe790 00000000`000001bc fffff802`6f341f34 fffff802`6f3fe7a0 00000000`00000000 00000000`00000000 fffff802`6f3fe7b0 00000000`00000000 00000000`00000000 kd> dq nt!KeServiceDescriptorTableShadow fffff802`6f3fe740 fffff802`6f341150 00000000`00000000 fffff802`6f3fe750 00000000`000001bc fffff802`6f341f34 fffff802`6f3fe760 fffff961`efd89000 00000000`00000000 fffff802`6f3fe770 00000000`0000046f fffff961`efd8b7ec
在xp中SSDT有4个表,后面3个都是空的,这里盲猜有2个表
SSDT中包含ntoskrl中的函数,SSDT Shadow中额外包含win32k中的函数
KiSystemServiceUser 可以看到不管是中断还是快速调用都会跳到KiSystemServiceUser
这里主要判断是否处于调试模式,如果是则保存调试相关寄存器Dr0-Dr7等一系列操作,如果不是则继续
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 .text:000000014014CD50 KiSystemServiceUser: ; DATA XREF: KiSystemService+2F↑o .text:000000014014CD50 C6 45 AB 02 mov byte ptr [rbp-55h], 2 ; ExceptionActive = 2 .text:000000014014CD54 65 48 8B 1C 25 88+ mov rbx, gs:188h ; _KTHREAD .text:000000014014CD54 01 00 00 .text:000000014014CD5D 0F 0D 8B 90 00 00+ prefetchw byte ptr [rbx+90h] ; Trap_Frame .text:000000014014CD5D 00 .text:000000014014CD64 0F AE 5D AC stmxcsr dword ptr [rbp-54h] ; Trap_Frame.MxCsr .text:000000014014CD68 65 0F AE 14 25 80+ ldmxcsr dword ptr gs:180h ; KPCRB.MxCsr .text:000000014014CD68 01 00 00 .text:000000014014CD71 80 7B 03 00 cmp byte ptr [rbx+3], 0 ; DebugActive .text:000000014014CD75 66 C7 85 80 00 00+ mov word ptr [rbp+80h], 0 ; Dr7 = 0 .text:000000014014CD75 00 00 00 .text:000000014014CD7E 0F 84 9A 00 00 00 jz loc_14014CE1E ; 不处于调试就跳转 .text:000000014014CD84 48 89 45 B0 mov [rbp-50h], rax .text:000000014014CD88 48 89 4D B8 mov [rbp-48h], rcx .text:000000014014CD8C 48 89 55 C0 mov [rbp-40h], rdx .text:000000014014CD90 F6 43 03 03 test byte ptr [rbx+3], 3 ; Logical Compare .text:000000014014CD94 4C 89 45 C8 mov [rbp-38h], r8 .text:000000014014CD98 4C 89 4D D0 mov [rbp-30h], r9 .text:000000014014CD9C 74 05 jz short loc_14014CDA3 ; Jump if Zero (ZF=1) .text:000000014014CD9E E8 AD 5E FF FF call KiSaveDebugRegisterState ; Call Procedure
这里首先保存FirstArgument SystemCallNumber TrapFrame
然后判断SystemCallNumber的第13位是否为1,如果是则是一个win32k调用走SSDT Shadow
接着获取SSDT中的函数地址表,在64位系统中这里并不是真正的地址而是需要经过一段计算FunAddr = ssdt[0] + *(ssdt[0]+4 * Index) >> 4
经过一些判断后最终跳转到KiSystemServiceCopyEnd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 .text:000000014014CE1E loc_14014CE1E: ; CODE XREF: KiSystemCall64+7E↑j .text:000000014014CE1E FB sti ; Set Interrupt Flag .text:000000014014CE1F 48 89 8B 88 00 00+ mov [rbx+88h], rcx ; FirstArgument .text:000000014014CE1F 00 .text:000000014014CE26 89 83 80 00 00 00 mov [rbx+80h], eax ; SystemCallNumber .text:000000014014CE2C 0F 1F 40 00 nop dword ptr [rax+00h] ; No Operation .text:000000014014CE30 .text:000000014014CE30 KiSystemServiceStart: ; DATA XREF: KiServiceInternal+5A↑o .text:000000014014CE30 ; .data:00000001402C4338↓o .text:000000014014CE30 48 89 A3 90 00 00+ mov [rbx+90h], rsp ; TrapFrame .text:000000014014CE30 00 .text:000000014014CE37 8B F8 mov edi, eax .text:000000014014CE39 C1 EF 07 shr edi, 7 ; Shift Logical Right .text:000000014014CE3C 83 E7 20 and edi, 20h ; Logical AND .text:000000014014CE3F 25 FF 0F 00 00 and eax, 0FFFh ; Logical AND .text:000000014014CE44 .text:000000014014CE44 KiSystemServiceRepeat: ; CODE XREF: KiSystemCall64+490↓j .text:000000014014CE44 4C 8D 15 35 29 23+ lea r10, KeServiceDescriptorTable ; Load Effective Address .text:000000014014CE44 00 .text:000000014014CE4B 4C 8D 1D EE 28 23+ lea r11, KeServiceDescriptorTableShadow ; Load Effective Address .text:000000014014CE4B 00 .text:000000014014CE52 F7 43 78 40 00 00+ test dword ptr [rbx+78h], 40h ; KTHREAD.GuiThread .text:000000014014CE52 00 .text:000000014014CE59 4D 0F 45 D3 cmovnz r10, r11 ; Move if Not Zero (ZF=0) .text:000000014014CE5D 42 3B 44 17 10 cmp eax, [rdi+r10+10h] ; typedef struct _SYSTEM_SERVICE_TABLE{ .text:000000014014CE5D ; PVOID ServiceTableBase;//地址表 .text:000000014014CE5D ; PVOID ServiceCounterTableBase;//该表被访问次数 .text:000000014014CE5D ; ULONGLONG NumberOfServices;//函数个数 .text:000000014014CE5D ; PVOID ParamTableBase;//参数个数表 .text:000000014014CE5D ; } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; .text:000000014014CE62 0F 83 EF 02 00 00 jnb loc_14014D157 ; Jump if Not Below (CF=0) .text:000000014014CE68 4E 8B 14 17 mov r10, [rdi+r10] .text:000000014014CE6C 4D 63 1C 82 movsxd r11, dword ptr [r10+rax*4] ; 64位下需要计算得到真实的地址FunAddr =ssdt[0] + *(ssdt[0]+4 * Index)>>4 .text:000000014014CE70 49 8B C3 mov rax, r11 .text:000000014014CE73 49 C1 FB 04 sar r11, 4 ; Shift Arithmetic Right .text:000000014014CE77 4D 03 D3 add r10, r11 ; Add .text:000000014014CE7A 83 FF 20 cmp edi, 20h ; ' ' ; Compare Two Operands .text:000000014014CE7D 75 51 jnz short loc_14014CED0 ; Jump if Not Zero (ZF=0) .text:000000014014CE7F 4C 8B 9B F0 00 00+ mov r11, [rbx+0F0h] ; teb .text:000000014014CE7F 00 .text:000000014014CE86 .text:000000014014CE86 KiSystemServiceGdiTebAccess: ; DATA XREF: KiSystemServiceHandler+D↑o .text:000000014014CE86 41 83 BB 40 17 00+ cmp dword ptr [r11+1740h], 0 ; teb.GdiBatchCount .text:000000014014CE86 00 00 .text:000000014014CE8E 74 40 jz short loc_14014CED0 ; Jump if Zero (ZF=1) .text:000000014014CE90 48 89 45 B0 mov [rbp-50h], rax .text:000000014014CE94 48 89 4D B8 mov [rbp-48h], rcx .text:000000014014CE98 48 89 55 C0 mov [rbp-40h], rdx .text:000000014014CE9C 49 8B D8 mov rbx, r8 .text:000000014014CE9F 49 8B F9 mov rdi, r9 .text:000000014014CEA2 49 8B F2 mov rsi, r10 .text:000000014014CEA5 B9 07 00 00 00 mov ecx, 7 .text:000000014014CEAA 33 D2 xor edx, edx ; Logical Exclusive OR .text:000000014014CEAC 4D 33 C0 xor r8, r8 ; Logical Exclusive OR .text:000000014014CEAF 4D 33 C9 xor r9, r9 ; Logical Exclusive OR .text:000000014014CEB2 E8 E9 0F 2D 00 call PsInvokeWin32Callout ; Call Procedure .text:000000014014CEB7 48 8B 45 B0 mov rax, [rbp-50h] .text:000000014014CEBB 48 8B 4D B8 mov rcx, [rbp-48h] .text:000000014014CEBF 48 8B 55 C0 mov rdx, [rbp-40h] .text:000000014014CEC3 4C 8B C3 mov r8, rbx .text:000000014014CEC6 4C 8B CF mov r9, rdi .text:000000014014CEC9 4C 8B D6 mov r10, rsi .text:000000014014CECC 0F 1F 40 00 nop dword ptr [rax+00h] ; No Operation .text:000000014014CED0 .text:000000014014CED0 loc_14014CED0: ; CODE XREF: KiSystemCall64+17D↑j .text:000000014014CED0 ; KiSystemCall64+18E↑j .text:000000014014CED0 83 E0 0F and eax, 0Fh ; Logical AND .text:000000014014CED3 0F 84 B7 00 00 00 jz KiSystemServiceCopyEnd ; Jump if Zero (ZF=1) .text:000000014014CED9 C1 E0 03 shl eax, 3 ; Shift Logical Left .text:000000014014CEDC 48 8D 64 24 90 lea rsp, [rsp-70h] ; Load Effective Address .text:000000014014CEE1 48 8D 7C 24 18 lea rdi, [rsp+70h+var_58] ; Load Effective Address .text:000000014014CEE6 48 8B B5 00 01 00+ mov rsi, [rbp+100h] ; rsp .text:000000014014CEE6 00 .text:000000014014CEED 48 8D 76 20 lea rsi, [rsi+20h] ; Load Effective Address .text:000000014014CEF1 F6 85 F0 00 00 00+ test byte ptr [rbp+0F0h], 1 ; cs .text:000000014014CEF1 01 .text:000000014014CEF8 74 16 jz short loc_14014CF10 ; Jump if Zero (ZF=1) .text:000000014014CEFA 48 3B 35 FF 22 23+ cmp rsi, cs:MmUserProbeAddress ; 用户态最大地址 .text:000000014014CEFA 00 .text:000000014014CF01 48 0F 43 35 F7 22+ cmovnb rsi, cs:MmUserProbeAddress ; Move if Not Below (CF=0) .text:000000014014CF01 23 00 .text:000000014014CF09 0F 1F 80 00 00 00+ nop dword ptr [rax+00000000h] ; No Operation .text:000000014014CF09 00 .text:000000014014CF10 .text:000000014014CF10 loc_14014CF10: ; CODE XREF: KiSystemCall64+1F8↑j .text:000000014014CF10 4C 8D 1D 79 00 00+ lea r11, KiSystemServiceCopyEnd ; Load Effective Address .text:000000014014CF10 00 .text:000000014014CF17 4C 2B D8 sub r11, rax ; Integer Subtraction .text:000000014014CF1A 41 FF E3 jmp r11 ; Indirect Near Jump
KiSystemServiceCopyEnd:
1 2 3 4 5 6 7 8 9 10 11 12 13 .text:000000014014CF90 KiSystemServiceCopyEnd: ; CODE XREF: KiSystemCall64+1D3↑j .text:000000014014CF90 ; DATA XREF: KiSystemServiceHandler+27↑o .text:000000014014CF90 ; KiSystemCall64:loc_14014CF10↑o .text:000000014014CF90 F7 05 EE 22 23 00+ test dword ptr cs:PerfGlobalGroupMask+8, 40h ; Logical Compare .text:000000014014CF90 40 00 00 00 .text:000000014014CF9A 0F 85 55 02 00 00 jnz loc_14014D1F5 ; Jump if Not Zero (ZF=0) .text:000000014014CFA0 41 FF D2 call r10 ; Indirect Call Near Procedure .text:000000014014CFA3 .text:000000014014CFA3 loc_14014CFA3: ; CODE XREF: KiSystemCall64+54A↓j .text:000000014014CFA3 65 FF 04 25 38 2E+ inc dword ptr gs:2E38h ; KeSystemCalls .text:000000014014CFA3 00 00 .text:000000014014CFAB .text:000000014014CFAB KiSystemServiceExit: ; CODE XREF: KiSystemCall64+B2↑j
进程与线程 EPROCESS进程结构体 每个windows进程在0环都有一个对应的结构体:EPROCESS,这个结构体包含了进程所有重要的信息。
KPROCESS
Header :_DISPATCHER_HEADER
内核对象结构体带有此字段即为可等待对象,比如Mutex互斥体,Event事件等(WaitForSingleObject)
DirectoryTableBase
页目录表的基址,进程切换时就是使用另外的一个进程的该字段填入CR3中
KernelTime/UserTime
统计信息,记录一个进程在内核模式/用户模式下所花的时间
Affinity
规定进程里的所有线程能在哪个CPU上跑,如果值为1,那么只能在0号CPU上跑(00000001);如果值为3,那么可以在0、1号CPU上跑(00000011)
BasePriority
基础优先级或最低优先级,该进程中的所有线程最起码的优先级
EPROCESS其他成员
CreateTime/ExitTime
UniqueProcessId
ActiveProcessLinks
双向链表,所有轰动滚成都连接在一起构成的一个链表,对其解链可以达到隐藏进程的目的。PsActiveProcessHead指向全局链表头
QuetaUsage/QuotaPeak
CommitCharge/PeakVirtualSize/VirtualSize
VadRoot
平衡二叉树,标识0-2G用户空间哪些地址被分配,模块隐藏相关
DebugPort/ExceptionPort
ObjectTable
句柄表,反调试:查询其他进程的句柄表,若发现自身的EPROCESS则说明该进程正在对我调试
ImageFileName
ActiveThreads
PEB
参考 潘爱民老师《windows内核原理与实现》第三章
BeingDebugged
LDR 进程模块信息,模块链表
ETHREAD 线程结构体 KTHREAD
Header :_DISPATCHER_HEADER
内核对象结构体带有此字段即为可等待对象,比如Mutex互斥体,Event事件等(WaitForSingleObject)
InitialStack,StackLimit,KernelStack
Teb
Thread Environment Block 线程环境块,三环FS:[0] -> TEB,在其中可以找到PEB
DebugActive
如果被调试就不是-1,如果是-1,则不能使用调试寄存器Dr0-Dr7
ApcState,ApcQueueLock,ApcStatePointer,SavedApcState
State
BasePriority
初始值是所属进程的BasePriority的值(KPROCESS->BasePriority),以后可以通过KeSetBasePriorityThread()函数重新设定
WaitBlock
等待哪个对象(WaitForSingleObject)
ServiceTable
TrapFrame
PreviousMode
ThreadListEntry
双向链表,一个进程所有的线程都处在一个链表中,一共有两个这样的链表,EPROCESS中同样有
ETHREAD其他成员
Cid
ThreadsProcess
ThreadListEntry
双向链表,一个进程所有的线程都处在一个链表中,一共有两个这样的链表,EPROCESS中同样有
KPCR CPU控制区(Processor Control Rregion)
当线程进入0环时,FS:[0]指向KPCR(3环时指向TEB)
每个CPU都有一个KPCR结构体(一个核心一个)
KPCR中储存了CPU本身要用的一些重要数据:GDT、IDT、以及线程相关的一些信息
_NT_TIB主要成员
ExceptionList
储存0环异常处理程序链表SEH,对比TEB中同样有这个结构储存3环异常处理程序链表SEH
StackBase,StackLimit
Self
KPCR其他成员
SelfPcr
Prcb
IDT
GDT
TSS
Number
PrcbData:_KPRCB
KPRCB
CurrentThread
NextThread
IdleThread
等待链表、调度链表 在进程中对线程链表进行断链,OD等调试器查询不到该线程,但线程仍然在跑。说明两个问题,OD查询线程使用的API是通过线程链表去查询的;CPU在调度线程执行的时候不是使用这个链表。
线程有3仲状态:
等待链表 KiWaitListHead 双向链表指向KTHREAD.WaitListEntry,线程调用Sleep()或者WaitForSingleObject()等函数时,就插入这个链表
33个链表 处于运行状态的链表在KPCR中,就绪和等待全在另外33个链表中,1个等待链表,32个就绪链表
调度(就绪)链表 KiDispatcherReadyListHead 存储32个双向链表的链表头,按优先级为下标排序
版本差异 32位只有33个双向链表,多核也只有33个
64位有65个
服务器版本:KiWaitListHead只有一个,但KiDispatcherReadyListHead这个数组有几个CPU就有几组
模拟线程切换
线程不是被动切换而是主动切换
线程切换并没有使用TSS来保存寄存器,而是使用堆栈
线程切换的过程就是堆栈切换的过程
主动切换
windows中大多情况下通过调用SwapContext函数进行线程切换
线程切换时会比较是否属于同一进程,如果不是则切换cr3寄存器
如果不调用API,就可以一致占用CPU吗?
时钟中断切换 时间片管理切换 线程切换之TSS Intel设计TSS的目的是为了任务切换(线程切换),但Windows与Linux并没有使用,而是采用栈来保存线程的各种寄存器。
一个CPU只有一个TSS(保存在KPCR中),但是线程很多,如何用一个TSS来保存所有线程的ESP0呢?
切换线程时会将从TEB中取出目标线程的esp0 和 cr3 存到TSS中,其他不动
线程切换之FS 系统中同时存在许多个线程,这就意味着fs在3环时指向的TEB要有多个(每个线程一个)
但在实际的使用中我们发现,当我们在3环查看不同线程的FS寄存器时,FS的段选择子都是相同的,那是如何实现通过一个FS寄存器指向多个TEB呢?
线程切换时会取出目标进程的TEB存入FS段描述符基址中
线程优先级 有三种情况会导致线程切换:
当前线程主动调用API:KiSwapThread KiSwapContext
当前线程时间片稻妻:KiDispatchInterrupt KiQuantumEnd
有备用线程(KPCR.PrcbData.NextThread):KiDispatchInterrupt
在KiSwapThread与KiQuantumEnd 函数中都是通过KiFindReadyThread来找下一个要切换的线程,那么KiFindReadyThread
时根据什么条件
来选择下一个要执行的线程呢?
根据之前说到的KiDispatcherReadyListHead 数组中的双向链表的下标作为优先级进行调度
进程挂靠