系统调用 1.API函数的调用过程(3环进0环)

30 篇文章 4 订阅
17 篇文章 2 订阅

1.API函数的调用过程(3环进0环)

一、Windows API

  1. Application Programming Interface,简称 API 函数。

  2. Windows有多少个API?
    主要是存放在 C:\WINDOWS\system32 下面所有的dll

  3. 几个重要的DLL
    Kernel32.dll:最核心的功能模块,比如管理内存、进程和线程相关的函数等.
    User32.dll:是Windows用户界面相关应用程序接口,如创建窗口和发送消息等.
    GDI32.dll:全称是Graphical Device Interface(图形设备接口),包含用于画图和显示文本的函数.比如要显示一个程序窗口,就调用了其中的函数来画这个窗口.
    Ntdll.dll:大多数API都会通过这个DLL进入内核(0环).

二、分析WriteProcessMemory(Ring3 To Ring0)

先用IDA找到WriteProcessMemory
在这里插入图片描述
调用了一个函数
在这里插入图片描述
在导入表里找到是ntdll的函数
在这里插入图片描述
再打开ntdll找NtProtectVirtualMemory函数,就这么几行代码
在这里插入图片描述
eax保存的是编号,用来找到函数

它到底是怎么进ring0的呢?那就不得不介绍_KUSER_SHARED_DATA结构了

三、_KUSER_SHARED_DATA结构

在这里插入图片描述

  1. 在 User 层和 Kernel 层分别定义了一个 _KUSER_SHARED_DATA 结构区域,用于 User 层和 Kernel 层共享某些数据
  2. 它们使用固定的地址值映射,_KUSER_SHARED_DATA 结构区域在 User 和 Kernel 层地址分别为:
    User 层地址为:0x7ffe0000
    Kernnel 层地址为:0xffdf0000

特别说明:
虽然指向的是同一个物理页,但在User 层是只读的,在Kernnel层是可写的.
并且该表由操作系统来填充
在这里插入图片描述
相信大家都注意到了0x7FFE0300正好是_KUSER_SHARED_DATA偏移0x300的位置
在这里插入图片描述

四、0x7FFE0300(SsytemCall)到底存储的是什么?

它存储的是一个函数地址,功能就是用来进Ring0

五、实验:查看CPU是否支持快速调用(大部分都支持)

在这里插入图片描述
可以看到EDX的第11位为1(B为1100)
当通过eax=1来执行cpuid指令时,处理器的特征信息被放在ecx和edx寄存器中,其中edx包含了一个SEP位(11位),该位指明了当前处理器知否支持sysenter/sysexit指令

支持:
KiFastSystemCall
在这里插入图片描述
不支持:
KiIntSystemCall
在这里插入图片描述

六、sysenter进0环

无论是KiFastSystemCall还是KiIntSystemCall都是为了提权进Ring0,进Ring0就必须换栈,换栈就必须保存原来的SS,ESP,CS,EIP寄存器,还得提供新的SS,ESP,CS,EIP,其中CS和EIP由中断门描述符来提供,ESP和SS由TSS来提供

接下来介绍一下sysenter指令

在执行sysenter指令之前,操作系统必须指定0环的CS段、SS段、EIP以及ESP.

MSR索引
IA32_SYSENTER_CS174H
IA32_SYSENTER_ESP175H
IA32_SYSENTER_EIP176H

可以通过RDMSR/WRMST来进行读写(操作系统使用WRMST写该寄存器):

kd> rdmsr 174   //查看CS		//SS = CS + 8
kd> rdmsr 175   //查看ESP
kd> rdmsr 176   //查看EIP

参考:Intel白皮书第二卷(搜索sysenter)

七、总结

API通过中断门进0环(KiIntSystemCall):

1)  固定中断号为0x2E
2)  CS/EIP由门描述符提供   ESP/SS由TSS提供
3)  进入0环后执行的内核函数:NT!KiSystemService

API通过sysenter指令进0环(KiFastSystemCall):

1)  CS/ESP/EIP由MSR寄存器提供(SS是算出来的)  SS=CS+8
2)  进入0环后执行的内核函数:NT!KiFastCallEntry

内核模块:ntoskrnl.exe/ntkrnlpa.exe

二者区别

  1. KiFastSystemCall(快速调用):操作系统会提前将CS/SS/ESP/EIP的值存储在MSR寄存器中,sysenter指令执行时,CPU会将MSR寄存器中的值直接写入相关寄存器,没有读内存的过程,所以叫快速调用
  2. KiIntSystemCall:通过中断门进0环,需要的CS、EIP在IDT表中,需要查内存(SS与ESP由TSS提供)

八、实现ReadProcessMemory和WriteProcessMemory的3环部分

BOOL ReadProcessMemory(
			HANDLE hProcess,   			// 被读取进程的句柄
			LPCVOID lpBaseAddress, 		// 基址 从具体何处读取
			LPVOID lpBuffer,   			// Out类型 接收数据的Buffer地址 将读取的内容写入此处
			DWORD nSize,				// 读取的字节数
			LPDWORD lpNumberOfBytesRead // Out类型 读取实际大小
);

BOOL WriteProcessMemory(
			HANDLE hProcess,   				// 被读取进程的句柄
			LPVOID lpBaseAddress, 			// 要写的内存首地址
			LPVOID lpBuffer,				// 指向缓冲区的指针,该缓冲区提供数据。
			DWORD nSize,					// 要写入的字节数
			LPDWORD lpNumberOfBytesWritten	// Out 实际写入的字节数
);

