对int、iret和栈的深入理解

我们以一个程序来展开讨论:用7ch中断例程完成loop指令的功能,在屏幕中间显示80个"!"

1.程序框架如下

assume cs:code
code segment
start:
	mov ax,0b800h	;向8086CPU显存的地址
	mov di,160*12	;显示位置
	mov bx,offset s-offset se	;跳转的位移
	mov cx,80	;循环次数
s:
	mov byte ptr es:[di],'!'
	add di,2
	int 7ch	;进入中断处理程序
se:	nop
	mov ax,4c00h
	int 21h

code ends
end start

2.中断处理程序的功能如下:

1)dec cx

2)如果(cx)!=0,转到标号s处执行,否则向下执行

那么如何实现到目的地址s的转移?

1)转移到s显然应设(CS)=s的段地址,(IP)=s的偏移地址

2)那么中断程序如何得到s的段地址和偏移地址?

3)现在知道,可以从栈中直接和间接获得s的段地址和偏移地址,那么如何用他们设置CS:IP?

7c的中断程序如下:

lp:
	push bp
	mov bp,sp
	dec cx
	jcxz lpret
	add [bp+2],bx
lpret:
	pop bp
	iret

这里跟高级语言里面函数调用栈帧很相似,后面我会专门更新一篇介绍函数调用栈帧的文章。
<think>我们讨论Windows线程的内核。每个用户线程在进入内核模式时都会使用一个独立的内核。内核用于存储线程在内核模式下执行时的函数调用、局部变量以及一些关键数据结构(如陷阱帧)。下面详细讲解内核的内容分布作用。###Windows线程内核的内容分布内核从高地址向低地址增长。其典型布局如下(以x86为例,但x64类似,只是具体大小偏移可能不同): 1. **高地址区域(底)**:-当线程第一次进入内核模式时,顶位于高地址(底),然后随着函数调用局部变量的使用向低地址增长。2.**陷阱帧(Trap Frame)**:-当发生中断、异常或系统调用时,处理器会切换到内核,并保存用户模式的执行上下文。这个保存的上下文就是陷阱帧(_KTRAP_FRAME)。-陷阱帧通常位于内核的较低地址(但靠近底,因为它是中断发生时较早压入的内容)。-陷阱帧包含了从用户模式进入内核模式时的寄存器状态,用于在返回用户模式时恢复现场。 3. **函数调用帧**:-在内核模式下执行的函数会像用户模式一样使用空间,包括返回地址、保存的寄存器、局部变量等。4.**异常处理链**:-内核上会存储一个异常处理链(ExceptionRegistrationRecord)。每个节点包含一个异常处理函数一个指向下一个节点的指针。这个链表的头指针存储在FS寄存器(x86)或GS寄存器(x64)指向的TIB(Thread InformationBlock)中,具体来说是TIB的第一个成员EXCEPTION_REGISTRATION_RECORD。-当内核代码中发生异常时,系统会遍历这个链表,直到找到能够处理该异常的处理器。 5. **中断服务例程(ISR)的上下文**:-如果线程在处理中断,那么上可能会有中断服务例程所需的上下文。6.**切换回用户模式的返回信息**:-在返回用户模式前,内核会设置好返回所需的内容,包括用户模式的指针、指令指针等。###内核的作用1.**保存用户模式上下文**:当线程从用户模式进入内核模式时,处理器会自动将一些寄存器(如SS、ESP、EFLAGS、CS、EIP)压入内核。然后内核代码会保存其他寄存器,形成完整的陷阱帧。2.**为内核函数提供执行环境**:内核函数调用使用内核来保存返回地址、参数局部变量。3.**异常处理**:内核上存储了异常处理链,使得内核代码能够通过结构化异常处理(SEH)来捕获处理异常。 4. **中断处理**:当硬件中断发生时,当前线程的内核会被用来处理中断(如果中断发生在用户线程,则使用该线程的内核;如果发生在内核线程,则使用当前线程的内核)。5.**支持系统调用**:系统调用时,通过内核传递参数保存返回地址。 ###示例:进入内核模式时的布局(x86) 下面是一个简化的示意图,展示当发生系统调用(如int0x2E)时内核的布局(从高地址到低地址):``` 高地址|-------------------| |用户模式SS|(由处理器压入) |用户模式ESP|| EFLAGS| |用户模式CS||用户模式EIP|<---进入内核时的顶|-------------------| |错误代码(可选)| |-------------------| |内核代码保存的寄存器 |(形成_KTRAP_FRAME)| ...| |-------------------| |函数调用| (后续的内核函数调用)| ...|低地址```###重要注意事项- **内核大小**:在Windows中,内核的大小是固定的。对于32位系统,通常为12KB或16KB;对于64位系统,通常为24KB或更大。如果内核溢出,会导致系统崩溃(蓝屏)。 -**每个线程一个内核**:每个线程都有自己独立的内核,因此多线程在内核模式下执行不会相互干扰。- **内核的分配**:当线程创建时,其内核由内存管理器分配。###代码示例:如何在内核驱动中访问当前线程的内核虽然在内核驱动中直接操作内核是危险的,但有时为了调试或特殊需求,可能需要访问上的信息。以下是一个示例,展示如何在内核驱动中获取当前线程的内核信息(注意:此代码仅用于演示,实际使用需谨慎): ```c#include <ntddk.h> voidDebugKernelStack() {PETHREADpThread= PsGetCurrentThread();PKTRAP_FRAMEpTrapFrame= (PKTRAP_FRAME) ((ULONG_PTR)pThread->InitialStack- sizeof(KTRAP_FRAME));//注意:InitialStack指向底(高地址),而陷阱帧在底下方(因为从高地址向低地址增长)//实际上,陷阱帧位于InitialStack之下,但具体偏移可能因Windows版本架构而异//更可靠的方式:使用内联函数获取当前陷阱帧PKTRAP_FRAMEpCurrentTrapFrame =KeGetCurrentThreadTrapFrame();if(pCurrentTrapFrame) {DbgPrint("Currenttrap frameat:0x%p\n", pCurrentTrapFrame);DbgPrint("EIP:0x%X\n",pCurrentTrapFrame->Eip);// ...其他寄存器}} ```注意:上面的代码中,`KeGetCurrentThreadTrapFrame`是Windows内核提供的函数,用于获取当前线程的陷阱帧。但是,这个函数在公开的WDK头文件中可能没有,因此实际使用时可能需要通过其他方式获取(例如,通过线程对象的TrapFrame字段)。###总结线程的内核是Windows内核中非常重要的数据结构,它支持了用户模式内核模式之间的切换、函数调用、异常处理以及中断处理。理解内核的布局对于深入理解操作系统内核的工作原理至关重要。 §§相关问题§§: 1.在x64架构下,Windows线程的内核布局与x86有什么不同? 2.当内核溢出时,Windows如何检测并处理?3.结构化异常处理(SEH)在内核上是如何组织的? 4.如何在内核驱动中安全地扩展内核的大小?</think>### Windows 线程内核详解 在 Windows 内核中,每个线程都有两个: 1. **用户模式**:用于用户空间函数调用 2. **内核模式**:用于内核空间函数调用(本文重点) 内核是线程进入内核模式时使用的关键数据结构,其内容分布作用如下: --- #### 内核的内存布局(从高地址到低地址) | 内存地址 | 内容 | 大小 | 作用 | |----------|------|------|------| | **高地址** | 顶起始位置 | - | 增长起点 | | ↓ | 函数调用帧 | 可变 | 存储函数返回地址、局部变量等 | | ↓ | `KEXCEPTION_FRAME` | 约 0x40 字节 | 结构化异常处理(SEH)框架 | | ↓ | `KTRAP_FRAME` | 约 0x130 字节 | **中断/异常时的寄存器状态** | | ↓ | 浮点状态区 | 约 0x210 字节 | 保存 FPU/SSE 寄存器状态 | | ↓ | 调用参数区 | 可变 | 系统调用参数传递 | | ↓ | 对齐填充区 | 可变 | 内存对齐保证 | | ↓ | 溢出保护区 | 1 页 (4KB) | 检测溢出 | | **低地址** | 底 | - | 增长终点 | --- #### 关键区域详解 1. **`KTRAP_FRAME` (陷阱帧)** - **位置**:位于内核中部 - **作用**: - 保存从用户模式切换到内核模式时的完整寄存器状态 - 包含关键信息:`EIP/RIP`(指令指针)、`ESP/RSP`(指针)、段寄存器、通用寄存器 - 用于系统调用返回时恢复用户模式上下文 - **触发场景**: - 系统调用 (INT 2E/SYSENTER/SYSCALL) - 硬件中断 (IRQ) - 异常处理 (如缺页异常) 2. **`KEXCEPTION_FRAME` (异常帧)** - **作用**: - 实现结构化异常处理(SEH) - 存储异常处理链(`EXCEPTION_REGISTRATION_RECORD`) - 包含异常处理回调函数指针 - **关键字段**: ```c typedef struct _KEXCEPTION_FRAME { EXCEPTION_REGISTRATION_RECORD ExceptionList; CONTEXT ContextRecord; // ... 其他异常处理相关字段 } KEXCEPTION_FRAME; ``` 3. **浮点状态区** - **作用**: - 保存线程的 FPU/MMX/SSE 寄存器状态 - 确保浮点运算状态在上下文切换中不被破坏 - **结构**: ```c typedef struct _FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; } FLOATING_SAVE_AREA; ``` 4. **溢出保护区** - **位置**:底最低地址处 - **大小**:1 个内存页 (通常 4KB) - **作用**: - 通过页面保护属性(PAGE_GUARD)检测溢出 - 当访问此区域时触发`STATUS_GUARD_PAGE_VIOLATION`异常 - 内核可据此扩展空间或终止线程 --- #### 内核生命周期示例 1. **用户模式 -> 内核模式切换** ```c // 伪代码:系统调用进入内核 void SysCallHandler() { // 1. 自动保存:SS, ESP, EFLAGS, CS, EIP (CPU完成) // 2. 构建KTRAP_FRAME:保存剩余寄存器 BuildTrapFrame(); // 3. 设置异常帧 SetupExceptionFrame(); // 4. 执行系统调用 KiSystemService(); } ``` 2. **内核模式 -> 用户模式返回** ```c void KiServiceExit() { // 1. 检查挂起的APC CheckForApcDelivery(); // 2. 从KTRAP_FRAME恢复寄存器 RestoreFromTrapFrame(); // 3. 执行IRET返回用户模式 __asm { iretd } } ``` --- #### 内核大小配置 | Windows 版本 | 默认内核大小 | |--------------|----------------| | 32-bit | 12 KB | | 64-bit | 24 KB | 可通过注册表调整: ```registry HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\kernel "StackSizeInBytes"=dword:00006000 ; 24KB for 64-bit ``` --- #### 实际调试示例(WinDbg) ``` 0: kd> !thread THREAD ffffe0017a0a0080 Cid 0b34.0b38 Teb: 0000004b`3d5e7000 Win32Thread: ... 0: kd> dq ffffe0017a0a0080+0x5c0 L2 ; 获取内核范围 ffffe001`7a0a0640 00000000`00300000 00000000`00300000 ; 基址上限 0: kd> .cxr 0xfffff801`7b3c6a50 ; 查看陷阱帧 rax=000000000000007b rbx=ffffe0017a0a0080 ... ``` --- ### 总结 Windows 线程内核是内核操作的基石,其精心设计的布局: 1. 保证高效的模式切换(通过`KTRAP_FRAME`) 2. 提供健壮的异常处理(通过`KEXCEPTION_FRAME`) 3. 防止溢出损坏(通过保护页) 4. 保持浮点状态一致性 5. 支持参数传递局部存储 理解内核布局对于调试蓝屏(BSOD)、分析内存转储开发内核驱动至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值