在Xeno Kovah's Introduction to x86 Assembly托管的OpenSecurityTraining第一天的作业中,他指定了,
我们现在知道的说明(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
的{{1}} / IMUL
变种也不计算而不是DIV
随机可执行文件并审核它们然后创建源代码,是否可以找到GCC当前输出的x86汇编指令列表?
这个问题的基础似乎是实际使用的指令子集很少,需要知道逆向工程(这是课程的重点)。 Xeno似乎试图找到一种有趣的指导方式来表达这一点,
我认为知道大约20-30(不计算变化)就足够了,你很快就会检查手册
虽然我欢迎大家和我一起参加OpenSecurityTraining这个很棒的课程,但问题是关于我提出的从GCC中找出它的方法(如果可能的话)。不是,人们实际上做了Xeno的任务。 ;)
答案 0 :(得分:3)
这个问题的基础似乎是实际使用的指令子集很少,需要知道反向工程
是的,这通常是正确的。 gcc永远不会发出一些指令,like enter
(因为它在现代CPU上 比push rbp
/ mov rbp, rsp
/ sub rsp, some_constant
慢。< / p>
xlat
和loop
之类的其他陈旧/模糊不清的东西也将被闲置,因为它们不会更快,并且gcc的-Os
不会全力以赴地优化尺寸而不关心关于表现。 (clang -Oz
更具攻击性,但IDK如果有人不愿教导loop
指令。)
当然,gcc永远不会发出像wrmsr
这样的特权指令。对于某些无特权的说明like rdtsc
或__builtin_...
,有一些内在函数(cpuid
函数)不是“正常”。
是否可以找到GCC当前输出的x86汇编指令列表?
这将是gcc机器定义文件。作为可移植编译器的GCC具有自己的基于文本的语言,用于描述编译器指令集的机器定义文件。 (每个指令的作用,它可以使用的寻址模式,以及优化器可以最小化的某种“成本”。)
请参阅gcc-internals documentation for them。
此问题的另一种方法是查看x86指令参考手册(例如this HTML extract,并查看x86标记wiki中的其他链接)并查看对于那些你还没见过的人。然后编写一个gcc会发现它有用的函数。
e.g。如果你还没有看到movsx
(符号扩展名),那么写一下
long long foo(int x) { return x; }
和gcc -O3将发出(from the Godbolt compiler explorer)
movsx rax, edi
ret
或者在rax
内获取cdqe
(aka cltq
in AT&T syntax)符号扩展名,强制gcc在符号扩展之前进行数学运算,因此它可以先在eax
中生成结果(使用副本和-add lea
)。
long long bar(unsigned x) { return (int)(x+1); }
lea eax, [rdi+1]
cdqe
ret
# clang chooses inc edi / movsxd rax, edi
另见 Matt Godbolt的CppCon2017演讲:“What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid” 和How to remove "noise" from GCC/clang assembly output?。
让gcc发出旋转指令很有意思。 Best practices for circular shift (rotate) operations in C++。你把它写成移位/或者gcc可以识别为旋转。
因为C不提供现代CPU可以做的许多事情的标准函数(旋转,弹出,计数前导/尾随零),唯一的可移植事情是编写一个等效函数并且具有编译器识别该模式。如果你很幸运的话,gcc和clang可以在使用popcnt
编译时将整个循环优化为单个-mpopcnt
指令(例如,由-march=haswell
启用)。如果没有,你会得到一个愚蠢的慢循环。可靠的非可移植方式是使用__builtin_popcount()
,如果目标支持,则编译为popcnt
指令,否则使用表查找。 _mm_popcnt_u64
是popcnt
或者没有:如果目标不支持该指令,它就不会编译。
当然,这种方法的缺陷是它只有在您已经知道x86指令集并且任何给定指令是优化编译器的正确选择时才有效!
(以及gcc选择做什么,例如内联字符串在某些情况下对于短字符串与rep cmpsb
进行比较,但我不确定这是最优的。只有rep movs
/ rep stos
在现代CPU上有“快速字符串”支持。但我不认为gcc会使用lods
或任何带有rep
前缀的“字符串”指令。)
答案 1 :(得分:2)
是否可以找到GCC当前输出的x86汇编指令列表,而不是随机执行随机可执行文件并审核它们然后创建源代码?
您可以查看gcc使用的machine description files。在其源代码树中,查看gcc / config / i386并查看.md
文件。 x86的核心是i386.md; x86的各种扩展还有其他扩展(可能包含在针对不同处理器进行优化时使用的启发式调整)。
警告:这绝对不是一个简单的阅读。
我认为知道大约20-30(不计算变化)就足够了,你很快就会检查手册
这是真的;根据我进行逆向工程的经验,99%的代码总是相同的东西,指令方面;比了解整个x86指令集更有用的是熟悉程序集习语,尤其是编译器经常发出的习惯用法。
话虽如此,从我的脑海中开始,一些非常常见的指令缺失(经常发出并且没有启用扩展指令集)是:
movzx
/ movsx
inc
/ dec
(很少见gcc,common with VC++)neg
cdq
(before idiv
)jcxz
/ jecxz
(gcc很少见,VC ++有些常见)setCC
cmpxchg
(在同步代码中); cmovCC
adc
(在32位代码中进行64位运算时)int3
(通常在功能边界上发出,通常作为填充物发布)scas
/ cmps
),特别是旧编译器上的固定序列然后是整个SSE&amp;共...