运行前:
在这里插入图片描述
运行后:
在这里插入图片描述

#include "stdafx.h"
#include <windows.h>

BOOL EnableDebugPrivilege()
{
	HANDLE hToken;
	BOOL fOk=FALSE;
	if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken))
	{
		TOKEN_PRIVILEGES tp;
		tp.PrivilegeCount=1;
		LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid);

		tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
		AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);

		fOk=(GetLastError()==ERROR_SUCCESS);
		CloseHandle(hToken);
	}
	return fOk;
}

//写进程内存(中断门调用)
BOOL WINAPI WriteProcessMemory_KiInt(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten)
{
	LONG NtStatus;
	__asm
	{
		mov eax, 0x115;			//编号
		lea edx,hProcess;		//取第一个参数地址
		int 0x2E;				//中断门
		mov NtStatus, eax;		//返回值
	}
	if (lpNumberOfBytesWritten != NULL)
	{
		*lpNumberOfBytesWritten = nSize;		
	}
	// 错误检查
	if (NtStatus < 0)
	{
		return FALSE;
	}
	return TRUE;
}
//读进程内存(中断门调用)
BOOL WINAPI ReadProcessMemory_KiInt(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten)
{
	LONG NtStatus;
	__asm
	{
		mov eax,0xBA		//KIntSystemCall需要eax存储编号
			lea	edx,hProcess	//edx存储[esp+8]的地址
			int 0x2E			//中断门
			mov NtStatus, eax	//返回值
	}
	if (lpNumberOfBytesWritten != NULL)
	{
		*lpNumberOfBytesWritten = nSize;		
	}
	// 错误检查
	if (NtStatus < 0)
	{
		return FALSE;
	}

	return TRUE;
}

// 读进程内存(快速调用)
BOOL WINAPI ReadProcessMemory_FAST(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten)
{
	LONG NtStatus;
	__asm
	{
		lea     eax, nSize
		push    eax
		push	nSize
		push	lpBuffer
		push	lpBaseAddress
		push	hProcess
		sub		esp,0x4
		mov		eax, 0xBA
		push	FromFastcallRet
		mov		edx,esp
		_emit	0x0F
		_emit	0x34
FromFastcallRet:
		add		esp, 0x18
		mov		NtStatus, eax
	}

	if (lpNumberOfBytesWritten != NULL)
	{
		*lpNumberOfBytesWritten = nSize;		
	}
	// 错误检查
	if (NtStatus < 0)
	{
		return FALSE;
	}

	return TRUE;
}

// 写进程内存(快速调用)
BOOL WINAPI WriteProcessMemory_FAST(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten)
{
	LONG NtStatus;
	__asm
	{
		lea     eax, nSize
			push    eax
			push	nSize
			push	lpBuffer
			push	lpBaseAddress
			push	hProcess
			sub		esp,0x4
			mov		eax, 0x115
			push	FromFastcallRet
			mov		edx,esp
			_emit	0x0F
			_emit	0x34
FromFastcallRet:
			add		esp, 0x18
			mov		NtStatus, eax
	}

	if (lpNumberOfBytesWritten != NULL)
	{
		*lpNumberOfBytesWritten = nSize;		
	}
	// 错误检查
	if (NtStatus < 0)
	{
		return FALSE;
	}

	return TRUE;
}


int main(int argc, char* argv[])
{
	EnableDebugPrivilege();

	DWORD pid;
	DWORD lpBaseAddress;
	printf("请输入PID和地址(十六进制)\n");
	scanf("%X %X",&pid,&lpBaseAddress);
	wchar_t wtBufferWrite1[100] = L"WriteProcessMemory_FAST";
	wchar_t wtBufferWrite2[100] = L"WriteProcessMemory_KiInt";
	char lpBufferRead[100] = {0};
	DWORD nSize = 10;
	DWORD lpNumberOfBytesWritten;
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);

	ReadProcessMemory_FAST(hProcess, (PVOID)lpBaseAddress, lpBufferRead, nSize,&lpNumberOfBytesWritten);
	printf("函数:ReadProcessMemory_FAST读取的字符串:%ls\n",lpBufferRead);
	memset(lpBufferRead,0,100);

	ReadProcessMemory_KiInt(hProcess, (PVOID)lpBaseAddress, lpBufferRead, nSize,&lpNumberOfBytesWritten);
	printf("函数:ReadProcessMemory_KiInt读取的字符串:%ls\n",lpBufferRead);

	//wcslen返回值是字符个数,不是字节数
	WriteProcessMemory_FAST(hProcess,(LPCVOID)lpBaseAddress,wtBufferWrite1,2*wcslen(wtBufferWrite1),&lpNumberOfBytesWritten);
	printf("函数:WriteProcessMemory_FAST写的字符串:%ls\n",wtBufferWrite1);

	WriteProcessMemory_KiInt(hProcess,(LPCVOID)(lpBaseAddress),wtBufferWrite2,2*wcslen(wtBufferWrite2),&lpNumberOfBytesWritten);
	printf("函数:WriteProcessMemory_KiInt写的字符串:%ls\n",wtBufferWrite2);

	return 0;
}



在这里插入图片描述
下一节具体分析一下到底是如何进0环的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值