Windbg command's each result as parameter in s

2019-09-03 06:45发布

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
}

标签: windbg
3条回答
手持菜刀,她持情操
2楼-- · 2019-09-03 06:54

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
查看更多
爷、活的狠高调
3楼-- · 2019-09-03 07:00

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 !addresses only that are PAGE_EXECUTE
  • WinDbg scripts are often cryptic and not very maintainable
查看更多
霸刀☆藐视天下
4楼-- · 2019-09-03 07:13

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.

查看更多
登录 后发表回答