初出茅庐逆向狗:恶意程序分析第一课

阅读量274043

|评论7

|

发布时间 : 2018-11-06 16:30:39

不知道在座的各位逆向狗中,有多少人是从CTF开始的逆向之路,在我认识的逆向狗中,要么是从一些小小的破解入坑,要么就是通过CTF

而开始通过CTF入坑的童鞋们,在CTF逆向题做多了之后,都会想要找一个现实生活场景中的案例来试验一下学到的技术:比如恶意程序分析!

今天笔者就提供给大家一个自己遇到的样例:一只可爱的远程控制木马,供各位练练手。

程序不是很出名,“市面上”应该也找不到,大体来源就是有一位给人开发票的大叔想套客户信息,做了个伪装成发票样板的远控马…blabla……分析难度不是很大,但也挺费力气,拿来作为人生第一个分析的恶意程序难度刚刚好~整个分析过程有章可循,对思维启发还是很好的

程序下载链接:木马在这里吃草~

Warning:木马真实有效,功能为远程控制,但已确定无其他对计算机的破坏性行为,请自行丢虚拟机调试!!!若不慎运行可立即断开网络并检查开机启动项,即可解除危险

 

一、文件概览

顶级文件夹内就是这些东西,除了exe外皆隐藏,exe图标进行了伪造,打开文件夹1,里面是一个无害txt发票样板

 

二、文件类型初步窥探

notepad++看一下bin文件和db文件,分别如下:

bin文件是什么文件暂时看不出来,但是db比较有意思,是一串base64编码,我们尝试解码一下:

有发现,竟然是对一个pe文件做了编码(脑洞选手可能已经想到这个可能是木马功能代码,但是我们出于训练逆向能力的目的,不要去解码来分析得到的文件,先一步一步走)

 

三、静态分析

就目前的情况来看,诱骗用户运行的应该就是那个exe,我们把它丢进IDA来分析一下:

这是WinMain的汇编开头部分,F5看它的伪代码:

可以看到,程序开始运行后,首先是打开了文件夹1中的txt,让用户看到所谓的发票模板,然后就开始搞事情了。

CreateFileA是创建了一个文件指针,指向了log.bin那个文件,返回的文件指针给了v4,v4就可以理解成代表这个文件的文件指针,对文件的操作都是通过v4来完成的。

GetFileSize,顾命思意,拿到文件的大小,值给了v5.

VirtualAlloc分配了一块内存空间,刚刚的v5作为参数表示分配的大小,0x40时该空间的属性为可执行,这意图就很明显了,想创建一块和log.bin一样大的内存,然后属性还是可执行,显然就是要把这个文件载入这块内存,而且内容还是可执行的代码!然后v6就指向了这儿

果然,下面马上ReadFile把文件读进来了,此时,v6就指向了这么一块可执行区域,内容即是log.bin

倒数第二行讲v6当作函数指针使用,也就使得程序跳到了那块内存去执行了

(函数具体的参数和说明请自行百度!)

可见,exe只是个启动器,它只是加载了log.bin中的代码,更阴暗的东西在log.bin里头

 

四、动态分析

log.bin并不是一个完整的程序,因此IDA已经无能为力了

想要知道log.bin的代码载入内存后究竟做了什么,只能依靠动态调试的力量,我们祭出神器:x64dbg(这个例子中实际是32位的)

调试器打开exe后,先运行一下,让程序运行到Entrypoint:

接下来我们需要找到刚刚静态分析时载入bin文件的地方,结合IDA静态分析的结果,该位置用到了一些字符串,如”1\1.txt” “log.bin”等等,我们使用调试器的搜索字符串功能来辅助定位。

右键功能里面有:

很快有了结果:

我们在第一条结果右键转到汇编代码,就可以转到对应的汇编代码的位置如下:

对比之前IDA的反汇编,可以确认就是这儿了,此外,可以发现,指令的地址和IDA中是不一样的,应该是开启了随机化,只有低位的地址是和原来一样

下一步我们往下找,找到IDA中调用函数指针v6的地方,因为就是从那儿跳转到log.bin的代码:

很容易就找到了,readfile调用之后,call eax就是对之前v6函数指针的调用,具体讲的话,就是前面VirtualAlloc返回值eax(函数指针)先压在了栈中,然后通过mov又给回了eax

