29.2 可执行代码

29.2.1

可执行代码花指令的意思是在真实的代码中插入一些垃圾代码,但是保证原有程序的执行正确。

举个简单的例子:

  1. add eax, ebx
  2. mul ecx

代码清单29.1: 花指令

  1. xor esi, 011223344h ; garbage
  2. add esi, eax ; garbage
  3. add eax, ebx
  4. mov edx, eax ; garbage
  5. shl edx, 4 ; garbage
  6. mul ecx
  7. xor esi, ecx ; garbage

这里的花指令使用原程序代码中没有使用的寄存器(ESI和EDX)。无论如何,增加花指令之后,原有的汇编代码变得更为枯涩难懂,从而达到不轻易被逆向工程的效果。

29.2.2 替换与原有指令等价的指令

  1. mov op1, op2可以替换为 push op2/pop op1这两条指令。
  2. jmp label可以替换为 push label/ret这两条指令,IDA将不会显示被引用的label
  3. call label可以替换为push label_after_call_instruction/push label/ref这三条指令。
  4. push op可以替换为 sub esp, 4(或者8)/mov [esp], op这两条指令。

29.2.3 绝对被执行的代码与绝对不被执行的代码

如果开发人员肯定ESI寄存器始终为0:

  1. mov esi, 1
  2. ... ; some code not touching ESI
  3. dec esi
  4. ... ; some code not touching ESI
  5. cmp esi, 0
  6. jz real_code
  7. ;fakeluggage
  8. real_code:

逆向工程需要一段时间才能够执行到real_code。这也被称为opaque predicate。 另一个例子(同上,假设可以肯定ESI寄存器始终为0):

  1. add eax, ebx ; real code
  2. mul ecx ; real code
  3. add eax, esi ; opaque predicate. XOR, AND or SHL, etc, can be here instead of ADD.

29.2.4打乱执行流程

举个例子,比如执行下面这三条指令:

  1. instruction 1
  2. instruction 2
  3. instruction 3

可以被替换为:

  1. begin:
  2. jmp ins1_label
  3. ins2_label:
  4. instruction 2
  5. jmp ins3_label
  6. ins3_label:
  7. instruction 3
  8. jmp exit
  9. ins1_label:
  10. instruction 1
  11. jmp ins2_label
  12. exit:

29.2.4使用间接指针

  1. dummy_data1 db 100h dup (0)
  2. message1 db hello world’,0
  3. dummy_data2 db 200h dup (0)
  4. message2 db another message’,0
  5. func proc
  6. ...
  7. mov eax, offset dummy_data1 ; PE or ELF reloc here
  8. add eax, 100h
  9. push eax
  10. call dump_string
  11. ...
  12. mov eax, offset dummy_data2 ; PE or ELF reloc here
  13. add eax, 200h
  14. push eax
  15. call dump_string
  16. ...
  17. func endp

IDA仅会显示dummy_data1和dummy_data2的引用,但无法引导到文本字符串,全局变量甚至是函数的访问方式都可能使用这种方法以达到混淆代码的目地。