恶意代码分析实战 Lab 7-3 习题笔记

Lab 7-3

问题

1.这个程序如何完成持久化驻留,来确保在计算机被重启后它能继续运行?

解答: 我们还是先开始按静态分析来开始我们的分析(这里同时要分析.dll.exe

图片

我们会发现这里有一个CreateFileACopyFileA这两个函数,说明会创建一个文件和复制一个文件,这个创建文件可能会是什么日志之类的,复制文件可能是把病毒复制到某个地方

然后是FindFirstFileAFindNextFileA这两个函数,说明这个函数会在系统中查找什么文件

然后CreateFileMappingAMapViewOfFile告诉我们,这个程序会打卡一个文件,然后将它映射到内存中

但是在导入表中我们并没有发现LoadLibrary或者GetProcAddress,说明这个函数并没有在运行的时候加载这个DLL

然后看看字符串

图片

这里指示了一个路径C:\\windows\\system32\\kerne132.dll,然后这里的确会很迷惑人,书中不说我都没看见~

.data:0040304C 00000021 C C:\\windows\\system32\\kerne132.dll

这里注意是132.dll,为了方便解释,最后那三个是数字132

我们再来分析一下这个dll文件

还是先从导入表开始看起

图片

这个dll文件会创建和打开一个互斥变量,也就是这个函数CreateMutexAOpenMutexA

然后还会创建进程CreateProcessA这个函数

最后还会调用Slee函数来休眠

其他函数没什么价值,我们分析分析字符串

图片

这里有个exec这个字符串,有可能这个程序是个后门程序,只是猜测,然后还有个127.26.152.13这个ip,看到这个ip就很有可能是个后门木马了

然后我们继续书中的介绍,来找一下导出函数

图片

标黄那里就是导出函数所在的地方,先点选LAB07-03.DLL,然后再查看黄色标注的这里,如果有导出函数,这里就会显示,但是这里并没有

然后便是开始IDA分析,因为动态分析书上并没有得出什么结论,我们也不浪费什么时间了,直接开搞

我们根据书中的步骤

我们开始分析dll这个文件

书上的分析方法是直接只列出call语句的代码,然后这样分析,我们还是老规矩,毕竟也不是很长的代码,我们还是先一条一条分析下去看看

图片

一开始是调用了这个__alloca_probe这个函数,这个函数是用来在空间中分配栈空间的函数,然后这个函数的入参是11F8h也就是4600d,然后我们继续往下看,IDAfdwReason的值赋值给了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的互斥量,然后查看调用结果,如果结果eax0了,jnz不跳转,反之如果不为0了,jnz跳转

MSDN

MSDN里面写明了这个OpenMutexA的返回值,逻辑上归纳一下就是,如果调用失败,返回NULL,在计算机中也就是0jnz不会跳转,继续执行代码,反之如果调用成功,则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已经标注出来这个什么类型的

图片

这里我们可以根据以前的分析方法,右键选择这个值所代表的类型,然后把他替换成能看懂的代码

图片

下面就是我替换之后的代码

图片

这里是初始话了一个TCPINET连接,然后将返回值赋值给esi,之后和0,FFFF,FFFFh进行比较

这个值换算成十进制之后就是‭4294967295‬d,然后我们会发现这么大的一个值,在MSDNWindows 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
其他的sesi代表的是刚刚我们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函数的入参是esi1,我们查一下这个shutdown函数的一些说明

图片

然后依旧是替换成人类可读的代码

图片

这个SD_SEND的解释是这样的

图片

这个意思就是关闭这个socket连接的意思

然后也是比较返回值,如果调用失败,跳转结束函数

如果函数调用成功,则执行这个

图片

然后我们会发现这个

这里没什么技术含量,就是recv一个数据,然后存入buf

最后判断一个返回值eax的值,test的运算和and运算一样,区别是不会保存结果

我们在Lab 4的时候完整分析了test会影响的标志位,然后最后会的出下面结论

其中一定的是

指令执行后 
CF=0 
OF=0

然后JLE跳转的条件是ZF=1 or SF<>OF

主要分析这三个标志位ZFSFOF,因为也只有这三个标志位才会影响JLE的跳转

我们把eax分为这么三种情况

eax=0
eax>0
eax<0

如果eax=0and之后,结果为0,所以ZF=1,然后不论eax等于多少,OF=0,然后就是SF,如果运算结果是正数,则SF=0,反之SF=10是正数,所以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的值

MSDN

recvMSDN说明里面说明了,这个函数是返回接受的字节数,如果数据已经传输完毕,然后没有接受到数据(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也就是‭393216‬dms,也就是393.216s,同时也是6.053min,差不多就是6分钟

执行完这些后,这个代码片段跳到一开始发送hello那里开始执行

图片

如果接受的参数不是sleep的话,执行左边的这串,这里是判断发送的字符串是不是exec,如果是的话,跳转右边的红线

图片

我们先看如果接收到不是exec的情况,也就是左边这个,因为这个短一点

这里是一个比较

图片

比较这个buf的大小和71h71h的话等于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(?)这个东西

我们知道这个dbdouble byte的缩写,然后在计算机中,没有3/2个字节的说法,这里我们整理出了4091个字节的数组,联想到2^12=4096,而40964091差了5个字节,刚好就是前面我们的strncmpcount的值加上一个空格的值,也就是,这个40914096减去了exec和一个空格之后剩下的部分

然后也就是服务器发送来的字符串我们假设会是这样的exec C:\\Windows\someshell.exe

然后程序将exec+(空格)剔除之后剩下的部分就是那个CommandLine的部分,这个取决有服务器发送,是个不定值,无法从代码中看出来,所以这里的意思就是为这个C:\\Windows\someshell.exe专门创建一个进程来运行它,这个可执行文件一般是事先就上传到服务器的病毒木马之类的

然后这个代码片段运行完之后,又返回到发送hello那里继续循环执行,然后整个DLL文件就分析完了

下面我们分析EXE文件

我们先从main的地方开始

图片

这里我们看到有个argc这个东西,这是给这个可执行文件传入的参数的个数

这里代码将这个argc2进行了比较之后,有个跳转

一般argvargv是存储传入参数的数组,argc是入参的个数)的第一个参数是这个函数的名字,第二个开始就是用户输入的参数

如果eax=2的话,ZF=1jnz不会跳转,继续往下执行,如果不等于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这个地方,这个地方并不是结束函数

逻辑上归纳下就是如果dlbl指向的值相同(也就是第一个字符),就往红线往下执行,如果不相同的话,则跳转

我们先按红色这跟线往下走看

图片

这里用test来检测这个cl也就是指向argv[1]的第一个字符是不是为0cl在上一个图片的时候被赋值为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,这个函数的入参基本就是eax31,从上面我们分析可以得出此时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中的解释是这样的

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=0jz不跳转,继续往下执行,如果dwFileAttributes不为10h的话,也是往下执行,但是当我们的dwFileAttributes00h或者01h的话,我们就会直接jz跳转

01hwindwos SDK中代表了FILE_ATTRIBUTE_READONLY,也就是只读模式

如果不跳转,直接往下执行的话,是这样的

图片

然后这里开始将.移动到esi
然后再将cFileName的地址放到eax中,其实这是eax就是一个指向cFileName的指针了

下一步

图片

我们来看看这个代码片段

这里将eax指向的值给了dl又给了cl,其实也就是上面的cFileName参数

然后把eai的值给了bl,其实也就是上面的.这个字符串

这里的意思应该是比较找到的cFileName.是不是相等的

如果cFileName.相等,结果则为0ZF=1,然后jnz不跳转

如果不想等了,ZF=0jnz跳转

这里跳转位置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=4ebp代表了要判断的内存的起始地址是哪里,然后ucb代表了这块内存的大小,这里指明了是4

然后函数继续往下执行

图片

这里比较了ebp+0这个地址上值和4550h的大小,ebp我们说过,是栈基底地址,也就是这个函数栈开始的第一个地址(注意是这个函数,上个函数的值我们在这个函数无法调用)

这里如果满足要求了,不跳转,继续往下执行

这里

这里也是继续往后比较这个内存可不可读

图片

然后最后我们可以看到,如果这个比较都符合了,会来这调用stricmp来比较这个kernel32.dll

然后便是下面这些晦涩难懂的代码

图片

注意第三行的repne scasbrepne是重复前缀的意思,也就是一直重复后面这个scasb,而scasb是检索目标字符串直到字符串最后的\0,然后这个指令是用ecx来计数的,ecx是计数寄存器的意思,这个我们汇编原理的时候大概提了一下

然后下面的not ecx就是得到检索的次数,这里我们再明显的说一下这个repne scasb的个个参数,第一次见到这个函数会让人比较蒙

第一行的edi是要检索的字符串
然后第二行的ecx是要循环的次数,这里设置成了-1说明不限次数
然后还有个检索内容是在eax中,而这个eax是上一步影响的

六

如果eax0的话,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.dllkerne132.dll

要是我来解决的话,我的想法就是直接修改程序中的字符串,这个可以通过OD来做,也就是原程序或者叫病毒是查找kernel32.dll并替换成kerne132.dll,我们可以将这个两个字符串颠倒一下,就可以了

本文完

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值