恶意代码分析实战 Lab 7-3 习题笔记
2017-09-30 14:06
1681 查看
Lab 7-3
问题
1.这个程序如何完成持久化驻留,来确保在计算机被重启后它能继续运行?
解答: 我们还是先开始按静态分析来开始我们的分析(这里同时要分析.dll和
.exe)
我们会发现这里有一个
CreateFileA和
CopyFileA这两个函数,说明会创建一个文件和复制一个文件,这个创建文件可能会是什么日志之类的,复制文件可能是把病毒复制到某个地方
然后是
FindFirstFileA和
FindNextFileA这两个函数,说明这个函数会在系统中查找什么文件
然后
CreateFileMappingA和
MapViewOfFile告诉我们,这个程序会打卡一个文件,然后将它映射到内存中
但是在导入表中我们并没有发现
LoadLibrary或者
GetProcAddress,说明这个函数并没有在运行的时候加载这个
DLL
然后看看字符串
这里指示了一个路径
C:\\windows\\system32\\kerne132.dll,然后这里的确会很迷惑人,书中不说我都没看见~
.data:0040304C 00000021 C C:\\windows\\system32\\kerne132.dll
这里注意是
132.dll,为了方便解释,最后那三个是数字
132
我们再来分析一下这个
dll文件
还是先从导入表开始看起
这个
dll文件会创建和打开一个互斥变量,也就是这个函数
CreateMutexA和
OpenMutexA
然后还会创建进程
CreateProcessA这个函数
最后还会调用
Slee函数来休眠
其他函数没什么价值,我们分析分析字符串
这里有个
exec这个字符串,有可能这个程序是个后门程序,只是猜测,然后还有个
127.26.152.13这个
ip,看到这个
ip就很有可能是个后门木马了
然后我们继续书中的介绍,来找一下导出函数
标黄那里就是导出函数所在的地方,先点选
LAB07-03.DLL,然后再查看黄色标注的这里,如果有导出函数,这里就会显示,但是这里并没有
然后便是开始
IDA分析,因为动态分析书上并没有得出什么结论,我们也不浪费什么时间了,直接开搞
我们根据书中的步骤
[b]我们开始分析
dll这个文件[/b]
书上的分析方法是直接只列出
call语句的代码,然后这样分析,我们还是老规矩,毕竟也不是很长的代码,我们还是先一条一条分析下去看看
一开始是调用了这个
__alloca_probe这个函数,这个函数是用来在空间中分配栈空间的函数,然后这个函数的入参是
11F8h也就是
4600d,然后我们继续往下看,
IDA将
fdwReason的值赋值给了
eax,这里的
[esp+11F8h+fdwReason]这里说明已经将
[esp+11F8h]这个地方分配出去了
最后我们将返回值和
1比较大小,如果不等于
1呢,则下面的
jnz跳转执行,
jnz跳转之后就马上执行了返回,所以这个代码是希望这个
eax也就是
fdwReason是等于
1的
然后我们继续分析主干
这里我们是先将这个
byte_10026054赋值给
al,我们来看看这个
byte_10026054是个啥
db是申请一个字节然后,后面的
0代表了存储的数据
所以这里是将
al置为
0的意思
然后呢将
al也就是
0存入
[esp+1208h+buf]的位置之后,将
eax置为
0,然后就是用
OpenMutexA打开了一个叫
SADFHUHF的互斥量,然后查看调用结果,如果结果
eax为
0了,
jnz不跳转,反之如果不为
0了,
jnz跳转
MSDN里面写明了这个
OpenMutexA的返回值,逻辑上归纳一下就是,如果调用失败,返回
NULL,在计算机中也就是
0,
jnz不会跳转,继续执行代码,反之如果调用成功,则
jnz跳转,跳转之后我们顺着箭头可以看到是结束执行了
所以这是判断当前系统中是否有相同程序的作用,一个系统中只能运行一个这个程序
然后如果
OpenMutexA调用失败,执行上面这段代码,也就是没跳转之后执行的代码
这里是调用
CreateMutexA来创建一个叫
SADFHUHF互斥量,然后在调用
WSAStartup这个函数,这个函数是干什么的呢
这个函数是
Windows异步套接字启动命令,从
MSDN中我们可以分析这个函数 入参有哪些
从
IDA的标注中我们可以看到,这个函数是这样的入参模式
wVersionRequested的值是
202h,而
lpWSAData的值是
ecx,我们继续分析这个入参会做什么
这里说,这个
wVersionRequested是
调用者可以使用的最高版本的Windows Sockets规范。高位字节指定次要版本号;低位字节指定主版本号
那根据这个入参
202h换算成二进制就是
0000,0010,0000,0010,分成高字节和低字节之后就是
(00000010, 00000010)也是就
(2.2)
所以这里指定的套接字版本是
2.2
而这个
lpWSAData是
指向WSADATA数据结构的指针,用于接收Windows Sockets实现的详细信息
这里的返回值是这样的
如果成功了,返回
0
逻辑上总结一下就是,
WSAStartup之后,返回值经过那个
test之后,如果成功,返回
0,然后
jnz不跳转,反之如果不成功了,跳转结束程序
然后我们走主干,假设调用成功了,程序就会来到这里执行这个,这里有几个不明确的值,但是
IDA已经标注出来这个什么类型的
这里我们可以根据以前的分析方法,右键选择这个值所代表的类型,然后把他替换成能看懂的代码
下面就是我替换之后的代码
这里是初始话了一个
TCP的
INET连接,然后将返回值赋值给
esi,之后和
0,FFFF,FFFFh进行比较
这个值换算成十进制之后就是
4294967295d,然后我们会发现这么大的一个值,在
MSDN的
Windows Sockets Error Codes根本没有
于是我们联想到有符号数,这个值有可能是个负值,根据以前我们介绍过的计算方法,先将这个赋值减
1,然后计算反码,然后在将非符号位转换成十进制
就是这样
FFFF,FFFF-
1=
FFFF,FFFE
然后除了符号位之外,全部取反码,最后就是
1000,0000,0000,0000,0000,0000,0000,0001
最后换算成十进制就是
-1d
如果返回值大于
-1d的话,
jnz跳转,
jz不跳转
所以逻辑上归纳一下就是,如果返回值大于
-1d的话,函数继续执行,不跳转
反之返回值小于等于
-1d的话,
jz跳转,之后就是做一些清理工作就退出程序了
假设函数没有跳转,之后便会执行这些函数
这里主要是执行了这个函数
connect
这里
IDA已经帮我们标注参数出来了
s的值是
esi
name的值是
edx
namelen的值是
10h
这里的
namelen好理解,
10h换算成十进制也是
10d
其他的
s的
esi代表的是刚刚我们
WSAStartup初始话之后保存在
esi栈中的套接字,这个我们不用管他太多
然后就是
edx的值,从上面一直从上面看下来,我们会发现,其实
edx指向的就是
127.26.152.13:80,当然,这个时候已经不是
127.26.152.13:80这个值了,经过
hton一系列变化之后已经从主机(Host)序转换成网络(Network)序了,,注意调用
hton之前
push进栈的
50h,这个是端口号,然后网络序是计算机在网络上通信使用的底层编码,我们知道这个值代表了这个
IP和端口就够了
然后这个
connect函数的返回值是这样的
这个图片翻译过来就是如果没有错误,就返回
0
最后这个判断和上个代码片是一样的
逻辑上就是如果返回值大于
-1d的话(也就是返回值是
0),函数继续执行,
jz不跳转
反之返回值小于等于
-1d的话,
jz跳转,之后就是做一些清理工作就退出程序了
然后依旧假设程序返回值是
0,然后继续分析主干代码
之后便会来到上面这个图片这里的位置
这里是将
ebp存储
strncmp函数的位置,其实就是指向
strncmp函数的一个指针
ebx也是同样的道理,是指向
CreateProcessA的指针
然后这里没有跳转,继续往下执行,下面就是
注意这里的
or运算,运算规则在下
0∨0=0 0∨1=1 1∨0=1 1∨1=1
可以看出来,只有有一个
1,最后的结算结果就是
1,而我们运算的第二个因子是
FFFF,FFFFh,所以分析可知,这个
or运算的目的是将
ecx全部置
1
然后将
eax全部置
0
之后最主要的就是调用了
send这个函数,这里最主要的是将
buf里面的值
hello发送出去
然后我们可以从图片中看出,这个函数的一些入参,这里的
flags一般置为
0
然后我们看一下返回值
如果没出错的话,返回的是发送的字节数,这个置一般大于
0
在代码片段的最后,有个判断的地方
这里的结构和上面的一样的,如果返回值大于
-1d的话,函数继续执行,
jz不跳转
反之返回值小于等于
-1d的话,
jz跳转,这里
jz跳转也是跳转结束
逻辑上就是如果
send函数出错的话,跳转函数结束
然后我们依旧假设
send没有报错,我们就会来到这里
注意这里调用了
shutdown函数,这个
shutdown函数的入参是
esi和
1,我们查一下这个
shutdown函数的一些说明
然后依旧是替换成人类可读的代码
这个
SD_SEND的解释是这样的
这个意思就是关闭这个
socket连接的意思
然后也是比较返回值,如果调用失败,跳转结束函数
如果函数调用成功,则执行这个
然后我们会发现这个
这里没什么技术含量,就是
recv一个数据,然后存入
buf中
最后判断一个返回值
eax的值,
test的运算和
and运算一样,区别是不会保存结果
我们在
Lab 4的时候完整分析了
test会影响的标志位,然后最后会的出下面结论
其中一定的是
指令执行后 CF=0 OF=0
然后
JLE跳转的条件是
ZF=1 or SF<>OF
主要分析这三个标志位
ZF、
SF、
OF,因为也只有这三个标志位才会影响
JLE的跳转
我们把
eax分为这么三种情况
eax=0 eax>0 eax<0
如果
eax=
0,
and之后,结果为
0,所以
ZF=
1,然后不论
eax等于多少,
OF=
0,然后就是
SF,如果运算结果是正数,则
SF=
0,反之
SF=
1,
0是正数,所以
SF=
0
分析一圈之后就是
如果
eax=
0的话
ZF=1 OF=1 SF=0
根据
Lab 4我们讲的跳转条件
当
eax=
0的时候,
JLE会跳转
如果
eax>
0的话,按上面逻辑分析
ZF=0 OF=0 SF=0
这个
JLE不会跳转
如果
eax<
0的话,我们可以得出一下结论
ZF=0 OF=0 SF=1
这里
OF<>
SF,所以
JLE也会跳转
所以总结一下就是,当
eax<=
0的时候,
JLE会跳转,那什么时候会返回小于等于
0的值
recv的
MSDN说明里面说明了,这个函数是返回接受的字节数,如果数据已经传输完毕,然后没有接受到数据(
eax=
0),或者报错的时候(
eax<
0),
JLE就会跳转
这时候函数就会跳会这个代码片段以前的地方重复执行
然后有会重新发送一个
hello出去,然后关闭连接,接收一个回执,如果接收失败又跳回去发送
hello
这个代码片段如果用C语言来写的话,是这样的
iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 ); // 以下是发送失败跳转处理函数 if (iResult == SOCKET_ERROR) { //printf("send failed: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } //printf("Bytes Sent: %ld\n", iResult); // shutdown the connection since no more data will be sent // 关闭连接 iResult = shutdown(ConnectSocket, SD_SEND); if (iResult == SOCKET_ERROR) { //printf("shutdown failed: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } // 循环发送 // 接受数据失败跳转 do { iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); if ( iResult > 0 ) //printf("Bytes received: %d\n", iResult); do_something(iResult); else if ( iResult == 0 ) printf("Connection closed\n"); else printf("recv failed: %d\n", WSAGetLastError()); } while( iResult > 0 );
然后我们依旧假设我们接受数据成功了,接下来就会处理这个代码片段
我们看看
MSDN里面对
strncmp的定义
根据以前的知识,可以从汇编代码中得出一下C伪代码
const char string1[] = "sleep"; const char *string2 = buf; //buf是从recv获得的数据 size_t count = 5; strncmp(string1, string2, count);
strncmp的话会比较前
count个字符串相当或不相等,我们这里的
count是等于
5,所以也就是
sleep这个字符串的长度,它会比较接受的字符串是不是
sleep
然后
test一下
eax的值是不是为
0,然后如果不为
0了的话,
jnz跳转
逻辑上归纳一下就是,如果接收的字符串是
sleep的话,函数跳转到左边的地方执行,也就是调用
sleep函数来是进程休眠,休眠的时间是
6,0000h也就是
393216dms,也就是
393.216s,同时也是
6.053min,差不多就是6分钟
执行完这些后,这个代码片段跳到一开始发送
hello那里开始执行
如果接受的参数不是
sleep的话,执行左边的这串,这里是判断发送的字符串是不是
exec,如果是的话,跳转右边的红线
我们先看如果接收到不是
exec的情况,也就是左边这个,因为这个短一点
这里是一个比较
比较这个
buf的大小和
71h,
71h的话等于
113d,如果
buf=
71h的话,
ZF=1,那么
jz跳转,绿线,跳转之后就是结束程序
如果不相等的话,红线跳转,也就是下面的地方
这里会休眠一下,然后休眠完之后会跳转到一开始发送
hello那个地方,这里这个代码片段的作用应该是判断缓冲区是否大于某个值的
我们回到右边,如果接受的字符串是
exec,那么将执行下面这些函数
我们注意到这里的一个调用函数就是
CreateProcessA这个函数,我们去看看
MSDN的定义
然后根据汇编代码,我们可以整理出这些入参的具体值是多少~
lpApplicationName=0 lpCommandLine=edx lpProcessAttributes=0 lpThreadAttributes=0 bInheritHandles=1 dwCreationFlags=8000000h lpEnvironment=0 lpCurrentDirectory=0 lpStartupInfo=ecx lpProcessInformation=eax
然后我们剔除没有意义的初始值为
0的参数只剩下以下的参数
lpCommandLine=edx bInheritHandles=1 dwCreationFlags=8000000h lpStartupInfo=ecx lpProcessInformation=eax
然后吧其中的寄存器换成具体的变量就是
lpCommandLine=CommandLine bInheritHandles=1 dwCreationFlags=8000000h lpStartupInfo=StartupInfo lpProcessInformation=ProcessInformation
然后这里最重要的就是
CommandLine这个参数,表明了我们要为什么可执行文件创建一个教程来运行,但是我们点开这个
CommandLine的时候会发现这个在栈中的数据并没有表明具体是什么值
这里书中说的怎么判断
CommandLine是什么的方法有点笼统,我也是有点看的头大
我的分析方法是,先把
CommandLine建一个数组,然后就会发现,后面接的是
db 4091 dup(?)这个东西
我们知道这个
db是
double byte的缩写,然后在计算机中,没有3/2个字节的说法,这里我们整理出了
4091个字节的数组,联想到
2^12=
4096,而
4096和
4091差了
5个字节,刚好就是前面我们的
strncmp的
count的值加上一个空格的值,也就是,这个
4091是
4096减去了
exec和一个空格之后剩下的部分
然后也就是服务器发送来的字符串我们假设会是这样的
exec C:\\Windows\someshell.exe
然后程序将
exec+
(空格)剔除之后剩下的部分就是那个
CommandLine的部分,这个取决有服务器发送,是个不定值,无法从代码中看出来,所以这里的意思就是为这个
C:\\Windows\someshell.exe专门创建一个进程来运行它,这个可执行文件一般是事先就上传到服务器的病毒木马之类的
然后这个代码片段运行完之后,又返回到发送
hello那里继续循环执行,然后整个
DLL文件就分析完了
[b]下面我们分析
EXE文件[/b]
我们先从
main的地方开始
这里我们看到有个
argc这个东西,这是给这个可执行文件传入的参数的个数
这里代码将这个
argc和
2进行了比较之后,有个跳转
一般
argv(
argv是存储传入参数的数组,
argc是入参的个数)的第一个参数是这个函数的名字,第二个开始就是用户输入的参数
如果
eax=
2的话,
ZF=1,
jnz不会跳转,继续往下执行,如果不等于
2的话,函数跳转左边
左边我们看一下会是什么,左边是结束函数,这里说明这个
exe文件执行的时候需要在后面跟一个参数,我们继续
OK,我们假设我们入参是
2了,那往下将执行这些函数
然后这里有个作者写的提示字符串,我们可以不管,主要是这个第一行代码和第三行代码需要我们注意一下
这里将
eax指向了
argv(注意区分一开始的
argc)的开始地址,然后第三行这里又将
eax这个指针向后移动了
4个位,注意,这里是
4个位,不是字节,根据计算机尝试,
4位等于一个字节,也就是这时候
eax指向的是
argv[1]这个地方,也是就入参的具体值
然后继续往下执行
我们会看到上面这个代码,我们通过上一个图片的分析知道,这个
eax其实存储的
argv[1]的地址,然后现在
dl又通过这个指针的指针指向这个
argv[1],
esi存储了那句作者的提示信息,这里比较了这么两个指针指向的值是否相同
这有点像口令验证登录,口令就是这个
WARNING_THIS_WILL_DESTROY_YOUR_MACHINE
如果相同了之后,
ZF=
1,然后代码继续往下执行,如果不相同了,跳转
loc_401488这个地方,这个地方并不是结束函数
逻辑上归纳下就是如果
dl和
bl指向的值相同(也就是第一个字符),就往红线往下执行,如果不相同的话,则跳转
我们先按红色这跟线往下走看
这里用
test来检测这个
cl也就是指向
argv[1]的第一个字符是不是为
0(
cl在上一个图片的时候被赋值为
dl)
这里的
argv[1]可以看出字符串数组,用指针操作字符串数组
这么两个图片的意思和逻辑就是
如果入参的第一个字符相同了,那看看是不是为
0,如果是的话,
jz跳转,走绿色那条线,如果不是的话,继续走红线
红线走下来就是将
dl指向了字符串数组的第二个字符,然后再比较,最后直到比较完所有的字符串
如果不相同了,跳转来这里
然后直接将
eax置为
0
如果相等的话,跳转来这里
然后这里的
sbb是借位减法的意思,也就是
eax-eax-CF得到的值再保存在
eax中
这个
CF是上一步操作影响的标志位结果,我们可以看看这个代码片段都是哪里来的
会影响
CF标志位的地方就这么两个,都用黄色标注出来了,因为
cmp指令其实就是
sub指令的变种,只是
cmp没有保存结果而已
如果我们的
dl<
bl的话,就会存在借位,如果存在借位的话,那
sbb eax, eax
就会等于
sub eax, eax sub eax, CF
这时候的
CF=
1
最后的结果就是
eax=
-1d=
0FFFFFFFFh
第二个
sbb也是一样,前一个操作
0-1的情况,肯定存在借位,所以这里的
sbb eax, 0FFFFFFFFh
就等于
sub eax, 0FFFFFFFFh sub eax, CF
最后结果也是
0FFFFFFFFh
如果前面不存在借位的话,也就是
dl>=
bl的情况的话
最后的
eax=
1
所以这就出了三个
eax的值
if(eax=0){ 输入字符串=预存字符串; } else if(eax=-1){ dl<bl; } else if(eax=1){ dl>=bl; }
然后我们下一步就是这里的代码片段
这里我们判断
eax的值,如果为
0也就相等的情况下,那么
ZF=
1,走红线,反之,走绿线(绿线就是跳转结束了)
然后我们还是假设走了红线
红线下一个代码片段有点长,我们先一个
call一个
call的看
这里我们先看这个函数调用了
CreateFileA,这个函数的入参基本就是
eax和
3、
1,从上面我们分析可以得出此时
eax=
0
然后这个函数的具体参数就不贴出来了,网络吃了shi,我们公司一群傻b管理网络。。。出口400M带宽只买了个100M的路由。。。呵呵
这个函数主要就是在
C:\\Windows\\System32\\Kernel32.dll这里创建或者打开一个这个文件
然后下一个函数调用是这样的
这里的函数
CreateFileMappingA是将我们上面创建的文件
C:\\Windows\\System32\\Kernel32.dll映射到内存中的作用
我们只要注意这个最后压入栈的参数,是我们上面创建的文件就
ok
然后我们继续下去,这里调用了
MapViewOfFile这个函数,这个函数的作用是将上面创建的那个内存映射最后映射到进程中
这里的
hFileMappingObject=
eax,然后这里的
dwDesiredAccess是等于
4,这个我们可以查一下具体是那个参数代表了
4
最后我们可以得到这个参数的名字,在
MSDN中的解释是这样的
这里指这个文件是只读的意思
继续往下
这里是创建或打开了一个叫
Lab07-03.dll的文件,这个文件是已经存在我们这个分析目录的了,所以这里应该是打开这个文件
下面这里的代码是这样的
这里将我们调用
CreateFileA的返回值进行比较,这里的十六进制
0FFFFFFFF代表的是十进制的
-1
CreateFileA返回值如果成功,则是句柄,如果失败则是
INVALID_HANDLE_VALUE
(暂时写到这里,我先去破一下公司限速玛德傻逼)
(2017-11-7)公司服务器被搞瘫了。。。。继续写文章
如果上面那个调用失败,则直接调用
exit然后结束进程
然后如果成功了,则调用
CreateFileMappingA来将这个文件映射到内存中
然后又调用
MapViewOfFile来将这个文件映射到内存中的指定位置
然后就是进行一些内存的操作,然后我们按照书中的步骤,一直往下拉,然后可以看到这些
这里开始调用
CloseHandle来关闭我们打开的两个文件
然后我们可以看到这里还调用了
CopyFileA来将
Lab07-03.dll复制到
C:\\windows\\system32\\kerne132.dll这个地方
这里注意这个目录的最后是
xxx132.dll不是
xxxl32.dll,这个注意看到就明白这个函数的作用了
然后我们继续往下
这里调用了
sub_4011E0这个函数,然后调用之前是
push了一个参数入栈,
IDA显示这个参数的
C:\\*
然后我们进入这个函数看看,然后我们可以发现这些东西
这个函数的第一个入参被
IDA标注为了
lpFileName,说明这个入参很有可能就是一个文件的名字
然后函数继续调用
FindFirstFileA
我们可以从
IDA的注释中发现,这个
lpFileName其实就是
C:\\*
然后这里将这个函数的返回值于
0FFFFFFFFh也就是
-1d比较,一般函数调用发生错误都是返回
-1或者
1,然后成功一般返回
0,调用失败就跳转函数结束了
然后下面一个主线
这里我们已经把数字
10h替换成了标准变量的模式
如果
dwFileAttributes不是
File_ATTRIBUTE_DERECTORY的话
test指令为逻辑与运算,也就是如果
dwFileAttributes也是
10h的话,逻辑与的结果也是
10h,结果不为
0,所以
ZF=
0,
jz不跳转,继续往下执行,如果
dwFileAttributes不为
10h的话,也是往下执行,但是当我们的
dwFileAttributes为
00h或者
01h的话,我们就会直接
jz跳转
而
01h在
windwos SDK中代表了
FILE_ATTRIBUTE_READONLY,也就是只读模式
如果不跳转,直接往下执行的话,是这样的
然后这里开始将
.移动到
esi中
然后再将
cFileName的地址放到
eax中,其实这是
eax就是一个指向
cFileName的指针了
下一步
我们来看看这个代码片段
这里将
eax指向的值给了
dl又给了
cl,其实也就是上面的
cFileName参数
然后把
eai的值给了
bl,其实也就是上面的
.这个字符串
这里的意思应该是比较找到的
cFileName和
.是不是相等的
如果
cFileName和
.相等,结果则为
0,
ZF=
1,然后
jnz不跳转
如果不想等了,
ZF=
0,
jnz跳转
这里跳转位置
loc_401255可以理解成退出函数,因为在
loc_401255这个位置,其实做的是一些函数的清理工作
然后主线继续
这里检测一下
cFileName为不为
0
为
0的话也是跳转结束
然后继续主线
然后我们可以看到这样的操作,上面我们就已经说过了
eax是指针,然后
eax+1的意思就是指针往后偏移一个位置,
esi也是同样的操作
eax+1之后,
dl其实就是指向了
cFileName字符串数组的第二个字符
这里我们不能确定就是
esi+1之后,会指向了什么地方
但是我们安装数据段的分析
esi此时是指向了
.data.00403040的地方,往后偏移一个位置,也就是
.data.00403044,也就是
C:\*的位置
这时候是将
cFileName这个参数的第二个字符以后的数据和
C:\*比较
如果这两个参数相同,则继续往下执行而不跳转
然后这个继续主线
这时候就是将这个指针往后偏
2个位置,
esi最后就会偏向这里了
一个叫
NewFileName的数据,内容是
C:\windows\system32\kerne132.dll
然后如果相同,继续跳转回去继续比较
然后我们注意这里的
sbb,上面有两根绿色的线,都是函数比较大小失败的跳转
sbb其实就是
sub - CF的意思,
CF由上一步影响
因为是
cmp之后,如果
cmp的第一个函数比第二个函数小,那么
CF=
1,所以最后
eax会变成
-1,然后后门因为
eax=
eax,所以
CF=
0,最后
eax就会变成
0
最后这个函数可以看到是个递归函数,然后分析到这里,基本可以确定这个函数是在
C:\\下面查找
.exe文件
然后我们为了查明这个函数找到
.exe文件之后会做什么,我们要分析
sub_4010a0这个函数,为什么呢
因为这里代码找到了
.exe文件之后,就会调用这个函数,所以我们要查明这个函数是做什么的,这里有个参数被
push入了栈,就是
ebp了,这个参数具体是什么我们可以看
IDA的注释,
IDA已经表明这个是
lpFileName了
也就是我们一开始传入的参数
lpFileName
然后我们进入
sub_4010a0这个函数看看,是什么东西
这个函数的第一个调用是这样的
这个会创建一个名字为
lpFileName值的文件或者打开这个文件,这时候的
lpFileName的值其实是来自
C:\\*下面任意个
.exe文件,那这里因为这个文件存在了,所以
CreateFileA其实是打开它
下一个调用
这里大概就是将这个映射到内存中(一般是方便进程间共享数据的)
然后这里就调用
MapViewOfFile来获取共享的内存地址
我们也不要放过小尾巴
这里我们可以看到,函数判断了调用的
MapViewOfFile的返回值,这个函数人如果成功是返回一个地址,共享文件在内存中的地址,失败返回
NULL也就是
0
如果
esi也就是返回值
eax=
0的话,则
jz跳转,也就是会跳到
loc_4011D5的地方
这个地方是函数结束的地方
也就是调用失败就跳转结束了,然后我们继续主线分析
这里出现了一个我们以前没遇到的函数
IsBadReadPtr,这个函数按照
MSDN的解释就是检查调用这个函数的进程谁都对这个内存有读取权限
也就是测试这个
.exe文件是否有读的权限
如果没有也是跳转结束
对于
IsBadReadPtr我们有两个参数,一个是
lp=
ebp,一个是
ucb=
4,
ebp代表了要判断的内存的起始地址是哪里,然后
ucb代表了这块内存的大小,这里指明了是
4
然后函数继续往下执行
这里比较了
ebp+0这个地址上值和
4550h的大小,
ebp我们说过,是栈基底地址,也就是这个函数栈开始的第一个地址(注意是这个函数,上个函数的值我们在这个函数无法调用)
这里如果满足要求了,不跳转,继续往下执行
这里也是继续往后比较这个内存可不可读
然后最后我们可以看到,如果这个比较都符合了,会来这调用
stricmp来比较这个
kernel32.dll
然后便是下面这些晦涩难懂的代码
注意第三行的
repne scasb,
repne是重复前缀的意思,也就是一直重复后面这个
scasb,而
scasb是检索目标字符串直到字符串最后的
\0,然后这个指令是用
ecx来计数的,
ecx是计数寄存器的意思,这个我们汇编原理的时候大概提了一下
然后下面的
not ecx就是得到检索的次数,这里我们再明显的说一下这个
repne scasb的个个参数,第一次见到这个函数会让人比较蒙
第一行的
edi是要检索的字符串
然后第二行的
ecx是要循环的次数,这里设置成了
-1说明不限次数
然后还有个检索内容是在
eax中,而这个
eax是上一步影响的
如果
eax为
0的话,
jz跳转,
jnz不跳转,不跳转的话就是我们上面那个代码片段,所以这里,如果
_stricmp成功了返回
0,就会跳转到这里
然后这时候,
eax=
0,也就是我们要查找的内容是
0,准确的说,应该是
\0,也就是字符串结尾的那个字符
所以这个会将
edi指向的字符长度返回,然后这里的
dword_403010其实是
kerne132.dll,注意这里是
1(数字)32.dll
这个代码真难分析。。。我分析到这里的感觉
这个代码的意思就是在整个文件系统中寻找以
.exe结尾的文件,然后在
.exe文件内容中找到
kernel32.dll的位置,然后替换成
kerne132.dll,然后将这个
kerne132.dll深深的植入系统中,如果你删了
kerne132.dll这个文件,系统还运行不了
然后我们试试用动态分析看看,记得做好系统的快照
然后我们一开始分析就知道了,要运行这个病毒的话首先就是要输入一个参数
比如这样
然后执行就ok了
我们在
Process Monitor里面设置
filter之后,就可以看到这个代码在整个文件系统里面找
exe文件
然后你就会在发现多了一个文件出来
名字是相当相似的
这个函数如果你分析到这里,像我的话,还是比较懵逼,到底这个函数替换完之后会做什么(因为时间隔太长了我都记不得以前分析的结果了)?
其实在现实生活中,肯定是做一些后门或者勒索病毒之类的活
分析基本到这里
2.这个恶意代码的两个明显的基于主机特征是什么?
解答: 就是使用了一个叫kerne132.dll的文件,和一个叫
SADFHUHF的互斥量
3.这个程序的目的是什么?
解答: 目的就是创建一个很难删除的后门程序,如果你单单删除了kerne132.dll的话,整个系统就会崩了,然后会连接一个远程的主机,一个用来执行命令,一个用来睡眠
4.一旦这个恶意代码被安装,你如何移除它?
解答: 这是比较关键的一个问题,按照书上的说法是这个程序很难被删除,因为它感染了整个文件系统的
.exe文件,最好的方法是从备份系统中恢复或者留下这个恶意
kerne132.dll文件并修改它,或者复制
kernel32.dll为
kerne132.dll
要是我来解决的话,我的想法就是直接修改程序中的字符串,这个可以通过
OD来做,也就是原程序或者叫病毒是查找
kernel32.dll并替换成
kerne132.dll,我们可以将这个两个字符串颠倒一下,就可以了
本文完
相关文章推荐
- 恶意代码分析实战 Lab 9-3 习题笔记
- 恶意代码分析实战 Lab 9-2 习题笔记
- 恶意代码分析实战 Lab 1-4 习题笔记
- 恶意代码分析实战 Lab 4 习题笔记
- 恶意代码分析实战 Lab 10-2 习题笔记
- 恶意代码分析实战 Lab 7-1 习题笔记
- 恶意代码分析实战 Lab 3-2 习题笔记
- 恶意代码分析实战 Lab 6-2 习题笔记
- 恶意代码分析实战 Lab 8 习题笔记
- 恶意代码分析实战 Lab 3-4 习题笔记
- 恶意代码分析实战 Lab 1-3 习题笔记
- 恶意代码分析实战 Lab 6-3 习题笔记
- 恶意代码分析实战 Lab 1-2 习题笔记
- 恶意代码分析实战 Lab 3-1 习题笔记
- 恶意代码分析实战 Lab 10-1 习题笔记
- 恶意代码分析实战 Lab 3-3 习题笔记
- 恶意代码分析实战 Lab 6-4 习题笔记
- 恶意代码分析实战 Lab 5-1 习题笔记
- 恶意代码分析实战 Lab 9-1 习题笔记