6

问题 :

我已经构建了生成二进制文件的https://github.com/reorg/pg_repack项目。这个二进制文件与 postgres 9.6 redistributable 相关联(我使用那些由 EntrepriseDb 提供的)。在 Windows 7 上一切正常。在构建期间或运行时我没有问题。但在 Windows 8 或 10 上,应用程序总是会按照以下顺序崩溃。该二进制文件是在 Windows 7 上使用 Visual Studio 2013 从 C 源生成的(我尝试过在 Windows 10 上生成的版本,但它没有改变任何东西),在 x64 系统上,用于 x64 应用程序,优化被禁用和它使用动态基础。为了确保使用正确的二进制文件,我已将所有可再发行二进制文件复制到我的应用程序文件夹中。

Windows 7 上的装配细节(工作案例):

在 main 之后的几行,应用程序调用了函数 set_pglocale_pgservice

set_pglocale_pgservice(argv[0], "pgscripts");
00007FF6E4B39C85  mov         eax,8  
00007FF6E4B39C8A  imul        rax,rax,0  
00007FF6E4B39C8E  lea         rdx,[default_options+120h (07FF6E4B43B10h)]  
00007FF6E4B39C95  mov         rcx,qword ptr [argv]  
00007FF6E4B39C9A  mov         rcx,qword ptr [rcx+rax]  
00007FF6E4B39C9E  call        qword ptr [__imp_set_pglocale_pgservice (07FF6E4B40520h)] 

内存在 07FF6E4B40520h

0x00007FF6E4B40520  00000001403e1da0   .>@....
0x00007FF6E4B40528  0000000000000000  ........
0x00007FF6E4B40530  0000000000000000  ........
0x00007FF6E4B40538  00007ff6e4b35348  HS.äö...

(注意:从 0x7FF6E4B40000 到 0x00007FF6E4B40560 内存包含函数地址,mapfile 显示:0002:00000520 __imp_set_pglocale_pgservice postgres:postgres.exe [postgres 可再发行,动态链接])

然后调用qword ptr [__imp_set_pglocale_pgservice(07FF6E4B40520h)]

00000001403E1DA0  mov         qword ptr [rsp+18h],rbx  
00000001403E1DA5  push        rdi  
00000001403E1DA6  sub         rsp,0C40h  
00000001403E1DAD  mov         rax,qword ptr [1405F8C60h]  
00000001403E1DB4  xor         rax,rsp  
00000001403E1DB7  mov         qword ptr [rsp+0C30h],rax  
00000001403E1DBF  mov         rbx,rdx  
00000001403E1DC2  mov         rdi,rcx  
00000001403E1DC5  lea         rdx,[140430540h]  
00000001403E1DCC  mov         rcx,rbx  
00000001403E1DCF  call        00000001403F67FA

然后拨打 00000001403F67FA

00000001403F67FA  jmp         qword ptr [1403F8998h]

内存在 1403F8998h

0x00000001403F8998  00007ffe87a5cc60  `Ì¥.þ...
0x00000001403F89A0  00007ffe87a47060  `p¤.þ...
0x00000001403F89A8  00007ffe87a5f8a4  ¤ø¥.þ...

(注意:从 0x00000001403F8000 到 0x00000001403F8F08 内存包含函数地址,mapfile 显示:0002:00000998 ??_C@_04FHBLDJDJ@?1bin?$AA@ libpgport:path.obj [postgres 可再发行,静态链接])

然后跳转到 00007ffe87a5cc60 之后

00007FFE87A5CC60  sub         rdx,rcx  
00007FFE87A5CC63  test        cl,7  
00007FFE87A5CC66  je          00007FFE87A5CC7C 

...一切正常

Windows 10 上的装配细节(非工作案例):

在 main 之后的几行,应用程序调用了函数 set_pglocale_pgservice

set_pglocale_pgservice(argv[0], "pgscripts");
00007FF7E9879C85  mov         eax,8  
00007FF7E9879C8A  imul        rax,rax,0  
00007FF7E9879C8E  lea         rdx,[default_options+120h (07FF7E9883B10h)]  
00007FF7E9879C95  mov         rcx,qword ptr [argv]  
00007FF7E9879C9A  mov         rcx,qword ptr [rcx+rax]  
00007FF7E9879C9E  call        qword ptr [__imp_set_pglocale_pgservice (07FF7E9880520h)]

内存在 07FF7E9880520h

0x00007FF7E9880520  00000001403e1da0   .>@....
0x00007FF7E9880528  0000000000000000  ........
0x00007FF7E9880530  0000000000000000  ........
0x00007FF7E9880538  00007ff7e9875348  HS.é÷...

