prog.c
We're particularly interested in displaying the values of the three parameters of the function func
as it is called.
#include <windows.h>
HANDLE stdOut;
static void func(int number, const char* ansi_text, const wchar_t* utf16_text) {
wchar_t buf[200];
wchar_t* out = buf;
out += wsprintfW(out, L"number = %d, ansi_text = ", number);
//
// Convert ansi text to wtf16 and write directly
// to output buffer:
//
out += MultiByteToWideChar(CP_ACP, 0, ansi_text, -1, out, 50)
- 1; // subtract terminating zero
out += wsprintfW(out, L", utf16_text = %-15s\n", utf16_text);
DWORD charsWritten;
WriteConsoleW(stdOut, buf, (DWORD) (out - buf) , &charsWritten, NULL);
}
ULONG __stdcall run(void* PEB) {
stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
func( 7, "seven" , L"sieben" );
func( 2, "two" , L"zwei" );
func(42, "forty-two", L"zweiundvierzig");
func( 5, "five" , L"fünf" );
return 0;
}
The script
We also need a script that contains the function that is called whenever func
is brokein into.
This function is
func_breakpoint
. It basically reads the values of the three
registers ecx
(32-bit),
rdx
and
r8
which store the three function parameter values. (See
calling convention for x64).
The values of the two latter parameters (rdx
and r8
) are pointers to memory that contain the actual strings. So, we use host.memory.readString(reg.rdx)
and host.memory.readWideString(
reg.r8`) to get corresponding string values.
var ctrl;
var log_file_name = "log.out"
var text_writer;
function create_log_file () {
var log_file = host.namespace.Debugger.Utility.FileSystem.CreateFile (log_file_name, 'CreateNew');
log_file.Close();
}
function log(text) {
log_file = host.namespace.Debugger.Utility.FileSystem.CreateFile (log_file_name, 'OpenExisting');
//
// This lines seems actually necessary!
//
log_file.Position = log_file.Size;
text_writer = host.namespace.Debugger.Utility.FileSystem.CreateTextWriter(log_file );
text_writer.WriteLine(text);
log_file.Close();
}
function func_breakpoint() {
var reg = host.currentThread.Registers.User;
var num = reg.ecx ; // 1st parameter in rcx/ecx
var ansi_text = host.memory.readString (reg.rdx); // 2nd parameter in rdx/edx
var utf16_text = host.memory.readWideString(reg.r8 ); // 3rd parameter in r8d/r8
log(
'num = ' + num +
', ansi_text = ' + ansi_text +
', utf16_text = ' + utf16_text);
if (num != 42) {
//
// go on if num = 42
//
host.namespace.Debugger.Utility.Control.ExecuteCommand("g");
}
}
function invokeScript() {
ctrl = host.namespace.Debugger.Utility.Control;
create_log_file();
log('setting breakpoint');
ctrl.ExecuteCommand('bp prog!func "dx @$scriptContents.func_breakpoint()"')
log('running program');
ctrl.ExecuteCommand('g');
log('Num is 42. Explore your environment.');
log('Use g to continue');
}
Starting the debugger
We're now ready to start the debugger and execute the script. The script initialize function invokeScript
(which is called when the script is loaded) sets the breakpoint on func
and makes sure that hitting the breakpoint calls func_breakpoint
:
cdb -2 -c ".scriptrun log-parameters.js" .\prog.exe
The script writes the following content into the log file (log.out
):
setting breakpoint
running program
num = 7, ansi_text = seven, utf16_text = sieben
num = 2, ansi_text = two, utf16_text = zwei
num = 42, ansi_text = forty-two, utf16_text = zweiundvierzig
Num is 42. Explore your environment.
Use g to continue
num = 5, ansi_text = five, utf16_text = fünf