我们在这儿下上了一个断点,运行到这儿以后再单步运行一下,就可以进入到log.bin的地盘了。

我们运行到断点,然后步进到log.bin的区域:

先是如之前分析那样,到断点时打开了txt

然后步进:

这就是log.bin的代码区域了,之前被加载进来的文件就是这些!

大体扫一眼,不知道各位是否已经看出了端倪?是不是某个地方有点似曾相识呢?

没错!我们看到了从0x3510064开始进行了一系列的push,而push的3、1、80000000和之前IDA中看到的某个函数调用惊人的相似!

而这个函数,回头去看,正是CreateFileA!

还要CreateFile?!这次又要关联哪个文件呢?稍加分析思考,无非两种可能:

1、上面的一大堆call调用的函数创建了一些文件,然后在此处关联上它

2、并没有创建新的文件,那这个文件十有八九就是前面文件夹里的log.db了

显然,下面call esi应该就是调用CreateFileA,我们在那儿下断点,过去就知道是打开哪个文件了:

不出所料,就是log.db,看来这回是要加载log.db了,那么大概大家也可以猜到这个程序的“好意”了:很有可能就是把db文件读进来,base64解码它,然后执行!

接着往下看汇编,果不出我们所料:

正如上图,接下来的三个call依次调用了GetFileSize、VirtualAlloc、ReadFile,与之前的操作如出一辙!

但是再仔细看一下就会发现,VirtualAlloc传参时,压进的第一个参数4,这个参数之前说过是内存属性,为4时是不可执行的!

这就很奇怪,log.db不是要执行的吗?为啥是不可执行的?是因为这里读进来的是base64编码,当然不可执行,这也提示我们,后面肯定要做解码,然后肯定会去执行它!

我们可以猜到,很可能程序是这样一个思路:exe加载执行bin  ->  bin再加载、解码、执行db  ->  恶意功能实现在db中,整个就是一套跳板

当然只是猜测,我们顺着这个思路来尝试一下:

思路1、既然是先解码再调用,那会不会这两步是连在一起的,也就是说找到了解码函数就离调用不远了呢?

思路2、解码后,要把解码得到的可执行代码放到一块内存,这块内存还要设置成可执行的属性才行,这样的话是否可以利用VirtualAlloc压栈0x40来定位呢?找一下push 0x40就行

还真别说,稍稍往下一看,隔了没几行代码,还真找着了:

看到了push 40,后面push的值也和之前类似,可以断定最下面那个call就是VirtualAlloc,设置了一块可执行内存,那么上面那俩神秘函数有可能是base64解码也说不定。

我们把这三个call都下断点,去跑一下:

1:我们先进入第一个call,看了看有一些字母表数字表的字符串处理,所以应该是和base64有关,有可能是初始化对照表吧

2:完了以后就是第二个call,可以看到第二个call是有两个参数的,分别是ebx和esi压进去做了参数,此外还mov了一个edi,我们在第二个call下个断点,断到那儿以后回头看这仨寄存器的值:

esi和edi对应了两块内存,对应看:

可见esi是一个未经写入的新内存区域,而edi指向的是之前db文件载入的base64编码

我们往上回溯看一下两个寄存器的来源,就可以看出:在call 0x3510143和读db的call ReadFile之间,还有一个VirtualAlloc的调用,它返回的内存指针eax,mov给了现在的esi;而之前用来存db内容的VirtualAlloc得到的返回指针也是由eax,mov给了现在的edi

而作为参数之一的ebx寄存器,细心往上回溯也可以找到来源:是GetFileSize的返回至mov给它的(夹在中间那个VirtualAlloc也用的它的大小)

也就是说,在进入第二个call之前:传的两个参数是db文件的大小对应着这个大小的空内存区域,并且还把db文件base64编码内容的内存指针拷贝给了edx

结合这一层分析,我们开动脑洞来猜一下第二个call要干嘛,如果说edx也是个函数接口的化,那么可以认为这个函数有三个参数:大小、空内存、base64编码内存

便越想越容易怀疑到:这个函数要进行base64解码,大小作为参数可能是为了进行循环、空内存是目标写入区域、ebx就是编码原文!

跟进去看看,会有一堆字母数字字符表操作、一个大大的循环、中间某步还看到了MZ哈哈,怎么看怎么像

