C程序:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
char* text1 = "AAAAAAAAAAAAA";
int target;
char* text2 = "BBBBBBBBBBBBB";
void vuln(char *string)
{
printf(string);
if(target) {
printf("you have modified the target :)\n");
}
}
int main(int argc, char **argv)
{
vuln(argv[1]);
printf("Program name: %s", argv[0]);
}
而我的筹码堆位于
0x804
区域,这就是为什么我完全迷路的原因。您可能会看到我正在尝试做的事情,但是我试图找到0x414141,然后是目标,然后是0x42424242,但是在esp附近的堆栈区域中没有任何41s或42s。我目前正在接受有关格式字符串漏洞的教育,但是在这一点上,我什至无法在堆栈上定位变量。有什么我想念的吗?谢谢。#1 楼
您无法在程序运行时堆栈上找到text1
的原因是,在运行时text1
位于虚拟内存(而不是堆栈)中运行的进程的data
段中。为了将对text1
的引用写入堆栈,必须将text1
作为参数传递给被调用的函数。当调用函数并在运行时创建新的堆栈框架时该函数的堆栈,在该函数中声明的任何局部变量的堆栈上也会分配内存。但是,
text1
,text2
和target
是在任何函数外部声明的全局变量。这样的直接后果是不会为堆栈上的text1
,text2
和target
分配内存。相反,text1
和text2
将位于进程的data
段中,而target
将位于bss
段中。为了理解原因,必须熟悉ELF和System V应用程序二进制接口。虚拟内存中的x86 Linux进程布局
对于某些情况,这里有一个Gustavo Duarte的文章“内存中的程序剖析”文章中x86 Linux系统上虚拟内存中的进程布局图:
帮助您弄清所看到的内存地址的重要性。在x86 Linux系统上,堆栈的虚拟内存很大,并且向下扩展。这就是为什么在检查堆栈时会看到诸如
0xffffcc10
和0xffffccb0
之类的内存地址的原因。全局变量text1
,text2
和target
在虚拟内存中的位置将更接近程序入口点,因为data
和bss
段与内存不足的text
段相邻。鉴于此,有意义的0x804a01c
的内存地址为text1
。ELF和系统V ABI
从ABI的第4节(目标文件)开始:
可执行文件包含适合执行的程序;该文件指定函数
exec
如何创建程序的过程映像。和
由汇编器和链接编辑器创建,目标文件是二进制的打算直接在处理器上执行的程序的表示形式。
一旦程序被编译,组装和链接,它实质上是对程序外观的描述。这意味着可以对可执行二进制文件进行静态分析,以了解程序运行时的外观。分析了根据以上提供的源代码构造的二进制文件后,发现
text1
和text2
的值位于.rodata
部分中: -19),.rodata
部分保存通常构成过程映像中不可写段的只读数据。不可写过程映像段的示例是上面提到的text
和data
段。这意味着text1
和text2
将在程序加载到内存时位于程序指令所在的位置,即text
段附近。指令存储器地址看起来比text1
和text2
的存储器地址更类似于堆栈上的存储器地址。target
变量是未初始化的全局变量,因此其数据将保存在bss
中段。函数的参数写到堆栈中
如果要使这些变量的指针出现在运行时堆栈中,则必须将它们作为参数传递给以下函数:在整个过程执行过程中的某个时刻被调用,因为函数的参数通常被写入调用者的参数构建区域中的堆栈中,如下图所示。
具有多个框架的堆栈布局(来自CSAPP):例如,不是将
argv[1]
作为vuln
函数的参数传递,而是可以传递全局变量text1
。然后,将在调用text1
之前将指向vuln
的指针保存在堆栈上。 (或任何其他ASCII字符)作为在Shell中执行程序时在命令行上的参数。这将导致您传递的所有值都存储在argv[1]
中,这是vuln
的参数。应注意,由于它们的全局范围,因此
text1
,text2
和data
可以在没有作为参数传递,但在格式字符串漏洞和printf
的上下文中,并不是特别有用。
更多信息关于
printf
在x86 Linux环境中的行为,可以看看以下有关stackoverflow的问题的答案,在该问题中,用户以非标准的方式调用printf
:“ ELF32二进制,是否为小字节序?” “计算机系统:程序员的观点”中的第3.7节(标题为“过程”)涵盖了汇编级别的函数调用和堆栈,并提供了一些有用的图表。
#2 楼
堆放时,您主要在哪里?这些变量是全局初始化变量,直到在函数调用之前将它们压入堆栈之后,它们才会在堆栈中。在此之前,它们将位于程序的bss或data部分中。打印主程序的反汇编,并且应该在调用函数之前看到对字符串的引用被压入堆栈。这可以是push op或mov op btw,具体取决于您的编译器设置。