Skip to content

Instantly share code, notes, and snippets.

@Jasemalsadi
Created November 25, 2023 08:02
Show Gist options
  • Select an option

  • Save Jasemalsadi/94e4ae01cd051f07dc64c6bc113f0c09 to your computer and use it in GitHub Desktop.

Select an option

Save Jasemalsadi/94e4ae01cd051f07dc64c6bc113f0c09 to your computer and use it in GitHub Desktop.
Windbg JS script to print all calls as json tree structure to easily view it, call only trace_calls function
//"use strict";
var addrresses_we_return_to = [];
class CallTreeNode {
constructor(name, address,is_outside_exedll=false) {
this.name = name;
this.address = address;
this.is_outside_exedll = is_outside_exedll
this.children = [];
}
addChild(node,is_outside_exedll=false) {
this.children.push(node);
if (!is_outside_exedll)
addrresses_we_return_to.push(node.address)
}
// Find a node by function name
findByName(name) { // make sure name is not the same
if (this.name === name) return this;
for (let child of this.children) {
let result = child.findByName(name);
if (result) return result;
}
return null;
}
findParentOfAddress(address, parent = null) {
for (let child of this.children) {
if (child.address === address) {
return parent; // Parent node found
}
let parentOfChild = child.findParentOfAddress(address, child);
if (parentOfChild) return parentOfChild;
}
return null; // No parent found with a child of the given address
}
// Find a node by function address
findByAddress(address) { // address need to be string with 0x
if (this.address === address) return this;
for (let child of this.children) {
let result = child.findByAddress(address);
if (result) return result;
}
return null;
}
toJSON() {
return {
name: this.name,
address: this.address,
children: this.children.map(child => child.toJSON())
};
}
traverse(indent = 0) {
logln(' '.repeat(indent * 2) + this.name);
this.children.forEach(child => child.traverse(indent + 1));
}
printToFile(intent = 0) {
var file = fso.CreateTextFile("C:\\Scripts\\trace_function_call_output.txt", true);
file.WriteLine("This is some text output.");
file.WriteLine("Add more lines as needed.");
file.Close();
}
}
function writeTreeToFile(rootNode, filePath) {
var logFilePath = filePath;
var logFile;
if (host.namespace.Debugger.Utility.FileSystem.FileExists(logFilePath)) {
logFile = host.namespace.Debugger.Utility.FileSystem.CreateFile(logFilePath, "OpenExisting");
}
else {
logFile = host.namespace.Debugger.Utility.FileSystem.CreateFile(logFilePath);
}
var treeJson = JSON.stringify(rootNode.toJSON(), null, 2); // Pretty print with 2-space indentation
//logln(treeJson);
var textWriter = host.namespace.Debugger.Utility.FileSystem.CreateTextWriter(logFile, "Utf16");
try {
textWriter.Write(treeJson)
}
finally {
logFile.Close();
}
}
function trace_calls(start_end=undefined) {
var binary_name = ""
if (typeof start_end == 'undefined' || start_end === null) {
var a = host.namespace.Debugger.Utility.Control.ExecuteCommand("lm a $exentry")[2]
start_end = a.split(" ")[0] +","+a.split(" ")[1]
binary_name = a.split(" ")[4]
}
/*
bp wsock32!recv
.scriptunload a.js
.scriptload C:\Users\HA\Documents\a.js
.scriptdebug C:\Users\HA\Documents\a.js
sxe en
*/
// .scriptdebug C:\Users\HA\Documents\a.js
// sxe en
// q
// dx @$scriptContents.trace_calls("00400000,00c0c000")
// Paste the json input on :
// https://jsoncrack.com/editor
// Choose unfold names to get the full function name
// Export it as SVG
var start = parseInt("0x" + start_end.split(",")[0], 16)
var end = parseInt("0x" + start_end.split(",")[1], 16)
var rootNode = null
var call_trace = "";
var tmp = host.namespace.Debugger.Utility.Control.ExecuteCommand("k");
var currentNode = {};
// Getting previous nodes
for(var k =1;;k++) {
try {
var currentCall = tmp[k]
var CallAddress = currentCall.split(" ")[2]
var callAddressHex = parseInt("0x" + CallAddress, 16)
if (callAddressHex < start || callAddressHex > end) { // we reached to root node that has our main
var rootfunction = tmp[k+1]
rootNode = new CallTreeNode(rootfunction, '0x' + rootfunction.split(" ")[2],false); // we make it false since we can return to it
/*
rootNode.addChild(new CallTreeNode("rootfunction", '0x' + rootfunction.split(" ")[2]) )
rootNode.addChild(new CallTreeNode("rootfunction2", '0x' + rootfunction.split(" ")[2]) )
rootNode.addChild(new CallTreeNode("rootfunction3", '0x' + rootfunction.split(" ")[2]) )
*/
// Right now we need to add childs
var prevChild = rootNode
for(var k2 =k;k2>=1;k2--) {
var currentChildCall = tmp[k2]
var ChildCallAddress = "0x"+currentChildCall.split(" ")[2]
var childNode = new CallTreeNode(currentChildCall, ChildCallAddress);
prevChild.addChild(childNode)
prevChild = childNode
}
currentNode = prevChild;
break;
}
// currentNode += tmp[k] + "\n"
}catch (err) {
break;
}
}
var i = 0; var j = 0;
var isOutsideAddress = false
while(1) {
if (!isOutsideAddress) { //
host.namespace.Debugger.Utility.Control.ExecuteCommand("t"); // tct: Target executes until it reaches a call instruction or return instruction.
}else {
host.namespace.Debugger.Utility.Control.ExecuteCommand("pt");
host.namespace.Debugger.Utility.Control.ExecuteCommand("t");
isOutsideAddress = false
}
var current_instruction = host.namespace.Debugger.Utility.Control.ExecuteCommand("u eip")[1];
var currentEIPwithut0x = host.namespace.Debugger.Utility.Control.ExecuteCommand("r eip")[0].split("=")[1];
var currentEIPRaw = "0x" + host.namespace.Debugger.Utility.Control.ExecuteCommand("r eip")[0].split("=")[1];
var currentEIP = parseInt("0x" + host.namespace.Debugger.Utility.Control.ExecuteCommand("r eip")[0].split("=")[1],16)
if (current_instruction.includes("call")) {
// We check next instruciton inside function being called to see if eip will be going to address outside our range
host.namespace.Debugger.Utility.Control.ExecuteCommand("t");
// dx Debugger.State.Scripts.a.Contents.evalee("host.namespace.Debugger.Utility.Control.ExecuteCommand(\"dds esp L1\")")
var nextInstruction = "0x"+ host.namespace.Debugger.Utility.Control.ExecuteCommand("dds esp L1")[0].split(" ")[2];
var currentNextEIP = parseInt("0x" + host.namespace.Debugger.Utility.Control.ExecuteCommand("r eip")[0].split("=")[1],16)
var newCall = new CallTreeNode(current_instruction, nextInstruction);
if (currentNextEIP >= start && currentNextEIP <= end) { // we have call belonging to current exe/dll address space
// let's check if it's a callback from outside adddress space to our own address space
if (currentEIP < start || currentNextEIP > end) { // we have a callback !!
newCall.name = newCall.name + "(It's callback!)"
}
currentNode.addChild(newCall)
currentNode = newCall
i++;
}else { // it's function call outside our address
currentNode.addChild(newCall,true)
isOutsideAddress = true // so we can skip it using pt command
} // we didn't handle if code has call the call directly in assembly, which is very very unusal
} else if (addrresses_we_return_to.includes(currentEIPRaw)) {
currentNode = rootNode.findParentOfAddress(currentEIPRaw)
if (currentNode.address=== rootNode.address) // we reached rootnode address, so we can just exit
break;
}
/*
else if (current_instruction.includes("ret")) {
host.namespace.Debugger.Utility.Control.ExecuteCommand("t");
currentEIPRaw = "0x" + host.namespace.Debugger.Utility.Control.ExecuteCommand("r eip")[0].split("=")[1];
if(addrresses_we_return_to.includes(currentEIPRaw)) { // We arrived a return after a call
// find the return
currentNode = rootNode.findByAddress(currentEIPRaw)
if (currentNode.address=== rootNode.address) // we reached rootnode address, so we can just exit
break;
}
// we return
logln("goods")
}
*/
// host.namespace.Debugger.Utility.Control.ExecuteCommand(".cls")
//if (i!=j)
if ((i+1)%50 == 0) { // we print every 50 calls
rootNode.traverse(0)
var timestamp = new Date().getTime();
writeTreeToFile(rootNode,"C:\\Scripts\\"+binary_name+"_output_"+timestamp+".json")
return;
}
if(i>10000) {
break;
}
j = i
}
}
function evalee(data){
logln(eval(data))
}
function logln(e) {
host.diagnostics.debugLog('\n'+e + '\n\n\n');
}
function initializeScript()
{
//
// Return an array of registration objects to modify the object model of the debugger
// See the following for more details:
//
// https://aka.ms/JsDbgExt
//
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment