因此,基本上,我在32位x86 Linux上使用objdump,反汇编了gcc编译的一些静态链接二进制文件。

在反汇编的asm代码中,我发现了这一点:

 80ade23:       74 01                   je     0x80ade26
 80ade25:       f0 0f c1 16             lock xadd %edx,(%esi) // lock
 80ade29:       89 54 24 14             mov    %edx,0x14(%esp)
 80ade2d:       8b 54 24 14             mov    0x14(%esp),%edx
 80ade31:       3b 15 f0 0e 0f 08       cmp    0x80f0ef0,%edx
 80ade37:       73 75                   jae    0x80adeae
 80ade39:       65 83 3d 0c 00 00 00    cmpl   
 je     S_0x80ade26
 lock   
 S_0x80ade26: xadd %edx,(%esi) // lock
x0,%gs:0xc 80ade40: 00 80ade41: 74 01 je 0x80ade44 80ade43: f0 0f c1 0d dc 0e 0f lock xadd %ecx,0x80f0edc // lock 80ade4a: 08


因此,据我所知,基本上lock是x86 asm操作码的前缀,在这里是合法的。

看来je跳到lock之后的位置。

这是我的问题:


objdump的拆卸结果正确吗?很少见到objdump会生成这种“跳转到指令”的asm代码。.(无论如何,我是逆向工程的新手,所以... :))
然后如何调整它使其重新

我试图以这种方式进行更改,然后使用gcc重新进行组装,
它可以通过组装过程,但是基本上我真的不知道它是否正确否。

q4312078q

#1 楼

我不会走那么远,指出objdump提供的输出不正确。没错,线性扫描不能正确处理数据,跳转表和shellcode通常是反汇编错误的根源。但这仍然不是错误。

如果仔细看一下代码,您会发现您有je。意味着,仅当前一条指令(肯定是cmptest)返回true时,才执行跳转。 x86 ISA(指令集)允许跳转到指令的中间,或者如果需要的话可以跳转到字节流。有时这是用来避免使用某些前缀,例如rep,...,以及您所需要的lock

我100%确信所提供的输出是正确的,并且程序员(或编译器) )使用此技巧来避免不必要的附加代码。

评论


谢谢yaspr,所以基本上,我的使用方式(在我的问题底部)是否正确?

– lllllllllllll
2014年7月9日14:13

实际上,您重写它的方式与以前没有什么区别。为什么呢?好吧,在二进制级别,指令之间没有分隔符,因为它们在x86中是可变长度的。而且,您可以在字节流中跳转。

– Yaspr
2014年7月9日14:30



#2 楼

实际上,objdump使用线性扫描算法反汇编可执行文件。这意味着它一步一步地反汇编指令。像这样:



首先进入入口并反汇编第一条指令(并获得其大小):

4028c0:       41 57                   push   %r15



然后,知道前一条指令的大小,就将当前地址更新为下一条指令,并将其反汇编(并重新获得其大小):

4028c2:       41 56                   push   %r14



然后,一次又一次地迭代(返回2),直到到达当前节的末尾:

4028c4:       41 55                   push   %r13
4028c6:       41 54                   push   %r12
4028c8:       55                      push   %rbp
4028c9:       48 89 f5                mov    %rsi,%rbp
4028cc:       53                      push   %rbx
...



objdump实现在此简单算法上仅增加了一个小增量,即使它出现在当前反汇编指令的中间,它也会在每个符号上开始。这意味着您可能遇到以下情况(我在研究混淆软件时遇到了这种情况):

   4028c0:       41 57                   push   %r15
   4028c2:       41 56                   push   %r14
   4028c4:       41 55                   push   %r13
   4028c6:       41 54                   push   %r12
   4028c8:       55                      push   %rbp
   4028c9:       48 89 f5                mov    %rsi,%rbp
   4028cc:       48 85 c0                test   %rax,%rax

00000000004028cd <.f668>:
   4028cd:       85 c0                   test   %eax,%eax
   4028cf:       53                      push   %rbx
   ...


反汇编程序首先将4028cc作为amd64指令反汇编,但符号是在4028cd。因此,该算法将重置为该值并从那里重新开始。

最后,请注意,线性扫描算法被广泛认为是不正确的。它可能很容易误导。它的主要问题是它没有考虑所有指令的语义,因此当到达动态跳转(jmp %rax)时,该算法将无法遵循执行流程。当然,还有很多其他方法可以误导该算法,在这里我不会尝试穷尽所有这些技术(请注意,递归遍历并不是更好)。

回到您的原始问题:


线性扫描算法无法跟踪程序的执行流程。如果位于指令中间,则将无法跳过数据。但是,当符号指向该指令在上一条指令的中间跳转的位置时,objdump可能是正确的(请参阅我之前描述的情况)。
要正确地反汇编此程序,就没有希望了与objdump。但是,您可以使用gdb并通过Python脚本对其进行检测,从而收集执行跟踪。同样,其他反汇编程序也不会被这种简单的布局所欺骗。您可以尝试radare或Benny建议的IDAPro。我还可以发布一些自己的工具,即Insight框架中的cfgrecovery(但对于这样一个简单的技巧来说有点过分了。)。


#3 楼

我认为您所说的“跳入指令”可能是一种称为反汇编反同步的抗静电分析技术,该技术将数据字节与代码交错,以使反汇编程序混乱。在IDA Pro书籍的第21章(混淆代码分析)中对此技术和其他技术进行了解释。
如果您满足以下条件,则使用IDA Pro可以正确地反汇编代码:


使用IDA(免费或专业版)打开二进制文件


将光标放在有问题的一行代码上,在您的情况下,该行位于以下地址:80ade2580ade43
lock xadd %edx,(%esi) // lock
...    
lock xadd %ecx,0x80f0edc // lock



单击IDA的“编辑”菜单,然后选择“取消定义”


现在将光标放在跳转指令所指向的地址上是0x80ade2680ade44


再次单击IDA的“编辑”菜单,然后选择“这次编码”。


请注意,已应用此抗静电分析技术在您的代码中两次。因此,您需要两次执行步骤2-5。
更新:但是,在objdump输出中没有反汇编反同步,正如Peter Ferrie在下面的注释中指出的那样。跳入指令是提高性能的一种手段。但是,对于那些偶然发现您的问题并且反汇编实际上正遭受失步困扰的人,我保留此答案。

评论


原来的拆卸是正确的,答案是错误的。代码正在检查线程数并避免锁定:如果只有一个线程。此操作可以提高性能。

–彼得·弗里
14年7月8日在16:18

@peterferrie感谢您的信息。我之前从未见过这种优化。因此,我认为这是反汇编不同步的情况。我现在想知道如果不动态计算跳转地址,如何不跳过锁?

–本尼
2014年7月8日在16:24

分支和锁定指令是作为一个集合生成的,因此总是je $ + 3 / lock / 。 cmp不是集合的一部分,因此可以分开以进行更好的流水线处理(以允许插入无关的mov指令)。

–彼得·弗里
2014年7月8日在16:31