此次致电(装配)后的以下说明是什么

时间:2017-02-01 03:37:31

标签: assembly x86

我目前正在分析一个恶意软件。我已经确定了几种类型的shell,这个特别是一个icmp shell。在下面的屏幕截图中,您可以看到0x804af87处的值被推入堆栈,即“icmp”。这是为了满足getprotobyname中的“ char * name”。

icmp_shell asm screenshot

然而,在拨打电话后,有一些说明对我没有意义。我理解这些指令本身,但是我无法理解他们对这个icmp shell的看法。

例如,在 getprotobyname 调用之后,立即将0x10添加到堆栈指针。这样做的目的是什么?并按顺序执行以下说明。

2 个答案:

答案 0 :(得分:2)

以下是您正在分析的代码,文字形式:

mov   DWORD PTR [ebp-0x1c], 0x1f90
sub   esp, 0xc
push  0x804af87
call  0x8048cc0 <getprotobyname@plt>
add   esp, 0x10
mov   DWORD PTR [ebp-0x102c], eax
sub   esp, 0x4
mov   eax, DWORD PTR [ebp-0x102c]

让我们一次看一下这条指令,以确保我们了解他们正在做的事情:

  1. mov DWORD PTR [ebp-0x1c], 0x1f90

    这将常量0x1f90存储在堆栈上,特别是位于ebp-0x1c的位置,该位置是当前基指针(ebp)后面的28个字节。

  2. sub esp, 0xc

    这会从堆栈指针(esp)中减去12个字节,这实际上会在堆栈上分配一些空间。您将要么这样做,因为您需要在那里存储一些数据,或者因为您要在需要特定对齐的ABI下进行函数调用(例如the System V ABI for x86)。

  3. push 0x804af87

    这会将常量值0x804af87推送到堆栈上,这会将堆栈指针隐式递减4个字节并将值存储在那里。

    这很可能是在为函数调用做准备,因为许多x86调用约定都在栈上传递参数。

  4. call 0x8048cc0 <getprotobyname@plt>

    这会调用getprotobyname函数,位于绝对地址0x8048cc0

  5. add esp, 0x10

    这会向堆栈指针(esp)添加16个字节,从而清除我们先前在堆栈上创建的空间。回想一下,我们先前已经将堆栈指针递减了12个字节,然后将一个4字节的值压入堆栈,总共16个字节。该指令有效地“解除”了这一点,释放了我们不再需要的堆栈空间。

    一些调用约定要求调用者在函数调用后清理堆栈;显然,这段代码正在使用这样的调用约定。

  6. mov DWORD PTR [ebp-0x102c], eax

    在此代码的调用约定中,函数的返回值存储在eax寄存器中。 (对于我所知道的所有x86调用约定中的所有整数大小的返回值都是如此。)因此,getprotobyname函数(在eax中)的结果将存储在内存中,位置ebp-0x102c,在当前基指针(ebp)后面4140个字节。

  7. sub esp, 0x4

    从堆栈指针中减去4个字节,有效地在堆栈上分配4个字节的存储空间。

  8. mov eax, DWORD PTR [ebp-0x102c]

    这将检索位于ebp-0x102c位置的内存中的值,并将其存储在eax寄存器中。

  9. 现在我们已经了解了这里发生了什么,很明显这是次优代码,其中一些指令是冗余/多余的。

    特别是,getprotobyname函数的返回值从eax开始,存储到内存中,然后从内存中检索并放回eax。这些都不是必要的。所有这些说明都可以消除。

    此外,向堆栈指针添加16个字节,然后从堆栈指针中减去4个字节与向堆栈指针添加12个字节相同。因此,这些addsub指令可以组合成一个。没有明显的理由让他们单独指示。

    我猜想这是你正在查看的未经优化的代码(例如可能是由禁用了优化的C编译器生成的),或者这是由程序员故意完成的用于填充(而不是插入nop指令)。

    更优化(更理智)的代码版本如下:

    mov   DWORD PTR [ebp-0x1c], 0x1f90     ; store value in memory
    sub   esp, 0xc                         ; allocate 12 bytes of stack space
    push  0x804af87                        ; push function argument
    call  0x8048cc0 <getprotobyname@plt>   ; call function
    add   esp, 0xc                         ; clean up stack space
    ; function's return value is now in EAX—use as desired
    ; ...
    ; if you like, store it in memory:
    ; mov   DWORD PTR [ebp-0x102c], eax
    

    正如Gene在评论中所建议的,请务必告知自己标准的x86调用约定 - 特别是__cdecl调用约定。详细信息可在标记wiki中找到。

答案 1 :(得分:1)

sub   esp, 0xc
push  0x804af87
call  0x8048cc0 <getprotobyname@plt>
add   esp, 0x10
  

这从堆栈指针(esp)中减去12个字节,这实际上在堆栈上分配了一些空间。您可以这样做,因为您需要在那里存储一些数据,或者因为您即将进行需要它的函数调用。

对于指令,它不用于保留一些临时空间,它实际上用于堆栈对齐(参考here)。

根据gcc的手册:-mpreferred-stack-boundary = num =&gt;尝试将堆栈边界保持对齐2到num字节边界。如果未指定-mpreferred-stack-boundary,则默认值为4(16字节或128位)。

因此,如果在gcc命令行中附加“-mpreferred-stack-boundary = 2,3或4”,那么您将获得不同的asm生成,这将演示编译器堆栈对齐问题。