当前位置: 代码迷 >> 综合 >> [保护模式]调用门
  详细解决方案

[保护模式]调用门

热度:71   发布时间:2023-12-21 20:19:14.0

文章目录

    • 调用门
      • 调用门执行流程
      • 调用门描述符
    • 调用门实验
      • 无参调用门
        • 示例代码
        • 构造调用门
        • 修改GDT表
        • 执行流程
        • 中断现场
      • 有参调用门
        • 示例代码
        • 构造调用门
        • 修改GDT表
        • 中断现场
      • 总结

调用门

调用门执行流程

CALL FAR的指令格式:CALL CS:EIP(EIP是废弃的)

执行步骤:

  1. 根据CS的值 查GDT表,找到对应的段描述符,这个描述符是一个调用门
  2. 在调用门描述符中存储另一个代码段的段选择子
  3. 选择子指向的段 段的Base+偏移 就是真正要执行的地址

调用门描述符

调用门描述符如图:

在这里插入图片描述

调用门实验

无参调用门

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>void __declspec(naked)GetRegister()
{__asm{int 3;retf;}
}int main()
{__asm{int 3;}printf("%p", GetRegister);//构造call cs eip指令char buff[6];*(DWORD*)&buff[0] = 0x12345678;	//废弃的EIP*(WORD*)&buff[4] = 0x48;		//段选择子		__asm{call fword ptr[buff];}getchar();return 0; 
}

构造调用门

首先查看函数地址

在这里插入图片描述

记录下这个值:00401040

offset in segment 31:16:0040
P:1 DPL:11 S:0--->E
Type:1100--->C
Param Count:0
Segment Select:0008
offset in Segment 15:00:1040
#调用门描述符
0040EC00`00081040

修改GDT表

查看GDT表

kd> dq gdtr
80b95000  00000000`00000000 00cf9b00`0000ffff
80b95010  00cf9300`0000ffff 00cffb00`0000ffff
80b95020  00cff300`0000ffff 80008b1e`400020ab
80b95030  834093f7`1c003748 0040f300`00000fff
80b95040  0000f200`0400ffff 00000000`00000000
80b95050  830089f6`f0000068 830089f6`f0680068
80b95060  00000000`00000000 00000000`00000000
80b95070  800092b9`500003ff 00000000`00000000

80b95048这个位置并没有用到,我们就将构造好的调用门放到这个位置,然后查看修改后的结果

kd> eq 80b95048 0040EC00`00081040
kd> dq gdtr
80b95000  00000000`00000000 00cf9b00`0000ffff
80b95010  00cf9300`0000ffff 00cffb00`0000ffff
80b95020  00cff300`0000ffff 80008b1e`400020ab
80b95030  834093f7`1c003748 0040f300`00000fff
80b95040  0000f200`0400ffff 0040ec00`00081040
80b95050  830089f6`f0000068 830089f6`f0680068
80b95060  00000000`00000000 00000000`00000000
80b95070  800092b9`500003ff 00000000`00000000

执行流程

接下来捋一遍整个调用门的执行流程

	char buff[6];*(DWORD*)&buff[0] = 0x12345678;	//废弃的EIP*(WORD*)&buff[4] = 0x48;		//段选择子 __asm{
    call fword ptr[buff];}

当代码执行到call fword ptr[buff];时,其实就相当于执行下面这句

call 0x48:0x12345678

首先拆分0x48这个段选择子

?01001000?
index:9
TI:0
PRL:0

接着查IDT表的第十项 也就是我们构造的调用门。

进行段权限检查,检查通过,切换CS段寄存器提升权限,并跳转到段的Base+Offset的位置。

中断现场

将程序编译后放到虚拟机运行,此时windbg第一次产生中断,这个时候中断在用户层

kd> g
Break instruction exception - code 80000003 (first chance)
001b:00401056 cc              int     3

查看一下用户层的寄存器环境

kd> r
eax=00203340 ebx=7ffd9000 ecx=3d9c2800 edx=00000002 esi=74c62108 edi=002040b0
eip=00401056 esp=0012ff38 ebp=0012ff40 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
001b:00401056 cc              int     3
  • EIP=00401056
  • esp=0012ff38
  • cs=001b
  • ss=0023

接着再次运行程序,此时windbg第二次产生中断,这个时候中断于内核层

kd> g
Break instruction exception - code 80000003 (first chance)
00401040 cc              int     3

再次查看当前的寄存器环境

