概要
在上一篇dotnet查找xxx.ni.dll中jit代码中,使用了手工定位jit后的方法,出于提升效率的目的,我们还是需要能有一个自动化查找提取的过程,下面通过windbg中js脚本来完成这个操作,解放程序员这双宝贵的手
js脚本
最新的windbg中增加了对js脚本的支持,通过js脚本,我们可以很方便的开发一些功能强大的功能,例如自动化等等。js脚本的解析,需要依赖jsprovider.dll,所以在开始执行js脚本前,需要先加载该dll
.load jsprovider.dll
js脚本简介
下面的是官网的一个例子,有3个关键的函数,初始化,脚本执行,和反初始化函数。一般来说,重要的是invokeScript这个函数。
"use strict";
// Root of Script
host.diagnostics.debugLog("***>; Code at the very top (root) of the script is always run \n");
function initializeScript()
{
// Add code here that you want to run every time the script is loaded.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***>; initializeScript was called \n");
}
function invokeScript()
{
// Add code here that you want to run every time the script is executed.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***>; invokeScript was called \n");
}
function uninitializeScript()
{
// Add code here that you want to run every time the script is unloaded.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***>; uninitialize was called\n");
}
如何自动化完成查找jit后的汇编代码
现在回到一开始的主题。脚本解析dll加载后,在windbg命令行窗口调用下面的命令,即可执行脚本。
.scriptrun C:\Users\Administrator\Desktop\FindJitCode.js
贴一段简单的代码,在这段代码中 invokeScript 是入口函数。
"use strict";
// 输出 jit 后的汇编代码
function FormatJitCode(addr)
{
// 通过 !U 命令获取完整的jit后的汇编代码
var cmdUAddr = "!U /d " + addr;
var ctl = host.namespace.Debugger.Utility.Control;
var jitCodeAddr = ctl.ExecuteCommand(cmdUAddr);
for (var line of jitCodeAddr)
{
host.diagnostics.debugLog(line);
host.diagnostics.debugLog("\n");
}
}
function GetJitCode(cmd, funcName)
{
var curStep = 1;
var ctl = host.namespace.Debugger.Utility.Control;
var clsMTCmd = "!DumpMT -MD ";
host.diagnostics.debugLog(cmd);
host.diagnostics.debugLog("\n");
// 查找目标类的 MethodTable。用来输出完整的方法列表。
var clsInfo = ctl.ExecuteCommand(cmd);
for (var line of clsInfo)
{
host.diagnostics.debugLog(line);
host.diagnostics.debugLog("\n");
if (line.indexOf("MethodTable") != -1)
{
var sepPos = line.indexOf(":");
if (sepPos == -1)
{
return ;
}
var MTName = line.substring(0, sepPos);
var MTVal = line.substring(sepPos + 1);
clsMTCmd = clsMTCmd + MTVal;
curStep = 2;
break;
}
}
host.diagnostics.debugLog(clsMTCmd);
host.diagnostics.debugLog("\n");
if (curStep != 2)
{
return ;
}
var bpCmd = "!bpmd -MD "
// 根据输出的method table 定位到关键函数。获取函数的 MethodDesc 描述信息
var mtCmdInfo = ctl.ExecuteCommand(clsMTCmd);
for (var line of mtCmdInfo)
{
host.diagnostics.debugLog(line);
host.diagnostics.debugLog("\n");
if (line.indexOf(funcName) != -1)
{
var ss = line.split(" ");
bpCmd = bpCmd + ss[1];
curStep = 3;
break;
}
}
if (curStep != 3)
{
return ;
}
host.diagnostics.debugLog(bpCmd);
host.diagnostics.debugLog("\n");
// 通过断点,获取jit后的地址
var jitCodeAddr = ctl.ExecuteCommand(bpCmd);
for (var line of jitCodeAddr)
{
host.diagnostics.debugLog(line);
host.diagnostics.debugLog("\n");
if (line.indexOf(" bp ") != -1)
{
var kk = line.split(" ");
FormatJitCode(kk[3]);
break;
}
}
}
function invokeScript()
{
var ctl = host.namespace.Debugger.Utility.Control;
// 执行 windbg 的命令,
ctl.ExecuteCommand(".loadby sos clr");
GetJitCode("!name2ee System.dll System.Net.WebClient","DownloadFile(System.Uri");
}
结尾
通过参照着上一篇文章dotnet查找xxx.ni.dll中jit代码,对照着这个脚本,有助于更好的理解其中的交互过程。以及完成一些其它需求的脚本。js脚本是及其强大的,这只不过是一个小小的应用的例子罢了。