是否可以找到 GCC 可以生成的所有汇编指令的列表?

Eva*_*oll 6 x86 assembly gcc instructions

Xeno KovahOpenSecurityTraining 上主持的x86 程序集介绍的第一天作业中,他指定:

我们现在知道的说明(24)

NOP PUSH/POP CALL/RET MOV/LEA ADD/SUB JMP/Jcc CMP/TEST AND/OR/XOR/NOT SHR/SHL IMUL/DIV REP STOS,REP MOV LEAVE

编写一个程序来查找我们尚未涵盖的指令,并在明天报告该指令。

他进一步断言这个任务是,

  • 后面要介绍的说明不重要:SAL/SAR
  • 上跳跃或变化MUL/IDIV变体IMUL/DIV也不要指望
  • 额外的禁区说明:任何浮点数(因为我们没有在本课程中介绍这些内容。)
  • 他在视频中说你不能使用内联汇编。(被问到时提到)。

是否可以找到 GCC 当前输出的 x86 汇编指令列表,而不是objdump随机执行并审核它们然后创建源代码

这个问题的基础似乎是实际使用的指令的一个非常小的子集,人们需要知道逆向工程(这是课程的重点)。Xeno 似乎试图找到一种有趣的、有指导意义的方式来说明这一点,

我认为知道大约 20-30(不包括变化)就足够了,你很少会检查手册

虽然我欢迎大家加入我在 OpenSecurityTraining 的这个很棒的课程,但问题是我提出的从 GCC 中找出它的方法(如果可能的话)。不是,让人们真正完成 Xeno 的任务。;)

Pet*_*des 5

\n

这个问题的基础似乎是实际使用的指令的一小部分需要知道才能进行逆向工程

\n
\n

是的,这通常是正确的。有一些指令 gcc 永远不会发出,例如enter(因为它比现代 CPU 上慢得多) 。push rbpmov rbp, rspsub rsp, some_constant

\n

其他旧的/晦涩的东西,比如xlatloop也将不会被使用,因为它们并不更快,并且 gcc-Os不会在不关心性能的情况下全力优化大小。(clang -Oz更具攻击性,但我不知道是否有人愿意教它有关指令loop。)

\n

当然,gcc 永远不会发出像wrmsr. __builtin_...对于一些非特权指令,例如rdtsc或 ,有cpuid一些不“正常”的内在函数(函数)。

\n
\n
\n

是否可以找到 GCC 当前输出的 x86 汇编指令列表?

\n
\n

这将是 gcc 机器定义文件。GCC 作为便携式编译器,拥有自己的基于文本的机器定义文件语言,这些文件向编译器描述指令集。(每条指令的作用是什么,它可以使用什么寻址模式,以及优化器可以最小化的某种“成本”。)

\n

请参阅gcc-internals 文档了解它们

\n
\n

解决此问题的另一种方法是查看 x86 指令参考手册(例如此 HTML 摘录,并查看标签 wiki 中的其他链接)并查找您尚未见过的内容。然后编写一个 gcc 会觉得有用的函数。

\n

例如,如果您还没有看到movsx(符号扩展),那么写

\n
long long foo(int x) { return x; }\n
Run Code Online (Sandbox Code Playgroud)\n

gcc -O3 将发出(来自 Godbolt 编译器资源管理器

\n
    movsx   rax, edi\n    ret\n
Run Code Online (Sandbox Code Playgroud)\n

或者要获得cdqe(又名cltqAT&T 语法)内的符号扩展rax,请强制 gcc 在符号扩展之前进行数学运算,以便它可以在eax第一个中生成结果(使用复制和添加lea)。

\n
long long bar(unsigned x) { return (int)(x+1); }\n\n    lea     eax, [rdi+1]\n    cdqe\n    ret\n\n   # clang chooses inc edi  /  movsxd rax, edi\n
Run Code Online (Sandbox Code Playgroud)\n

另请参阅Matt Godbolt 的 CppCon2017 演讲:\xe2\x80\x9c我的编译器最近为我做了什么?拧开编译器的 Lid\xe2\x80\x9d,以及如何从 GCC/clang 程序集输出中删除“噪音”?

\n
\n

让 gcc 发出旋转指令很有趣。 C++ 中循环移位(旋转)操作的最佳实践。您将其写为“移位/或”,gcc 可以将其识别为“旋转”。

\n

因为 C 没有为现代 CPU 可以执行的许多操作(旋转、popcnt、计算前导/尾随零)提供标准函数,所以唯一可移植的事情是编写等效函数并让编译器识别该模式。如果幸运的话, gcc 和 clang 可以popcnt在编译时将整个循环优化为单个指令-mpopcnt(例如,由 启用)。-march=haswell如果没有,你就会得到一个愚蠢的慢循环。可靠的非可移植方法是使用__builtin_popcount()popcnt如果目标支持它,它会编译为指令,否则会进行表查找。 _mm_popcnt_u64popcnt或什么都不是:如果目标不支持该指令,则不会编译。

\n
\n

当然,这种方法的第 22 条缺陷是,只有当您已经了解 x86 指令集并且任何给定指令都是优化编译器的正确选择时,它才有效!

\n

(以及 gcc 选择做什么,例如,内联字符串rep cmpsb在某些情况下与短字符串进行比较,尽管我不确定这是否是最佳的。只有rep movs/rep stos在现代 CPU 上有“快速字符串”支持。但我不认为gcc 将永远使用lods, 或任何不带rep前缀的“字符串”指令。)

\n


Mat*_*lia 3

是否可以找到 GCC 当前输出的 x86 汇编指令列表,而不是 objdumping 随机可执行文件并审核它们然后创建源代码?

您可以查看gcc 使用的机器描述文件。在其源代码树中,查看 gcc/config/i386 下的文件.md。x86 的核心是i386.md;还有其他针对 x86 的各种扩展(并且可能包含针对不同处理器进行优化时使用的启发式调整)。

请注意:这绝对不是一本容易阅读的书。

我认为了解 20-30 左右(不包括变化)就足够了,您很少会检查手册

这是千真万确的;根据我进行逆向工程的经验,99% 的代码在指令方面都是相同的;比了解整个 x86 指令集更有用的是熟悉汇编习惯用法,尤其是编译器经常发出的习惯用法。


话虽这么说,从我的想法来看,一些非常常见的缺少指令(经常发出且未启用扩展指令集)是:

  • movzx/movsx
  • inc/ dec(对于 gcc 很少见,对于 VC++ 常见
  • neg
  • cdqidiv
  • jcxz/ jecxz(在 gcc 中很少见,在 VC++ 中有些常见)
  • setCC
  • cmpxchg(在同步代码中);
  • cmovCC
  • adc(在 32 位代码中进行 64 位算术时)
  • int3(通常在功能边界上发出,通常作为填充物)
  • 一些其他字符串指令 ( scas/ cmps),特别是旧编译器上的固定序列

然后是上交所的整个世界......


归档时间:

查看次数:

993 次

最近记录:

7 年,6 月 前