windows x32调用门实现 ring3提权

news2024/11/15 16:22:03

概述

调用门是Intel提供的一个机制,用于控制不同权限级(ring0-ring3)的程序函数调用。简单点就是提供了一个ring3 调用ring0 函数的机制。

intel手册volume3-Chapter 5.83描述如下

Call gates facilitate controlled transfers of program control between different privilege levels.
They are typically used only in operating systems or executives that use the privilege-level protection mechanism

详细可参阅intel Volume3-Chapter5

实现调用门需要构造一个调用门描述符Call-Gate Descriptor放入GDT或者LDT中。

在这里插入图片描述
Segment Selector 指向代码段的段选择子,P表示门是否有效,如果栈转化那么Param Count 指示要从调用方栈拷贝到目标栈的word(16位)数。type固定为1100.

typedef struct _GateDescriptor {
	unsigned int offset_low16 : 16;
	unsigned int selector : 16;
	unsigned int Param_count : 5;
	unsigned int res : 3;
	unsigned int type : 4;
	unsigned int s : 1;
	unsigned int dp1 : 2;
	unsigned int p : 1;
	unsigned int offset_hei16 : 16;
}GateDescriptor;

栈转化

如果调用的代码段是ring3权限(CPL),而目标调用门是ring0(RPL)权限,栈区是不共享的因此需要将栈区的参数拷贝到目标栈中。

因此调用调用门intel会自动按照如下图进行栈拷贝。
在这里插入图片描述
但是你需要注意FS寄存器intel并没有保存,但是window在ring3程序fs存储的TIB,在ring0存储KPCR,也就是说在window下你需要手动处理。

调用调用门

call 调用门选择子
jmp 调用门选择子

比如下面的汇编调用0x4bh的选择子

call    0x004B:00000000

但是VC编译器无法编写上面的指令,你只能利用下面的汇编指令

__asm {
		_emit 0x9a;
		_emit 0x00;
		_emit 0x00;
		_emit 0x00;
		_emit 0x00;
		_emit 0x4b;
		_emit 0x00;
	
		//call    0x004B:00000000;
	}

调用门函数的编写

首先函数一般使用裸函数编写,结尾使用retf返回,如下图所示

void Syscall() {
	DbgPrint("[My learning] %s \r\n", __FUNCTION__);
}
//对外提供的调用门函数
__declspec(naked) void SyscallProxy() {
	__asm {
		push ebp;
		mov ebp, esp;
		//windwow ring 0 fs应该指向30h
		//注意!! windbg调试内核的话会自动修改fs为30h
		mov ax, 30h;
		mov fs, ax;
		call Syscall;
		mov esp, ebp;
		pop ebp;
		//这里要返回到ring 3所以应该还原fs
		//ring 3程序固定指向3bh
		mov ax, 3bh;
		mov fs,ax;
		retf 0;
	}
}

为什么要使用裸函数?假设我们代码如下:

void SyscallProxy(){
}

对应的汇编指令
在这里插入图片描述
可以发现返回的时候使用ret而不是retf,两个指令最大的差别在于是否会修正cs等。如果直接使用原始的函数那么调用门将不会正确的返回。(cs没有被正确的修正)

实现调用门

我们通过一个驱动程序来编写一个调用门函数。

首先我们需要查看系统哪个GDT表项是空的,让我们插入自己实现的调用门描述符。

//查看gdt表 0到100的表项
dg 0 100

在这里插入图片描述
我们注意到0x48是空白的,所以我们可以利用这个进行插入我们自己的调用门描述符。0x48对应的ring3 的段选择子是0x4bh,
计算过程如下:
首先段选择子格式
在这里插入图片描述
index: 1001(第9个gdt项)
TI :0
RPL:11
RPL 表示当前权限因为是ring3 所以是11
组合上面的数据后就是 1001011 也就是4bh


#pragma push
#pragma pack(1)
typedef struct _GDTR {
	short limit;
	int base;
}GDTR;
#pragma pop

typedef struct _GateDescriptor {
	unsigned int offset_low16 : 16;
	unsigned int selector : 16;
	unsigned int Param_count : 5;
	unsigned int res : 3;
	unsigned int type : 4;
	unsigned int s : 1;
	unsigned int dp1 : 2;
	unsigned int p : 1;
	unsigned int offset_hei16 : 16;
}GateDescriptor;

