I intend to search opcodes in specific memory area in process's dump.
I want to have some conditions while searching.
like:
$$><<>script.wds #call 00400000 L? 01000000
for(00400000 ~ 01000000)
{
// this condition is if argument's opcode which is address is not in this area
.if(arg1's opcode !in 00400000 ~ 01000000)
.print arg1
}
You can use a combination of #
, .foreach
and $spat
.
Lets say you want to find the few first call
opcodes in notepad!WinMain
. You could do something like this:
0:000> .foreach (addr {# call notepad!WinMain L20}) { .if ($spat("addr", "notepad!*:")) {.echo addr} }
notepad!WinMain+0xa:
notepad!WinMain+0x19:
notepad!WinMain+0x20:
notepad!WinMain+0x33:
notepad!WinMain+0x39:
notepad!WinMain+0x45:
And now the explanation.
# call notepad!WinMain L20
searches for the string "call" in the disassembly on the specified range. This is the output:
0:000> # call notepad!WinMain L20
notepad!WinMain+0xa:
01002940 ff1514110001 call dword ptr [notepad!_imp__GetCommandLineW (01001114)]
notepad!WinMain+0x19:
0100294f ff151c120001 call dword ptr [notepad!_imp__GetSystemMetrics (0100121c)]
notepad!WinMain+0x20:
01002956 ff1510110001 call dword ptr [notepad!_imp__GetProcAddress (01001110)]
notepad!WinMain+0x33:
01002969 ffd0 call eax
notepad!WinMain+0x39:
0100296f e874f2ffff call notepad!SkipProgramName (01001be8)
notepad!WinMain+0x45:
0100297b e8e51b0000 call notepad!NPInit (01004565)
The .foreach (var {cmd}) {commands}
executes cmd, splits the output at whitespaces, and executes {commands} once for each of the tokens it got with "var" being replaced with that token.
Without the .if
we'd get something like:
0:000> .foreach (addr {# call notepad!WinMain L20}) {.echo addr}
notepad!WinMain+0xa:
01002940
ff1514110001
call
dword
ptr
[notepad!_imp__GetCommandLineW
(01001114)]
notepad!WinMain+0x19:
0100294f
ff151c120001
call
dword
ptr
[notepad!_imp__GetSystemMetrics
.
.
.
Finally we add the .if
to check whether our token starts with "notepad!" and ends with ":". Note that we have to test for the colon at the end. Otherwise we might get "notepad!SkipProgramName" and "notepad!NPInit". Even testing for "notepad!WinMain*" isn't enough, since it might be the target of local jumps.
Now, if you want to pass this address to any command, you have to get rid of the colon at the end of the addr alias the .foreach
command created. I'm not sure there's even a way to do it, so we do one last trick. Every time we find and addr that fits the pattern we set a flag, and at every iteration we check the flag. If the flag is raised, we use the current token. Since the output of #
is of the form
Symbol:
address opcode_byte opcode_mnemonic argument ...
The token following the symbol that matches our pattern is a clean address.
For example, the following command disassembles two opcode for each call
found:
0:000> .foreach (addr {# call notepad!WinMain L20}) { .if (@$t0==1) { u addr L2; r @$t0=0;}; .if ($spat("addr", "notepad!*:")) { r @$t0 = 1} }
notepad!WinMain+0xa:
01002940 ff1514110001 call dword ptr [notepad!_imp__GetCommandLineW (01001114)]
01002946 68d8130001 push offset notepad!`string' (010013d8)
notepad!WinMain+0x19:
0100294f ff151c120001 call dword ptr [notepad!_imp__GetSystemMetrics (0100121c)]
01002955 50 push eax
notepad!WinMain+0x20:
01002956 ff1510110001 call dword ptr [notepad!_imp__GetProcAddress (01001110)]
0100295c 33f6 xor esi,esi
notepad!WinMain+0x33:
01002969 ffd0 call eax
0100296b ff7514 push dword ptr [ebp+14h]
notepad!WinMain+0x39:
0100296f e874f2ffff call notepad!SkipProgramName (01001be8)
01002974 50 push eax
notepad!WinMain+0x45:
0100297b e8e51b0000 call notepad!NPInit (01004565)
01002980 85c0 test eax,eax
(The pseudo-register $t0
is the flag.)
And now, after this horror is done I join Thomas in suggesting you use PyKd if you want to go even a further beyond that.
The mere fact that we can do this using WinDbg's awful awful scripting language doesn't mean that we should.
I'm not exactly sure if I understood what you need, but I think it is similar to the following.
Create a script which does what you want, using pseudo registers, e.g.
.for(r @$t2=@$t0; @$t2<@$t1; r @$t2=@$t2+1) {.printf "%d, ", @$t2}
Set up the "parameters" in pseudo registers
0:000> r @$t0 = 0x4
0:000> r @$t1 = 0x10
Then run the script:
0:000> $$>< script.wds
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
Considering the whole task, I'd not recommend doing that in plain WinDbg and use some higher level abstraction such as PyKD, because
- it's not easy to get the opcode from an ASM statement
- you might want to search in
!address
es only that are PAGE_EXECUTE
- WinDbg scripts are often cryptic and not very maintainable
your query is unclear do you want to do something like this
0:000> .shell -ci ".echo" type c:\foo.txt
# ${$arg1} ${$arg2} ${$arg3}.shell: Process exited
running the above script by providing three arguments you can search for
any instruction in the range you provide
the argument 1 is call so i want to search for call instructions
arg 2 is an expression therefor windbg will be evaluated to an address like 0x12340000
arg 3 again is an expression evaluated to size
result as follows
0:000> $$>a< c:\foo.txt call windbg L900
windbg+0x1009:
01341009 e8340170e8 call e9a41142
windbg+0x10b5:
013410b5 e834010000 call windbg+0x11ee (013411ee)
windbg+0x1171:
01341171 e8000060e8 call e9941176