发布于:2017-8-10 15:14 | 619267次阅读 作者: 管理员 | 原作者: 张震 | 来自: 原创
1 静态分析1.1 main文件静态分析查看了一下start函数,入口是sub_9EC0。 1.2 mscorlib.dll文件静态分析首先利用exeinfope查看发现是个利用C#写的.NET的dll文件。利用Reflector发现里面有Mono字样,Mono是U3D的在android上使用的一种框架。 2、动态分析动态分析,开始在sub_9EC0函数起始处下断点,发现程序并没停在断点处。这里我懵了一下,难道是有花指令或者是反调试技术让我跑飞了?于是决定单步调试,经过一系列的跳转,发现程序并没有正常的从sub_9EC0函数的起始处进入,而是先进入sub_6C130,然后通过sub_6C130函数的返回到sub_9EC0函数。 然后对程序内的某个数据块(长度为0x1600)进行了异或处理(与0x11异或)。然后再和和0x58进行异或处理。我看了一下进行了处理的数据块后,发现其文件头是MZ字样,哇是个PE文件,看来加密算法就是在这文件里面了,于是将其dump出来,用exeinfope查看了一下,发现也是个.NET文件,扔进Reflector,发现打不开,难道是加密了或者是加壳了?于是想了想,认为无论是加密或者是加壳,最终都会在内存中有解密后或者是脱壳后的文件,于是继续往下调试。 后来经过一系列眼花缭乱的单步跟踪,并没有发现解密后或者是脱壳后的PE文件。我又懵了一会。经过了长久的思考,想到既然是mono框架,应该有其调用.NEd的dll文件的机制,于是乎就去查阅了mono框架的如何实现C#函数的代码编译和执行,发现其是利用了runtime_invoke函数实现对真正函数的调用,于是经过漫长的单步调试,最后发现了runtime函数的调用,调试途中发现了jit等字样,应该是mono的JIT机制实现的动态编译过程,于是乎找到了调用runtime_invoke的mono_jit_runtime_invoke处,在mono_jit_runtime_invoke中,发现了将要调用的函数名和其所属的类,也就是MonoMethod和compiled_method,于是在能够查看这两个结构的地方下断点。 通过断点调查,有一个好消息和一个坏消息,好消息是解密后的PE文件中的类名字是encrypt,说明加密函数确实是在这里面;但是坏消息是main程序根本没有调用encrypt函数中的加密函数!!!!也就是说,encrypt函数这部分的代码可能不会解密或者脱壳放在内存里,于是乎在通过动态调试在内存dump其源码似乎是不可能了。我双蒙蔽了,陷入了深深的思考当中。 经过漫长的思考,我得到了2点;1、发现程序后面并没有对释放出来的文件再作修改,就是说经过异或后的文件(也就是我当时dump出来的文件)就是最终文件了。2、程序能够正常调用其中的sayHello函数,发现代码段应该是没有问题的。那么出问题的,很有可能就是文件头部分,但是就是修复文件头目前也是无从下手。 3找到加密函数经过思考,发现mono的代码是开源的,那么我既然可以还原runtime_revoke函数和runtime_jit_revoke函数,也就是可能还原其他函数。于是乎就去git下载了Mono的源码,一开始发现master版本的,发现这个版本的函数和程序的大多数都对不上,于是查阅了一下资料,发现最常用的Mono版本是2.6.5,于是我就下载了2.6.7的Mono源码。发现下对版本了,然后就是根据函数内的打印信息来搜索其对应的文件和函数。经过漫长的还原,还原了大部分的mono的函数。主要用到的是图1和图2的地方。 图1 从顺序看我么第一个要看的地方是mono_image_open_from_data_with_name,图2所示是其还原的代码的重点关注部分。 图2 发现了do_mono_image_load函数,这个是装载我这个“有问题”的PE文件的。跟进去,如图3所示。有好多个verify,我就打算利用其人之道还其人之身,用你自己的程序去验证这个“有问题”的PE文件。在这之前,程序生成了一个镜像(image),对应的是MonoImage,在源码查看了一下这个结构体,发现其包含了这个PE文件,没有多大的影响。于是就进入了mono_verifier_verify_pe_data函数。哈哈哈哈,终于发现这“有问题”的PE文件是如何过验证的了,如图4所示,原来是mono_verifier_is_enabled_for_image的返回值是false,因为没有对其进行验证,嘻嘻,我来帮你一把,于是就改了R0的值为1,继续跟进,终于,终于在verify_peheader处发现了头文件的第一处错误,就是PE文件的NT头,发现其Signature并不是0x50 0x54 0x00 0x00,而是多了一个1,如图5红圈所示,将其修改为0即可。然后扔进Reflector发现仍然是不能正常打开。说明还未完全修复,但是起码知道这个方法是可行的!!! 图3 图4 图5 然后发现mono_verifier_verify_pe_data和mono_image_load_pe_data都没有问题。那么继续跟进到cli了。首先查看cli的头,这里使用了CFF Explorer查看cli头,刚扔进去就弹了一个错误信息的框,如图6所示。 图6 从图6可以看出貌似是元数据流的数量261是不符合逻辑的。如图7所示,我查看了一下,元数据头。最下面的应该是元数据流的数量了,发现是0x105换算成10进制刚好是261。应该是这里错了。 图7 然后流数据的名字前面一般都带”#”,于是看了一下2进制,如图8所示,发现有5个井号(如红圈所示),然后发现元数据数量那里的是0x105,将其改为0x5就可以了。 图8 改了之后,元数据流依然不能正常显示,如图9所示。然后继续查看了一下元数据流的相关信息。发现了比较有用的信息是元数据流的名字是4字节对齐的,再看看图8,发现有不和谐的地方,就是黄圈部分莫名其妙地多了4字节的0。于是在查一下资料,发现String数据流的名字少了个s,补回就可以了。如图9所示,源数据流能够正常显示了。 然后扔进Reflector,发现还是有错误,如图10所示,是数据引索超出了引索界限。但是在CFF Explorer又看不出所以然。于是继续用动态调试跟进到图3中的mono_verifier_verfiy_cli_data函数,如图11所示。 图9 图10 图11 经过动态调试,发现verify_tables_schema这个部分出问题了,貌似就是引索过大的问题。然后去查元数据表头后面的表引索。发现查这个首先要查元数据表头的有效表的值。如图12所示,其2进制的值是 100000100100100000000000101001010101010111 可以看到,有14个1,表明有14个表。而元数据表头后面紧接着的是一串4字节的数据,表明该表有多少项记录。那么一共14个表,就是14*4 = 96个字节。如图13中的黄圈所示,就是记录表的项。我们可以发现红圈部分,其中的最高位是非0,表面这个项的数目非常大,而文件却不大,因此会很有可能造成数据越界。因此,将红圈部分的高位改为0即可。 图12 图13 再扔进Reflector,我叒懵了,这次居然成功了!!如图14所示,然后接下来进行解密部分。 图14 4解密函数看了一下EncryptDataFile()函数,发现了其读取了一个名为”areyouok.png”的图片然后调用Encrypt进行加密。然后进入加密函数Encypt,发现其首先会对文件进行8字节的对对齐。然后以8字节为单位,进行异或,并在调用code函数对其加密,然后得到8字节的密文。然后进入code函数后,如图15所示,发现有点眼熟。然后查阅了资料,发现就是TXEA的算法的变种。然后TXEA的解密函数就是TXEA的加密函数的反向算法,因此以这个思想写出来这个变种TXEA加密算法的解密算法。解密代码在同目录zzfile文件夹内的txea.cpp文件中。 图15 运行了一下代码,查看了一下解密数据的前16个字节,发现了PNG的固定头部0x89 0x50 0x4e 0x47,确认了解密算法没错。于是就得到了解密图片areyouok.png,如图16所示。 图16 5找图片这个图片并非目标图片。首先利用binwalk查看,发现里面并没有包含着其他文件,把文件改为zip或者rar也没有效果。然后用Stegsolve查了一下,也并没有用LSB隐藏图片文件,也没用某原色通道来隐藏。然后用pngcheck来查看了一下,发现出错了!说有多个sRGB模块。如图17所示。 图17 嗯嗯?用UE32查看一下,如图18所示。 图18 发现确实有2个sRGB模块,查阅资料发现这个模块不能存在多个。并且前面那个模块才是合法的。后面那个是不合法的,而且这个模块长度有0x116的长度。难道是要修复这个模块,然后尝试了RNG的其他所有public模块去替代这个模块,发现都不行。 将其这部分的数据单独做成一个文件,发现也不能打开。没啥头绪的时候,发现这个文件的头部是GG。难道是头部被改了?然后以尝试的心态去修改,这个文件很小,也不符合很多常用的图片文件,能够符合的目前只有BMP格式的图片了,将前面的GG改为了BM试试效果。 然后……然后我叕懵了,居然就这么得到图片了,居然就这么不明不白地把这题做出来了。也许是我还没反应过来,这个时刻我反而非常平静。嗯,赶紧写报告,我是这样想的。最后的图片如图19所示。 图19 |
最新评论
发表评论