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. POJ2251——Dungeon Master(三维BFS)

    和迷宫问题区别不大,相比于POJ1321的棋盘问题,这里的BFS是三维的,即从4个方向变为6个方向. 用上队列的进出操作较为轻松. #include<iostream> #include& ...

  2. jquery .play()报错is not a function

    报错原因:play()方法属于DOM对象方法,$('#audio')为jquery对象解决办法:将jquery对象转换为DOM对象首先打印jquery对象$('#audio') 两种转换方式将一个jQ ...

  3. 根据类拼凑成url参数

    /// <summary>        /// 根据类拼凑成url参数        /// </summary>        /// <typeparam name ...

  4. Java基础系列(38)- 数组的使用

    数组的使用 For-Each循环 数组作方法入参 数组作返回值 For-Each循环 普通型 package array; import sun.security.util.Length; publi ...

  5. Shell系列(39) - dirname

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

  6. win10 移动端 android 测试环境搭建

    一.移动端自动化测试的基础环境配置1:安装Java环境 关于安装Java环境以及相关环境变量的配置在我之前的博文分享中已有详细介绍,有需要的可以直接查找翻阅,这里就不再一一介绍. 二.移动端自动化测试 ...

  7. 为什么说Mysql预处理可以防止SQL注入

    简单点理解:prepareStatement会形成参数化的查询,例如:1select * from A where tablename.id = ?传入参数'1;select * from B'如果不 ...

  8. Fabric SSH链接时关于找不到主机的问题

    先上一段简单的fabric代码: from __future__ import with_statementfrom fabric.api import *env.user = 'zhangsan'e ...

  9. c++ 的学习 第二集函数的重载

    1. 规则 函数名相同参数个数不同.参数类型不同.参数顺序不同 2. 注意 返回值类型与函数重载无关 调用函数时,实参的隐式类型转换可能会产生二义性 返回值类型与函数重载无关 什么意思? 返回 ...

  10. Ubuntu20.04安装 maven并配置阿里源

    Ubuntu20.04安装 maven并配置阿里源 sudo apt update sudo apt install maven #安装maven,默认安装路径为/usr/share/maven 添加 ...