How to get native callstack on dump of 32-bit process done by 64-bit tool

My previous post explained that for 32-bit process, 32-bit tool should be used, or most tools got confused. Visual Studio able to handle standard PDB files but got confused on PDB files that was custom generated. Normally I would recommend using 32-bit tool and forget about this problem. But in some cases, crash could be so rare, that it worth investigating how to get anything useful from such “bad” dump. And I will show, how to do it.

Firstly, load 32-bit WinDbg and execute following commands:
.load wow64exts.dll
!sw
!r

This will load standard wow64exts WinDbg extension, switch to 32-bit mode, even dump is 64 bit and displays 32-bit context record. From context record we need value of register ebp. Place this value into script file for this line:
var EBP =

And run script file using this command:
.scriptrun <Path to my script file.js>

It will slowly print callstack.

How it works? Quite simple. Register EPB will point to address in stack with 2 DWORDs. First is address where previous register EBP is stored and next is return address.  For example, let us assume that EBP = 100. In this case EBP chain will look like this:

100: [140]
104: [Return Address]

140: [180]
144: [Return Address]

180: [230]
184: [Return Address]

Then at address 104 will be return address, Next EBP will be 140 and return address will be at 144. Then EBP will be 180, then 230 etc. For return address script executes command lm to find nearest symbol.

I hope it helps someone.

Here is script:

"use strict";

let logLine = function (e) {
    host.diagnostics.debugLog(e + '\n');
}

function getValueAt(control, addr) {
    var cmd = "? poi(" + addr.toString(16) + ")";
    var cmdResult = control.ExecuteCommand(cmd);
    var addr = cmdResult.getValueAt(0);
    var addrParts = addr.split(' ').filter(function (i) { return i });
    return parseInt(addrParts[2], 10);
}

function PrintSymbolAt(control, addr) {
    var cmd = "ln " + addr.toString(16);
    var cmdResult = control.ExecuteCommand(cmd);
    var poiResultAddr = cmdResult.getValueAt(3);
    var index = poiResultAddr.indexOf("|");
    logLine(poiResultAddr.substring(0, index));
}

function invokeScript() {
    var control = host.namespace.Debugger.Utility.Control;
    // Replace value of regisetr EBP in line below
    var EBP = 0x0018f290; 
    for (var i = 0; i < 100; i++) {
        var nextEPB = getValueAt(control, EBP);
        var symbolAddr = getValueAt(control, EBP + 4);
        PrintSymbolAt(control, symbolAddr);
        EBP = nextEPB;
    }
}

It is quick and dirty solution but it works.