在汇编中用printf/MessageBox调试MFC程序遇异常及控制台初始化求助
调试MFC程序IO端口时输出值的问题及解决方案
问题描述
我正在用x32dbg调试一款MFC程序的IO端口工作机制,想在in/out汇编指令后、函数返回前加代码显示相关值,但遇到了异常:
- 使用
printf的汇编代码:
push eax //eax contains the value call printf add esp,4 ret
触发C0000005 EXCEPTION_ACCESS_VIOLATION异常。
- 使用
MessageBoxA的汇编代码:
push 0 //OK push eax // eax contains the value push eax push 0 // HWND call dword ptr ds:[54352C] // linked user32.MessageBoxA ret
同样触发上述异常。
更新:尝试使用putchar:
push eax call putchar add esp,4 ret
由于未初始化控制台,没有任何输出。想知道能否通过汇编初始化控制台并与主程序并行运行?
异常原因分析
printf调用异常:
- MFC GUI程序默认可能未初始化C运行时(CRT),
printf依赖CRT的缓冲区、全局变量等结构,直接调用会因环境缺失触发访问违规。 - 参数完全错误:
printf第一个参数必须是格式字符串指针,直接压入eax里的IO数值,相当于把数值当作内存地址访问,必然踩非法内存。
- MFC GUI程序默认可能未初始化C运行时(CRT),
MessageBoxA调用异常:
- 硬编码的
ds:[54352C]无效:程序开启ASLR(地址空间布局随机化)后,user32.dll的加载基址会变化,静态地址会指向非法内存。 - 参数传递错误:
MessageBoxA的参数顺序是(窗口句柄, 文本字符串指针, 标题字符串指针, 弹窗类型),把eax的数值直接当字符串指针用,访问的是无效内存,触发异常。
- 硬编码的
可行的输出方法
1. 修复MessageBoxA调用
- 不要用硬编码地址:在x32dbg里找到
user32.dll的MessageBoxA导出地址(可以用GetProcAddress动态获取,或者直接在调试器的导出表中查看)。 - 先把数值转成字符串:不能直接把整数压入栈,得用汇编实现十进制转ASCII的逻辑,或者调用
itoa(前提是CRT已初始化),把数值转成可显示的字符串。 - 正确传递参数示例:
; 假设eax的数值已转成ASCII字符串,存在ebx指向的内存 push 0x00000000 ; MB_OK弹窗类型 push ebx ; 标题字符串指针 push ebx ; 文本字符串指针 push 0 ; 父窗口句柄 call [MessageBoxA] ; 替换成调试器中获取的正确地址
2. 直接用x32dbg的日志断点
这是最简单的方法,无需修改程序内存:
在in/out指令处下断点,右键选Breakpoint -> Log Breakpoint,设置日志内容为"IO端口值:%eax",每次触发断点时,调试器会自动把eax的值输出到日志窗口。
3. 初始化控制台输出
用汇编调用Windows API初始化控制台,然后用WriteConsoleA输出,无需依赖CRT:
; 第一步:创建控制台 call AllocConsole push -11 ; STD_OUTPUT_HANDLE(标准输出的句柄标识) call GetStdHandle mov ebx, eax ; 保存标准输出句柄到ebx ; 第二步:把eax中的数值转成ASCII字符串(假设存在esi指向的缓冲区,ecx是字符串长度) push 0 push ecx push esi push ebx call WriteConsoleA
AllocConsole会创建独立的控制台窗口,和MFC主程序的GUI界面并行运行,互不干扰。
4. 写入文件输出
如果不想弹出窗口或控制台,可以把值写入文件:
用汇编调用CreateFileA创建文件,WriteFile写入转换后的字符串,最后CloseHandle关闭文件,全程依赖Windows API,不需要CRT支持。
关于控制台初始化的问题
完全可以通过汇编调用AllocConsole初始化控制台,它会生成一个独立的控制台窗口,和MFC主程序并行运行,不会阻塞主程序的GUI线程。初始化后用WriteConsoleA就能正常输出内容,比依赖CRT的putchar靠谱得多。
内容的提问来源于stack exchange,提问作者javierMarquez
相关产品推荐
相关产品推荐