void Syscall() {
	//KdBreakPoint();
	DbgPrint("[My learning] %s \r\n", __FUNCTION__);
}
//对外暴露的调用门函数
__declspec(naked) void SyscallProxy() {
	__asm {
		//int 3;
		push ebp;
		mov ebp, esp;
		//push fs;
		mov ax, 30h;
		mov fs, ax;
		call Syscall;
		mov esp, ebp;
		pop ebp;
		mov ax, 3bh;
		mov fs,ax;
		retf 0;
	}
}

//安装调用门到GDT中
void InstallGate() {
	//KdBreakPoint();
	DbgPrint("[My learning] %s \r\n", __FUNCTION__);
	GateDescriptor gate = { 0 };
	//指向代码段的选择子,因为ring0代码段是gdt第1个项目且DPL是0
	gate.selector = 0x8;
	//函数
	gate.offset_low16 = (ULONG)SyscallProxy & 0xffff;
	gate.offset_hei16 = ((ULONG)SyscallProxy >> 16) & 0xffff;
	//参数是0
	gate.Param_count = 0;
	//固定数值
	gate.type = 0xc;
	gate.s = 0;
	gate.p = 1;
	//权限因为是给ring3准备的所以是3 
	gate.dp1 = 3;
	


	KAFFINITY mask = KeQueryActiveProcessors();
	KAFFINITY shift = 1;
	while (mask)
	{
		KeSetSystemAffinityThread(shift);
		GDTR gdt = { 0 };
		__asm sgdt  gdt;

		DbgPrint("[My learning] %s base:%p limit %p \r\n", __FUNCTION__, gdt.base, gdt.limit);

		GateDescriptor*pGate = (GateDescriptor*)gdt.base;

		if (MmIsAddressValid(pGate))
		{
			pGate[9] = gate;
		}
		shift <<= 1;
		mask >>= 1;
	}


}
//卸载函数
void UnInstallGate() {
	//KdBreakPoint();
	DbgPrint("[My learning] %s \r\n", __FUNCTION__);


	KAFFINITY mask = KeQueryActiveProcessors();
	KAFFINITY shift = 1;
	while (mask)
	{
		KeSetSystemAffinityThread(shift);
		GDTR gdt = { 0 };
		__asm sgdt  gdt;
		DbgPrint("[My learning] %s base:%p limit %p \r\n", __FUNCTION__, gdt.base, gdt.limit);


		GateDescriptor*pGate = (GateDescriptor*)gdt.base;

		if (MmIsAddressValid(pGate))
		{
			pGate[9].p = 0;
		}
		shift <<= 1;
		mask >>= 1;
	}

}

结合驱动代码

#include<ntifs.h>
#include <Ntddk.h>
#include<intrin.h>



#pragma push
#pragma pack(1)
typedef struct _GDTR {
	short limit;
	int base;
}GDTR;
#pragma pop

typedef struct _GateDescriptor {
	unsigned int offset_low16 : 16;
	unsigned int selector : 16;
	unsigned int Param_count : 5;
	unsigned int res : 3;
	unsigned int type : 4;
	unsigned int s : 1;
	unsigned int dp1 : 2;
	unsigned int p : 1;
	unsigned int offset_hei16 : 16;
}GateDescriptor;

void Syscall() {
	//KdBreakPoint();
	DbgPrint("[My learning] %s \r\n", __FUNCTION__);
}
__declspec(naked) void SyscallProxy() {
	__asm {
		//int 3;
		push ebp;
		mov ebp, esp;
		//push fs;
		mov ax, 30h;
		mov fs, ax;
		call Syscall;
		mov esp, ebp;
		pop ebp;
		mov ax, 3bh;
		mov fs,ax;
		retf 0;
	}
}