(注意:从 0x00007FF7E9880000 到 0x00007FF7E9880560 内存包含函数地址,mapfile 显示:0002:00000520 __imp_set_pglocale_pgservice postgres:postgres.exe [postgres 可再发行,动态链接])

然后调用qword ptr [__imp_set_pglocale_pgservice(07FF7E9880520h)]

00000001403E1DA0  mov         qword ptr [rsp+18h],rbx  
00000001403E1DA5  push        rdi  
00000001403E1DA6  sub         rsp,0C40h  
00000001403E1DAD  mov         rax,qword ptr [1405F8C60h]  
00000001403E1DB4  xor         rax,rsp  
00000001403E1DB7  mov         qword ptr [rsp+0C30h],rax  
00000001403E1DBF  mov         rbx,rdx  
00000001403E1DC2  mov         rdi,rcx  
00000001403E1DC5  lea         rdx,[140430540h]  
00000001403E1DCC  mov         rcx,rbx  
00000001403E1DCF  call        00000001403F67FA

然后拨打 00000001403F67FA

00000001403F67FA  jmp         qword ptr [1403F8998h] (should call C@_04FHBLDJDJ@?1bin?$AA@ libpgport:path.obj [postgres redistributable, which is linked statically]))

内存为 1403F8998h (此处应用程序与 Windows 7 不同)

0x00000001403F8998  000000000059e6a2  ¢æY.....
0x00000001403F89A0  000000000059e6ac  ¾Y.....
0x00000001403F89A8  000000000059e6b6  ¶æY.....

(注意:0x00000001403F8998是函数中间的操作码地址,不是函数地址)

00000001403F8998  mov         byte ptr [AC000000000059E6h],al  
00000001403F89A1  out         59h,al  
00000001403F89A3  add         byte ptr [rax],al  
...

内存在 000000000059e6a2h

000000000059E69F  ?? ?? 
000000000059E6A0  ?? ?? 
000000000059E6A1  ?? ?? 

然后跳转到 000000000059e6a2 => 崩溃后

Windows 7 上用于加载库的进程监视器详细信息(此处为 libpq.dll):

[...]
"16:48:40,2946466","pg_repack.exe","7216","Load Image","C:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Image Base: 0x180000000, Image Size: 0x30000"
[...]

Windows 10 上的进程监视器详细信息(此处为 libpq.dll)(都与 Windows 7 非常相似,除了库的加载)

[...]
"11:52:20,6264717","pg_repack.exe","12464","QueryOpen","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","CreationTime: 21/08/2017 11:38:04, LastAccessTime: 21/08/2017 12:06:56, LastWriteTime: 09/05/2017 06:45:07, ChangeTime: 21/08/2017 18:04:09, AllocationSize: 184 320, EndOfFile: 183 296, FileAttributes: A"
"11:52:20,6265789","pg_repack.exe","12464","CreateFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Desired Access: Read Data/List Directory, Execute/Traverse, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened"
"11:52:20,6266332","pg_repack.exe","12464","QuerySecurityFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Information: 0x20"
"11:52:20,6266513","pg_repack.exe","12464","CreateFileMapping","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","FILE LOCKED WITH ONLY READERS","SyncType: SyncTypeCreateSection, PageProtection: PAGE_EXECUTE"
"11:52:20,6266921","pg_repack.exe","12464","CreateFileMapping","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","SyncType: SyncTypeOther"
"11:52:20,6267619","pg_repack.exe","12464","Load Image","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Image Base: 0x180000000, Image Size: 0x30000"
"11:52:20,6274889","pg_repack.exe","12464","CreateFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened"
"11:52:20,6275293","pg_repack.exe","12464","QuerySecurityFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Information: 0x20"
"11:52:20,6275471","pg_repack.exe","12464","QueryBasicInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","CreationTime: 21/08/2017 11:38:04, LastAccessTime: 21/08/2017 12:06:56, LastWriteTime: 09/05/2017 06:45:07, ChangeTime: 21/08/2017 18:04:09, FileAttributes: A"
"11:52:20,6276255","pg_repack.exe","12464","CloseFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS",""
"11:52:20,6291170","pg_repack.exe","12464","CloseFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS",""
[...]
"11:52:20,6539022","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","BUFFER OVERFLOW","Name: \testF"
"11:52:20,6539202","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","BUFFER OVERFLOW","Name: \testF"
"11:52:20,6539363","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","BUFFER OVERFLOW","Name: \testF"
"11:52:20,6539512","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","BUFFER OVERFLOW","Name: \testF"
"11:52:20,6539664","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","BUFFER OVERFLOW","Name: \testF"
"11:52:20,6603867","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Name: \testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll"
"11:52:20,6604319","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Name: \testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll"
"11:52:20,6604778","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Name: \testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll"
"11:52:20,6605211","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Name: \testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll"
"11:52:20,6605635","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","SUCCESS","Name: \testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll"
[...]