kd> r
eax=00000048 ebx=7ffd9000 ecx=74c19a18 edx=74c610a4 esi=74c62108 edi=002040b0
eip=00401040 esp=a74ffca0 ebp=0012ff40 iopl=0         nv up ei pl nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000202
00401040 cc              int     3
  • eip=00401040 ->我们指定的偏移
  • cs=0008->我们指定的零环的段选择子
  • ss=0010 ->零环的ss段寄存器
  • esp=a74ffca0 ->零环的堆栈

接着再查看一下当前的ESP

kd> dd esp
a74ffca0  0040107c 0000001b 0012ff38 00000023
a74ffcb0  00000000 00000000 00000000 00000000
a74ffcc0  0000027f 00000000 00000000 00000000
a74ffcd0  00000000 00000000 00001f80 0000ffff
a74ffce0  00000000 00000000 00000000 00000000
a74ffcf0  00000000 00000000 00000000 00000000
a74ffd00  00000000 00000000 00000000 00000000
a74ffd10  00000000 00000000 00000000 00000000
  • 0040107:三环的返回地址
  • 0000001b:三环段选择子
  • 0012ff38:三环的ESP
  • 00000023:三环的SS段寄存器

有参调用门

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>void __declspec(naked)GetRegister()
{__asm{int 3;retf;}
}int main()
{__asm{int 3;}printf("%p", GetRegister);//构造call cs eip指令char buff[6];*(DWORD*)&buff[0] = 0x12345678;		//废弃的EIP*(WORD*)&buff[4] = 0x48;			//段选择子		__asm{//有参数的时候 参数需要自己指定push 1;push 2;push 3;call fword ptr[buff];}getchar();return 0; 
}

构造调用门

offset in segment 31:16:0040
P:1 DPL:11 S:0--->E
Type:1100--->C
Param Count:3
Segment Select:0008
offset in Segment 15:00:1040
#调用门描述符
0040EC03`00081040

Param Count参数个数修改为3 其他不变

修改GDT表

kd> eq 80b95048 0040EC03`00081040
kd> dq gdtr
80b95000  00000000`00000000 00cf9b00`0000ffff
80b95010  00cf9300`0000ffff 00cffb00`0000ffff
80b95020  00cff300`0000ffff 80008b1e`400020ab
80b95030  834093f7`1c003748 7f40f3fd`f0000fff
80b95040  0000f200`0400ffff 0040ec03`00081040
80b95050  830089f6`f0000068 830089f6`f0680068
80b95060  00000000`00000000 00000000`00000000
80b95070  800092b9`500003ff 00000000`00000000

中断现场

编译后在虚拟机中运行,此时windbg第一次中断,这个时候中断在用户层,查看一下当前的寄存器环境

kd> g
Break instruction exception - code 80000003 (first chance)
001b:00401056 cc              int     3
kd> r
eax=00203340 ebx=7ffda000 ecx=8966fe00 edx=00000002 esi=74c62108 edi=002040b0
eip=00401056 esp=0012ff38 ebp=0012ff40 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
001b:00401056 cc              int     3
  • eip=00401056
  • esp=0012ff38
  • cs=001b
  • ss=0023

接着再次运行,程序第二次中断,此时中断在内核层,并查看当前的寄存器环境

kd> r
eax=00000048 ebx=7ffda000 ecx=74c19a18 edx=74c610a4 esi=74c62108 edi=002040b0
eip=00401040 esp=8d303c94 ebp=0012ff40 iopl=0         nv up ei pl nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000202
00401040 cc              int     3
  • eip=00401040–>我们指定的Offset
  • esp=8d303c94–>零环的堆栈
  • cs=0008–>我们指定的零环的段选择子
  • ss=0010–>零环的SS段寄存器

接着再查看一下当前的堆栈

kd> dd esp
8d303c94  00401082 0000001b 00000003 00000002
8d303ca4  00000001 0012ff2c 00000023 00000000
8d303cb4  00000000 00000000 00000000 0000027f
8d303cc4  00000000 00000000 00000000 00000000
8d303cd4  00000000 00001f80 0000ffff 00000000
8d303ce4  00000000 00000000 00000000 00000000
8d303cf4  00000000 00000000 00000000 00000000
8d303d04  00000000 00000000 00000000 00000000
  • 00401082为三环的返回地址
  • 0000001b为三环的CS段选择子
  • 00000003 00000002 00000001为我们传递的三个参数
  • 0012ff2c为三环的ESP
  • 00000023为三环的SS段寄存器

总结

  1. 当通过调用门,权限提升到零环时,会Push四个值:返回地址 CS ESP SS
  2. 新的CS值由调用门决定
  3. 新的SS和ESP由TSS提供