void InstallGate() {
	//KdBreakPoint();
	DbgPrint("[My learning] %s \r\n", __FUNCTION__);
	GateDescriptor gate = { 0 };
	gate.selector = 0x8;
	gate.offset_low16 = (ULONG)SyscallProxy & 0xffff;
	gate.offset_hei16 = ((ULONG)SyscallProxy >> 16) & 0xffff;
	gate.Param_count = 0;
	gate.type = 0xc;
	gate.s = 0;
	gate.dp1 = 3;
	gate.p = 1;


	KAFFINITY mask = KeQueryActiveProcessors();
	KAFFINITY shift = 1;
	while (mask)
	{
		KeSetSystemAffinityThread(shift);
		GDTR gdt = { 0 };
		__asm sgdt  gdt;

		DbgPrint("[My learning] %s base:%p limit %p \r\n", __FUNCTION__, gdt.base, gdt.limit);

		GateDescriptor*pGate = (GateDescriptor*)gdt.base;

		if (MmIsAddressValid(pGate))
		{
			pGate[9] = gate;
		}
		shift <<= 1;
		mask >>= 1;
	}


}
void UnInstallGate() {
	//KdBreakPoint();
	DbgPrint("[My learning] %s \r\n", __FUNCTION__);


	KAFFINITY mask = KeQueryActiveProcessors();
	KAFFINITY shift = 1;
	while (mask)
	{
		KeSetSystemAffinityThread(shift);
		GDTR gdt = { 0 };
		__asm sgdt  gdt;
		DbgPrint("[My learning] %s base:%p limit %p \r\n", __FUNCTION__, gdt.base, gdt.limit);


		GateDescriptor*pGate = (GateDescriptor*)gdt.base;

		if (MmIsAddressValid(pGate))
		{
			pGate[9].p = 0;
		}
		shift <<= 1;
		mask >>= 1;
	}

}


//这个函数被注册用于驱动卸载调用
VOID myUnload(
	struct _DRIVER_OBJECT* DriverObject
) {
	UNREFERENCED_PARAMETER(DriverObject);

	DbgPrint("hello  drive unloaded");

	PDEVICE_OBJECT DeviceObject = DriverObject->DeviceObject;
	UnInstallGate();
	if (DriverObject->DeviceObject != NULL)
	{
		DbgPrint("驱动文件不为空执行删除");
		IoDeleteDevice(DeviceObject);


		UNICODE_STRING symbolDevName;
		RtlInitUnicodeString(&symbolDevName, L"\\DosDevices\\MytestDriver");
		IoDeleteSymbolicLink(&symbolDevName);
	}
}



//驱动被加载的时候会调用此函数
NTSTATUS
DriverEntry(
	_In_ struct _DRIVER_OBJECT* DriverObject,
	_In_ PUNICODE_STRING    RegistryPath
)
{
	//如果你没有用到参数需要告诉系统。
	UNREFERENCED_PARAMETER(RegistryPath);


	InstallGate();

	//打印信息
	DbgPrint("[My learning]  drive loaded");
	
	DriverObject->DriverUnload = myUnload;

	

	UNICODE_STRING ustrDevName;
	RtlInitUnicodeString(&ustrDevName, L"\\Device\\MytestDriver");
	PDEVICE_OBJECT  pDevObj = NULL;

	auto ret = IoCreateDevice(DriverObject, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDevObj);




	if (NT_SUCCESS(ret))
	{
		//指定IO模式
		pDevObj->Flags |= DO_DIRECT_IO;
		DbgPrint("IoCreateDevice 成功 \r\n");
	}
	else {
		DbgPrint("IoCreateDevice 失败 %d\r\n", ret);
		return STATUS_FAIL_CHECK;
	}


	UNICODE_STRING symbolDevName;
	RtlInitUnicodeString(&symbolDevName, L"\\DosDevices\\MytestDriver");
	ret = IoCreateSymbolicLink(&symbolDevName, &ustrDevName);
	if (NT_SUCCESS(ret))
	{
		DbgPrint("IoCreateSymbolicLink 成功 \r\n");
	}
	else {
		DbgPrint("IoCreateSymbolicLink 失败%d\r\n", ret);

		IoDeleteDevice(pDevObj);

		return STATUS_FAIL_CHECK;
	}

	return STATUS_SUCCESS;
}

最后ring3 层的调用代码

// ring3Demo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>


