如何在 windbg 上制作 python 脚本以使其更容易使用?
How to make python script over windbg to make it's use easier?
import sys
import subprocess
command = 'C:\Program Files (x86)\Windows Kits\Debuggers\x64 -y ' + sys.argv[1] + ' -i ' + sys.argv[2] + ' -z ' + sys.argv[3] + ' -c "!analyze" '
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
我试过这段代码,我正在尝试输入崩溃转储名称和 exe 位置,然后我必须显示用户可以理解的崩溃分析 ouput.How 才能使用 python 脚本来做到这一点?使用 cpp 脚本更容易吗?
take input of crash dump name and exe location and then I have to display user understandable crash analysis ouput.
您似乎想要解析 !analyze
命令的文本输出。你可以这样做,但你应该知道这个命令可以有很多不同的输出。
假设您正在分析用户模式故障转储。在这种情况下,我会先 运行 一些简单的命令来检查你是否得到了合法的转储。您可以尝试以下命令:
||
检查转储类型(应该是 "user")
|
获取可执行文件的名称(应与您的应用程序匹配)
lmvm <app>
检查可执行文件的版本号
如果一切正常,您可以继续:
.exr -1
:区分crash和hang。 80000003 断点更可能是挂起或根本没有。
这可以帮助您决定是否应该 运行 !analyze
或 !analyze -hang
.
How to do that using Python scripting?
[...] \Windows Kits\Debuggers\x64 -y ' + [...]
此路径包含反斜杠,因此您想转义它们或使用类似 r"C:\Program Files (x86)\Windows Kits\..."
.
的 r 字符串
您可能应该在此处启动一个可执行文件以使其运行。 cdb.exe
是 WinDbg 的命令行版本。
command.split()
这不仅会拆分参数,还会拆分可执行文件的路径。因此 subprocess.popen()
将尝试一个名为 C:\Program
的应用程序,它不存在。
这可能会更频繁地失败,具体取决于 sys.argv[]
.
中带空格的参数
我建议您按原样传递选项:
command = r'C:\Program Files (x86)\Windows Kits\Debuggers\x64\cdb.exe'
arguments = [command]
arguments.extend(['-y', sys.argv[1]]) # Symbol path
arguments.extend(['-i', sys.argv[2]]) # Image path
arguments.extend(['-z', sys.argv[3]]) # Dump file
arguments.extend(['-c', '!analyze']) # Command(s) for analysis
process = subprocess.Popen(arguments, stdout=subprocess.PIPE)
请注意,没有涉及 split()
,这可能会在错误的位置拆分。
旁注:-i
可能无法按预期工作。如果您从客户端收到故障转储,它们的版本可能与您在磁盘上的版本不同。设置适当的符号服务器来缓解这种情况。
Is it easier with CPP scripting?
会有所不同,不会更容易。
工作示例
这是考虑到上述情况的 Python 代码。由于延迟等原因,它仍然有点老套,但除了时间和输出之外没有真正的指标来决定命令何时完成。 Python 3.8 在 Windows Explorer 的崩溃转储中成功。
import subprocess
import threading
import time
import re
class ReaderThread(threading.Thread):
def __init__(self, stream):
super().__init__()
self.buffer_lock = threading.Lock()
self.stream = stream # underlying stream for reading
self.output = "" # holds console output which can be retrieved by getoutput()
def run(self):
"""
Reads one from the stream line by lines and caches the result.
:return: when the underlying stream was closed.
"""
while True:
line = self.stream.readline() # readline() will block and wait for \r\n
if len(line) == 0: # this will only apply if the stream was closed. Otherwise there is always \r\n
break
with self.buffer_lock:
self.output += line
def getoutput(self, timeout=0.1):
"""
Get the console output that has been cached until now.
If there's still output incoming, it will continue waiting in 1/10 of a second until no new
output has been detected.
:return:
"""
temp = ""
while True:
time.sleep(timeout)
if self.output == temp:
break # no new output for 100 ms, assume it's complete
else:
temp = self.output
with self.buffer_lock:
temp = self.output
self.output = ""
return temp
command = r'C:\Program Files (x86)\Windows Kits\Debuggers\x64\cdb.exe'
arguments = [command]
arguments.extend(['-y', "srv*D:\debug\symbols*https://msdl.microsoft.com/download/symbols"]) # Symbol path, may use sys.argv[1]
# arguments.extend(['-i', sys.argv[2]]) # Image path
arguments.extend(['-z', sys.argv[3]]) # Dump file
arguments.extend(['-c', ".echo LOADING DONE"])
process = subprocess.Popen(arguments, stdout=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
reader = ReaderThread(process.stdout)
reader.start()
result = ""
while not re.search("LOADING DONE", result):
result = reader.getoutput() # ignore initial output
def dbg(command):
process.stdin.write(command+"\r\n")
process.stdin.flush()
return reader.getoutput()
result = dbg("||")
if "User mini" not in result:
raise Exception("Not a user mode dump")
else:
print("Yay, it's a user mode dump")
result = dbg("|")
if "explorer" not in result:
raise Exception("Not an explorer crash")
else:
print("Yay, it's an Explorer crash")
result = dbg("lm vm explorer")
if re.search(r"^\s*File version:\s*10\.0\..*$", result, re.M):
print("That's a recent version for which we should analyze crashes")
else:
raise Exception("That user should update to a newer version before we spend effort on old bugs")
dbg("q")
如果你不想使用 windbg,它是一个图形用户界面
使用 cdb.exe 它是控制台模式 windbg 它将所有结果输出到终端
这是一个演示
F:\>cdb -c "!analyze -v;qq" -z testdmp.dmp | grep -iE "bucket|owner"
DEFAULT_BUCKET_ID: BREAKPOINT
Scope: DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
BUCKET_ID
FOLLOWUP_NAME: MachineOwner
BUCKET_ID: BREAKPOINT_ntdll!LdrpDoDebuggerBreak+30
BUCKET_ID_IMAGE_STR: ntdll.dll
BUCKET_ID_MODULE_STR: ntdll
BUCKET_ID_FUNCTION_STR: LdrpDoDebuggerBreak
BUCKET_ID_OFFSET: 30
BUCKET_ID_MODTIMEDATESTAMP: c1bb301
BUCKET_ID_MODCHECKSUM: 1f647b
BUCKET_ID_MODVER_STR: 10.0.18362.778
BUCKET_ID_PREFIX_STR: BREAKPOINT_
FAILURE_BUCKET_ID: BREAKPOINT_80000003_ntdll.dll!LdrpDoDebuggerBreak
Followup: MachineOwner
grep 是一个通用的字符串解析器
它内置于 Linux
它在多个地方 windows 可用
如果是 32 位,您可以从 gnuwin32 包/Cygwin
使用它
如果是 64 位,你可以在 git
中找到它
您可以使用本机 findstr.exe 也
:\>dir /b f:\git\usr\bin\gr*
grep.exe
groups.exe
或在 msys / mingw / Cygwin / wsl / third party clones /
:\>dir /b /s *grep*.exe
F:\git\mingw64\bin\x86_64-w64-mingw32-agrep.exe
F:\git\mingw64\libexec\git-core\git-grep.exe
F:\git\usr\bin\grep.exe
F:\git\usr\bin\msggrep.exe
F:\msys64\mingw64\bin\msggrep.exe
F:\msys64\mingw64\bin\pcregrep.exe
F:\msys64\mingw64\bin\x86_64-w64-mingw32-agrep.exe
F:\msys64\usr\bin\grep.exe
F:\msys64\usr\bin\grepdiff.exe
F:\msys64\usr\bin\msggrep.exe
F:\msys64\usr\bin\pcregrep.exe
或者您可以在 python / JavaScript / typescript / c / c++ / ruby / rust / whatever
中编写自己的简单字符串解析器
这是一个示例 python 单词查找和重复脚本
import sys
for line in sys.stdin:
if "BUCKET" in line:
print(line)
让我们检查一下
:\>dir /b *.py
pyfi.py
:\>cat pyfi.py
import sys
for line in sys.stdin:
if "BUCKET" in line:
print(line)
:\>cdb -c "!analyze -v ;qq" -z f:\testdmp.dmp | python pyfi.py
DEFAULT_BUCKET_ID: BREAKPOINT
Scope: DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
BUCKET_ID
BUCKET_ID: BREAKPOINT_ntdll!LdrpDoDebuggerBreak+30
BUCKET_ID_IMAGE_STR: ntdll.dll
BUCKET_ID_MODULE_STR: ntdll
BUCKET_ID_FUNCTION_STR: LdrpDoDebuggerBreak
BUCKET_ID_OFFSET: 30
BUCKET_ID_MODTIMEDATESTAMP: c1bb301
BUCKET_ID_MODCHECKSUM: 1f647b
BUCKET_ID_MODVER_STR: 10.0.18362.778
BUCKET_ID_PREFIX_STR: BREAKPOINT_
FAILURE_BUCKET_ID: BREAKPOINT_80000003_ntdll.dll!LdrpDoDebuggerBreak
import sys
import subprocess
command = 'C:\Program Files (x86)\Windows Kits\Debuggers\x64 -y ' + sys.argv[1] + ' -i ' + sys.argv[2] + ' -z ' + sys.argv[3] + ' -c "!analyze" '
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
我试过这段代码,我正在尝试输入崩溃转储名称和 exe 位置,然后我必须显示用户可以理解的崩溃分析 ouput.How 才能使用 python 脚本来做到这一点?使用 cpp 脚本更容易吗?
take input of crash dump name and exe location and then I have to display user understandable crash analysis ouput.
您似乎想要解析 !analyze
命令的文本输出。你可以这样做,但你应该知道这个命令可以有很多不同的输出。
假设您正在分析用户模式故障转储。在这种情况下,我会先 运行 一些简单的命令来检查你是否得到了合法的转储。您可以尝试以下命令:
||
检查转储类型(应该是 "user")|
获取可执行文件的名称(应与您的应用程序匹配)lmvm <app>
检查可执行文件的版本号
如果一切正常,您可以继续:
.exr -1
:区分crash和hang。 80000003 断点更可能是挂起或根本没有。
这可以帮助您决定是否应该 运行 !analyze
或 !analyze -hang
.
How to do that using Python scripting?
[...] \Windows Kits\Debuggers\x64 -y ' + [...]
此路径包含反斜杠,因此您想转义它们或使用类似 r"C:\Program Files (x86)\Windows Kits\..."
.
您可能应该在此处启动一个可执行文件以使其运行。 cdb.exe
是 WinDbg 的命令行版本。
command.split()
这不仅会拆分参数,还会拆分可执行文件的路径。因此 subprocess.popen()
将尝试一个名为 C:\Program
的应用程序,它不存在。
这可能会更频繁地失败,具体取决于 sys.argv[]
.
我建议您按原样传递选项:
command = r'C:\Program Files (x86)\Windows Kits\Debuggers\x64\cdb.exe'
arguments = [command]
arguments.extend(['-y', sys.argv[1]]) # Symbol path
arguments.extend(['-i', sys.argv[2]]) # Image path
arguments.extend(['-z', sys.argv[3]]) # Dump file
arguments.extend(['-c', '!analyze']) # Command(s) for analysis
process = subprocess.Popen(arguments, stdout=subprocess.PIPE)
请注意,没有涉及 split()
,这可能会在错误的位置拆分。
旁注:-i
可能无法按预期工作。如果您从客户端收到故障转储,它们的版本可能与您在磁盘上的版本不同。设置适当的符号服务器来缓解这种情况。
Is it easier with CPP scripting?
会有所不同,不会更容易。
工作示例
这是考虑到上述情况的 Python 代码。由于延迟等原因,它仍然有点老套,但除了时间和输出之外没有真正的指标来决定命令何时完成。 Python 3.8 在 Windows Explorer 的崩溃转储中成功。
import subprocess
import threading
import time
import re
class ReaderThread(threading.Thread):
def __init__(self, stream):
super().__init__()
self.buffer_lock = threading.Lock()
self.stream = stream # underlying stream for reading
self.output = "" # holds console output which can be retrieved by getoutput()
def run(self):
"""
Reads one from the stream line by lines and caches the result.
:return: when the underlying stream was closed.
"""
while True:
line = self.stream.readline() # readline() will block and wait for \r\n
if len(line) == 0: # this will only apply if the stream was closed. Otherwise there is always \r\n
break
with self.buffer_lock:
self.output += line
def getoutput(self, timeout=0.1):
"""
Get the console output that has been cached until now.
If there's still output incoming, it will continue waiting in 1/10 of a second until no new
output has been detected.
:return:
"""
temp = ""
while True:
time.sleep(timeout)
if self.output == temp:
break # no new output for 100 ms, assume it's complete
else:
temp = self.output
with self.buffer_lock:
temp = self.output
self.output = ""
return temp
command = r'C:\Program Files (x86)\Windows Kits\Debuggers\x64\cdb.exe'
arguments = [command]
arguments.extend(['-y', "srv*D:\debug\symbols*https://msdl.microsoft.com/download/symbols"]) # Symbol path, may use sys.argv[1]
# arguments.extend(['-i', sys.argv[2]]) # Image path
arguments.extend(['-z', sys.argv[3]]) # Dump file
arguments.extend(['-c', ".echo LOADING DONE"])
process = subprocess.Popen(arguments, stdout=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
reader = ReaderThread(process.stdout)
reader.start()
result = ""
while not re.search("LOADING DONE", result):
result = reader.getoutput() # ignore initial output
def dbg(command):
process.stdin.write(command+"\r\n")
process.stdin.flush()
return reader.getoutput()
result = dbg("||")
if "User mini" not in result:
raise Exception("Not a user mode dump")
else:
print("Yay, it's a user mode dump")
result = dbg("|")
if "explorer" not in result:
raise Exception("Not an explorer crash")
else:
print("Yay, it's an Explorer crash")
result = dbg("lm vm explorer")
if re.search(r"^\s*File version:\s*10\.0\..*$", result, re.M):
print("That's a recent version for which we should analyze crashes")
else:
raise Exception("That user should update to a newer version before we spend effort on old bugs")
dbg("q")
如果你不想使用 windbg,它是一个图形用户界面 使用 cdb.exe 它是控制台模式 windbg 它将所有结果输出到终端
这是一个演示
F:\>cdb -c "!analyze -v;qq" -z testdmp.dmp | grep -iE "bucket|owner"
DEFAULT_BUCKET_ID: BREAKPOINT
Scope: DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
BUCKET_ID
FOLLOWUP_NAME: MachineOwner
BUCKET_ID: BREAKPOINT_ntdll!LdrpDoDebuggerBreak+30
BUCKET_ID_IMAGE_STR: ntdll.dll
BUCKET_ID_MODULE_STR: ntdll
BUCKET_ID_FUNCTION_STR: LdrpDoDebuggerBreak
BUCKET_ID_OFFSET: 30
BUCKET_ID_MODTIMEDATESTAMP: c1bb301
BUCKET_ID_MODCHECKSUM: 1f647b
BUCKET_ID_MODVER_STR: 10.0.18362.778
BUCKET_ID_PREFIX_STR: BREAKPOINT_
FAILURE_BUCKET_ID: BREAKPOINT_80000003_ntdll.dll!LdrpDoDebuggerBreak
Followup: MachineOwner
grep 是一个通用的字符串解析器
它内置于 Linux
它在多个地方 windows 可用
如果是 32 位,您可以从 gnuwin32 包/Cygwin
使用它
如果是 64 位,你可以在 git
您可以使用本机 findstr.exe 也
:\>dir /b f:\git\usr\bin\gr*
grep.exe
groups.exe
或在 msys / mingw / Cygwin / wsl / third party clones /
:\>dir /b /s *grep*.exe
F:\git\mingw64\bin\x86_64-w64-mingw32-agrep.exe
F:\git\mingw64\libexec\git-core\git-grep.exe
F:\git\usr\bin\grep.exe
F:\git\usr\bin\msggrep.exe
F:\msys64\mingw64\bin\msggrep.exe
F:\msys64\mingw64\bin\pcregrep.exe
F:\msys64\mingw64\bin\x86_64-w64-mingw32-agrep.exe
F:\msys64\usr\bin\grep.exe
F:\msys64\usr\bin\grepdiff.exe
F:\msys64\usr\bin\msggrep.exe
F:\msys64\usr\bin\pcregrep.exe
或者您可以在 python / JavaScript / typescript / c / c++ / ruby / rust / whatever
中编写自己的简单字符串解析器这是一个示例 python 单词查找和重复脚本
import sys
for line in sys.stdin:
if "BUCKET" in line:
print(line)
让我们检查一下
:\>dir /b *.py
pyfi.py
:\>cat pyfi.py
import sys
for line in sys.stdin:
if "BUCKET" in line:
print(line)
:\>cdb -c "!analyze -v ;qq" -z f:\testdmp.dmp | python pyfi.py
DEFAULT_BUCKET_ID: BREAKPOINT
Scope: DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
BUCKET_ID
BUCKET_ID: BREAKPOINT_ntdll!LdrpDoDebuggerBreak+30
BUCKET_ID_IMAGE_STR: ntdll.dll
BUCKET_ID_MODULE_STR: ntdll
BUCKET_ID_FUNCTION_STR: LdrpDoDebuggerBreak
BUCKET_ID_OFFSET: 30
BUCKET_ID_MODTIMEDATESTAMP: c1bb301
BUCKET_ID_MODCHECKSUM: 1f647b
BUCKET_ID_MODVER_STR: 10.0.18362.778
BUCKET_ID_PREFIX_STR: BREAKPOINT_
FAILURE_BUCKET_ID: BREAKPOINT_80000003_ntdll.dll!LdrpDoDebuggerBreak