文章目录

  • 如何获取中断现场环境
  • 中段现场环境
    • 观察中断现场堆栈环境
    • 观察中断现场的寄存器环境
  • 段选择子
    • 段寄存器结构
    • 变化的段寄存器的具体含义
  • 遗留问题:SS段寄存器和栈顶指针来自于哪?
    • 什么是TSS
    • TSS的工作细节
    • 中断提权的任务切换过程
  • 实验代码

上一课我们已经实现了利用中断提权的方式让自己写的函数拥有了零环的权限,但是为了更方便的写零环代码。我们还需要搞清楚中断现场的上下文环境,什么资源可以被使用,什么资源不能被使用。如果不清楚现场的环境,就很容易写出有问题的代码,从而导致系统蓝屏。

如何获取中断现场环境

比较方便的一个方式是在函数内部将当前的寄存器包括通用寄存器 段寄存器甚至是控制寄存器等等全部打印出来。

中段现场环境

观察中断现场堆栈环境

我们加上下面几行代码,来观察一下中断之前和之后的堆栈环境,看一下int 20这条指令对堆栈的影响

DWORD g_esp[2];void __declspec(naked) IdtEntry()
{__asm{mov [g_esp+4], esp;iretd;}
}void go()
{__asm mov[g_esp], esp;__asm int 0x20;
}

运行程序,可以看到在中断提权之前,地址是一个三环的esp,而中断之后esp的地址则变成了零环的地址。

接下来我们在代码中加入一条int3指令,在调试器中观察一下堆栈内的数据情况

kd> dps esp
a6e63c9c  0040114f
a6e63ca0  0000001b
a6e63ca4  00000246
a6e63ca8  0012ff44
a6e63cac  00000023
......

可以看到栈顶的数据是0x0040114f,接着用ub命令观察这个地方的反汇编

kd> ub 0040114f
00401131 668cc0          mov     ax,es
00401134 66a380334000    mov     word ptr ds:[00403380h],ax
0040113a 668ce0          mov     ax,fs
0040113d 66a37c334000    mov     word ptr ds:[0040337Ch],ax
00401143 668ce8          mov     ax,gs
00401146 66a378334000    mov     word ptr ds:[00403378h],ax
0040114c 58              pop     eax
0040114d cd20            int     20h

可以发现,这个地方就是我之前写的代码,也就是三环的返回地址。堆栈中的其他数据含义如下:

  • 0000001b是三环的cs
  • 00000246是三环的eflags
  • 0012ff44是三环的esp
  • 00000023是三环的ss

总结:通过中断门进入零环之前需要先保存三环的寄存器环境来达到再次返回三环的目的

观察中断现场的寄存器环境

接下来我们修改一下代码,把所有的寄存器尽可能全部打印出来。接着运行我们写的程序

可以看到中断现场前后的寄存器环境中,ESP从三环栈切换到了零环栈,CS从0x1B变成了0x8,代码提权也是从这里体现的,另外ss也发生了变化。

那么问题也就来了,这些寄存器改变的背后是一种什么样的机制,另外这些变化的寄存器都是从哪来的。要想知道这个问题的答案,需要了解一个东西叫做段选择子

段选择子

段寄存器结构

段寄存器结构可以抽象成以下的结构

struct SegMent {WORD selector;   //段选择子WORD attribute;   //属性DWORD base;     //基址DWORD limit;    //段限长
}
  • selector: 首先可见部分16位对应上面SegMent的selector成员。在 OD 中,可以看到段寄存器后面就跟着一个数字,比如 ds 后面的 0023。而 0023 后面的部分就是,剩余部分不可见部分,不过 OD 也给我们展示出来了
  • attribute: attribute 属性记录了该段是否有效,是否可读写等权限。如果往一个不可写的段执行写数据,会报异常。
  • base 表示基地址
  • limit 表示段界限,如果在超出了段界限进行读写,会报错。

在段寄存器中,16位的段选择子是可见的,其余80位的不可见的。我们主要关注段选择子

例如上图中CS的段选择子位0x23,SS的段选择子为0x2B。段选择子就是一个数字,一共有16位,结构如下:

|   1   |     0    |  字节
|7654321076543 2 10|  比特
|-------------|-|--|  占位
|    INDEX    |T|R |  含义
|             |I|P |
|             | |L |
  • INDEX:高13位表示的是在GDT数组或LDT数组的索引号
  • TI:Table Indicator,这个值为0表示查找GDT,1则查找LDT
  • RPL:请求特权级。以什么样的权限去访问段。

变化的段寄存器的具体含义

明白了段选择子的结构,接下来就来拆解其中的具体含义,以CS从0x1B变成了0x8为例:

0x1B=‭00011011‬

  • 索引号:00011=3,代表查找GTB表下标为3的描述符
  • TI:0代表查GDT表
  • RPL:11=3 代表特权等级为3 也就是三环权限

0x8=‭00001000

  • ‬索引号:00001,代表查找GTB表下标为1的描述符
  • TI:0代表查GDT表
  • RPL:0 代表特权等级为0,也就是零环权限

总结:CS段寄存器从0x1B变成0x8,主要是代表特权等级从三环提升到了零环

