Searching memory including unknown values
Asked Answered
B

3

9

In WinDbg I can search the memory for bytes using the s command, e.g.

s 0012ff40 L?2000 48 65 6c 6c 6f

Is there also a way to include unknown bytes in the search sequence, e.g.

s 0012ff40 L?2000 48 65 ?? ?? ?? 6c 6f

where ?? is a byte with an arbitrary value?

Idea

How about doing ((memory XOR 48 65 00 00 00 6c 6f) AND FF FF 00 00 00 FF FF) and compare that against 00 00 00 00 00 00 00? But I don't know how to do that in WinDbg either.

Borman answered 6/1, 2014 at 13:59 Comment(0)
B
1

We can use to achieve this. Find the downloads linked from PyKD Wiki or PyKD Downloads. When using WinDbg Preview, copy the DLLs into

%LOCALAPPDATA%\DBG\EngineExtensions

for 64 bit or

%LOCALAPPDATA%\DBG\EngineExtensions32

for 32 Bit.

Since this is only the WinDbg extension, you also need the Python module as well:

pip install pykd

Use the power of Python to do what WinDbg can't do. Save the following script in a good place for WinDbg, i.e. in a short path without spaces.

from pykd import *
import sys
import re
import struct

if len(sys.argv)<4:
    print("Wildcard search for memory")
    print("Usage:", sys.argv[0], "<address> <length> <pattern> [-v]", sep=" ")
    print("      <address>: Memory address where searching begins.")
    print("                 This can be a WinDbg expression like ntdll!NtCreateThreadEx.")
    print("      <length> : Number of bytes that will be considered as the haystack.")
    print("      <pattern>: Bytes that you're looking for. May contain ?? for unknown bytes.")
    print("      [-v]     : (optional) Verbose output")
    print()
    print("Examples:")
    print("     ", sys.argv[0], "00770000 L50 01 02 03 ?? 05")
    print("         will find 01 02 03 04 05 or 01 02 03 FF 05, if present in memory")
    sys.exit(0)

verbose = False
if sys.argv[-1][0:2] == "-v":
    verbose = True

if verbose:
    for n in range(1, len(sys.argv)):
        print(f"param {n}: " + sys.argv[n])

address = expr(sys.argv[1])
if verbose: print("Start address:", "0x{:08x}".format(address), sep=" ")

length = sys.argv[2]
length = length.replace("L?","") # consider large address range syntax
length = length.replace("L","") # consider address range syntax
length = expr(length)
if verbose: print("Length:", "0n"+str(length), "bytes", sep=" ")

regex = b""
for n in range(3, len(sys.argv) - 1 if verbose else 0):
    if sys.argv[n] == "??":
        regex += bytes(".", "ascii")
    else:
        char = struct.pack("B", expr(sys.argv[n]))
        if char == b".":
            regex += struct.pack("B", ord("\\"))
        regex += char
if verbose: print("Regex:", regex, sep=" ")

memorycontent = loadBytes(address, length)
if verbose: print("Memory:", memorycontent, sep=" ")

result = re.search(regex, bytes(memorycontent))
print("Found:", ' '.join("0x{:02x}".format(x) for x in result.group(0)), "at address", "0x{:08x}".format(address+result.start()))

The script constructs a Regex for a Bytes object. It uses . for the wildcard and escapes literal . to \..

Let's prepare a proper sample in WinDbg:

0:006> .dvalloc 1000
Allocated 1000 bytes starting at 00900000
0:000> eu 0x00900000 "Test.with.regex"
0:000> db 0x00900000 L0n30
00900000  54 00 65 00 73 00 74 00-2e 00 77 00 69 00 74 00  T.e.s.t...w.i.t.
00900010  68 00 2e 00 72 00 65 00-67 00 65 00 78 00        h...r.e.g.e.x.

Load the PyKD extension, so we'll be able to run the script:

0:006> .load pykd

and run the script:

0:000> !py d:\debug\scripts\memwild.py 00900000 L10 2e ?? 77
Found: 0x2e 0x00 0x77 at address 0x00900008
Borman answered 2/4, 2020 at 17:59 Comment(0)
Z
6

Am not sure if the search command supports wild card. But you can use .foreach command, to achieve what you want.

Here is a sample that i used to search a memory pattern such as ff ?? 00

.foreach (hit {s -[1]b 00007ffabc520000 L100 ff }) {db hit L3; s ${hit}+2 L1 00}

Here is a brief description of how it works :

NOTE - Open up the debugger help from windbg to get complete documentation. That is within Windbg, Help | Contents

{s -[1]b 00007ffabc520000 L100 ff }

Use -[1] flag with s, so that only the memory address is given as the output.

s ${hit}+2 L1 00

For each hit, pass that memory address to the next search command. Increase the memory by the number of bytes that you want to skip and mention the last part of search pattern.

db hit L3

