我目前正在分析一个恶意软件。我已经确定了几种类型的shell,这个特别是一个icmp shell。在下面的屏幕截图中,您可以看到0x804af87处的值被推入堆栈,即“icmp”。这是为了满足getprotobyname中的“ char * name”。
然而,在拨打电话后,有一些说明对我没有意义。我理解这些指令本身,但是我无法理解他们对这个icmp shell的看法。
例如,在 getprotobyname 调用之后,立即将0x10添加到堆栈指针。这样做的目的是什么?并按顺序执行以下说明。
答案 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]
让我们一次看一下这条指令,以确保我们了解他们正在做的事情:
mov DWORD PTR [ebp-0x1c], 0x1f90
这将常量0x1f90
存储在堆栈上,特别是位于ebp-0x1c
的位置,该位置是当前基指针(ebp
)后面的28个字节。
sub esp, 0xc
这会从堆栈指针(esp
)中减去12个字节,这实际上会在堆栈上分配一些空间。您将要么这样做,因为您需要在那里存储一些数据,或者因为您要在需要特定对齐的ABI下进行函数调用(例如the System V ABI for x86)。
push 0x804af87
这会将常量值0x804af87
推送到堆栈上,这会将堆栈指针隐式递减4个字节并将值存储在那里。
这很可能是在为函数调用做准备,因为许多x86调用约定都在栈上传递参数。
call 0x8048cc0 <getprotobyname@plt>
这会调用getprotobyname
函数,位于绝对地址0x8048cc0
。
add esp, 0x10
这会向堆栈指针(esp
)添加16个字节,从而清除我们先前在堆栈上创建的空间。回想一下,我们先前已经将堆栈指针递减了12个字节,然后将一个4字节的值压入堆栈,总共16个字节。该指令有效地“解除”了这一点,释放了我们不再需要的堆栈空间。
一些调用约定要求调用者在函数调用后清理堆栈;显然,这段代码正在使用这样的调用约定。
mov DWORD PTR [ebp-0x102c], eax
在此代码的调用约定中,函数的返回值存储在eax
寄存器中。 (对于我所知道的所有x86调用约定中的所有整数大小的返回值都是如此。)因此,getprotobyname
函数(在eax
中)的结果将存储在内存中,位置ebp-0x102c
,在当前基指针(ebp
)后面4140个字节。
sub esp, 0x4
从堆栈指针中减去4个字节,有效地在堆栈上分配4个字节的存储空间。
mov eax, DWORD PTR [ebp-0x102c]
这将检索位于ebp-0x102c
位置的内存中的值,并将其存储在eax
寄存器中。
现在我们已经了解了这里发生了什么,很明显这是次优代码,其中一些指令是冗余/多余的。
特别是,getprotobyname
函数的返回值从eax
开始,存储到内存中,然后从内存中检索并放回eax
。这些都不是必要的。所有这些说明都可以消除。
此外,向堆栈指针添加16个字节,然后从堆栈指针中减去4个字节与向堆栈指针添加12个字节相同。因此,这些add
和sub
指令可以组合成一个。没有明显的理由让他们单独指示。
我猜想这是你正在查看的未经优化的代码(例如可能是由禁用了优化的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
调用约定。详细信息可在x86标记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生成,这将演示编译器堆栈对齐问题。