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)的更多相关文章

  1. Windows内核基础知识-1-段寄存器

    Windows内核基础知识-1-段寄存器 学过汇编的应该都知道段寄存器,在Windows里段寄存器有很多,之前可能只接触了ds数据段,cs 代码段这种,今天这个博客就介绍Windows一些比较常用的段 ...

  2. Windows内核基础知识-2-段描述符

    Windows内核基础知识-2-段描述符 比如: ES 002B 0(FFFFFFFF) 意思就是es段寄存器,段选择子/段选择符 为002B, 起始地址base为0, 限制范围Limit地址最大能寻 ...

  3. Windows内核基础知识-8-监听进程、线程和模块

    Windows内核基础知识-8-监听进程.线程和模块 Windows内核有一种强大的机制,可以在重大事件发送时得到通知,比如这里的进程.线程和模块加载通知. 本次采用链表+自动快速互斥体来实现内核的主 ...

  4. windows内核基础与异常处理

    前两日碰到了用异常处理来做加密的re题目 所以系统学习一下windows内核相关 windows内核基础 权限级别 内核层:R0 零环 核心态工作区域 大多数驱动程序 应用层:R3 用户态工作区域 只 ...

  5. 第0章Linux环境到内核基础知识

    #include<stdio.h> int main(void) { printf("hello world\n"); return 0; } gcc -g -wall ...

  6. Windows内核原理-同步IO与异步IO

    目录 Windows内核原理-同步IO与异步IO 背景 目的 I/O 同步I/O 异步I/O I/O完成通知 总结 参考文档 Windows内核原理-同步IO与异步IO 背景 在前段时间检查异常连接导 ...

  7. 学习 FPGA之前的基础知识

    在学习一门技术之前往往应该从它的编程语言入手,比如学习单片机时,往往从汇编或者C语言入门.所以不少开始接触FPGA的开发人员,往往是从VHDL或者Verilog开始入手学习的.但小编认为,若能先结合& ...

  8. Windows内核开发-3-内核编程基础

    Windows内核开发-3-内核编程基础 这里会深入讲解kernel内核的API.结构体.和一些定义.考察代码在内核驱动中运行的机制.最后把所有知识合在一起写一个有用的驱动. 本章学习要点: 1:通用 ...

  9. Windows核心编程 第六章 线程基础知识 (上)

    第6章 线程的基础知识 理解线程是非常关键的,因为每个进程至少需要一个线程.本章将更加详细地介绍线程的知识.尤其是要讲述进程与线程之间存在多大的差别,它们各自具有什么作用.还要介绍系统如何使用线程内核 ...

随机推荐

  1. mysql 基础配置经验

    创建库: 排序:utf8_unicode_ci和utf8_general_ci对中.英文来说没有实质的差别.utf8_general_ci校对速度快,但准确度稍差. 普遍的意思utf8_unicode ...

  2. 小学生都能读懂的网络协议之:WebSocket

    目录 简介 webSocket vs HTTP HTTP upgrade header websocket的优点 webScoket的应用 websocket的握手流程 WebSocket API 总 ...

  3. Java基础系列(12)- 运算符

    运算符 算数运算符:+ - * / % ++ -- 赋值运算符:= += -= *= /= 关系运算符:> < >= <= == != instanceof 逻辑运算符:&am ...

  4. Shell系列(39) - dirname

    dirname作用 dirname实际工作中常常是跟$0一起使用 用于获取当前运行脚本的绝对路径 这个命令要放在shell脚本中使用,在命令行使用意义不大 [yutao@master01 ~]$ di ...

  5. LeetCode2-链表两数和

    目录 LeetCode2-链表两数和 题目描述 示例提示 经验教训 参考正解 题目描述 示例提示 经验教训 链表题的判空条件不是万能的,有时候示例会极其复杂,根本难以通过判空来区分不同情况. /** ...

  6. Spring,AOP实现功能级别权限验证

    1. 首先是问题出现的原因 对于一个我的一个个人博客网站,我希望游客可以浏览我的博客,但是评论功能是需要登录才能使用 这就需要对某个功能进行权限验证 对于过滤器,拦截器,AOP的区别日后再讨论,现在是 ...

  7. 聊聊并发(一)——初始JUC

    一.volatile 1.介绍 JDK 5.0 提供了java.util.concurrent包,在此包中增加了并发编程中很常用的使用工具类,用于定义类似于线程的自定义子系统,包括线程池.异步IO和轻 ...

  8. 理解hashMap

    首先需要理解几个基本概念: 什么是数据结构?(摘自 java数据结构系列--什么是数据结构 (baidu.com)) 数据结构是计算机组织.存储数据的方式.简单来说就是,数据按指定的规则进行存储,从而 ...

  9. 从0到1使用Kubernetes系列——Kubernetes入门

    基本概念 Docker 是什么 Docker 起初是 dotCloud 公司创始人 Solomon Hykes 在法国的时候发起的一项公司内部项目,Docker 是基于 dotCloud 公司多年云服 ...

  10. cadvisor+prometheus+alertmanager+grafana完成容器化监控告警(一)

    一.概况 1.拓扑图 2.名词解释 Grafana 可视化监控容器运行情况 Prometheus: 开源系统监视和警报工具包 Alertmanager 一个独立的组件,负责接收并处理来自Prometh ...