【发布时间】:2015-06-05 05:52:26
【问题描述】:
例如,这个应用程序:
#define _WIN32_WINNT 0x0500
#include <windows.h>
int __stdcall NoCRTMain(void)
{
int result;
PWSTR lpCmdLine = GetCommandLine();
for (;;)
{
if (*lpCmdLine == L'"')
{
lpCmdLine++;
for (;;)
{
if (*lpCmdLine == L'"') break;
if (*lpCmdLine == L'\0') break;
lpCmdLine++;
}
}
if (*lpCmdLine == L' ') break;
if (*lpCmdLine == L'\0') break;
lpCmdLine++;
}
while (*lpCmdLine == ' ') lpCmdLine++;
result = MessageBox(NULL, lpCmdLine, L"Scripting Engine", MB_OK | MB_SYSTEMMODAL);
if (result != IDOK) for (;;) Sleep(INFINITE);
ExitProcess(0);
}
在 Visual Studio 2010 中作为 64 位应用程序构建,没有 C 运行时,这在大多数情况下都能完美运行。有时它会在没有明显原因的情况下在启动时开始崩溃。此崩溃发生在应用程序中的任何代码运行之前(见下文)。
当问题发生时,它只发生在可执行文件的特定实例上,即特定的可执行文件。文件的逐字节相同副本将正常运行。重新启动计算机后,问题可能(会?)消失。我正在运行测试以尝试可靠地重现问题,以便我可以确定可能发生问题的情况,例如,仅在安装了 Visual Studio 的情况下?只有安装了防病毒软件?但到目前为止,除了运气不好之外,我还没有通过任何程序重现这个问题。
大多数时候,调试显示 kernel32!BaseThreadInitThunk 调用的是无效地址而不是 NoCRTMain 的地址,尽管最近的一些运行在此之前就失败了,显然是在加载 DLL 时。
我相信我已经跟踪到加载模块时 ImageBase 设置不正确的问题。在工作实例上,相对于可执行模块的 0x00D8 处的内存转储,结构 _IMAGE_OPTIONAL_HEADER64 来自 winnt.h:
00000001`3f5900d8 0b 02 0a 00 00 02 00 00 00 06 00 00 00 00 00 00
00000001`3f5900e8 00 10 00 00 00 10 00 00 00 00 59 3f 01 00 00 00
显示ImageBase(最后八个字节)包含模块的起始地址,在本例中为1`3f590000。在失败的实例上,相同的内存转储
00000001`3fc600d8 0b 02 0a 00 00 02 00 00 00 06 00 00 00 00 00 00
00000001`3fc600e8 00 10 00 00 00 10 00 00 00 00 8f 3f 01 00 00 00
显示 ImageBase 不是预期的 1`3fc60000,而是 1`3f8f0000。
这似乎发生在调试器可以检查进程的最早时间点之前,所以我不确定如何继续。也许我需要做内核调试?我目前有一个出现问题的 VMWare vSphere 虚拟机,并且我有一个可以还原到的快照,因此我可以进行实验。
所以:
有谁知道这种行为的原因,更重要的是,如何预防?
我对内存转储的解释有误吗?
有任何调试/故障排除建议吗?
编译器选项:
/Zi /nologo /W3 /WX- /O2 /Oi /GL /D "WIN32" /D "NDEBUG" /D "_WINDOWS"
/D "_UNICODE" /D "UNICODE" /Gm- /EHsc /MT /GS- /Gy /fp:precise /Zc:wchar_t
/Zc:forScope /Fp"x64\Release\sehalt.pch" /Fa"x64\Release\" /Fo"x64\Release\"
/Fd"x64\Release\vc100.pdb" /Gd /errorReport:queue
链接器选项:
/OUT:"C:\documents\code\w7lab-scripting\sehalt\x64\Release\sehalt.exe"
/INCREMENTAL:NO /NOLOGO "kernel32.lib" "user32.lib" "gdi32.lib"
"winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib"
"oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /NODEFAULTLIB
/MANIFEST /ManifestFile:"x64\Release\sehalt.exe.intermediate.manifest"
/ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG
/PDB:"C:\documents\code\w7lab-scripting\sehalt\x64\Release\sehalt.pdb"
/SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF
/PGD:"C:\documents\code\w7lab-scripting\sehalt\x64\Release\sehalt.pgd"
/LTCG /TLBID:1 /ENTRY:"NoCRTMain" /DYNAMICBASE /NXCOMPAT
/MACHINE:X64 /ERRORREPORT:QUEUE
PS:看看我的哪些应用程序已知以这种方式失败,哪些没有,我怀疑问题只发生在小于一页(4096 字节)大小的可执行文件上。
【问题讨论】:
-
回复:“文件的逐字节相同副本将正常运行。重新启动计算机后问题可能(会?)消失。”建议我认为问题并不严格存在于您的源代码中,而可能与链接/加载器过程有关。可能是可执行文件中缺少某些信息,导致操作系统的加载程序不得不“猜测”某些参数(有时是错误的)并缓存该猜测。
-
你如何编译和链接你的代码?您能否将相关设置/命令行添加到您的问题中?
-
@stakx:谢谢;我已经添加了编译器和链接器命令行。
-
这上面写满了 ASLR,省略 /DYNAMICBASE 链接器选项。根本问题可能是搬迁问题,很难看到。
-
EMET 无法在没有重定位的情况下重定位可执行文件。如果您不使用 /DYNAMICBASE 或 /FIXED:NO 选项,您的可执行文件将不会有重定位。
标签: windows visual-studio-2010