tgjarwl的博客

道可道,非常道;名可名,非常名

记录生活,记录更好的自己。


windbg中使用js脚本定位.net jit后的汇编代码

概要

在上一篇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脚本是及其强大的,这只不过是一个小小的应用的例子罢了。