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

Lab 6-1

问题

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

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

先静态分析一下

静态分析

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

这个函数

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

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

值

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

KERNEL32.DLL

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

GetACP

这个GetACP函数是干这个的

然后就是GetCPinfo

这个

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

./a.exe a b c

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

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

然后我们检查一下字符串

字符串

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

字符串

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

结构

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

然后我们点开Flow chart

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

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

图

然后输出"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=0jnz跳转,然后就去执行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里面有,MinGWMinimalist 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这个

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

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

在系统中对应的值就是

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

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

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


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

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

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

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

本文完

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值