我使用一些混淆技巧分析了x86 / x86-64中的一些二进制文件。一种称为重叠指令。有人可以解释这种混淆的工作原理以及解决方法吗?

评论

我想知道正确的用词是什么。我听说过“指令断言”的使用非常频繁。

我遇到了几种谈论这种技术的方式。但是,的确,没有一个被社区广泛采用。自从现在以来,“重叠说明”是我使用最多的。但是,我可能只阅读了有关它的现有文档的一小部分。

#1 楼

x86可执行文件的静态分析论文很好地解释了重叠的指令。摘自以下示例(第28页):

0000: B8 00 03 C1 BB  mov eax, 0xBBC10300
0005: B9 00 00 00 05  mov ecx, 0x05000000
000A: 03 C1           add eax, ecx
000C: EB F4           jmp $-10
000E: 03 C3           add eax, ebx
0010: C3              ret


通过查看代码,不清楚返回指令上eax的值是多少(或者就此而言,到达返回指令)。这是由于从000C跳转到0002,该地址未在列表中明确显示(jmp $ -10表示从当前程序计数器值0xC到0xC10 = 2的相对跳转)。该跳转将控制转移到地址0000处的五个字节长移动指令的第三个字节。执行从地址0002开始的字节序列会展开一个全新的指令流:

0000: B8 00 03 C1 BB  mov eax, 0xBBC10300
0005: B9 00 00 00 05  mov ecx, 0x05000000
000A: 03 C1           add eax, ecx
000C: EB F4           jmp $-10
0002: 03 C1           add eax, ecx
0004: BB B9 00 00 00  mov ebx, 0xB9
0009: 05 03 C1 EB F4  add eax, 0xF4EBC103
000E: 03 C3           add eax, ebx
0010: C3              ret


知道Ida Pro(尤其是Hex Rays插件)是否/如何进行处理将很有趣。也许@IgorSkochinsky可以对此发表评论...

#2 楼

这也称为“中间跳跃”技巧。

解释

执行规则


大多数指令需要多个指令要编码的字节

它们在现代CPU上最多可以占用15个字节


只要权限有效,执行就可以在任何位置开始

因此,指令第一个指令之后的任何字节都可以重新用于启动另一条指令。

使用反汇编程序



直接反汇编程序启动最后一条指令之后的下一条指令。

,这样的反汇编程序(不遵循流程)将隐藏在可见指令中间的指令。

示例

琐碎的

00: EB 01           jmp  3
02: 68 c3 90 90 90  push 0x909090c3


将有效地执行为

00: EB 01           jmp  3
03: C3              retn
...


>因为第一个jmp跳过以下指令的第一个字节68(编码立即推送)。

多个重叠

从例如,69 84定义了一个imul指令,最多可占用11个字节。因此,您可以在其“伪”操作数中放入几行指令。

00: EB02                    jmp  4
02: 69846A40682C104000EB02  imul eax, [edx + ebp*2 + 0102C6840], 0x002EB0040
0D: ....


实际上将被执行为

00: EB02       jmp  4
04: 6A40       push 040
06: 682C104000 push 0x40102C
0B: EB02       jmp  0xF
0F: ...


指令在其第二个字节中跳跃:

00: EBFF    jmp 1
02: C0C300  rol bl, 0


实际上将被执行为
/>
00: EBFF    jmp 1
01: FFC0    inc eax
03: C3      retn


不同的CPU模式

这种混淆可以扩展到跳转到相同的EIP,但是在不同的CPU模式下:


64b CPU仍支持32b指令
64b模式将0x33用于cs

某些指令仅在特定模式下可用:


在32b模式下的arpl

在64b模式下的movsxd



,因此您可以跳转到相同的EIP但使用不同的CS并获得不同的说明。

在此示例中,此代码首先在32b模式下执行:

00: 63D8   arpl   ax,bx
02: 48     dec    eax
03: 01C0   add    eax,eax
05: CB     retf


,然后在64位模式下重新执行为:

在这种情况下,指令是重叠的,不是因为EIP不同,而是因为CPU暂时从32b模式更改为64b模式。

评论


在任何主要操作系统上的用户空间中运行的程序是否都可以从32位模式更改为64位模式?

–道格拉
13年4月9日,0:26

@Dougall是的。在Windows上,这是通过X86SwitchTo64BitMode()完成的(或使用段选择器33通过远距离调用/跳转手动完成)。但是,我非常确定这是特定于Windows WOW64实现的,不适用于其他OS。

–user45891
2014年9月4日在20:17

