Date: | April 27, 2007 / year-entry #148 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20070427-00/?p=27083 |
Comments: | 14 |
Summary: | Back in the old days, if you wanted to call a function from inside the debugger, you had to do it by hand: Save the registers, push the parameters onto the stack (or into registers if the function uses fastcall or thiscall) push the address of the ntdll!DbgBreakPoint function, move the instruction pointer to the... |
Back in the old days, if you wanted to call a function from
inside the debugger, you had to do it by hand:
Save the registers, push the parameters onto the stack
(or into registers if the function uses
The Windows symbolic debugger engine (the debugging engine
behind int DoSomething(int i, int j); You can ask the debugger to do all the heavy lifting: 0:001> .call ABC!DoSomething(1,2) Thread is set up for call, 'g' will execute. WARNING: This can have serious side-effects, including deadlocks and corruption of the debuggee. 0:001> r eax=7ffde000 ebx=00000001 ecx=00000001 edx=00000003 esi=00000004 edi=00000005 eip=10250132 esp=00a7ffbc ebp=00a7fff4 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246 ABC!DoSomething: 10250132 55 push ebp 0:001> dd esp 00a7ffbc 00a7ffc8 00000001 00000002 ccfdebcc
Notice that the debugger nicely pushed the parameters onto the stack
and set the This technique even works with C++ methods: // pretend that we know that 0x00131320 is an IStream pointer 0:001> .dvalloc 1000 Allocated 1000 bytes starting at 00a80000 0:001> .call ABC!CAlphaStream::Read(0x00131320, 0xa80000, 0x1000, 0) Thread is set up for call, 'g' will execute. WARNING: This can have serious side-effects, including deadlocks and corruption of the debuggee.
Notice that when calling a nonstatic C++ method,
you have to pass the "this" parameter as an explicit first parameter.
The debugger knows what calling convention to use and puts the registers
in the correct location.
In this case, it knew that
And what's with that
But what if you want to call a method on an interface, and you
don't have the source code to the implementation?
For example, you want to read from a stream that was passed to you
from some external component.
Well, you can play a little trick.
You can pretend to call a function that you do
have the source code to, one that has the same function signature,
and then move the // pretend that we know that 0x00131320 is an IStream pointer 0:000> dp 131320 l1 00131320 77f6b5e8 // vtable 0:000> dps 77f6b5e8 l4 77f6b5e8 77fbff0e SHLWAPI!CFileStream::QueryInterface 77f6b5ec 77fb34ed SHLWAPI!CAssocW2k::AddRef 77f6b5f0 77f6b670 SHLWAPI!CFileStream::Release 77f6b5f4 77f77474 SHLWAPI!CFileStream::Read 0:000> .call SHLWAPI!CFileStream::Read(0x00131320, 0xa80000, 0x1000, 0) ^ Symbol not a function in '.call SHLWAPI!CFileStream::Read'
That error message is the debugger's somewhat confusing way of saying,
"I don't have enough information available to make that function call."
But that's okay, because we have a function that's "close enough",
namely 0:001> .call ABC!CAlphaStream::Read(0x00131320, 0xa80000, 0x1000, 0) Thread is set up for call, 'g' will execute. WARNING: This can have serious side-effects, including deadlocks and corruption of the debuggee. 0:000> r eip=SHLWAPI!CFileStream::Read 0:000> r eax=00131320 ebx=0007d628 ecx=00130000 edx=0013239e esi=00000000 edi=00000003 eip=77f77474 esp=0007d384 ebp=0007d3b0 iopl=0 nv up ei pl zr na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 SHLWAPI!CFileStream::Read: 77f77474 8bff mov edi,edi
Woo-hoo!
We got
This just skims the surface of what you can do with the
|
Comments (14)
Comments are closed. |
Neat trick. I’ll bookmark that for the future, but I’ve never been hardcore enough to use ntsd, cdb or windbg. I’m just fishing, but is there a similar technique to insert a temporary function call under VC++ (any recent version) without recompiling and using Edit and Continue?
Chris: the Immediate Window is your friend. When it works, that is ;)
This is the coolest thing I’ve seen in a long time. Thank you.
It’s been ~3 years since I’ve fired up windbg (either for a crash-dump analysis or to debug a driver over a serial cable. hah. Old boot param changes).
Do you find yourself doing this a lot ? (if that’s an answerable question in and of itself).. I mean, this is really neat stuff, but in my application experience, I’ve never had to do anything close to this. (Granted, I also don’t use COM, which from looking at your previous debugging posts, might be a good thing). Are you doing it for internal code, or external code ? (since you hint at "if you don’t have the source … ")
As others said, I now know where to look if I come across such a problem, but I feel too shortsighted to know/see where I would find myself in such a situation.
Raymond, aren’t you allocating a buffer of 1000 bytes and then passing it as being 0x1000 bytes long?
Of course, I’m assuming the third parameter to CAlphaStream::Read is the size of the buffer passed, and I could be completely wrong here.
Is there an undo function for .call?
If I thought I’d call something then realized I passed bad arguments, can I revert the register without actually doing the call?
(Maybe it’s in some documentation somewhere, maybe not. Beside, the answer will help millions of developpers. Or, at least, one.)
Pierre: read the documentation for .call. It’s call /c.
I wish there were a way of doing .call even if you have no symbols at all (it would make scripts easier to manage)…
Mark Steward:
You could do an extension !call that accepts a calling convention in addition to a list of parameters. It’d be very hard to do this entirely without symbols though… trying to reverse engineer something interesting? :)
BTW, the ? command that lets you evaluate expressions also allow calling functions. It existed in SYMDEB, but did not allow you to call functions until CodeView.
Hooray for Visual Studio.
Am I doing something wrong or do the public symbols not suffice to do this for the Win32 API?
Is this supported on Win64?
8irik66w9p4y6my 397n53dy 85j5erh9wopnxfax 65hkv649yb
8irik66w9p4y6my 85j5erh9wopnxfax 65hkv649yb