保护模式(四)长调用与短调用 调用门

前言

一、学习自滴水编程达人中级班课程,官网:https://bcdaren.com
二、海东老师牛逼! 三、本文转自https://blog.youkuaiyun.com/qq_41988448/article/details/102592866

要点回顾

通过JMP FAR可以实现段间的跳转,如果要实现跨段的调用就必须要学习CALL FAR,也就是长调用

CALL FARJMP FAR 要复杂,JMP并不影响堆栈,但CALL指令会影响


长调用与短调用

一、短调用

指令格式:

CALL 立即数 / 寄存器 / 内存

短调用
发生改变的寄存器:ESP EIP

二、长调用(跨段不提权)

指令格式:

CALL CS:EIP(EIP是废弃的)

长调用_跨段不提权
发生改变的寄存器:ESP EIP CS

三、长调用(跨段并提权)

指令格式:

CALL CS:EIP(EIP是废弃的)

长调用执行时:

长调用_跨段并提权
发生改变的寄存器:ESP EIP CS SS

注意:长调用执行后,堆栈已经不是原来的堆栈,而是0环的堆栈(ESP0)

执行返回(RETF)时:

长调用RETF
发生改变的寄存器:ESP EIP CS SS

总结

  1. 跨段调用时,一旦有权限切换,就会切换堆栈

  2. CS的权限一旦改变,SS的权限也要随着改变,CS与SS的等级必须一样

  3. JMP FAR 只能跳转到同级非一致代码段,但CALL FAR可以通过调用门提权,提升CPL的权限

SS与ESP从哪里来?参见TSS段

调用门

指令格式:

CALL CS:EIP(EIP是废弃的)

执行步骤:

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

门描述符

结构图:
门描述符
注意

  1. S位(第12位)必须为0,只有当S位为0时,段描述符才是系统段描述符;此时,当Type域为1100时,该描述符是门描述符
  2. 低四字节的16~31位是决定 调用的代码存在于哪个段段选择子
  3. 当长调用执行时:真正要执行的代码地址=门描述符中段选择子所指向的代码段的Base+门描述符高四字节的16~31位+门描述符低四字节的0~15位

调用门(无参)

实验:构造一个无参调用门
第一步:初步构造参数
Offset in Segment 31:16 = 0x0000		//暂定
					  P = 1
					DPL = 二进制:11
			Param.Count = 二进制:00000
	   Segment Selector = 0x0008
Offset in Segment 15:00 = 0x0000		//暂定

由上述参数构造出的门描述符为:0000EC00`00080000

第二步:确定 Offset in Segment

在VC6中执行如下代码并中断

#include "stdafx.h"
#include <windows.h>

void __declspec(naked) GetRegister()
{
__asm
{
int 3

	retf		// 注意返回,不能是ret
}

}

int main(int argc, char* argv[])
{
char buff[6];
(DWORD)&buff[0] = 0x12345678; // 在这行设置断点
(WORD)&buff[4] = 0x48; // 段选择子所在偏移
__asm
{
call fword ptr[buff] // 长调用
}
getchar();
return 0;
}

右键进入反汇编窗口,查看GetRegister函数起始地址,我这里是00401010
确定函数地址
至此,门描述符的最终确定为:0040EC00`00081010

第三步:将门描述符写入GDT表

GDT写入门描述符

第四步:继续执行第二步代码

运行后,虚拟机成功中断至WinDbg
WinDbg中断

第五步:再次运行程序,观察寄存器与堆栈的变化

长调用执行前
长调用执行前
长调用执行后

寄存器:
长调用执行后寄存器
堆栈:
长调用执行后堆栈
结果和需求完全一致,调用门执行成功!

第三步:修改代码

将代码修改为如下:

#include <windows.h>

BYTE GDT[6] = {0};
DWORD dwH2GValue;

void __declspec(naked) GetRegister()
{
__asm
{
pushad
pushfd

	mov eax,0x8003f00c	// 读取高2G内存
	mov ebx,[eax]
	mov dwH2GValue,ebx
	sgdt GDT;			// 读取GDT

	popfd
	popad

	retf
}

}

void PrintRegister()
{
DWORD GDT_ADDR = *(PDWORD)(&GDT[2]);
WORD GDT_LIMIT = *(PWORD)(&GDT[0]);

printf("%x %x %x \n", dwH2GValue, GDT_ADDR, GDT_LIMIT);

}

int main(int argc, char* argv[])
{
__asm
{
mov ebx,ebx
mov ebx,ebx
}

char buff[6];
*(DWORD*)&amp;buff[0] = 0x12345678;
*(WORD*)&amp;buff[4] = 0x48;	// segment select

__asm
{
	call fword ptr[buff]
}
PrintRegister();
getchar();
return 0;

}

注意:若GetRegister函数起始地址发生改变,则需要修调相应的门描述符

执行结果:
执行结果
成功读取了GDT,提权成功!


修正:sgdt命令实际上并不需要0环权限【大写尴尬】

调用门(有参)

实验:构造一个带参数的调用门
第一步:初步构造参数
Offset in Segment 31:16 = 0x0000		// 暂定
					  P = 1
					DPL = 二进制:11
			Param.Count = 二进制:00011	// 注意变化!
	   Segment Selector = 0x0008
Offset in Segment 15:00 = 0x0000		// 暂定

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
第二步:执行代码

代码如下:

#include <windows.h>

DWORD x;
DWORD y;
DWORD z;

void __declspec(naked) CateProc()
{
__asm
{
pushad
pushfd

	mov eax,[esp+0x24+8+8]
	mov dword ptr ds:[x],eax
	mov eax,[esp+0x24+8+4]
	mov dword ptr ds:[y],eax
	mov eax,[esp+0x24+8+0]
	mov dword ptr ds:[z],eax

	popfd
	popad

	retf 0xC			// 注意堆栈平衡 写错蓝屏
}

}

void PrintRegister()
{
printf("%x %x %x \n", x, y, z);
}

int main(int argc, char* argv[])
{
char buff[6];
(DWORD)&buff[0] = 0x12345678;
(WORD)&buff[4] = 0x48;
__asm
{
push 1 // 参数1
push 2 // 参数2
push 3 // 参数3
call fword ptr[buff]
}
PrintRegister();
getchar();
return 0;
}

注意:需要将门描述符的段偏移修改为CateProc函数的起始地址

执行结果:
门描述符(有参)
传入的参数被成功输出,有参调用门构造成功!

思考:pushad、pushfd、popfd、popad 这几条指令有什么意义?是必须的吗?

总结

  1. 当通过门,权限不变的时候,只会PUSH两个值:CS返回地址,新的CS的值由调用门决定
  2. 当通过门,权限改变的时候,会PUSH四个值:SSESPCS返回地址,新的CS的值由调用门决定,新的SS和ESP由TSS提供
  3. 通过门调用时,要执行哪行代码由调用门决定;但使用RETF返回时,由堆栈中压入的值决定;这就是说,进门时只能按指定路线走,出门时可以翻墙(只要改变堆栈里面的值就可以想去哪去哪)
  4. 问:可不可以再建个门出去呢?也就是再用CALL出去
    答:当然可以了,前"门"进,后"门"出
需要练习:建一个门进去,再门里面再建一个门出去
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值