WinDbg - getting the address of the last loaded PE into an Alias - debugging

I need to get the address of the last loaded PE, into the debugged process, into an Alias / register.
There's no problem to perform this action manually in a WinDbg session, using lm command, and then just to manually copy the address of the loaded module -- but this has to be a part of a WinDbg script.
I can pass the PE's name, full path, pdb path to WinDbg as arguments without a problem. But I doesn't seem to find a way to get the address, without copying it manually, into an Alias.
Maybe there's a way to address the last loaded module's address? (maybe through a specific register, which holds this value)

Maybe the answer is really just that simple: the name of the module is already the address, so there's no additional need to find it out.
0:006> lm m notepad*
Browse full module list
start end module name
011f0000 0145c000 notepad__ (deferred)
0:006> ? notepad__
Evaluate expression: 18808832 = 011f0000
0:006> as lastloaded notepad__
0:006> ? lastloaded
Evaluate expression: 18808832 = 011f0000
If (for whatever reason) you need it as a number, use .printf:
0:006> as /c xx .printf "%d", notepad__
0:006> .echo ${xx}
18808832
0:006> as /c xxh .printf "%x", notepad__
0:006> .echo ${xxh}
11f0000
For me, the problem is rather on the emphasis of "last loaded". How would you figure this out from the list of lm? But since you say that's not a problem for you, I'll not consider this in my answer.

Related

Debugging with GDB - seeing code around a given breakpoint

I am trying to debug a C++ program using GDB. I have set 15 breakpoints. Most of the breakpoints are in different files. After the first 5 breakpoints, it became difficult to remember what line of code any given breakpoint refers to.
I struggle quite a bit simply trying to recall what a given breakpoint refers to. I find this quite distracting. I was wondering if there is a way to tell gdb to display code around a certain breakpoint.
Something like this - $(gdb) code 3 shows 30 lines of code around breakpoint 3. Is this possible today. Could you please show me how?
I run gdb in tui mode, and I also keep emacs open to edit my source files.
You can use gdb within emacs.
In emacs, type M-x gdb, after entering the name of the executable, type M-x gdb-many-windows. It brings up an IDE-like interface, with access to debugger, locals, source, input/output, stack frame and breakpoints.
You can find a reference and snapshot here.
I don't think you can do it exactly like this in gdb as such, but it can be scripted in gdb python.
This crude script should help:
import gdb
class Listbreak (gdb.Command):
""" listbreak n Lists code around breakpoint """
def __init__ (self):
super(Listbreak, self).__init__ ("listbreak", gdb.COMMAND_DATA)
def invoke (self, arg, from_tty):
printed = 0
for bp in gdb.breakpoints():
if bp.number == int(arg[0]):
printed = 1
print ("Code around breakpoint " + arg[0] + " (" + bp.location + "):")
gdb.execute("list " + bp.location)
if printed == 0:
print ("No such breakpoint")
Listbreak()
Copy this to listbreak.py, source it in gdb (source listbreak.py), then use it like this:
listbreak 2

WinDbg: Couldn't resolve error at xyz!abc::func while setting breakpoint

I have attached WinDbg to a process.
When I use the command bp to set at break point. I get the following error:
bp xyz!abc::func
Couldn't resolve error at xyz!abc::func
What's wrong?
When you did a lm m xyz you got
start end module name
4d6c0000 4dc59000 xyz (export symbols) C:\Program Files\path to xyz
where the term export symbols tells us it has only loaded the "public" functions of the DLL.
To load the private symbols, do
.sympath c:\path\to\your\pdb
.symfix+ c:\symbols
.reload /f
ld xyz
And then do a lm m xyz again. If it still does not show "private symbols", repeat the same with a !sym noisy before and it'll tell you specifically what it could or could not load.

How to prevent WinDbg from attaching to specific child processes?

