Windows内核实验004 API调用


前面几次实验我们已经完成了一个三环的程序调用零环API的必要条件。

  • 提升到零环权限
  • 使fs指向KPCR

完善代码

在这里插入图片描述

这次我们去掉之前的死循环代码,并且将函数地址写入到IDT表项,在虚拟机中运行一下程序,看看会有什么结果。

在这里插入图片描述

这里他抛出了一个内存访问异常。原因在于我们修改了fs寄存器之后,在iretd指令返回三环的时候,系统不会自动帮我们将FS寄存器还原。

所以我们在返回三环之前还需要将fs还原回去。代码如下:

__asm
	{
		push 0x30;
		pop fs;
		sti;
		push 0x3B;
		pop fs;
		iretd;
	}

再次运行我们的程序,

在这里插入图片描述

此时程序完全正常运行。

内核API调用

接下来我们在自己的代码中调用一个内核的API函数ExAllocatePool来分配一块内存。

在这里插入图片描述

首先在PC Hunter中将ntkrnlpa.exe拷贝出来,然后用IDA分析,接着设置基址和驱动模块的基地址一致。

在这里插入图片描述

在IDA中找到ExAllocatePool这个函数,并且记录下函数地址。

在这里插入图片描述

然后按Y键可以复制出函数原型,然后定义函数指针,并且将函数地址赋值给函数指针变量

typedef DWORD (__stdcall *EX_ALLOCATE)(DWORD PoolType, DWORD NumberOfBytes);
EX_ALLOCATE ExAllocatePool= (EX_ALLOCATE)0x83E51976;

接着编写调用代码如下:

void __declspec(naked) IdtEntry()
{
	__asm
	{
		push 0x30;
		pop fs;
		sti;
	}
	g_pool=ExAllocatePool(0, 4096);
	__asm
	{
		push 0x3B;
		pop fs;
		iretd;
	}
} 

运行程序

在这里插入图片描述

看到这里将我们申请的内存首地址打印出来了,而且是一个内核的地址,符合我们的预期

然后我们可以尝试调用一下DbgPrint,同样的方法找到函数地址和原型进行调用

typedef DWORD ( __cdecl *DBGPRINT)(char* Format, ...);
DBGPRINT MyDbgPrint=(DBGPRINT)0x83E5541F;
char str[] = "Hello GuiShou!";

void __declspec(naked) IdtEntry()
{
	__asm
	{
		push 0x30;
		pop fs;
		sti;
	}
	//g_pool=ExAllocatePool(0, 4096);
	MyDbgPrint(str);
	__asm
	{
		push 0x3B;
		pop fs;
		iretd;
	}
} 

运行程序以后

在这里插入图片描述

在windbg窗口打印出了我们设置好的字符串,说明API调用成功

修复一个潜在问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bD6wu6Cl-1573908064431)(assets/1573905826474.png)]

仔细观察我们写的这几句还原FS段选择子返回三环的代码,实际上是有问题的。

在pop fs这句代码执行完成之后,iretd执行之前;依然是开启中断的状态,那么意味着这两行汇编指令之间CPU有可能收到时钟中断,造成线程切换。

而线程切换需要用的FS寄存器的值,然而FS这个时候是一个三环的段选择子,而我们现在却处在一个零环的环境下,FS需要指向KPCR,但是却没有指向那个位置。

就是说如果在指向这两句代码之间发生了线程切换,就会发生蓝屏。尽管产生这个问题的几率很小,但是依然不能忽视。

复现问题

接下来我们稍微修改一下代码,就能复现这个问题:

void go()
{
	while(1)
		__asm int 0x20;
}

我们在int 0x20指令加上一条死循环。当CPU产生int 20异常时,会进入到我们设置好的IdtEntry函数提权到零环,然后IdtEntry函数会重新返回到三环。接着由于int 0x20这条指令是在死循环里面。所以这个程序就会一直在三环和零环之前往返。这就会大大增加在push 0x2B;pop fs这两句代码之间发生线程切换的几率。

在这里插入图片描述

运行程序,在不连接调试器的情况下会直接蓝屏,如果连接上调试器则虚拟机会出现卡死现象。

解决的方法很简单,就是让CPU在执行这两句代码时不接收硬件中断

在这里插入图片描述

在恢复FS寄存器之前关闭中断,即可解决问题,事实上KiFastCallEntry也是这么做的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CHN5izUD-1573908064445)(assets/1573907354519.png)]

再次运行程序,此时依然是死循环,但是因为已经关闭了中断,所以循环往返三环和零环都是安全的,不会发生蓝屏和卡死的现象。

完整代码

最后附上完整的实验代码

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

typedef DWORD (__stdcall *EX_ALLOCATE)(DWORD PoolType, DWORD NumberOfBytes);
EX_ALLOCATE ExAllocatePool= (EX_ALLOCATE)0x83E51976;
DWORD g_pool;

typedef DWORD ( __cdecl *DBGPRINT)(char* Format, ...);
DBGPRINT MyDbgPrint=(DBGPRINT)0x83E5541F;
char str[] = "Hello GuiShou!";

void __declspec(naked) IdtEntry()
{
	__asm
	{
		push 0x30;
		pop fs;
		sti;
	}
	//g_pool=ExAllocatePool(0, 4096);
	//MyDbgPrint(str);
	__asm cli;

	__asm
	{
	
		push 0x3B;
		pop fs;
		iretd;
	}  
} 

void go()
{
	while(1)
		__asm int 0x20;
}

//eq 80b95500 0040ee00`00081040
int main()
{
	if ((DWORD)IdtEntry != 0x401040)
	{
		printf("wrong addr:%p", IdtEntry);
		exit(-1);
	}
	go();
	//printf("%p\n", g_pool);
	system("pause");
}
发布了99 篇原创文章 · 获赞 89 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_38474570/article/details/103102643