前两天看了某公众号发布的最新的海莲花分析报告,于是乎跟着学习一波,遂有了本文之后要提及的故事了。在文章的结尾,出现了一个名为Denes家族的后门样本,由于没有搞过,想着自己跟着文章的步骤搞一搞,结果一搞就一发不可收拾,遇到这种问题,各种坑,我是被坑哭了,不知道各位大佬可曾遇到过这样的问题。
参考链接 样本来源
来来来,开始我的分析过程,首先是跟着文章发表者的分析过程,快速的找到Denes后门所在的内存区域,给它来个dump。这个记录已经是我分析完之后才发写的所以,我为了展现出我所踩过的坑,又踩了一遍给大家演示,我太难了。第一次分析,使用的是OllyICE,不带strongod的插件,异常设置如下所示,可能有读者会问我为啥要设置,当然是因为调试的时候发现会触发某些异常导致调试的问题,才会去设置异常。为了快速到达内存dump,我经过多次测试发现了一些好办法,首先是要等待恶意dll的加载,bp LoadLibraryW,让程序跑起来,看加载的位置。这时,取消LoadLibraryW,下VirtualAlloc断点,来看加载的SoftwareUpdateFiles.locale内容所在的内存区域。返回到用户层之后,发现了这段内存的地址,一般API返回地址通常在EAX中,内存窗口 等待写入过程。紧接着就有一个ReadFileAPI调用,估摸着就是准备加载SoftwareUpdateFiles.locale的内容了。此时内存中已经被写入了相关数据。这里还需要另外一个API来完成数据的解密写写入过程,RtlMoveMemory,这个API也是在分析文章中提到的,这里为了更快抵达战场,而进行的快速定位,bp RtlMoveMemory 之后,取消其他的断点,执行。这里的地址就是Denes文件的一部分了,在数据窗口,翻到头就可以发现,看到了熟悉的Dos头部。最后通过一串特征码定位,通过od的ctrl+s搜索命令序列来完成,记得勾选整个块,这串特征码是根据文章中提及的进入PE文件执行之前的最后一个调用call来确定的。可以看到这里od给了我们找到的结果,在call eax加断点,取消之前所有的断点执行到这里来。这时候整个Denes文件已经解码完成了,该收获成果了,从内存中dump出来看一看。本以为分析就此结束了,不曾想遇到个应急,没办法先放下了Denes的分析工作,先去把应急处理了。处理完应急回来继续分析时,已经是第二天了。将dump文件一看,我有点懵,啥情况难道昨天dump的文件不对劲?Entry Point为0?,难道没有入口点?估计要第二次分析了,ε=(´ο`*)))唉,准备开始第二次分析吧。第二次分析,因为之前的应急刚处理完,忘记了之前用的不是吾爱版的od,调试了之后发现程序莫名其妙的就崩溃了,我内心是崩溃的(之前调试不是好好的嘛,咋就崩溃了?)这错误也是尼玛的很奇怪,异常代码c0000005一般就是内存访问异常,但是给我的异常偏移为0?难道程序用到了0内存?一般情况下,访问0内存是会出现问题的,但是我记得昨天调试的时候,好像没有这个毛病啊?就这样,第二次调试以失败告终,还特意在群里问了问各位大佬,依旧是没有得到什么有用的信息。没办法就这样开启了第三次调试。第三次调试,不过这次用的x64dbg,此次调试很顺利,不过x64dbg有个比较麻烦的问题是API断下后,不像OD一样将参数集中在一起而是这样显示的。成功的从内存中dump出来了Denes样本,但是还是有很多疑问,对比看雪的一篇文章 https://bbs.pediy.com/thread-259596.htm。我发现个问题,为啥这个Denes资源不可见呢?这种问题几乎没遇到过,通过搜索引擎也得不到什么有效信息。想着要不就这么放弃了吧,反正也不差这么一点,但是之后自己做的和心里想的就表里不一了,继续从刚才的文章中寻找相关的信息。果然想的什么都是扯淡,动手搞一搞比啥都强,最后还是本着一颗追求到底的心,又开始第四次的调试过程。第四次调试,还是用回了之前的OllyICE继续调试,跳过之前定位到Denes的过程,来到最后的call eax这里。由于要加载资源函数所有就下了相关的API断点,FindResource将A系列和W系列都打上断点,还有LoadResource断点,运行看是个什么情况。嗯?什么东西,这个hModule这个地址咋这么熟悉呢?数据窗口一跟随,卧槽咧,这不就是Denes嘛,果然在资源上做了一些手段,导致资源编辑工具无法直接查看,但是程序功能成功的定位,就表示内容还是存在的。通过查询API发现,LoadResource返回的地址,就是资源所在的内存地址。果不其然,在内存中找到了这段资源文件。将整个资源文件从内存中dump出来放着,以防像之前一样重头再来,然后我就像个傻逼一样的盯着这段资源端,一直知道程序跑起来都没看到解码的过程,心想着啥情况,不是说会进行解密嘛,我咋没看到这数据有啥变化呢???不过在这个过程中我发现出现了域名的解析,但是没有转向内存,因为一心傻逼的盯着了资源数据的变化。没得搞了程序已经跑飞了,只有第五次调试了。第五次调试,在弄好之前的种种问题之后,缓慢仔细的调试着这段花指令代码,找到之前出现域名解析的call调用,应该就可以找到整段解密的过程了。现在的问题就是如何确定第二段代码需要的内存从哪里来?再一次调试来到了Call eax这里,查看内存的情况,可以看到目前第二段内存并没有出现。现在步过call eax,其中触发了一次HeapCreate分配,不过大小只有1024,分配好的地址如下图所示。卧槽咧,难道真的是用的HeapCreate来创建,并扩充了这个heap的大小?为什么这么说,来看一下内存的布局情况,已经无限接近于之前出现的第二段内存的情况了。当从call eax返回之时,可以发现内存之前的1000大小变成了3000。正当我以为这就是第二段内存是,VirtualAlloc的出现让我觉得,heap应该不是第二段的内存了,这时候再来看看内存布局的情况。这时查看内存时发现,到头来一场空,代码似乎已经解码完毕了。不要以为我真的只是调试了5次,只是省略了很多很多重复的步骤,从刚才的测试中也知道了解密过程是在call eax中完成的,所以必须要跟进call eax进一步查看具体是什么回事。第六次调试,也是我最后一次调试了,大多数已经弄明白了,就是最后的解密过程和内存分配问题了,已经找到了复制已解密的函数代码,但是依旧没有找到分配内存的API。第一次调用复制后的代码情况如下图所示。第二次调用复制后的代码情况如下图所示。自此整个解密的内存就找到了,只需要来一个dump即可拿出其中被解密出来的两个内置的dll文件了。
push ebxpush 1push esiadd eax,esicall eax这一段特征码的作用是定位到解密完成的Denes的位置,通过eax的值,可以找到Denes的Dos位置。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课