恶意代码分析实战 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
本文完
相关文章推荐
- 恶意代码分析实战 Lab 6-3 习题笔记
- 恶意代码分析实战 Lab 4 习题笔记
- 恶意代码分析实战 Lab 1-4 习题笔记
- 恶意代码分析实战 Lab 8 习题笔记
- 恶意代码分析实战 Lab 6-4 习题笔记
- 恶意代码分析实战 Lab 7-3 习题笔记
- 恶意代码分析实战 Lab 3-2 习题笔记
- 恶意代码分析实战 Lab 9-3 习题笔记
- 恶意代码分析实战 Lab 10-2 习题笔记
- 恶意代码分析实战 Lab 7-2 习题笔记
- 恶意代码分析实战 Lab 9-1 习题笔记
- 恶意代码分析实战 Lab 3-4 习题笔记
- 恶意代码分析实战 Lab 1-3 习题笔记
- 恶意代码分析实战 Lab 1-2 习题笔记
- 恶意代码分析实战 Lab 9-2 习题笔记
- 恶意代码分析实战 Lab 3-3 习题笔记
- 恶意代码分析实战 Lab 3-1 习题笔记
- 恶意代码分析实战 Lab 10-1 习题笔记
- 恶意代码分析实战 Lab 5-1 习题笔记