From the memory that has the beginning of the patter, dump the entire length. This is just to confirm that we are getting the right results!

Hope this helps. In case you need further clarification, i can try to provide that as well.

Zsigmondy answered 7/1, 2014 at 8:13 Comment(5)
Thanks, I'll give it a try, but it looks like this becomes far too complex in case of searching memory with multiple wildcards like AA ?? BB ?? CC ?? DDBorman
Yes i agree. this is more like a work-around than a perfect-solution :)Zsigmondy
@ThomasWeller - Did you happen to find a better solution? I'd love te be able to search like this s -d 0x0 L?FFFFFFFF b33d????Phoebephoebus
@LievenKeersmaekers: me too :-) If I manage to set up pykd, I'd probably do it with pykd. But sometimes, pykd fails for me and it if that's the case, it really takes long to fix it. I've not written a pykd script for this purpose yet.Borman
@LievenKeersmaekers: finally, I wrote a pykd script. Certainly not perfect yet, currently only supports bytes, not dwords. Only outputs first occurrence, etc. See another answer here.Borman
B
1

We can use to achieve this. Find the downloads linked from PyKD Wiki or PyKD Downloads. When using WinDbg Preview, copy the DLLs into

%LOCALAPPDATA%\DBG\EngineExtensions

for 64 bit or

%LOCALAPPDATA%\DBG\EngineExtensions32

for 32 Bit.

Since this is only the WinDbg extension, you also need the Python module as well:

pip install pykd

Use the power of Python to do what WinDbg can't do. Save the following script in a good place for WinDbg, i.e. in a short path without spaces.

from pykd import *
import sys
import re
import struct

if len(sys.argv)<4:
    print("Wildcard search for memory")
    print("Usage:", sys.argv[0], "<address> <length> <pattern> [-v]", sep=" ")
    print("      <address>: Memory address where searching begins.")
    print("                 This can be a WinDbg expression like ntdll!NtCreateThreadEx.")
    print("      <length> : Number of bytes that will be considered as the haystack.")
    print("      <pattern>: Bytes that you're looking for. May contain ?? for unknown bytes.")
    print("      [-v]     : (optional) Verbose output")
    print()
    print("Examples:")
    print("     ", sys.argv[0], "00770000 L50 01 02 03 ?? 05")
    print("         will find 01 02 03 04 05 or 01 02 03 FF 05, if present in memory")
    sys.exit(0)

verbose = False
if sys.argv[-1][0:2] == "-v":
    verbose = True

if verbose:
    for n in range(1, len(sys.argv)):
        print(f"param {n}: " + sys.argv[n])

address = expr(sys.argv[1])
if verbose: print("Start address:", "0x{:08x}".format(address), sep=" ")

length = sys.argv[2]
length = length.replace("L?","") # consider large address range syntax
length = length.replace("L","") # consider address range syntax
length = expr(length)
if verbose: print("Length:", "0n"+str(length), "bytes", sep=" ")

regex = b""
for n in range(3, len(sys.argv) - 1 if verbose else 0):
    if sys.argv[n] == "??":
        regex += bytes(".", "ascii")
    else:
        char = struct.pack("B", expr(sys.argv[n]))
        if char == b".":
            regex += struct.pack("B", ord("\\"))
        regex += char
if verbose: print("Regex:", regex, sep=" ")

memorycontent = loadBytes(address, length)
if verbose: print("Memory:", memorycontent, sep=" ")

result = re.search(regex, bytes(memorycontent))
print("Found:", ' '.join("0x{:02x}".format(x) for x in result.group(0)), "at address", "0x{:08x}".format(address+result.start()))

The script constructs a Regex for a Bytes object. It uses . for the wildcard and escapes literal . to \..

Let's prepare a proper sample in WinDbg:

0:006> .dvalloc 1000
Allocated 1000 bytes starting at 00900000
0:000> eu 0x00900000 "Test.with.regex"
0:000> db 0x00900000 L0n30
00900000  54 00 65 00 73 00 74 00-2e 00 77 00 69 00 74 00  T.e.s.t...w.i.t.
00900010  68 00 2e 00 72 00 65 00-67 00 65 00 78 00        h...r.e.g.e.x.

Load the PyKD extension, so we'll be able to run the script:

0:006> .load pykd

and run the script:

0:000> !py d:\debug\scripts\memwild.py 00900000 L10 2e ?? 77
Found: 0x2e 0x00 0x77 at address 0x00900008
Borman answered 2/4, 2020 at 17:59 Comment(0)
O
0

If the range of the search is not insanely large you could copy/paste the hex dump into sublime text and just do a find with regex mode enabled. For example I was looking for (1200 < X < 2400)

add esp, X
ret

In sublime text I searched using the regex 81 c4 .. .. .. 00 c3 and found an address with instructions for

add esp,600h
ret
Outrun answered 19/3, 2017 at 4:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.