标题改过一次了, 原标题太长了, 原标题是 "u.LoadDll.hFile 句柄为空引发的模块窗口exe信息为空等系列问题"
后来发现这一系列问题都是 GetMappedFileName 这个函数引起的...
---------------------------------------------------------------------------------------------------
第一次遇到 DebugEvent->u.LoadDll.hFile 句柄为空的情况.
先说说是怎样发现这个问题的.
今天在调试一个64位程序的时候, 下了个INT3断点重启后,发现断点没了.
打开UDD菜单,发现并没有保存当前调试目标的UDD文件.于是调试分析定位到了这个 DebugEvent->u.LoadDll.hFile 为空的原因引发的.
在接收到创建进程事件的时候, 通过 DebugEvent->u.LoadDll.hFile 句柄来获取模块完整路径. 代码如下:
[C++] 纯文本查看 复制代码
DWORD CDebugger::DebugX86_DispatcDebughEvent(const DEBUG_EVENT* lpDebugEvent)
{
switch (lpDebugEvent->dwDebugEventCode)
{
// 异常事件
case EXCEPTION_DEBUG_EVENT: return DebugX86_Exception(lpDebugEvent);
// 创建进程事件
case CREATE_PROCESS_DEBUG_EVENT: return DebugX86_CreateProcess(lpDebugEvent); // <---------------------通过这个事件来获取模块路径.其中 lpDebugEvent->u.LoadDll.hFile 就是那个句柄
// 创建线程事件
case CREATE_THREAD_DEBUG_EVENT: return DebugX86_CreateThread(lpDebugEvent);
// 退出线程事件
case EXIT_THREAD_DEBUG_EVENT: return DebugX86_ExitThread(&lpDebugEvent->u.ExitThread);
// 退出进程事件
case EXIT_PROCESS_DEBUG_EVENT: return DebugX86_ExitProcess(lpDebugEvent);
// 加载DLL事件
case LOAD_DLL_DEBUG_EVENT: return DebugX86_LoadDll(lpDebugEvent);
// 卸载DLL事件
case UNLOAD_DLL_DEBUG_EVENT: return DebugX86_UnLoadDll(&lpDebugEvent->u.UnloadDll);
// 日志输出
case OUTPUT_DEBUG_STRING_EVENT: return DebugX86_OutputDebugString(&lpDebugEvent->u.DebugString);
// rip
case RIP_EVENT: return DebugX86_RipEvent(&lpDebugEvent->u.RipInfo);
default:LogOutput("未知异常!"); return DBG_CONTINUE;
}
return false;
}
在另一篇贴子中 获取UnityPlayer.dll模块路径错误的问题 也是通过这个句柄来获取路径的.
这个问题很吊诡,怎么会为空呢? 第一次遇到这种情况. (也有可能之前也遇到过,只是没有注意到.)
下面是 lpDebugEvent 结构体:
[C++] 纯文本查看 复制代码
typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode;
DWORD dwProcessId;
DWORD dwThreadId;
union {
EXCEPTION_DEBUG_INFO Exception;
CREATE_THREAD_DEBUG_INFO CreateThread;
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; // 这个是创建进程的结构体.
EXIT_THREAD_DEBUG_INFO ExitThread;
EXIT_PROCESS_DEBUG_INFO ExitProcess;
LOAD_DLL_DEBUG_INFO LoadDll; // 而我在创建进程的事件中, 引用了加载DLL事件的结构体
UNLOAD_DLL_DEBUG_INFO UnloadDll;
OUTPUT_DEBUG_STRING_INFO DebugString;
RIP_INFO RipInfo;
} u;
} DEBUG_EVENT, *LPDEBUG_EVENT;
当初在写调试器框架的时候,看到有这么个句柄,就直接拿来用了. 不曾想会出现获取不到的情况.
既然这个程序会有这问题,我就又看了看 x64dbg 的模块窗口, 没有问题,可以获取到exe模块信息. 也不晓得x64dbg是怎么实现的,也懒得翻源码了.
又看了 yzdbg, 发现一样的是为空的. 看来yzdbg作者我俩写法应该差不多,都有这情况.
cpudbg模块窗口exe信息为空
cpudbg模块窗口exe信息为空
yzdbg模块窗口exe信息为空
yzdbg模块窗口exe信息为空
这个获取不到句柄的问题, 会导致UDD保存失败之外, 还会导致模块窗口获取不到主程序.exe的信息.等等.
既然问题找到了,回头空了再来解决吧.
有问题的这个demo我是真想放上来, 奈何服务器小水管,不抗造...
-----------------------------------------------------------
2022.10.13 刚刚我用了一个临时解决方案,
通过进程句柄,调用 GetModuleFileNameEx 获取文件绝对路径, 然后在调用 CreateFile 拿到文件句柄.(没办法,封装的子函数里面太多地方要用到文件句柄)
这一切都正常的, 包括打印的文件路径也是正确的. 我印象中,早期我是用的 GetModuleFileNameEx 来获取文件路径的, 可是后面有遇到获取不到的情况, 才改用 GetMappedFileName 函数来获取.
但没想到的是用 GetMappedFileName 遇到了获取的文件路径不对的情况, 具体的可参考这一篇贴子: 获取UnityPlayer.dll模块路径错误的问题
这篇贴子中遇到的问题: 明明路径是 c:\游戏\UnityPlayer.dll 可通过 GetMappedFileName 得到的路径确是 c:\UnityPlayer.dll
当时这路径都在一个逻辑分区上,所以也没想太多.
可是今天遇到的这个问题. 也是因为 GetMappedFileName 函数引发的.
本来目标程序是在 D 盘上的 后来我拷到 Z 盘上了. 也就是拷贝到 Z 盘之后, 我才发现 UDD 没有保存. 于是才发现获取不到文件句柄. 然后就是一系列的问题.
原来都是 GetMappedFileName 这个函数造成的. 文件我由D盘拷贝到Z盘,且D盘的文件删除之后, 为什么 GetMappedFileName 获取的还是之前D盘我删掉的路径?
另篇贴子中提到的 GetFinalPathNameByHandle 函数, 没有这类问题, 但这个函数新版本才有, 如果是在xp上运行32位的程序, 就又得专门处理一下. 而且我也不敢保证
这个函数就一定能获取所有的例子路径.
至于 GetMappedFileName 获取之前的路径原因还不清楚, 随便GG了一下, 也没有找到相关的信息. 回头空了再研究吧.
-----------------------------------------------------------
2022.10.13 刚才又看了一下, 不仅仅是 GetMappedFileName 有问题. 应该是从创建进程的时候开始就有问题了.
在创建进程后, WaitForDebugEvent 获取的第一个事件就是 CREATE_PROCESS_DEBUG_EVENT 而这里 DEBUG_EVENT 参数获取的句柄就已经是空的了.
这个问题还没找到原因. 有可能是操作系统的原因? 也有可能是系统的BUG? 搞不清楚, 回头空了再研究研究...
-----------------------------------------------------------
to be continued...
|