[英]Is it possible to force GCC to output the exact bitwise instructions I give it without inline assembly?
[英]Is it possible to find a list of all of the assembly instructions that GCC can generate?
在分配给OpenSecurityTraining的Xeno Kovah的x86大会简介的第一天的作业中,他指出 ,
我们现在知道的说明(24)
NOP PUSH / POP CALL / RET MOV / LEA ADD / SUB JMP / Jcc CMP / TEST和/或/ XOR / NOT SHR / SHL IMUL / DIV REP STOS,REP MOV LEAVE
编写程序以查找我们尚未涵盖的指令,并明天报告指令。
他进一步预测了作业,
SAL
/ SAR
IMUL
/ DIV
的MUL
/ IDIV
变体也不计算在内 而不是objdump
荷兰国际集团执行随机和审计他们再创造的源泉, 是有可能找到的是GCC目前输出的x86汇编指令列表?
这个问题的基础似乎是实际使用的指令子集很少,需要知道逆向工程(这是课程的重点)。 Xeno似乎试图找到一种有趣的指导方式来表达这一点,
我认为知道大约20-30(不计算变化)是足够好的,你将很少检查手册
虽然我欢迎大家和我一起参加OpenSecurityTraining这个很棒的课程,但问题是关于我提出的从GCC中找出它的方法(如果可能的话)。 不是,人们实际上做了Xeno的任务。 ;)
这个问题的基础似乎是实际使用的指令子集非常少,需要知道逆向工程
是的,这通常是正确的。 有一些指令GCC将绝不会发出, 就像enter
(因为它比慢得多 push rbp
/ mov rbp, rsp
/ sub rsp, some_constant
在现代的CPU)。
其他旧/模糊的东西,如xlat
和loop
也将被闲置,因为它们并不快,而gcc的-Os
并没有全力以赴地优化尺寸而不关心性能。 ( clang -Oz
更具侵略性,但IDK如果有人clang -Oz
教它loop
指令。)
当然,gcc永远不会发出像wrmsr
这样的特权指令。 有些内在函数( __builtin_...
函数)用于某些非特权指令, 如rdtsc
或cpuid
,它们不是“正常”。
是否可以找到GCC当前输出的x86汇编指令列表?
这将是gcc机器定义文件。 作为可移植编译器的GCC具有自己的基于文本的语言,用于描述编译器指令集的机器定义文件。 (每个指令的作用,它可以使用的寻址模式,以及优化器可以最小化的某种“成本”。)
请参阅gcc-internals文档 。
这个问题的另一种方法是查看x86指令参考手册(例如这个HTML提取 ,并查看x86标签wiki中的其他链接)并查找尚未看到的那些。 然后编写一个函数,gcc会发现它很有用。
例如,如果你还没有看过movsx
(符号扩展名),那就写吧
long long foo(int x) { return x; }
和gcc -O3将发出( 来自Godbolt编译器资源管理器 )
movsx rax, edi
ret
或者为了获得rax
符号扩展的cdqe
(在AT&T语法中也称为cltq
) ,强制gcc在符号扩展之前进行数学运算,因此它可以首先在eax
生成结果(使用复制和添加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演讲: “我的编译器最近为我做了什么? 解开编译器的盖子“ ,以及如何从GCC / clang组件输出中消除”噪音“? 。
让gcc发出旋转指令很有意思。 C ++中循环移位(旋转)操作的最佳实践 。 你把它写成移位/ OR,gcc可以识别为旋转。
因为C不提供现代CPU可以做的许多事情的标准函数(旋转,弹出,计数前导/尾随零),唯一可移植的东西是编写一个等效函数并让编译器识别该模式。 如果你很幸运,gcc和clang可以在使用-mpopcnt
进行编译时将整个循环优化为单个popcnt
指令(例如,由-march=haswell
启用)。 如果没有,你会得到一个愚蠢的慢循环。 可靠的非可移植方式是使用__builtin_popcount()
,如果目标支持,则编译为popcnt
指令,否则进行表查找。 _mm_popcnt_u64
是popcnt
或什么都没有:如果目标不支持该指令,它不会编译。
当然,这种方法的缺陷是它只有在您已经知道x86指令集并且任何给定指令是优化编译器的正确选择时才有效!
(以及gcc选择做什么,例如内联字符串在某些情况下对于短字符串比较rep cmpsb
,虽然我不确定这是最优的。只有rep movs
/ rep stos
rep movs
在现代CPU上有“快速字符串”支持。但是我不要以为gcc会使用lods
,或任何带有rep
前缀的“字符串”指令。)
是否可以找到GCC当前输出的x86汇编指令列表,而不是随机执行随机可执行文件并审核它们然后创建源代码?
您可以查看gcc使用的机器描述文件 。 在其源代码树中,查看gcc / config / i386并查看.md
文件。 i86的核心是i386.md ; 还有其他用于x86的各种扩展(并且可能包含在针对不同处理器进行优化时使用的启发式调整)。
警告:这绝对不是一个容易阅读。
我认为知道大约20-30(不计算变化)是足够好的,你将很少检查手册
这是真的; 根据我进行逆向工程的经验,99%的代码总是相同的东西,指令方面; 比了解整个x86指令集更有用的是熟悉程序集习语,尤其是编译器经常发出的习惯用法。
话虽如此,从我的脑海中,一些非常常见的指令缺失(经常发出并且没有启用扩展指令集)是:
movzx
/ movsx
inc
/ dec
(gcc很少见, 与VC ++相同 ) neg
cdq
( 在idiv
之前 ) jcxz
/ jecxz
(gcc很少见,VC ++有些常见) setCC
cmpxchg
(同步代码中); cmovCC
adc
(在32位代码中进行64位运算时) int3
(通常在函数边界上发出,通常作为填充int3
) scas
/ cmps
),尤其是旧编译器上的固定序列 然后就是整个世界的SSE&co ...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.