【问题标题】:How can I write cross platform (32 and 64bit) junk assembly code? [closed]如何编写跨平台(32 位和 64 位)垃圾汇编代码? [关闭]
【发布时间】:2017-12-18 01:59:48
【问题描述】:

我需要为 C++ 项目的一些混淆练习编写一些(可以安全使用的)随机垃圾汇编代码。 到目前为止我只能找到这个:

#define JUNKCODE        \
__asm{push eax}            \
__asm{xor eax, eax}        \
__asm{setpo al}            \
__asm{push edx}            \
__asm{xor edx, eax}        \
__asm{sal edx, 2}        \
__asm{xchg eax, edx}    \
__asm{pop edx}            \
__asm{or eax, ecx}        \
__asm{pop eax}

这似乎无法在 Xcode 上为 64 位项目编译

如何创建多个版本的垃圾汇编代码,这些代码可以安全地添加到我的项目中,并且兼容 32-64 位和跨平台(Windows VS2013 和 Mac Xcode8)?

【问题讨论】:

  • 64 位 VC++ 根本不支持内联汇编。对于那些这样做的人,语法是不可移植的。
  • 哦,我不知道。那么,即使对每个平台都使用检查,最好的方法是什么?
  • 混淆C++ ...通过添加垃圾代码...听起来很荒谬。优化的机器代码已经足够神秘,对 RE 来说是相当大的挑战,垃圾代码部分(特别是如果它们只有很少的变体并且它们不是随机生成的)最终可能会给经验丰富的 RE 工程师一个提示用垃圾块标记的重点在哪里。混淆真实代码会一些有意义,但这非常棘手,通常 2-3 层解释/自定义语言可以很好地工作,而不会完全牺牲源代码的可维护性。

标签: c++ assembly


【解决方案1】:

要编写将在任一模式下汇编的 asm,请使用 32 位操作数大小,并避免 push/pop:它们不适用于 x86-64 的 32 位操作数大小。只有模式的全宽寄存器大小和 16 位可用于推入/弹出。 (即机器码中的the 0x66 operand-size prefix works, but REX.W=0 doesn't,所以push eax在64位模式下是不可编码的。)

如果您希望相同的机器代码在两种模式下工作(32 位模式下的短编码成为 64 位模式下的 REX 前缀),也要避免使用 inc /dec。如果你只需要汇编的 asm 源代码,inc/dec 就可以了。


对于 C++ 中的内联汇编:

避免修改任何内存或进行任何函数调用。很难安全地做到这一点,所以如果你只想要垃圾代码,你可以玩寄存器。您甚至不能在 System V ABI 的 x86-64 代码中安全地使用push,因为编译器可能正在使用堆栈下方的红色区域and there's no way to tell it you want to clobber it。我不知道这是否适用于使用 MSVC inline-asm 语法而不是 GNU 语法的 Clang,其中必须显式声明 clobbers。

更喜欢 GNU C 内联 asm 语法(有适当的约束来告诉编译器你破坏了什么寄存器)。

MSVC 本身不支持 64 位模式下的任何内联 asm 语法。

Xcode (clang) 确实支持 MSVC 的语法以及 GNU 语法,它甚至可能在 64 位模式下支持 MSVC 语法。

MSVC 样式的内联 asm 语法对于包装短 sn-ps (输入必须通过内存反弹,因为没有语法要求寄存器中的输入)和 reportedly has been unreliable from version to version in MS's compiler 基本上很烂。 MS 可能正在寻找放弃它的理由或方法,并且不支持 x86-64 是有道理的。如果它们保持不变,从内联 asm 访问函数 args 会更加糟糕,因为它们不是在内存中开始的。无论如何,MSVC 内联 asm 语法是半死不活的,好摆脱。

无论如何,如果您真的想在 32 位 MSVC 中使用一些内联 asm,那么请务必使用该语法。 __asm __emit 可能会在 64 位 MSVC 中执行某些操作,I think IACA headers use that,所以如果你只是想让你的代码看起来很奇怪,你可以手动编码一些 NOP。

【讨论】:

  • 内联 asm(和 naked 函数)基本上也与 Windows 处理展开 64 位函数的方式不兼容。他们有这个特定的函数prologue 和 Epilog 格式(后者包括 emulating 作为展开的一部分的 Epilog 的概念!),它们用于允许展开:对装配的更多限制以换取更简单的展开策略(例如,与 eh_frame 或 DWARF 相比)。这么多类型的内联汇编是有问题的。
  • @BeeOnRope:为什么这会限制内联汇编呢? naked 当然,但内联 asm 不会阻止编译器发出任何序言/结尾。
  • 部分原因是您可能想在内联 asm 中执行的某些操作不再被允许(例如,操作 rsp、操作基指针,现在是任意的而不是固定为 @ 987654340@) - 至少不需要大量工作来解释内联汇编并使展开数据兼容。部分原因是内联 asm 的主要用途之一是在 naked 函数内部对 prolog 或 epilog 进行特殊处理,而这不再可能(即,没有 naked 函数会降低内联 asm 的用处)。也许基本上不相容太强了:可能“更难做”。
  • 澄清基本指针的想法:在 asm 中操作基本指针并不是一件常见的事情,但我的意思是在 64 位 Windows 二进制文件中 any i> 指针可以是基指针,并且它不得被修改(即使保存在内联 asm 周围),因为如果帧指针被临时更改,代码也无法可靠地展开。
  • @BeeOnRope:哦,好的一点,内联 asm 的主要用途之一正是从编译器下的“上下文切换”之类的 hacky 东西,这在该 ABI 中是不安全的。我主要是出于性能原因考虑使用它(除非你编写一个完整的循环,否则 MSVC 内联汇编是不好的)。关于基/帧指针的有趣内容。但是,如果它是任意的,一个不糟糕的编译器可以选择一个你不用作帧指针的 reg,或者错误。如果内联使用不同寄存器的两个 asm 块不会留下可能的基指针,这可能会限制内联。
猜你喜欢
  • 1970-01-01
  • 2017-10-20
  • 1970-01-01
  • 1970-01-01
  • 2013-02-06
  • 2010-09-07
  • 1970-01-01
  • 2022-06-24
  • 2011-09-22
相关资源
最近更新 更多