@Ange,您可以更新链接code.google.com/p/corkami/source/browse/trunk/src/CoST/…吗?谢谢!

–robert
17年7月3日在9:09

#3 楼

在x86 / x86_64中,几乎所有多字节指令都可以用作重叠指令。原因很简单:x86和x86_64指令集是CISC。这意味着,其中的一条指令没有固定的长度。因此,由于指令是可变长度的,请仔细地编写该机器代码,每条指令都容易隐藏重叠的指令。例如,给定以下代码:

[0x00408210:0x00a31e10]> b
0x000050f5 (01) 56                     PUSH ESI 
0x000050f6 (04) 8b742408               MOV ESI, [ESP+0x8] 
0x000050fa (01) 57                     PUSH EDI 
0x000050fb (03) c1e603                 SHL ESI, 0x3 
0x000050fe (06) 8bbe58a04000           MOV EDI, [ESI+0x40a058] 
0x00005104 (01) 57                     PUSH EDI 
0x00005105 (06) ff15f4804000           CALL 0x004080f4  ; 1 KERNEL32.dll!GetModuleHandleA
0x0000510b (02) 85c0                   TEST EAX, EAX 
0x0000510d (02) 750b                   JNZ 0x0000511a   ; 2 


假设在最后一条指令之后的某处,显示的代码中某条指令的中间出现了跳转,例如,跳转到MOV ESI ...指令的第二个字节:

[0x000050f7:0x00405cf7]> c
0x000050f7 (02) 7424                   JZ 0x0000511d    ; 1 
0x000050f7 ----------------------------------------------------------------------
0x000050f9 (03) 0857c1                 OR [EDI-0x3f], DL 
0x000050fc (02) e603                   OUT 0x3, AL 


原来,该指令已更改为JZ。哪个有效跳转到第3个字节...

[0x000050f7:0x00405cf7]> s +1
[0x000050f8:0x00405cf8]> c
0x000050f8 (02) 2408                   AND AL, 0x8 
0x000050fa (01) 57                     PUSH EDI 
0x000050fb (03) c1e603                 SHL ESI, 0x3 
0x000050fe (06) 8bbe58a04000           MOV EDI, [ESI+0x40a058] 


跳转到CALL指令的第2个字节:

[0x000050f5:0x00405cf5]> s 0x5106
[0x00005106:0x00405d06]> c
0x00005106 (05) 15f4804000             ADC EAX, 0x4080f4    ; '\x8e\x91'
0x0000510b (02) 85c0                   TEST EAX, EAX 
0x0000510d (02) 750b                   JNZ 0x0000511a   ; 1 


您可以看到,几乎所有多字节指令都可以用作重叠指令。

这种反反转技巧经常与不透明谓词一起使用,以使f ** k流程图。

评论


因此,您的意思是没有办法建立这样的列表?关于x86 / x86-64操作码,令我惊讶的另一点是,它在一段时间后可以与原始指令流重新同步。此属性还有助于使指令重叠。虽然,我不知道为什么重新同步如此出色。

–恐怖
13年4月4日在8:20



#4 楼

因为x86指令可以是任意长度,并且不需要对齐,所以一条指令的立即值可以是另一条指令。例如:

00000000  0531C0EB01        add eax,0x1ebc031
00000005  055090EB01        add eax,0x1eb9050
0000000A  05B010EB01        add eax,0x1eb10b0
0000000F  EBF0              jmp short 0x1


这完全按照它说的去做,直到跳转为止。当它跳转时,被添加到eax的立即值成为一条指令,因此代码如下所示:

手柱。在此示例中,短跳转指令用于跳过指令的add eax部分(05)。应该注意的是,可以通过使用单字节吃掉05来更有效地完成操作,例如3C05就是cmp al, 0x5,并且在不关心标志的代码中是无害的。

在上述模式中,您可以轻松地将所有05替换为90(nop),以查看正确的拆卸方法。通过将05用作隐藏代码的立即值(执行依赖),可以使此过程更加棘手。实际上,混淆代码的人可能不会一遍又一遍地使用add eax,并且可能会更改执行顺序以使其更难以跟踪。

我使用上述模式准备了一个示例。这是base64中的32位Linux ELF文件。隐藏代码的效果正在运行execve("//usr/bin/python", 0, 0)。我建议您不要从SE答案中运行随机二进制文件。但是,您可以使用它来测试反汇编程序。乍一看,IDA,Hopper和objdump都惨败,尽管我想您可以使IDA以某种方式正确地执行此操作。