有时开发一个Windows应用层程序,用Windbg调试,要进行源码中的断点调试,总需要如下繁复的操作:
- 查看进程管理器管理器,记下要调试的进程的id。
!process <进程id> 0,获取进程的EProcess.process /p /i <进程eprocess>,进入目标进程的上下文。.reload,加载调试符号。- 打开源文件,设置断点。
F5继续运行,触发断点。
为了节省打命令的时间,于是想写一个脚本,只要把目标进程的id提供给它,由它完成以上工作。先看一下!process <进程id> 0命令打印的结果:

思路就是遍历这个打印的结果中的每个单词,遇到PROCESS,则在循环的下一轮中可取到进程的EProcess值,然后用这个EProcess值切换到目标进程的上下文。完整的脚本如下:
ad /q ${/v:procInfo};
ad /q ${/v:eprocess};
ad /q ${/v:procName};
aS /c ${/v:procInfo} "!process $arg1 0";
.block {
.foreach /s (token "${procInfo}") {
.if ('${token}' == 'PROCESS') {
aS ${/v:eprocess} b;
.echo <z: found word `PROCESS`>;
.continue;
}
.if ('${eprocess}' == 'b') {
aS ${/v:eprocess} token;
.echo <z: found eprocess>: token;
.continue;
}
.if ('${token}' == 'Image:') {
aS ${/v:procName} b;
.echo <z: found word `Image:`>;
.continue;
}
.if ('${procName}' == 'b') {
aS ${/v:procName} token;
.echo <z: found procName>: token;
.break;
}
}
.block {
.if ('`${/n:eprocess}`' == '`eprocess`') {
.echo eprocess;
.process /p /i ${eprocess};
g;
} .else {
.echo <z: Cannot found process $arg1 >;
}
}
}
有两点要注意:
-
在脚本的开头用了
ad命令将后续用到的几个别名作一次清除,以防上次使用脚本后别名的残留值影响到本次运行。 -
使用
.block {}块,是为了防止脚本一运行便将.if ('${eprocess}' == 'b')中的${eprocess}展开。如果把.block {}去掉,就会发现${eprocess}一直都是eprocess。具体原因可参考这篇。 -
20230919修改: 将循环后的对
eprocess的判断语句改为:.if (‘`${/n:eprocess}`’ == ‘`eprocess`’)
测试过对
${/d:eprocess}进行判断(若eprocess已定义,则展开为1,否则展开为0),发现该值无论如何都展开为0,故选用${/n:eprocess}(若eprocess已定义,则展开为eprocess,否则展开为${/n:eprocess})。另外,不能直接写成:.if (‘${/n:eprocess}’ == ‘eprocess’)
因为
'eprocess'会以eprocess的实际数值展开,比如变成'ffffa8089eadf300'这样的。而写成 ‘`eprocess`’ ,即用反引号再加单引号包起来,就不会被展开(不能仅用单引号包起来,会有语法错误)。
使用时,直接执行$$>a<D:\zbh\myScripts\myWindbgScripts\myAttachProc.wds <进程id>。因为使用.process命令时传入了/i参数,即进行侵入式调试,会导致windbg在下面的g命令后停下来,这时脚本中剩下的命令不再执行,所以最后要手动执行一次.reload命令,加载调试符号。如下是运行脚本的结果,之后再手动.reload即可:


本文介绍了一个脚本,用于简化Windows应用的Windbg调试过程,通过接收进程ID自动执行一系列操作,包括获取进程信息、切换上下文、设置断点,极大提高了调试效率。
3万+

被折叠的 条评论
为什么被折叠?