int main()
{



	__asm {
		_emit 0x9a;
		_emit 0x00;
		_emit 0x00;
		_emit 0x00;
		_emit 0x00;
		_emit 0x4b;
		_emit 0x00;
		
	}
	printf("调用完毕syscall");
	system("pause");
	return 0;
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/132870.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法笔记(25)win10系统安装tensorflow-GPU环境亲测好用

环境准备 首先你的电脑得有GPU显卡&#xff0c;然后在tensorflow官网&#xff08;在 Windows 环境中从源代码构建 | TensorFlow&#xff09;找到各安装软件对应的版本&#xff0c;版本不对应将会导致安装失败。 标本机GPU显卡版本题本文选择的是官网最新的组合&#xff1a;p…

cubeIDE开发, stm32窗口看门狗WWDG的CubeMX配置及HAL库底层实现分析

一、stm32的WWDG说明 1.1 WWDG特点&#xff1a; 在前一篇博文介绍独立看门狗时就指出STM32 MCU提供两个看门狗&#xff0c;独立看门狗和窗口看门狗。 cubeIDE开发&#xff0c; stm32独立看门狗IWDT的CubeMX配置及HAL库底层实现分析_py_free的博客-CSDN博客 相比独立看门狗&am…

【C语言】数据类型

文章目录c语言的类型整数整数类型整数的内部表达整数的范围unsigned整数的格式化浮点型浮点类型输入输出输出精度字符类型逃逸字符类型转换自动类型转换强制类型转换逻辑类型bool逻辑运算条件运算符C语言的变量&#xff0c;必须在使用前定义&#xff0c;并且确定类型&#xff1…

ubuntu下使用doxygen生成软件文档

ubuntu下使用Doxygen生成软件文档一、软件下载1.系统安装Doxygen2.Vscode插件安装二、软件配置三、文档生成1.方法1&#xff1a;使用Doxygen-gui生成&#xff08;推荐&#xff09;2.方法&#xff1a;采用Doxygen命令&#xff08;不推荐&#xff09;另外Windows下使用方法提示&a…

【JavaScript】 Date 日期对象概述及相关方法

文章目录【JavaScript】 Date 日期对象的创建及相关方法一. 日期对象的创建二. 日期对象的相关方法三. 时间戳案例案例1&#xff1a;在页面上展示一个时钟&#xff0c;隔1s更新一次案例2&#xff1a;距离除夕倒计时【JavaScript】 Date 日期对象的创建及相关方法 一. 日期对象…

ArcGIS基础:构建点对连线表达点集内部相互关系

原始数据如下&#xff0c;为普通的点图层&#xff0c;总共是21个点。 点位分布如下&#xff1a; 属性表打开如下&#xff1a; 下面使用【构造视线】工具进行操作&#xff0c;其工具位于【3D分析工具】下的【可见性】工具栏。 打开【构造视线】对话框&#xff0c;把【视点…

Leetcode【494. 目标和】

题目描述 给你一个整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘’ 或 ‘-’ &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 例如&#xff0c;nums [2, 1] &#xff0c;可以在 2 之前添加 ‘’ &#xff0c;在 1 之前添加 ‘-’ &…

第10章 角色页的分页、排序、查询实现

1 重构WebApi.Controllers.RoleController. PostRolePageByFromBodyAsync //把所有符合条件的角色实例&#xff0c;按照指定字段进行排序操作。 if (!string.IsNullOrEmpty(pagination.OrderByFiled)) { var _obj JsonConvert.DeserializeAnonymousType(pagination.OrderByFil…

力扣 2351. 第一个出现两次的字母

题目 给你一个由小写英文字母组成的字符串 s &#xff0c;请你找出并返回第一个出现 两次 的字母。 注意&#xff1a; 如果 a 的 第二次 出现比 b 的 第二次 出现在字符串中的位置更靠前&#xff0c;则认为字母 a 在字母 b 之前出现两次。 s 包含至少一个出现两次的字母。 …

TopK问题的必会解法

目录经典解法&#xff0c;创建K个大小的堆不考虑空间的暴力排序做法不考虑空间的快排partition变形减治法思想(核心&#xff1a;找第K大的数)空间有限放不下&#xff0c;海量数据的分治法经典解法&#xff0c;创建K个大小的堆 传统的直接建立一个K个元素的小顶堆&#xff0c;类…

推荐两款可以将图片无损放大的在线工具

文章目录一、waifu2x网站预览传送地址二、BigJPG网站预览传送地址好不容易找到合适的图片素材但尺寸太小&#xff1f;那这里的工具正好适合你&#xff0c;能把你的小图放大几倍都不模糊。 一、waifu2x 本程序使用卷积神经网络对动漫风格的图片进行放大操作&#xff08;支持照…

再聊一下那 SQLSERVER 行不能跨页的事

一&#xff1a;背景 1. 讲故事 上一篇写完了之后&#xff0c;马上就有朋友留言对记录行的 8060byte 限制的疑惑&#xff0c;因为他的表记录存储了大量的文章&#xff0c;存储文章的字段类型用的是 nvarchar(max)&#xff0c;长度很显然是超过 8060byte 的&#xff0c;请问这个…

win10搭建 IIS 服务器

第一步: 打开程序和功能 找到 Internet 这个 勾选web管理工具和万维网服务 第二步: 在本地电脑创建一个文件夹(不要有中文空格等特殊字符) index.html 是默认访问的网址 第三步 关联访问的链接 选择本地物理路径 访问测试 第四步 修改绑定端口 第五步 修改防火墙 …

Linux | 文件操作的系统调用 open() read() write()

1. 基本含义辨析 文件操作的系统调用 open read write close &#xff08;系统调用&#xff1a;实现在内核中【用户态->内核态】—“陷入内核”&#xff09; man&#xff1a; 1 命令 2 系统调用 3 库函数 C操作文件的库函数 fopen fread fwrite fclose &#xff08;fo…

一个有意思的图片鼠标切换

做淘宝活动页面的时候&#xff0c;用到最多的就是锚点&#xff0c;一个图片标签&#xff0c;然后不断地在上面画热区。不过我想问的是有多少人研究过&#xff0c;用矩形画热点&#xff0c;四个坐标值各自表示的含义&#xff0c;还有它和background-position有什么相同或相似的地…

JavaWeb:RequestResponse的概述

1&#xff0c;Request和Response的概述 Request是请求对象&#xff0c;Response是响应对象。 这两个对象在我们使用Servlet的时候有看到&#xff1a; 此时&#xff0c;我们就需要思考一个问题request和response这两个参数的作用是什么&#xff1f; request&#xff1a;获取请…

RapidUp: Multi-Domain Permutation Protocol for Lookup Tables学习笔记

1. 引言 前序博客有&#xff1a; PLOOKUPPLOOKUP代码解析Efficient polynomial commitment schemes for multiple points and polynomials学习笔记PLONK PLOOKUPPlonKup: Reconciling PlonK with plookupPLONK: permutations over lagrange-bases for oecumenical nonintera…

2022年就要过去了,我的这份成绩单请您查收……

喜迎元旦岁月不居&#xff0c;时节如流年关交迭在即在2022行将尾声的此时想静静地坐下片刻和关注了云和恩墨这么久的老朋友们细数一遍这光阴里的纷纷呈呈这一年&#xff0c;充满了挑战与艰辛但所有努力和守望终有结果收成这一年&#xff0c;我们全心投入产品研发收获了更多的荣…

【隔离器使用说明】光隔、磁隔、容隔三兄弟介绍

文章目录前言一、三种常用隔离技术1. 光隔离2. 变压器隔离/磁隔3. 电容隔离二、隔离器重要指标1. 隔离电压值2. 爬电距离3. 共模瞬变抗扰度CMTI三、隔离器性能对比总结前言 本文简单介绍常用隔离器的类型和特点&#xff0c;是根据网络知识整理出来的 有错误的话请一定评论留言…

谈谈SpringBoot

1. Spring Boot 简介 简化Spring应用开发的一个框架&#xff1b; 整个Spring技术栈的一个大整合&#xff1b; J2EE开发的一站式解决方案&#xff1b; 2. HelloWorld 功能&#xff1a;浏览器发送hello请求&#xff0c;服务器接受请求并处理&#xff0c;响应Hello World字符串&a…