I'm debugging a script in WinDbg with .childdbg 1. (The script runs various test cases of software in infinite loop. This way I catch rare crashes.)
I need not to attach to specific child processes (for performance reasons and because they are third-party and crash often).
If I could specify them by process name, that would solve my problem. If you can propose an other debugger that can do what I need, I will be grateful.
NOTE: Configuring debugger to attach to specific processes via GFlags is not a solution in this specific case.
If you have activated .childdbg 1, you can make use of sxe cpr. With the -c switch, you can execute a command. Something like .if (yourcondition) {.detach} .else {g} could help.
Perhaps the cpr:ProcessName option is really helpful for you. It supports wildcard filters. I've never used it until now, see Controlling Exceptions and Events in WinDbg help.
I used the following .NET program to perform a test:
static void Main()
{
Console.WriteLine("Attach and press Enter");
Console.ReadLine();
Process.Start("Notepad.exe");
Process.Start("calc.exe");
Process.Start("Notepad.exe");
Process.Start("calc.exe");
Console.WriteLine("Started 4 processes");
Console.ReadLine();
}
I started the program under the debugger and did the following:
0:004> .childdbg 1
Processes created by the current process will be debugged
0:004> sxe -c ".detach;g" cpr:calc
0:004> g
...
77da12fb cc int 3
1:009> |
0 id: 1fe0 create name: DebugChildProcesses.exe
. 1 id: f60 child name: notepad.exe
1:009> g
...
77da12fb cc int 3
2:011> |
0 id: 1fe0 create name: DebugChildProcesses.exe
1 id: f60 child name: notepad.exe
. 2 id: 1d68 child name: notepad.exe
2:011> g
As you can see, the debugger is attached to Notepad only.
Unfortunately, you cannot use multiple sxe cpr:process commands. Whenever you use it again, it will overwrite the previous settings.
In that case, you need to use a generic CPR handler and do the rest inside the command. In my tests, !peb did not work well at that time, so I couldn't use it. However, WinDbg has already switched to that process, therefore |. gives us the process name along with some other information. To extract the process name only, .foreach /pS 6 (token {|.}) { .echo ${token}} worked for me.
With this, you can build trickier commands like
.foreach /pS 6 (token {|.}) {
.if (0==$scmp("${token}","notepad.exe")) {.echo "It's Notepad!"}
.elsif (0==$scmp("${token}","calc.exe")) {.echo "Do some math!"}
}
(formatted for readability, remove the line breaks)
When you try to combine this with sxe, you run into some nasty string escaping problems. Replace all quotes inside your command by \" to make it work:
sxe -c"
.foreach /pS 6 (token {|.}) {
.if (0==$scmp(\"${token}\",\"notepad.exe\")) {.echo \"It's Notepad!\"}
.elsif (0==$scmp(\"${token}\",\"calc.exe\")) {.echo \"Do some math!\"}
}
" cpr
(formatted for readability, remove the line breaks)
Now you can do whatever you like, e.g. .detach or .kill, just replace the .echo command in above example. You can execute several commands by separating them via semicolon (;) as usual.
BTW: If you use sxe cpr, you might perhaps want to turn off the initial process breakpoint by sxd ibp.

Batch - Pass variable parameters onto batch files that contain spaces

When my program finds itself outdated, it runs the updater 'data1.bat'.
In this case, i found that cmd was completely ignoring the double quotes in the command
This is the line of code (with variables(Original)):
start "" "%dirofbatch%data1.bat" "%downloc%" "%dirofbatch%" "%lver%" "%lget%"
This is the line of code (with those variables expanded):
start "" "C:\Users\Hello\Desktop\Minecraft Client\Versions\4.7\data1.bat" "\\SERVER\Users\Test User\Minecraft Hunger Games Client v4.8" "C:\Users\Hello\Desktop\Minecraft Client\Versions\4.7" "4.7" "4.8"
Instead of working, it seems to see:
start "" C:\Users\Hello\Desktop\Minecraft User\Minecraft Hunger Games
I found that out when every-time it ran, it started minecraft on my desktop, tried to login with the user name "User\Minecraft", the password "Hunger" and then tried to login to the server "Games"...
Is it possible to pass parameters with spaces to another batch program without this happening?
It's a bug of START or better of cmd, if in the path of the command is a space and at least in one parameter it fails.
A workaround to add a CALL
start "" CALL "C:\Users\Hello\Desktop\Minecraft Client\Versions\4.7\data1.bat" "\\SERVER\Users\Test User\Minecraft Hunger Games Client v4.8" "C:\Users\Hello\Desktop\Minecraft Client\Versions\4.7" "4.7" "4.8"

Process name missing from GetCommandLine()

I have a problem with the GetCommandLine() API.
It usually returns the executable name followed by a space and arguments. As documentation says, the first token may not have the complete path to the image and blah blah blah.
I never had problems until now that I used CreateProcess with lpApplicationName not NULL.
If I use:
CreateProcess(NULL, "\"c:\\myexe.exe\" param1 param2", ...)
GetCommandLine returns "c:\myexe.exe param1 param2" as expected.
But if I use:
CreateProcess("C:\myexe.exe", "param1 param2")
GetCommandLine returns only "param1 param2".
How do I know if the executable name is given on the command line if another application launches mine?
Also, MFC startup code assumes that the first token on the command line is the executable name and skips it. But if you launch a MFC application with the second CreateProcess API example, MFC's code will skip the first argument.
Not your problem. It's the job of the other application to construct the command line properly. You should simply assume that the first argument is an executable name as expected and skip over it.
I have a workaround which can be helpful in a case like this.
I guess we always be able to check how our module was been started.
In this case we should check first argument.
I will write code because I have some problem with English.
Here two ways:
The first case. we can compare module name with first command line argument.
something like this:
const TCHAR* csCommandLine = ::GetCommandLine();
// Attention!!! the first symbol can be quete
if (*csCommandLine == _T('\"'))
csCommandLine++;
TCHAR sModuleFileName[MAX_PATH];
DWORD dwModuleFileName = ::GetModuleFileName(NULL, sModuleFileName, MAX_PATH);
if (dwModuleFileName && !_tcsncmp(csCommandLine, sModuleFileName, dwModuleFileName)) {
// The command line contains the module name.
}
The second case. we can try to get file attributes for the first command line argument
something like this:
// Attention!!! don't use it case if you are going to pass a file path in command line arguments.
int nArgc;
LPTSTR* szArglist = ::CommandLineToArgvW(::GetCommandLine(), &nArgc);
if (nArgc && ::GetFileAttributes(szArglist[0]) != INVALID_FILE_ATTRIBUTES) {
// The command line contains the module name.
}
::LocalFree(szArglist);
I hope it can be helpful someone.
Regards, Vladimir

Resources