Windows内核实验001 中断提权


本篇文章基于周壑老师的讲解,感谢周壑老师。

实验环境

  1. windbg双机调试环境
  2. VS开发环境
  3. 32位 WIN7 虚拟机

内核提权

内核态的程序拥有一切权限,在Windows操作系统上,没有任何其他软件可以限制内核态的程序。如果让一个用户层的小程序,提升至内核权限,那么就可以操作系统上的一切资源,做任何想做的事。今天我们借助一个内核实验来完成用户层到内核层的提权。

IDT的基本知识

什么是中断

当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。即在程序运行过程中,系统出现了一个必须由CPU立即处理的情况。此时,CPU暂时终止程序的运行转而去处理这个情况的过程就叫中断

什么是IDT表

常见的中断有 除零(0号中断)、断点(3号中断)、系统调用(2e号中断), 每一种中断对应一个中断号。每一个中断号都有对应的中断处理函数。

这样操作系统就会用数据结构来维护这些中断处理函数,这个数据结构就是IDT 表。

IDT表全称:Interrupt Descriptor Table,中断描述符表。

当CPU产生中断时,就会去查IDT表,然后执行IDT表中对应的中断处理函数

在PC Hunter中查看IDT表

打开PC Hunter,点击内核钩子->系统中断表

在这里插入图片描述

我们可以看到当前系统的IDT表,其中序号代表的是中断号 函数名称指的是产生该序号中断时会执行的函数,后面则是对应的函数地址。

例如当CPU遇到除零异常时,就会抛出0号中断,此时查IDT表,找到对应的处理函数Divide error,然后跳转到函数地址处理除零异常。

中断提权的基本原理

IDT表是系统用于处理中断的,自然就有最高的内核权限。中断提权的基本原理实际上就是将我们自己的函数地址,去替换掉IDT表中的函数地址。进而抛出一个中断,让系统跳转到我们自己的函数。

那么替换掉哪个函数呢?还是回到PC Hunter,IDT表的位置往下拉

在这里插入图片描述

从序号0x20开始有一部分未被使用的IDT表,我们就将自己的函数地址替换到0x20的位置即可

写一个三环的小程序

基本代码如下

#include "pch.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void __declspec(naked) IdtEntry()
{
	__asm
	{
		iretd;
	}
}


int main()
{
    //0x401040为IdtEntry的函数地址
	if ((DWORD)IdtEntry!=0x401040)
	{
		printf("wrong addr:%p", IdtEntry);
		exit(-1);
	}
	printf("%p", IdtEntry);
	system("pause");
}

我们自己写一个裸函数,然后将地址打印出来。为了方便后面的调试

在这里插入图片描述

需要将属性设置为release,并且去除随机基址,改用固定基址。

修改IDT表

在配置好双机调试环境以后,用windbg查看IDT表的基址,IDT表的基址保存在idtr寄存器中

在这里插入图片描述

接着用dq命令查看IDT表,每8位对应一个IDT表的处理函数

在这里插入图片描述

对比PC Hunter中的函数地址可以发现,IDT表中的函数地址实际上是由IDT地址表中的前四位和后四位拼接而成。

在这里插入图片描述

接着我们找到第一个值为00000000的位置,也就是序号为0x20的位置写入我们自己的函数

kd> eq 80b95500 00408e00`00081040

接着将8e修改为ee(8e对应的是DPL描述符特权级 修改以后就能让用户层的程序访问了)

kd> eq 80b95500 0040ee00`00081040

提权测试

现在我们还需要让程序产生一个中断序号为0x20的中断,从而跳转到我们的函数中。那么这个怎么实现呢。只需要加上这样一条代码

void go()
{
	__asm int 0x20;
}

任何一个中断都能用int [index]的方式进入。

为了观察是否提权成功,我们在函数中读取一个内核的地址,我选择的是IDT表的基址

mov eax,dword ptr ds:  [0x80b95400];
mov g_tmp, eax;

最后在主函数中将这个地址打印出来。最后编译exe,在虚拟机中运行

在这里插入图片描述

可以看到程序已经打印出了IDT表的基址后半部分0x8CFC0

在这里插入图片描述

此时刷新一下PC Hunter,在0x20的位置 函数地址也显示为我们自己设置的0x401040。

到此,本次实验结束。最后附上完整代码

#include "pch.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

DWORD g_tmp;

void __declspec(naked) IdtEntry()
{
	__asm
	{
		mov eax,dword ptr ds:  [0x80b95400];
		mov g_tmp, eax;
		iretd;
	}
}

void go()
{
	__asm int 0x20;
}

int main()
{
	if ((DWORD)IdtEntry != 0x401040)
	{
		printf("wrong addr:%p", IdtEntry);
		exit(-1);
	}
	go();
	printf("%p", g_tmp);
	system("pause");
}


发布了93 篇原创文章 · 获赞 82 · 访问量 7万+
展开阅读全文
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览