权限切换了,esp堆栈和相应的段寄存器ss自然也会跟着切换。因为栈数据是线程的核心资源,特权切换必须伴随栈切换

遗留问题:SS段寄存器和栈顶指针来自于哪?

我们已经知道代码提权的时候是需要切换栈的。那么,还剩下一个问题:就是SS段寄存器和栈顶指针是来自于哪?

答案是来自于TSS 任务状态段

什么是TSS

TSS 全称task state segment,是指在操作系统进程管理的过程中,任务(进程)切换时的任务现场信息。注意不要把TSS和任务切换关联起来,它只是一块用于保存任务现场的一些数据

Inter白皮书给出的TSS在内存中的图是这样的:

看的一脸懵逼对吧?我也看的一脸懵逼。抽象成结构体就是这样的

typedef struct TSS {DWORD link; // 保存前一个 TSS 段选择子,使用 call 指令切换寄存器的时候由CPU填写。// 这 6 个值是固定不变的,用于提权,CPU 切换栈的时候用DWORD esp0; // 保存 0 环栈指针DWORD ss0;  // 保存 0 环栈段选择子DWORD esp1; // 保存 1 环栈指针DWORD ss1;  // 保存 1 环栈段选择子DWORD esp2; // 保存 2 环栈指针DWORD ss2;  // 保存 2 环栈段选择子// 下面这些都是用来做切换寄存器值用的,切换寄存器的时候由CPU自动填写。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;

TSS的工作细节

TSS在任务切换过程中起着重要作用。在任务切换的过程中,首先,处理器中的各寄存器的当前值被自动保存到TR(任务寄存器)所指定的TSS中;然后下一任务的TSS选择子被装入TR;最后,从TR所指定的TSS中取出各寄存器的值送到处理器的各寄存器中,从而实现任务切换。

中断提权的任务切换过程

三环程序通过中断门进入到零环时,首先会保存当前的寄存器环境到TR所指定的TSS中,这一点我们已经在中断现场的堆栈环境中见过了。

然后将零环所需要的选择子装入TR。

最后从TSS中取出各寄存器送到当前环境下。这其中就包括了ss段寄存器和栈顶指针。我们也可以从TSS的结构体中看到0环的ss段寄存器和esp栈顶指针。

总结:ss段寄存器和esp栈顶指针来自于TSS

实验代码

最后 附上本次实验所用到的代码

#include "pch.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>DWORD g_eax[2], g_ecx[2], g_edx[2], g_ebx[2];
DWORD g_esp[2], g_ebp[2], g_esi[2], g_edi[2];
WORD g_cs[2], g_ds[2], g_ss[2], g_es[2], g_fs[2], g_gs[2];
WORD g_tr;void __declspec(naked) IdtEntry()
{__asm{mov [g_eax + 4], eax;mov [g_ecx + 4], ecx;mov [g_edx + 4], edx;mov [g_ebx + 4], ebx;mov [g_esp + 4], esp;mov [g_ebp + 4], ebp;mov [g_esi + 4], esi;mov [g_edi + 4], edi;push eax;mov ax, cs;mov [g_cs + 2], ax;mov ax, ds;mov[g_ds + 2], ax;mov ax, ss;mov[g_ss + 2], ax;mov ax, es;mov[g_es + 2], ax;mov ax, fs;mov[g_fs + 2], ax;mov ax, gs;mov[g_gs + 2], ax;str ax;mov g_tr, ax;pop eax;int 3;iretd;}
}void go()
{__asm{mov[g_eax], eax;mov[g_ecx], ecx;mov[g_edx], edx;mov[g_ebx], ebx;mov[g_esp], esp;mov[g_ebp], ebp;mov[g_esi], esi;mov[g_edi], edi;push eax;mov ax, cs;mov[g_cs], ax;mov ax, ds;mov[g_ds], ax;mov ax, ss;mov[g_ss], ax;mov ax, es;mov[g_es], ax;mov ax, fs;mov[g_fs], ax;mov ax, gs;mov[g_gs], ax;pop eax;}__asm int 0x20;
}//eq 80b95500 0040ee00`00081040
int main()
{if ((DWORD)IdtEntry != 0x401040){printf("wrong addr:%p", IdtEntry);exit(-1);}go();printf("eax:%p,\tecx:%p\tedx:%p\tebx:%p\n", g_eax[0], g_ecx[0], g_edx[0], g_ebx[0]);printf("esp:%p,\tebp:%p\tesi:%p\tedi:%p\n", g_esp[0], g_ebp[0], g_esi[0], g_edi[0]);printf("cs:%p,\tds:%p\tss:%p\tes:%p\tfs:%p\tgs:%p\n", g_cs[0], g_ds[0], g_ss[0], g_es[0], g_fs[0], g_gs[0]);printf("eax:%p,\tecx:%p\tedx:%p\tebx:%p\n", g_eax[1], g_ecx[1], g_edx[1], g_ebx[1]);printf("esp:%p,\tebp:%p\tesi:%p\tedi:%p\n", g_esp[1], g_ebp[1], g_esi[1], g_edi[1]);printf("cs:%p,\tds:%p\tss:%p\tes:%p\tfs:%p\tgs:%p\n", g_cs[1], g_ds[1], g_ss[1], g_es[1], g_fs[1], g_gs[1]);printf("tr:%p\n", g_tr);system("pause");
}

Windows内核实验002 中断现场相关推荐

  1. Windows内核实验001 中断提权

    文章目录 实验环境 内核提权 IDT的基本知识 什么是中断 什么是IDT表 在PC Hunter中查看IDT表 中断提权的基本原理 写一个三环的小程序 修改IDT表 提权测试 本篇文章基于周壑老师的讲 ...

  2. windows内核试验05_中断现场

    文章目录 windbg写入中断表数据 VS写出能运行XP的exe 第一步(换平台工作集) 第二步(关闭ASLR) 第三步(关闭语言符合模式) 运行观察: CS变化 CE查看GDT表 windbg写入中 ...

  3. Windows内核实验003 再次回到中断

    文章目录 两个实验 死循环 开启中断后的死循环 KiFastCallEntry 调用零环API的两个条件 分析KiFastCallEntry 什么是KPCR 完善代码 完整代码 之前的实验我们已经实现 ...

  4. Windows内核实验004 API调用

    文章目录 完善代码 内核API调用 修复一个潜在问题 复现问题 完整代码 前面几次实验我们已经完成了一个三环的程序调用零环API的必要条件. 提升到零环权限 使fs指向KPCR 完善代码 这次我们去掉 ...

  5. Windows内核实验005 Inline Hook

    文章目录 准备工作 寻找Inline Hook的返回地址 编写代码 动态变化的返回地址 JmpTargetAddr Inline Hook基本框架 示例代码 实战HOOK KiTrap01 无需计算偏 ...

  6. Windows 内核之双机调试与windbg命令大全

    在今后会有相当的实验环节,对于windows内核实验,调试环境是必不可少的,本章讲解双机调试的环境搭建与常见的WINDBG指令. 准备材料: VMware workstation : [https:/ ...

  7. windows 内核情景分析

    原文很长:先转部分过来,有时间看一下: 一 windows 内核情景分析---说明 说明 本文结合<Windows内核情景分析>(毛德操著).<软件调试>(张银奎著).< ...

  8. Windows内核系统调用分析

    系统调用 进程 --> 调用OS API:OS进程管理 --> 调配进程. 仅从用户进程角度,OS就像是一个被动响应的运行时库.Windows提供了一个系统调用界面作为外层,即Win32A ...

  9. Windows内核函数的命名

    <Windows内核情景分析--采用开源代码ReactOS(上.下册)>本书通过分析ReactOS的源代码介绍了Windows内核各个方面的结构.功能.算法与具体实现.本小节为大家介绍Wi ...

最新文章

  1. Android 基于 Speex 的高度封装语音库,0 耦合,没三方jar包
  2. SDUT 2127 树-堆结构练习——合并果子之哈夫曼树(优先队列)
  3. 哥本哈根能效中心:阿里云用清洁的计算能力改变世界
  4. mysql 优化(一)
  5. 深度学习100例-卷积神经网络(CNN)识别验证码 | 第12天
  6. ML之DT:基于DT决策树算法(交叉验证FS+for遍历最佳FS)对Titanic(泰坦尼克号)数据集进行二分类预测
  7. python秒转换成小时分钟秒_新闻联播66分钟,康辉口播22分38秒,零失误
  8. python列表去重效率,你应该知道的python列表去重方法
  9. xcode3.2.6升级至4.0.2经验加教训总结(转)
  10. 猎豹浏览器缓存文件在哪 猎豹浏览器缓存文件位置说明
  11. 使用Eclipse创建maven项目
  12. 如果一栋楼起火谁赔偿_太原一辆快递车起火!赶紧看看有你的包裹没?
  13. 【开发环境】运行Hitool出现“A Java Runtime Environment (JRE) or Java Development Kit (JDK)”错误处理
  14. DOS 批处理高级教程精选合编20080331
  15. 总结AUTOCAD快捷键,持续更新~
  16. 描写计算机硬件的英语作文,介绍关于电脑的英语作文
  17. idea里面java文件只读,Java只读集合
  18. 开源GIS浅谈 【转】
  19. SolidWorks 如何制作装配体
  20. JavaScript 编程精解 中文第三版 三、函数

热门文章

  1. 成功解决bash syntax error near unexpected token from
  2. DL之RetinaNet:基于RetinaNet算法(keras框架)利用resnet50_coco数据集(.h5文件)实现目标检测
  3. DL之GCN:GCN算法的简介(论文介绍)、架构详解、案例应用等配图集合之详细攻略
  4. Numpy:利用Numpy库建立可视化输入的二次函数数据点集np.linspace+np.random.shuffle+np.random.normal
  5. OS_CORE.C(7)
  6. OS_CORE.C(4)
  7. ASP.NET的Page.IsPostBack 属性详细说明(转)
  8. 解密虚拟 DOM——snabbdom 核心源码解读
  9. Python基础训练题-简单数学公式
  10. windows php exec()不生效问题