Вызов статически связанной функции сбоя каждый раз в Windows 8/10, но не 7

Проблема:

У меня есть сборка https://github.com/reorg/pg_repack проекта, который генерирует двоичный файл. Этот двоичный файл связан с распространяемым Postgres 9.6 (я использую те, которые поставляются EntrepriseDb). Все отлично работает на Windows 7. У меня нет проблем во время сборки или во время выполнения. Но в Windows 8 или 10 приложение всегда аварийно завершает работу со следующими последовательностями. Двоичный файл генерируется из источников C с Visual Studio 2013, в Windows 7 (я пробовал с версией, созданной для Windows 10, но это ничего не меняет), в системе x64, для приложения x64, и оптимизация отключена и это использовать динамическую базу. Чтобы быть уверенным, что я использую правильные двоичные файлы, я должен скопировать все распространяемые двоичные файлы в папку моего приложения.

Сборочные детали на windows 7 (рабочий корпус):

Несколько строк после основного, приложение вызывает функцию 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 (не рабочий корпус):

Через несколько строк после основного, приложение вызывает функцию 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. Я был бы рад, если бы у кого-то были какие-то объяснения или идеи, чтобы исправить сбои. Не стесняйтесь спрашивать некоторые детали, если это необходимо.

1 ответ

Решение

Я смотрю ваш двоичный пакет и вижу, что вы исполняете pg_repack.exe которые статически импортируют из postgres.exe, и корень проблемы - это импорт из exe-файла. точнее из PE файла, который не имеет флага IMAGE_FILE_DLL в IMAGE_FILE_HEADER.Characteristics, потому что формальное расширение (exe, dll и т. д. не играют роли). для краткости я буду в следующем под exe значит PE файл без IMAGE_FILE_DLL в Characteristics, но не формальное расширение файла

при первой загрузке exe как dll некорректно - при загрузке exe таким способом - точка входа не вызывается. и не может быть вызван, потому что он не предназначен, чтобы называться как обратный вызов, получая DLL_PROCESS_* уведомление и звонок ExitProcess в конце. когда точка входа модуля не вызывается - обычно она не инициализируется. Предположим, вы вызываете какую-то экспортированную функцию из exe, но что, если эта экспортированная функция использует некоторые данные, которые обычно инициализируются в точке входа exe? Итак, вывод - мы можем вызывать экспортированные функции из exe только из dll, загруженных в этот exe- процесс. и в каждом процессе должен быть только один исполняемый файл (как исполняемый код).

на втором конкретном postgres.exe нет переезда (IMAGE_FILE_RELOCS_STRIPPED флаг в IMAGE_FILE_HEADER.Characteristics) - в результате этот PE может быть загружен только по жестко закодированному адресу. это не проблема для exe, который всегда отображается первым для обработки, когда почти все адресное пространство свободно. но в целом это проблема, когда PE загружается как DLL - не первым в процессе - база изображений жесткого кода может быть уже занята. поэтому вывод - вы не можете безопасно использовать любой EXE без перемещения в качестве DLL

однако корень сбоя в Windows 10 - потому что Windows 10 не разрешает импорт PE ("exe"), если у этого pe нет флага IMAGE_FILE_DLL, другими словами, он обрабатывает этот PE как LoadLibraryEx с флагом DONT_RESOLVE_DLL_REFERENCES - не загружает дополнительные исполняемые модули, на которые ссылается указанный модуль, и не разрешает импорт. в результате этот PE не инициализируется и будет зависать при первом вызове функции импорта (в вашем случае это strcmp).

и это происходит только на win10. на win 8.1 (сборка 9600) и win 7 разрешен импорт для exe- файлов (загруженных как dll). (вы говорите, что на вашем win 8.1 он потерпел крах - возможно, вы используете более новую сборку или какое-то обновление? или лучше проверьте)

самый простой тест для этого поведения вызова из теста exe MatchToken, функция, которая экспортируется из Netsh.exe, код может быть следующим:

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

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

сбой на win10, потому что MatchToken внутренне попробуйте позвонить _wcsnicmp от 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 помощник dll - "wshelper.dll" и вызываем InitHelperDll функция обратного вызова. внутренне InitHelperDll позвонить RegisterHelper функция из Netsh.exe. но опять же, потому что Netsh.exe не инициализирован (импорт не разрешен), когда он загружается как DLL в win 10 - он потерпел крах внутри RegisterHelper

Другие вопросы по тегам