- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我从 Smashing the Stack for Fun and Profit 复制了示例 3在 Linux x86_64 上。但是我无法理解为了跳过指令应该增加到返回地址的正确字节数是多少:
0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
这是我认为 x = 1
的地方指令是。我写了以下内容:
#include <stdio.h>
void fn(int a, int b, int c) {
char buf1[5];
char buf2[10];
int *ret;
ret = buf1 + 24;
(*ret) += 7;
}
int main() {
int x;
x = 0;
fn(1, 2, 3);
x = 1;
printf("%d\n", x);
}
并在 gdb 中对其进行反汇编。我已禁用地址随机化并使用 -fno-stack-protector
编译程序选项。
我可以从下面的反汇编程序输出中看到我想跳过地址 0x0000000000400595
处的指令: 来自 callq <fn>
的返回地址和 movl
的地址操作说明。因此,如果返回地址是 0x0000000000400595
, 下一条指令是 0x000000000040059c
, 我应该在返回地址上加 7 个字节吗?
0x0000000000400572 <+0>: push %rbp
0x0000000000400573 <+1>: mov %rsp,%rbp
0x0000000000400576 <+4>: sub $0x10,%rsp
0x000000000040057a <+8>: movl $0x0,-0x4(%rbp)
0x0000000000400581 <+15>: mov $0x3,%edx
0x0000000000400586 <+20>: mov $0x2,%esi
0x000000000040058b <+25>: mov $0x1,%edi
0x0000000000400590 <+30>: callq 0x40052d <fn>
0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
0x000000000040059c <+42>: mov -0x4(%rbp),%eax
0x000000000040059f <+45>: mov %eax,%esi
0x00000000004005a1 <+47>: mov $0x40064a,%edi
0x00000000004005a6 <+52>: mov $0x0,%eax
0x00000000004005ab <+57>: callq 0x400410 <printf@plt>
0x00000000004005b0 <+62>: leaveq
0x00000000004005b1 <+63>: retq
我注意到我可以将 5 个字节添加到返回地址而不是 7 个字节并获得相同的结果。当我这样做时,我不是跳到指令的中间吗 0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
?在这种情况下,为什么这不会使程序崩溃,例如当我向返回地址添加 6 个字节而不是 5 个字节或 7 个字节时。
Just before buffer1[] on the stack is SFP, and before it, the return address. That is 4 bytes pass the end of buffer1[]. But remember that buffer1[] is really 2 word so its 8 bytes long. So the return address is 12 bytes from the start of buffer1[].
在 Aleph 1 的示例中,他/她计算返回地址的偏移量为从 buffer1[] 开始的 12 个字节。因为我在 x86_64 上,而不是 x86_32,所以我需要重新计算返回地址的偏移量。在x86_64上,是不是buffer1[]还是2个字,也就是16个字节; SFP 和返回地址各为 8 个字节(因为我们使用的是 64 位),因此返回地址位于:buf1 + (8 * 2) + 8
相当于buf1 + 24
?
最佳答案
首先要注意的也是非常重要的一点:所有数字和偏移量都非常依赖于编译器。不同的编译器,甚至具有不同设置的同一个编译器,都可能产生截然不同的程序集。例如,许多编译器可以(并且将会)删除 buf2
,因为它未被使用。他们还可以删除 x = 0
,因为它的效果未被使用,稍后会被覆盖。他们还可以删除 x = 1
并将所有出现的 x
替换为常量 1
,等等。
也就是说,您绝对需要为您在特定编译器及其设置上获得的特定程序集编号。
问题 1由于您为 main()
提供了程序集,我可以确认您需要向返回地址添加 7 个字节,通常为 0x0000000000400595
,以跳过 x=1
并转到 0x000000000040059c
,它将 x
加载到寄存器中供以后使用。 0x000000000040059c - 0x0000000000400595 = 7
。
问题 2仅添加 5 个字节而不是 7 个字节确实会跳转到指令的中间。然而,这个 2 字节的指令尾恰好(纯属偶然)是另一个有效的指令代码。这就是它不会崩溃的原因。
问题 3这又非常依赖于编译器和设置。几乎所有事情都可能在那里发生。由于你们没有提供反汇编,所以我只能猜测。猜测如下:buf
和 buf2
向上舍入到下一个堆栈单元边界(x64 上为 8 个字节)。 buf
变成8字节,buf2
变成16字节。帧指针不会保存到 x64 上的堆栈,因此没有“SFP”。总共 24 个字节。
关于c - 粉碎堆栈 example3 ala Aleph One,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30598828/
出于好奇 - 我知道有 LAMP - Linux、Apache、MySQL 和 PHP。但是还有哪些其他 Web 堆栈替代方案的缩写呢?像 LAMR - Linux、Apache、MySQL Ruby
我有以下代码。 var stackMapIn = []; var stackMapOut = []; var stackBack = []; stackMapOut.push("m1"); $scop
我遇到了导致我的堆栈无法恢复的情况,我别无选择,只能将其删除。使用完全相同的模板,我继续创建了另一个同名的堆栈。 The following resource(s) failed to create:
这是我第一次查看 Node 堆栈,自从我学习使用 Ruby on Rails 进行 Web 开发以来,我对一些基本的东西有点困惑。我了解 Rails 目录是什么样的。 demo/ ..../app .
本文实例讲述了C语言使用深度优先搜索算法解决迷宫问题。分享给大家供大家参考,具体如下: 深度优先搜索 伪代码 (Pseudocode)如下: ?
我正在按照指南 here ,它告诉我: The stack setup will download the compiler if necessary in an isolatedlocation (
同时 trying to debug a different question ,我安装了一个似乎与我安装的其他一些软件包冲突的软件包。 我跑了 $ stack install regex-pcre-
我花了几个小时创建了一个方法,该方法将从堆栈 s1 中获取 null 元素,并将它们放入 s2 中。然后该类应该打印堆栈。方法如下 import net.datastructures.ArraySta
我有一个类Floor,它有一个Stack block ,但我不知道如何初始化它。我曾尝试过这样的: public class Floor { private Stack stack;
我知道这个问题已经问过很多次了,但搜索一个小时后我仍然遇到问题。 我想使用一个 lifo 堆栈,它可以存储最大数量的元素。达到最大数量后,首先删除该元素并将其替换为新元素,这样在第一次弹出时我可以获取
我需要编写一个方法,压缩以执行以下操作; 目标compress方法是从栈s1中移除所有null元素。剩余(非空)元素应按其初始顺序保留在 s1 上。辅助堆栈 s2 应用作s1 中元素的临时存储。在该方
我正在尝试验证以下代码发生的顺序。 function square(n) { return n * n; } setTimeout(function(){ console.log("H
我需要一个字符数组,其中包含基于特定文件夹中文件数量的动态数量的字符数组。我能够通过初始化 char (*FullPathNames)[MAX_FILENAME_AND_PATHNAME_LENGTH
我正在编写一些日志逻辑并想要进行一些缩进。了解是否存在任何函数调用或某个函数是否已完成的最简单方法是查看堆栈/帧的当前地址。让我们假设堆栈颠倒增长。然后,如果 log() 调用中的堆栈地址小于前一次调
所以内存分段在x86-64中被放弃了,但是当我们使用汇编时,我们可以在代码中指定.code和.data段/段,并且还有堆栈指针寄存器。 还有堆栈段、数据段和代码段寄存器。 代码/数据/堆栈的划分是如何
void main() { int x = 5; // stack-allocated Console.WriteLine(x); } 我知道 x 是堆栈分配的。但是关于 x 的堆栈中
这是我关于 SO 的第一个问题。这可能是一个愚蠢的问题,但到目前为止我还没弄明白。 考虑下面的程序 Reader.java: public class Reader { public
java中有没有一种快速的方法来获取嵌套/递归级别? 我正在编写一个函数来创建组及其成员的列表。成员也可以是团体。我们最终可能会得到一组循环的组/成员。 我想在某个任意级别停止。 我知道我可以将变量保
考虑以下代码: struct A{...}; A a[100]; A* pa = new A[100]; delete[] pa; a/pa 元素的销毁顺序是由标准定义的还是实现定义的(对于第二种情况
我在下面有一些代码。此代码是一个基本的压入/弹出堆栈类,我将其创建为模板以允许某人压入/弹出堆栈。我有一个家庭作业,我现在要做的是创建一个具有多个值的堆栈。 所以我希望能够创建一个基本上可以发送三个整
我是一名优秀的程序员,十分优秀!