您的位置:首页 > 编程语言

恶意代码分析实战 Lab 6-1 习题笔记

2017-09-12 18:03 501 查看

Lab 6-1

问题

1.在main函数调用的唯一子过程中发现的主要代码结构是什么?

解答: 我们照着书中的步骤走一遍

先静态分析一下



然后我们会发现这个
WININET.DLL
的导入有个函数
InternetGetConnectedState
,然后我们查询一下
MSDN
的说明



这是用于检测本地系统的连接状态的

这个函数会主要有这么几个值选项



LAN
方式,也有
MODEM
拨号方式,还有
OFFLINE
不在线状态,到此我们大概知道了这个函数是干什么的了



我们还发现这个比较不常见的函数
GetACP
GetCPinfo




这个
GetACP
函数是干这个的

然后就是
GetCPinfo




然后还要注意的就是这个
GetCommandLineA
,这个是获取输入参数的,比如

./a.exe a b c


GetCommandLineA
之后就会获得
./a.exe a b c
这个字符串

说明这个程序有可能需要输入参数

然后我们检查一下字符串



这是我们从书中看到的那个字符串,除了这个,我们还可以看到这些有趣的字符串



这个程序有可能会弹出一个窗口(
MessageBoxA




这是整个程序的大概函数执行过程

然后我们点开
Flow chart




会显示这个图



调用函数
sub_401000
之后,将函数的返回值存入
[ebp+var_4]
中,我们查看一下
sub_401000
的内容,看看会返回什么值



这段代码调用了
InternetGetConnectState
之后,将返回值
eax
也存入了
[ebp+var_4]
,然后比较了返回值和
0
的大小

MSDN
中写着



Returns TRUE if there is an active modem or a LAN Internet connection, or FALSE if there is no Internet connection, or if all possible Internet connections are not currently active. For more information, see the Remarks section.

也就是如果网络是可用的,就返回
TRUE
也就是
1
,反之亦然

call    ds:InternetGetConnectedState
mov     [ebp+var_4], eax
cmp     [ebp+var_4], 0
jz      short loc_40102B


我们来看这段汇编,如果网络可用,返回了
1
1>0
,所以
ZF
标志位为
0
jz
不跳转



然后输出
"Success: Internet Connection\n"
,这里并没有直接用
printf
系列的函数输出这个字符串,而是存入了
buf


然后如果相反,则
jz
跳转



则输出这个
"Error 1.1: No Internet\n"
,逻辑上和我们分析的一样

然后这个很明显的就是
if
语句的一般形式

int internet_status = InternetGetConnectedState();
if(internet_status > 0)
{
some_function("Success: Internet Connection\n");
}
else another_function("Error 1.1: No Internet\n");


这个函数大概就是这么个流程

然后还有最开始那个跳转的地方,是根据上面那个函数的返回值判断的

而这个函数的返回值是这样生成的



如果成功了(也就是可以联网了),则

mov eax, 1


然后返回的也就是
1


如果不可以了,则

xor eax, eax


然后返回的就是
0


我们回到主函数



这里将函数
sub_401000
的返回值和
0
比较,我们来分析一下这个逻辑顺序

如果可以联网了,返回
1
,然后
cmp 1. 0
的话,
ZF=0
jnz
跳转,然后就去执行
loc_401056
地方的代码了,最终
retn 1


反之,如果不可以联网,则最终
retn 0


于是我们可以写出这个汇编的C语言的大概样子

// 下面这些一般都是基本字符处理会用到的
// 但是这个程序不一定都用得到
// 只是为了给这个函数有点C语言的样子
// 注意:这是纯C代码,不是VC++代码
// 涉及到Windows API的部分我会用伪代码来表示
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int sub_401000(void)
{
// 下面这些是一些windows C#的代码
// 需要在开头导入一些Windows的SDK才能使用(这里没有导)
DWORD lpdwFlags = 0;
DWORD dwReserved = 0;
BOOL Online;

Online=InternetGetConnectedState(&lpdwFlags, &dwReserved);
if(Online = TRUE)
{
// sub_40105F是干什么的函数,我们下一个问题分析
sub_40105F("Success: Internet Connection\n");
return 1;
}
sub_40105F("Error 1.1: No Internet\n");
return 0;
}

int main(void)
{
// 因为汇编中就是var_4,然后我又懒得给他去个正常程序会用到的名字
// 一般这个名字会是像a,b,c,n,m这样的名字
int var_4 = -4;

var_4 = sub_401000();
if(var_4 > 0)
{
return 1;
}
else return 0;
}


具体
sub_40105F
是干什么的,我们下面分析

2.位于0x40105F的子过程是什么?

解答: 书中的分析是这样的

其中一种方法是,找到调用子例程前被压到栈上的参数,在这里的两处,都有一个格式化字符串被压栈,并且字符串结尾是\n这个换行符

然后就推断这个函数是
printf
,但是这未免有点牵强(我感觉)

我们来看这个函数



我把它复制出来一步一步分析

先对比
printf
的内部实现是这样的

int __cdecl printf (
const char *format,
...
)
/*
* stdout 'PRINT', 'F'ormatted
*/
{
va_list arglist;
int buffing;
int retval;

va_start(arglist, format);

_ASSERTE(format != NULL);

_lock_str2(1, stdout);

buffing = _stbuf(stdout); // 记住这个函数_stbuf

retval = _output(stdout,format,arglist); // 还有记住这里调用了一次外部函数

_ftbuf(buffing, stdout); // 记住这个函数_ftbuf

_unlock_str2(1, stdout);

return(retval);
}


然后再来看这段汇编就会好理解多了

sub_40105F proc near

arg_0= dword ptr  4
arg_4= dword ptr  8

push    ebx ; 这个和下面这个都不用管
push    esi
mov     esi, offset stru_407098 ; 这个就很重要了
push    edi
push    esi
call    __stbuf ; __stbuf的入参是最后一个push进去的那个
; 也就是上面那个文件描述符
; 如果这个入参是stdout则就是输出到标准输出
; 记得上面的_stbuf函数不?
mov     edi, eax
lea     eax, [esp+10h+arg_4]
push    eax             ; int
push    [esp+14h+arg_0] ; int
push    esi             ; FILE *
call    sub_401282      ; 记得上面那个调用外部函数不?
push    esi
push    edi
mov     ebx, eax
call    __ftbuf         ; 记得上面的那个_ftbuf函数不?
add     esp, 18h
mov     eax, ebx
pop     edi
pop     esi
pop     ebx
retn
sub_40105F endp


如果你记住了上面提的那三个地方的函数,这个汇编你一看就会明白了

但是这里输入的是
offset stru_407098
,而不是像
printf
的内部实现那里的
stdout
,然后我们查看一下这个汇编里的文件描述符



然后我们可以继续看看这个文件是啥



复制出来就是

FILE <0, 0, 0, 2, 1, 0, 0, 0>


然后这个代表什么意思,因为windows是闭源的操作系统,所以我Google了半天也没找到windows的文件描述符(file descriptor)的结构(structure)

只找到一个C语言学习网站里面的描述,是这样的

typedef struct
{
short level ;           1
short token ;           2
short bsize ;           3
char fd ;               4
unsigned flags ;        5
unsigned char hold ;    6
unsigned char *buffer ; 7
unsigned char * curp ;  8
unsigned istemp;        9
}FILE ;


这个有
9
个项,但是我们从
IDA
里面拷出来的就
8
个项

1  2  3  4  5  6  7  8
FILE <0, 0, 0, 2, 1, 0, 0, 0>


这个版本不对,我们继续找,终于找到一个,
stackoverflow
上有人说,这个定义在
stdio.h


但是我
/usr/include/stdio.h
找了半天没找到这个定义,我发现这个



注意看着绿色那行

typedef struct _IO_FILE FILE;


学过C语言的同学都知道,这是将
struct _IO_FILE
定义为
FILE


对C语言不熟的同学我稍微解释一下

也就是平时可能我们定义一个
FRIENDS
结构是这样的

struct FRIENDS{
int age;
char name[20];
};


然后要使用这个结构的话

struct FRIENDS one_friend;


但是如果我们在代码
include
的下面加上这句话

typedef struct FRIENDS FRIENDS;


那么现在
struct FRIENDS
=
FRIENDS


加了这句话我们使用这个结构就是这样了

FRIEND one_friend;


所以从刚刚这个



我们可以看出这个
FILE
的结构其实就是
_IO_FILE
的结构,现在我们就去找这个结构

其实这个结构就在
/usr/include/libio.h
里面,当然最后会发现,这是
Linux
的文件描述符的结构

但是这个太长了,我就不贴出来了,我们继续找windows下的

然后有人说,在
MinGW
里面有,
MinGW
Minimalist GNU for Windows
的缩写



最终在这里github上的mingw代码找到了这个定义

然后跑太远,回来,对比一下我们的

FILE <0, 0, 0, 2, 1, 0, 0, 0>


根据这个来,我们的这个文件描述符的各项的对应关系就是

struct _iobuf {
char *_ptr;      // -> 0
int _cnt;        // -> 0
char *_base;     // -> 0
int _flag;       // -> 2
int _file;       // -> 1
int _charbuf;    // -> 0
int _bufsiz;     // -> 0
char *_tmpfname; // -> 0
};
typedef struct _iobuf FILE;


然后这就好理解了

这里其他的都可以用不用看,主要来看看这个
_file
这个

这个代表了打开的文件在系统中的编号,一般我们编程的时候打开的句柄(也就是文件描述符)编号都是比较大的,因为本身系统就已经打开了比较多的文件原因

但是一般有三个文件的文件描述符是固定写死的,还比较小,那就是
stdin
stdout
stderr


在系统中对应的值就是

stdin  -> 0
stdout -> 1
stderr -> 2


然后我们搞了这么几百字,就可以搞明白这个文件描述符是干啥的了,这个文件描述符指向的是
stdout
,一般这个在计算机上是代表屏幕

所以根据前面的贴出来的
printf
函数的内部实现和对照汇编代码,我们最后发现这个文件描述符是
stdout
,我们基本就可以确定这个函数就
printf


3.这个程序的目的是什么?

解答: 上一问发散的有点深,就光分析那一个小函数去了

这个如果你能跟着我的思路来到这里,那现在恶意程序的全部代码都已经被我们吃透了

这个就是一个检测本地能不能使用网路连接的程序,恶意代码可以用于检测本地的连接是否ok,如果ok,程序会打印那个字符串,然后返回
1
,如果不ok,那也是打印一个字符串,然后返回
0


本文完
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  代码分析 病毒