我无法在堆栈上找到变量。我正在使用Fedora 25 x64,但使用的是32位程序btw。

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作为参数传递给被调用的函数。

当调用函数并在运行时创建新的堆栈框架时该函数的堆栈,在该函数中声明的任何局部变量的堆栈上也会分配内存。但是,text1text2target是在任何函数外部声明的全局变量。这样的直接后果是不会为堆栈上的text1text2target分配内存。相反,text1text2将位于进程的data段中,而target将位于bss段中。为了理解原因,必须熟悉ELF和System V应用程序二进制接口。

虚拟内存中的x86 Linux进程布局

对于某些情况,这里有一个Gustavo Duarte的文章“内存中的程序剖析”文章中x86 Linux系统上虚拟内存中的进程布局图:


帮助您弄清所看到的内存地址的重要性。在x86 Linux系统上,堆栈的虚拟内存很大,并且向下扩展。这就是为什么在检查堆栈时会看到诸如0xffffcc100xffffccb0之类的内存地址的原因。全局变量text1text2target在虚拟内存中的位置将更接近程序入口点,因为databss段与内存不足的text段相邻。鉴于此,有意义的0x804a01c的内存地址为text1

ELF和系统V ABI

从ABI的第4节(目标文件)开始:


可执行文件包含适合执行的程序;该文件指定函数exec如何创建程序的过程映像。





由汇编器和链接编辑器创建,目标文件是二进制的打算直接在处理器上执行的程序的表示形式。


一旦程序被编译,组装和链接,它实质上是对程序外观的描述。这意味着可以对可执行二进制文件进行静态分析,以了解程序运行时的外观。分析了根据以上提供的源代码构造的二进制文件后,发现text1text2的值位于.rodata部分中: -19),.rodata部分保存通常构成过程映像中不可写段的只读数据。不可写过程映像段的示例是上面提到的textdata段。这意味着text1text2将在程序加载到内存时位于程序指令所在的位置,即text段附近。指令存储器地址看起来比text1text2的存储器地址更类似于堆栈上的存储器地址。

target变量是未初始化的全局变量,因此其数据将保存在bss中段。

函数的参数写到堆栈中

如果要使这些变量的指针出现在运行时堆栈中,则必须将它们作为参数传递给以下函数:在整个过程执行过程中的某个时刻被调用,因为函数的参数通常被写入调用者的参数构建区域中的堆栈中,如下图所示。

具有多个框架的堆栈布局(来自CSAPP):例如,不是将argv[1]作为vuln函数的参数传递,而是可以传递全局变量text1 。然后,将在调用text1之前将指向vuln的指针保存在堆栈上。 (或任何其他ASCII字符)作为在Shell中执行程序时在命令行上的参数。这将导致您传递的所有值都存储在argv[1]中,这是vuln的参数。

应注意,由于它们的全局范围,因此text1text2data可以在没有作为参数传递,但在格式字符串漏洞和printf的上下文中,
并不是特别有用。

更多信息关于printf在x86 Linux环境中的行为,可以看看以下有关stackoverflow的问题的答案,在该问题中,用户以非标准的方式调用printf:“ ELF32二进制,是否为小字节序?”

“计算机系统:程序员的观点”中的第3.7节(标题为“过程”)涵盖了汇编级别的函数调用和堆栈,并提供了一些有用的图表。

#2 楼

堆放时,您主要在哪里?这些变量是全局初始化变量,直到在函数调用之前将它们压入堆栈之后,它们才会在堆栈中。在此之前,它们将位于程序的bss或data部分中。
打印主程序的反汇编,并且应该在调用函数之前看到对字符串的引用被压入堆栈。这可以是push op或mov op btw,具体取决于您的编译器设置。