栈帧结构的恶意利用解析

一、不直接访问变量本身而修改变量数据

【示例代码】

#include <stdio.h>
#include <windows.h>

void test(int x, int y) {
	int* p = &x;
	p++;
	printf("before: %d\n", *p);

	*p = 30;
	printf("after: %d\n", y);
}

int main() {
	int a = 10;
	int b = 20;

	test(a, b);

	system("pause");
	return 0;
}

【图解】

【解析】

如上图所示,当调用 test(a, b) 时,参数 x 和 y 会被压入栈中(顺序取决于调用约定,C/C++ 的默认约定 __cdecl 是从右往左压参的)。指针变量 p 首先指向 x 的地址,然后执行 p++ 向高地址移动4个字节指向到了 y 的地址,此时 *p 即为 y 的值20,接着执行 *p=30,间接修改 y 的值为30。

【运行结果】

二、非法调用第三方函数

【示例代码】

#include <stdio.h>
#include <windows.h>

void* res = NULL;

void bug() {
	int x;
	int* q = &x;
	q += 2;
	*q = res;
	printf("bug()\n");
}

void test(int x, int y) {
	int* p = &x;
	p--;
	res = *p;
	*p = bug;
	printf("test()\n");
}

int main() {
	int a = 10;
	int b = 20;

	test(a, b);

	__asm {
		sub esp,4
	}

	printf("main()\n");

	system("pause");
	return 0;
}

【解析】

当函数调用返回时,是利用被调用之前所保存的返回地址而跳转的,如果我们改写这个地址,函数便可跳转到其他地方。

对于上述代码而言,函数的调用过程为:main函数 → test函数 → bug函数 → main函数,具体流程如下:

  • 在main函数中 

调用 test(a, b) 时,先将返回地址压栈,即 test(a, b) 的下一条指令地址,然后程序跳转到 test 函数执行

  • 在test函数中

(1) 指针变量 p 指向 x 的地址

(2) p-- 让 p 指向存放函数返回地址的位置

(3) 保存返回地址到 res

(4) 将返回地址修改为 bug 函数的地址

(5) 打印 “test()”

(6) test 函数执行结束,程序读取栈上的返回地址,跳转到 bug 函数执行

  • 在bug函数中

(1) 定义变量 x,定位 bug 函数

(2) 指针变量 q 指向 x 的地址

(3) q+=2 让 q 指向存放函数返回地址的位置

(4) 将该位置的数据修改为原始的返回地址

(5) 打印 "bug()"

(6) bug 函数执行结束,程序读取栈上的返回地址,跳转到 main 函数执行

  • 在main函数中

(1) 执行 __asm { sub esp, 4} 内联汇编指令,将栈顶指针 esp 向低地址方向移动4个字节,目的是平衡栈帧。

(2) 当正常调用一个函数时,是通过 call 指令操作的,该指令会先将返回地址压栈,再进行函数跳转;函数执行完成后通过 ret 指令返回,该指令会先将返回地址弹出,再跳转到该地址。整个过程一入一出,栈帧平衡。

(3) 在上述代码中调用 bug 函数时,我们是通过修改 test 函数的返回地址进行非法跳转的,并没有执行 call 指令,也就没有数据压栈;而在 bug 函数返回时,又执行了 ret 指令,有数据弹出 。整个过程无入有出,栈帧不平衡,这将导致程序无法正确返回或访问数据,进而引发崩溃或未定义行为。

(4) 所以为了确保栈帧平衡,需要调整栈顶指针 esp 到正确的位置上。

【运行结果】

【注意】

上述代码行为都是未定义的,因为标准并未规定函数参数的栈布局。编译器可能以不同的方式实现(如从左往右压参、使用寄存器传参等),导致运行结果也会有所不同,甚至崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值