Windows内核基础知识-5-调用门(32-Bit Call Gate)
Windows内核基础知识-5-调用门(32-Bit Call Gate)
调用门有一个关键的作用,就是用来提权。调用门其实就是一个段。
调用门:
这是段描述符的结构体,里面的s字段用来标记是代码段还是数据段还是系统段,前面解析的时候讲的是 S==1的情况,也就是Code or data的情况,这次的调用门就是当s==0时的情况。
当s==0时,type的内容如下:
那么调用门其实就是 type为12的情况下的一个段寄存器。
32-Bit Call Gate。32位情况下的一个调用门。
就是一个 段描述符下且S==0 && type==12的一个段。
调用门的段描述符:
利用调用门提权
我们可以通过构造段选择子和段描述符来自己添加一个调用门也就是添加一个段来自己使用。
因为段寄存器无非就是拥有一个起始地址然后作为一个段的内容来让你使用。用汇编写过代码的肯定知道如果构造一个段:
sample PROC
ret
sample ENDP
那么我们自己构造一个系统段来执行我们的代码这不就行了吗。段寄存器只是系统默认的方便我们使用,我们自己通过 segment:eip这样来跳转使用不就完事了吗。
段寄存器比如说 ss(stack segment)无法也就是把栈段的内容通过段选择子和段描述符来集成了而已。
所以这里我们直接添加自己的段,也就是这里所谓的调用门。
解析调用门描述符:
我们要自己配一个调用们,肯定需要知道怎么配,段选择子我们知道了,这里还需要知道段描述符:
这个和前面的段寄存器的段描述符大相径庭,只是有一些不一样。
和前面的段描述符一样,上面的是高32位地址,下面的是低32位地址。
字段 | 内容 |
---|---|
offset in segment | 要跳转的函数的地址,或者是要跳转的地址 |
segment selector | 段选择子,要变成的段选择子(提权的关键) |
Param Count | 函数参数个数 |
高位5-7 | 固定的三个0 |
Type | 系统段只能是1100(10进制的12) |
高12地址 | 就是段描述符的S字段,就系统调用的必须是0 |
DPL | 肯定赋值为3呀,这样ring3才能。 |
p | 和段描述符一样表示该段是否有效,当P为0时无效,1时有效。 |
构造段描述符:
segment selector段描述符字段,可以通过WinDbg附加Windows双机调试时查看cs段寄存器的值就行了,因为此时系统正在启动肯定是在r0的情况下的:
所以这里的段选择子就构造为: 0008(因为段选择子是两个字节16位)
然后再根据前面的解析我们目前的构造是这样的:
高32位:
0-3:
0(十六进制)
4-7:
0(十六进制)
8-11:
C(十六进制)
12-15:
E(十六进制)
16-31:
函数地址的高地址:xxxx
低32:
0-15:
函数地址的低地址:xxxx
16-31:
0008
合集:
xxxxEC000008xxxx
目前就是函数地址需要解决,这个函数地址的话其实就是我们的代码的起始地址,然后就处理这个函数的内容了。
我们可以写一个函数不就完了嘛,但是需要修改成固定基址,这样函数地址就不会改变了。需要采用release版本,因为debug版本有个jmp,然后还得修改优化和随机基址:
#include<iostream>
#include<Windows.h>
using namespace std;
void _declspec(naked) test()
{
_asm
{
//这里我们访问一个0环才能访问的地址
//这样就知道是否是拿到了0环的权限
push eax
mov eax,0x80b95040
mov eax,[eax]
pop eax
ret
}
}
int main()
{
printf("%x\n", test);//输出函数的地址
system("pause");
return 0;
}
这里我的函数地址是:00401080。
所以完整的段描述符值为:
0040EC0000081080
将段描述符添加到gdt表:
这个段描述就到了gdt表偏移9的地方了。
使用调用门
前面调用们的段描述符我们已经配置好了。
现在需要使用调用门了,需要学习两个指令:
call far ;跨段调用 长调用
jmp far ;跨段跳转 长跳转
调用门只能采用call far,jmp far无法,因为jmp far无法做到越级这个作用。
然后还要配一个段选择子:
RPL:
00//采用0环
TI:
0
Index
1001
0100 1011
最终的段选择子就是:0x48
那么应该call far的地址为: 0x48:xxxx
但是在内联汇编里不能这样写,只能这样写:
BYTE code[] = {0,0,0,0,0x48,0};
//0,0,0,0 是eip,然后0x0048是段选择子,采用的是小端字节序
//这里用0000是因为这里不会用到
//因为段描述符里面已经指定了函数的地址了会自动跳转,所以写啥都行
_asm
{
call far fwrod ptr code
}
完整代码:
#include<iostream>
#include<Windows.h>
using namespace std;
void _declspec(naked) test()
{
_asm
{
//这里我们访问一个0环才能访问的地址
//这样就知道是否是拿到了0环的权限
push eax
mov eax,0x80b95040
mov eax,[eax]
pop eax
ret
}
}
int main()
{
//跳转
BYTE code[] = {0,0,0,0,0x48,0};
//段选择子为 0000
_asm
{
call far fwrod ptr code
}
system("pause");
return 0;
}
然后拿到虚拟机里运行一下:
直接系统出错WinDbg捕获了并且蓝屏了,但是至少有一个可以确定,就是我们肯定是跑到内核去执行了,不然怎么会导致系统出错呢,r3的应用层代码肯定不会导致系统的问题的。
就出现问题很正常,打几个断点观察下:
#include<iostream>
#include<Windows.h>
using namespace std;
void _declspec(naked) test()
{
_asm
{
//这里我们访问一个0环才能访问的地址
//这样就知道是否是拿到了0环的权限
int 3
push eax
mov eax, 0x80b95040
mov eax, [eax]
pop eax
ret
}
}
int main()
{
//跳转
BYTE code[6] = {0,0,0,0,0x48,0};
//段选择子为0x0048
__asm
{
call far fword ptr code
}
printf("%x\n", test);
system("pause");
return 0;
}
通过单步调试汇编代码发现是,这个ret的原因,call far或者jmp far,需要采用retf来使用。
改成retf:
#include<iostream>
#include<Windows.h>
using namespace std;
void _declspec(naked) test()
{
_asm
{
//这里我们访问一个0环才能访问的地址
//这样就知道是否是拿到了0环的权限
int 3
push eax
mov eax, 0x80b95040
mov eax, [eax]
pop eax
retf
}
}
int main()
{
//跳转
BYTE code[6] = {0,0,0,0,0x48,0};
//段选择子为0x0048
__asm
{
call far fword ptr code
}
printf("%x\n", test);
system("pause");
return 0;
}
这样之后我们函数内的代码是正常执行了,但是还是蓝屏了,通过我的观察,是段寄存器的问题,这里就通过od的观察看进入前和进入后的问题就可以判断是这个问题了。
所以这里我直接不要这个int 3断点了,可能在r0和r3下的int 3断点执行的东西不一样把:
#include<iostream>
#include<Windows.h>
using namespace std;
void _declspec(naked) test()
{
_asm
{
//这里我们访问一个0环才能访问的地址
//这样就知道是否是拿到了0环的权限
push eax
mov eax, 0x80b93040
mov eax, [eax]
pop eax
retf
}
}
int main()
{
//跳转
BYTE code[6] = {0,0,0,0,0x48,0};
//段选择子为0x0048
__asm
{
call far fword ptr code
}
printf("%x\n", test);
system("pause");
return 0;
}
这样我们的程序就可以美美的执行结束了:
小结
段是一个很重要的概念用来进行内存分割,一个段的描述有很多信息保存在段描述符里面,由于段很多所有就有段描述符表,然后这个表呢由一个段选择子来指向获取表内哪一个段描述符,而段寄存器是CPU为了方便使用的一个寄存器,用来保存的是段选择子这个东西。所以这里我们用的是一个系统调用段来执行我们的指令就是操作系统内部的操作所允许也是intel架构的内容,比较复杂,这里的话我们就明白到这里就可以了想深入的可以阅读本博客后面的参考文献。
参考文献:(3条消息) 段选择符 段寄存器farmwang的专栏-CSDN博客段寄存器
参考文献:(3条消息) 段选择符tasty-CSDN博客段选择符
Windows内核基础知识-5-调用门(32-Bit Call Gate)的更多相关文章
- Windows内核基础知识-1-段寄存器
Windows内核基础知识-1-段寄存器 学过汇编的应该都知道段寄存器,在Windows里段寄存器有很多,之前可能只接触了ds数据段,cs 代码段这种,今天这个博客就介绍Windows一些比较常用的段 ...
- Windows内核基础知识-2-段描述符
Windows内核基础知识-2-段描述符 比如: ES 002B 0(FFFFFFFF) 意思就是es段寄存器,段选择子/段选择符 为002B, 起始地址base为0, 限制范围Limit地址最大能寻 ...
- Windows内核基础知识-8-监听进程、线程和模块
Windows内核基础知识-8-监听进程.线程和模块 Windows内核有一种强大的机制,可以在重大事件发送时得到通知,比如这里的进程.线程和模块加载通知. 本次采用链表+自动快速互斥体来实现内核的主 ...
- windows内核基础与异常处理
前两日碰到了用异常处理来做加密的re题目 所以系统学习一下windows内核相关 windows内核基础 权限级别 内核层:R0 零环 核心态工作区域 大多数驱动程序 应用层:R3 用户态工作区域 只 ...
- 第0章Linux环境到内核基础知识
#include<stdio.h> int main(void) { printf("hello world\n"); return 0; } gcc -g -wall ...
- Windows内核原理-同步IO与异步IO
目录 Windows内核原理-同步IO与异步IO 背景 目的 I/O 同步I/O 异步I/O I/O完成通知 总结 参考文档 Windows内核原理-同步IO与异步IO 背景 在前段时间检查异常连接导 ...
- 学习 FPGA之前的基础知识
在学习一门技术之前往往应该从它的编程语言入手,比如学习单片机时,往往从汇编或者C语言入门.所以不少开始接触FPGA的开发人员,往往是从VHDL或者Verilog开始入手学习的.但小编认为,若能先结合& ...
- Windows内核开发-3-内核编程基础
Windows内核开发-3-内核编程基础 这里会深入讲解kernel内核的API.结构体.和一些定义.考察代码在内核驱动中运行的机制.最后把所有知识合在一起写一个有用的驱动. 本章学习要点: 1:通用 ...
- Windows核心编程 第六章 线程基础知识 (上)
第6章 线程的基础知识 理解线程是非常关键的,因为每个进程至少需要一个线程.本章将更加详细地介绍线程的知识.尤其是要讲述进程与线程之间存在多大的差别,它们各自具有什么作用.还要介绍系统如何使用线程内核 ...
随机推荐
- 免费iApp后台-云接口
免费稳定,UI易懂简洁,功能强大 应用名称:云接口 应用版本:1.5.9 应用大小:3.55 MB 适用平台:Android(安卓) 应用用处:详情请下载软件 软件安全无毒 更新内容: 1.支付宝当面 ...
- Jmeter系列(19)- 常用配置文件
JMeter.properties :跟Jmeter配置相关的配置信息都在这边,比如:Jmeter GUI页面的语言.日志级别设置等 User.properties:用户自定义相关的所有变量,会复写J ...
- uni-app跨平台框架介绍和快速入门
前言: 首先今天主要介绍的是一个多平台的前端框架uni-app,关于多平台的前端框架网上有很多成熟的解决方案比如说Taro,React Native,Flutter等这些都是一些非常优秀的前端跨平台的 ...
- 接口Mock测试
什么是Mock测试? Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取的比较复杂的对象(如 JDB ...
- HTML在网页上不能显示图片问题
我遇到的问题是写了一个HTML程序,结果在网页上面不能显示,原因是图片路径放置错了. 修改前代码: <!DOCTYPE html> <html> <head> &l ...
- 51nod1355-斐波那契的最小公倍数【min-max容斥】
正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1355 题目大意 定义\(f_i\)表示斐波那契的第\(i\)项,给出一个 ...
- Docker小白到实战之Docker网络简单了解一下
前言 现在对于Docker容器的隔离性都有所了解了,但对容器IP地址的分配.容器间的访问等还是有点小疑问,如果容器的IP由于新启动导致变动,那又怎么才能保证原有业务不会被影响,这就和网络有挂钩了,接下 ...
- Unity Event Trigger 事件响应(二维,三维)添加组件
EventTrigger 上主要的方法有PointerEnter.PointerExit.PointerDown.PointerUp.PointerClick............都会显示在面板上面 ...
- typora博客笔记上传图片时不能显示
前言 markdown具有轻量化.易读易写等特性,并且对于图片.超链接.图片.数学公式都有支持. 但是最近在使用Typora的过程中我发现,在写文章笔记的时候导入的图片,因为图片保存在我们电脑本地,当 ...
- UDP接收端和发送端_Socket编程
UDP接收端 接收端启动文件 1 import java.net.DatagramSocket; 2 import java.net.SocketException; 3 4 public class ...