我们跟进去,直接找到ret附件下断点来验证我们的猜想:

断到ret,看esi对应的那块内存:

之前参数传入的那块空内存已经被写进了base64解码得到的可执行二进制内容!猜想正确!

第一个call经验证的确也是base64编码初始化,完美!

3:之后就是用VirtualAlloc拿到了一块可执行的内存空间,并通过mov将返回的内存指针从eax交给了ebx

到这儿为止,关于db文件base64解码的操作过程就分析出来了

我们已经拿到了可执行内存空间,但是里面还没有把db解码得到的可执行内容拷贝过去,于是我们接着往下找,VirtualAlloc拿了可执行内存后紧接着的是call了三个神秘函数:

我们直接步过第一个call,然后去看那块可执行的空内存(ebx),里面已经有东西了:

说明第一个call就是我们要的拷贝函数了!

后面俩call忘记是做什么的了,反正不重要,我们略过

一直往后分析就不是很难了,后面ebx一直没有再被写过,也就是说ebx始终还是指向可执行内存的,然后做了一些玄妙的偏移计算:

反正这块偏移运算作用就是:可执行内存开头是pe文件头,并不是从开头就可执行的,因此这段就是通过偏移计算得到可执行内存中这只pe程序入口点!(应该能猜得到吧)

然后就是:

call eax,跳到db解码出来的pe的入口点去执行!!!

至此,我们终于把整个db文件的载入过程分析清楚并且成功的找到了db那块可执行代码的跳转点!

到这儿已经很累了,希望写这个木马的人有点良心,从这儿开始能正式进入木马关键代码┭┮﹏┭┮

call eax下断点后,步进去,就可以看到db对应可执行代码的空间了:

这里面的代码挺多的,我们祭出“所有call统统下断点大法”,挨个call下断!

注意:下断不要顾前不顾后!!万一你只下前面call的断点不检查中间有没有向后jmp,有可能人家直接jmp走了,你程序就丢失跟踪了!!!!这个很坑!!!!而且还有!!!还有!!!!!ret处也要下断点!!!!!不然直接jmp到ret呢!!!

咱们把所有函数调用都下上断点并且ret也断上以后,每断到一个函数我们就步进看看,第一次停到一个函数,进去看了,没啥,但是当第二次断到call时,跟进去,很惊喜:

这个WSAFDIsSet是和winsocket有关的东西,说明已经开始涉及网络链接功能了,看来db文件就是木马核心功能所在了

但是在执行这个jmp之前就已经ret了,我们ret出去以后再运行,结果就直接到了ret了,看来刚刚下了那么多断点,结果最后一共就执行了两个函数,而且都没什么敏感东西,猜测可能是做了一些简单的初始化吧,看来最重要的东西并不在这个call eax里面,我们ret出来以后继续往下看:

开始我们是进的上面那个call eax,也是从上面那里出来的,但是出来后我们看到,下面还有一个call eax调用,我们把它下上断点,跑过去,看一下eax对应的内存,是db解码出的pe的另一个偏移位置,我们步进这个call eax:

里面是这样的。

往下翻翻,这才是正确的画风(*^_^*)~~

好了,好戏要到了

往下是一大堆系统调用,中间还有个神奇的ShellExecute,竟然是个奇怪的1.jpg,也不知道是什么东西,反正逆了半天不知道干啥的,结合上下文是弄图标?还是留给大家来分析吧

但是这些系统调用看了看都和网络链接没有任何关系,但是我们的木马必须要连服务器啊,我们带着这个目的往下找:

首先略过所有与网络无关的系统调用

然后就到了靠近结尾那部分了,老规矩,call全下断点,一个个看,因为这段跳转太多

结果直接就跳到图中最后一个call了,中间的call都没执行

再往后看出来一个sleep就是ret了,已经没东西了

所以可以肯定关键部分就在图中最后这个call里面

而且另外一个细节值得我们警觉:

最后这个call,竟然是个持续的循环,下一行的jmp又跳到了这个call,反复执行!这就很“木马”了!!!都懂不多说

步进这个call:

一眼就看到,右侧有串base64,我们解码一下发现是乱码:

好,我们又开始瞎猜了:这么短的一串乱码,肯定不是指令,那它是乱码又不是指令还能是什么呢?会不会是加密的一条数据!比如服务器url?要把自身拷贝到的路径?注册表项?……