注意:我期待类似于 Windows 7 的东西,或者至少类似于:

"11:52:20,6539022","pg_repack.exe","12464","QueryNameInformationFile","B:\testFolder\pg_repack-master\msvc\bin\x64\Debug\libpq.dll","BUFFER OVERFLOW","Length: 63".

所以我不知道为什么这个应用程序在 Windows 8 或 10 上有如此奇怪的行为。如果有人有一些解释或想法来修复崩溃,我会很高兴的。如果需要,请不要犹豫,询问一些细节。

4

1 回答 1

5

我查看您的二进制包并查看您执行pg_repack.exe的静态导入的postgres.exe. 问题的根源 - 是从 exe 文件导入。更确切地说来自没有标记的PE文件,因为正式扩展名(exedll等不起作用)。简而言之,我将在exe下的下一个意思是PE文件没有in ,但不是正式的文件扩展名IMAGE_FILE_DLLIMAGE_FILE_HEADER.CharacteristicsIMAGE_FILE_DLLCharacteristics

首先加载exe作为dll不正确 - 当exe以这种方式加载时 - 它没有调用入口点。并且不能被调用,因为它没有被设计为回调,接收DLL_PROCESS_* 通知,ExitProcess最后调用。当模块的入口点没有被调用时——通常它没有被初始化。假设你从exe调用了一些导出的函数,但是如果这个导出的函数使用了一些通常在exe入口点初始化的数据怎么办?所以结论 - 我们只能从这个exe进程中加载​​的dll调用从exe导出的函数。并且在每个进程中必须只有一个exe(作为可执行代码)。

在第二个特定postgres.exe的没有重定位的(IMAGE_FILE_RELOCS_STRIPPED标志IMAGE_FILE_HEADER.Characteristics) - 结果这个PE只能在硬编码地址加载。这对于exe来说不是问题,当几乎所有地址空间都空闲时,它总是首先映射到进程。但这通常是当PE作为DLL加载时出现的问题- 不是第一个进程 - 硬代码图像库可能已经很忙。所以结论 - 如果没有重定位作为DLL,您将无法安全使用任何exe

但是,Windows 10 崩溃的根源 - 因为如果此 pe 没有标志,Windows 10 无法解析PEIMAGE_FILE_DLL (“exe”)导入。换句话说,它像处理标志 一样处理这个PE - 不加载指定模块引用的其他可执行模块,也不解析导入。结果这个PE没有初始化并且会在第一次导入函数调用时崩溃(在你的情况下是)。LoadLibraryExDONT_RESOLVE_DLL_REFERENCESstrcmp

这仅在win10上发生。在 win 8.1 (build 9600) 和 win 7 上导入exe文件(作为dll加载)已解决。(你说在你的 win 8.1 上它崩溃了——可能是你使用了更多的新版本或一些更新?或者更好的检查

对此行为的最简单测试来自 test exe MatchToken函数调用,该函数从Netsh.exe. 代码可以是下一个:

#include <Netsh.h>
#pragma comment(linker, "/defaultlib:Netsh.lib")

MatchToken(L"*", L"*");// crash here on win 10

在 win10 上崩溃,因为MatchToken内部尝试调用_wcsnicmpfrom msvcrt.dll,但在 win10 中,此导入未解决。但在 win8.1、win7、win xp 上 - 此代码运行良好。

稍微复杂一点的例子:

if (HMODULE hmod = LoadLibraryW(L"wshelper.dll"))
{
    DWORD (WINAPI * InitHelperDll)(_In_ DWORD dwNetshVersion, PVOID pReserved);

    if (*(void**)&InitHelperDll = GetProcAddress(hmod, "InitHelperDll"))
    {
        InitHelperDll(1, 0);// crash here on win10 only
    }
    FreeLibrary(hmod);
}

这里我们加载标准 Windows NetShell helper dll - “wshelper.dll”并调用InitHelperDll回调函数。从Netsh.exe内部InitHelperDll调用该RegisterHelper函数。但同样,因为Netsh.exe在 Win 10 中作为 DLL 加载时未初始化(导入未解析) - 它在内部崩溃RegisterHelper

于 2017-08-22T22:16:37.587 回答