【问题标题】:Avoid the use of .data segment避免使用 .data 段
【发布时间】:2020-02-20 21:21:44
【问题描述】:

有没有办法避免编译后的代码使用 .data 段?是否可以编写 C 代码并使用编译器选项强制所有内容都在 .text 中? 我问的原因是因为我想将另一个程序的汇编代码放入堆栈并从那里运行代码。所以如果这个程序正在使用数据段,它将无法工作。

【问题讨论】:

  • 如果你没有全局变量,并且没有使用有全局变量的库,那么数据段中就没有任何东西。 (请注意,这过于严格;声明为 const 的全局变量和没有初始化器的全局变量很可能在其他段中。)
  • 您为什么认为不使用.data 将允许从堆栈运行代码?为什么你认为使用.data 会阻止它?
  • 知道最初的问题是什么会很有趣。你在做什么似乎是一个黑客。我有一种感觉,如果我们知道最初的问题是什么,我们可能会提供一个不涉及将汇编程序加载到堆栈中的解决方案。你想要完成什么,为什么?
  • 程序 A 的字节码应该从程序 B 加载。程序 B 加密/解密程序 A 的代码并从堆栈中运行它。我不想用汇编程序编写整个程序 A,而是用 C 编写。因此这里的问题。
  • 为什么要堆叠?在链接描述文件中定义一些可执行部分并使用__attribute__((section("name")) 加载代码

标签: c gcc assembly shellcode


【解决方案1】:

有没有办法避免编译后的代码使用 .data 段?

有多种方法 - 编写根本不使用数据的代码;使用编译器特定的扩展(例如 GCC 中的“__attribute__((section("name")))”),编写一个链接器脚本,将输入文件的 .data.rodata 部分中的所有内容推入输出文件的 .text 部分;有一个名为 .myData 的全新部分,而不是 .data,等等。

是否可以编写 C 代码并使用编译器选项强制所有内容都在 .text 中?

是的,如果您想花费数年时间编写自己的编译器等,一切皆有可能。

问题在于这是关于权限的。现代 CPU 允许操作系统说“此内存区域不可执行”或“此内存区域不可修改”,并由硬件强制执行以捕获错误并避免安全灾难。部分是您如何告诉操作系统权限应该是什么(例如,可执行和只读.text,不可执行和只读.rodata,不可执行和可修改.data)。如果打破它,你最终会得到比必要更糟糕的结果(检测错误的机会更少,安全灾难的机会更多)。

它还可能导致 CPU 出现性能问题(“L1 指令缓存”的一半浪费了缓存数据,“L1 数据缓存”的一半浪费了缓存指令,CPU 认为这是自修改代码,因为您在同一缓存行或与指令相同的页面)。

我问的原因是因为我想把另一个程序的汇编代码放在堆栈中并从那里运行代码。

通常堆栈是“不可执行的”,因此它可能会崩溃。要解决这个问题,您可能需要特殊代码来更改堆栈使用的内存区域的权限,但是如果您可以修改其他程序来做到这一点,那么您也可以修改其他程序以直接包含程序集并避免需要愚蠢的废话。

【讨论】:

  • 使用的内存将有执行权限
  • @LuHell:那么,充其量只是一场毫无意义的性能灾难。
  • 仍然不清楚没有.data与它有什么关系。
  • @EugeneSh.:有两种情况 - 根本没有任何数据,有数据但没有 .data 部分(例如,将数据放入 .text 代替)。我试图涵盖这两种情况;但是对于根本没有任何数据几乎没有什么可说的,而且如果没有数据,很可能就不会被问到这个问题(因为你为什么要关心一个空白部分?);所以大多数讨论最终都是针对“有数据但没有.data 部分”的情况(以及如何做到这一点,以及这样做的后果)。
【解决方案2】:

听起来您想使用编译器输出来制作可用于代码注入的 shellcode。 (例如,通过溢出堆栈缓冲区,在具有可执行堆栈的遗留程序中)。

是的,一切都需要在一个字节中,基本上是一个扁平的二进制文件,并且与位置无关。

您可以手动编辑编译器的 asm 输出以将 .rodata 甚至 .data 放入 .text。您可以通过编译与ld -N (--omagic) 的链接以使.text 可写,从而在独立的可执行文件中对此进行测试。

当然,这只适用于您正在编译的 ISA 具有 PC 相对寻址,例如 x86-64 但不是 32 位 x86。在 32 位 x86 中对内存操作数使用标签会导致机器代码中出现绝对地址,这在注入到未知地址的堆栈时显然无法工作。

(相关:-zexecstack 使所有内存都可执行,因此代码注入攻击可以工作。这不是默认设置;代码注入在维护 W^X 的系统上不起作用(没有可写和可执行的页面).)


所以你真正应该做的就是不使用任何库函数,不使用任何全局变量或static 变量。即编写不使用任何静态存储类变量的代码,只有自动的。如果您需要一个小缓冲区,请使用本地数组。

您还需要使用内联 asm 包装宏进行系统调用,而不是通常的 libc 包装函数。

【讨论】:

  • 将 .rodata 和 .data 移动到 .text 时,代码仍然对那里的地址进行硬编码引用。我没有在其他程序中注入代码。两个代码都是我的,我可以确保整个内存都是可执行的。
  • @LuHell:这个答案明确表示仅适用于以 PC 相关方式处理静态数据的 ISA,例如 x86-64。 (但它确实工作。例如,mov eax, [RIP + my_label] (GAS .intel_syntax) 在为 x86-64 组装时与位置无关。)对于没有 PC 相关数据寻址的 ISA,您需要确保编写编译时不引用相对分支以外的任何标签的代码。
猜你喜欢
  • 1970-01-01
  • 2021-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-30
相关资源
最近更新 更多