不管它是啥,我们一定要解出来

可以看到,这串base64字符串是作为一个参数传给了24fb330这个函数,应该是base64解码函数

直接步过它看返回值,结果令人哭笑不得:

搞半天base64解码和解码后的解密都在这里面了,没办法,我好奇怎么解密的,就重新加载了程序看(因此地址变了,大家看了不要奇怪):

进了这个函数是这样子的:

经分析,里面第一个call是base64解码函数,下面选中的那段循环则是对base64解码所得字符串的解密过程,反编译一下:

凯撒加异或,很简单的加密过程

这两步以后解码得到一个ip地址,可见原来那个base64串是一个对应了一个ip,很可能就是服务器ip

往后看,到这儿对应了两条base64串,第一个和之前一样,第二个我们同理解码出来,是一个端口号:

此后肯定就在某一步以这对ip地址和端口号为参数建立socket链接了,没什么悬念,就不分析具体的链接过程,我们接着往下看,可以看到几个神秘的非系统调用函数:

出于好奇,我都下了断点,打算一个一个看看:

第一个call进去以后就是进行socket链接如上,没啥好说的

只是这儿很坑,因为如果服务器没开它就会一直搁在这里面等,给你造成一种调试器对程序失去控制了或者崩溃了的假象,其实过一会儿链接不上它就自己步过了,等一会儿(很久)就好

真正令人窒息的是,如果网络链接不上的话,这个负责链接的call执行完后,jne不会执行,因此就会执行到后面的jmp,就往回跳了,进入了一个无限的循环,直到链接成功,才jne跳出去继续往下执行(如下图)。。。因此如果调试的时候服务器没开,唯一的办法就是把jne改成无条件跳转。。

改了以后就可以跳出去了,断在了下一个有断点的call,跟进去,是一些和网络链接有关的东西,以及获取了中招机器的系统信息,免杀模块也再这里面(附在第二张图) :

免杀模块就是下图选中的那条(下图中那个call GetSystemInfo和上面那张图中的是同一个,由于这是两次调试的图片,所以地址被随机化了,就不太一样,但其实是同一处的代码):

跟进这个免杀模块可以看到具体的东西:

可以得出结论:建立socket链接完毕后,我们后面的第一个call(也就是我们强行修改为无条件跳转跳出来后遇到的第一个调用),完成了受害机器本地信息获取以及免杀两项工作,我们可以将这个call看作木马的“本地运行环境侦测与处置”模块。

之后的call就没啥意思了,我们直接走到下面这个函数调用:

断到这儿后,调着调着就会发现,这个是循环执行的!也就是说你断到这儿后运行,eip又回到这,看起来就跟没运行一样;如果你把断点删去,运行,它就会一直处于运行状态;处于运行状态的时候你突然下上断点,它就会立即停到这儿

综合以上,说明最终木马的最核心实现就在这儿了,它在这个函数里面,会保持和服务器已经建立的链接,持续监听(wait)服务器的具体指令,根据对面的具体指令,去执行具体的功能函数代码!

我们这时候再打开字符串搜索,搜个”Ten”看看出来啥:

读者请自己跟到汇编处去看那部分都是干嘛的,反正很邪恶哦~(据说看到了steam)

 

五、分析报告与总结

1、该远控木马的大体执行过程如下:

通过将exe图标变换伪装成txt欺骗用户运行,运行后由exe打开“发票”txt使用户看到所需内容,然后将log.bin载入内存并执行

log.bin的代码将载入log.db,并对其进行base64解码得到可执行代码,并执行得到的代码

log.db解码得到的代码执行过程中,首先对服务器ip和端口号进行base64解码,得到密文,并解密得到真实的ip和port,建立socket链接

之后进行本地运行环境侦测与处置,获取受害机器信息并进行对自身进行免杀保护

最后开启对服务器的命令监听模式,根据接收到的命令执行相应的远程控制操作

2、db文件其实可以手动解码,丢ida分析的,我们为了练习能力没有这样做,回头读者可以自己尝试

静态动态调试相结合是分析恶意代码的重要方法哦~

本文由Magpie原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/163183

安全客 - 有思想的安全新媒体

分享到:微信
+121赞
收藏
Magpie
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66