Принудительная загрузка библиотеки DLL размером более 2 ГБ (0x80000000) в 32-разрядном процессе в Windows - PullRequest
0 голосов
/ 09 мая 2018

Чтобы проверить угловой случай в нашем отладчике, мне нужно найти программу, в которой DLL загружена более 2 ГБ (0x80000000). Текущий тестовый пример - игра с несколькими ГБ, которая загружает> 700 DLL, и я хотел бы иметь что-то попроще и меньше. Есть ли способ достичь этого надежно без особых хлопот? Я предполагаю, что мне нужно использовать /LARGEADDRESSAWARE и каким-то образом потреблять достаточно места для виртуальной машины, чтобы поднять новые библиотеки DLL больше 2 ГБ, но я не совсем уверен в деталях ...

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

для вашей собственной DLL вам нужно установить 3 компоновщика:

обратите внимание, что link.exe разрешает только полное изображение, расположенное ниже 3 ГБ (0xC0000000) для 32-битного изображения. другими словами, он хочет, чтобы ImageBase + ImageSize <= 0xC0000000 скажем, /BASE:0xB0000000 будет в порядке, /BASE:0xBFFF0000 только если размер вашего изображения <= 0x10000 и для <code>/BASE:0xC0000000 и выше, мы всегда получаем ошибку LNK1249 - изображение превышает максимальный экстент с базовым адресом и размером размера

также EXE обязательно должен иметь также /LARGEADDRESSAWARE, поскольку все 4 ГБ пространства доступны для процесса wow64 на основе только параметров EXE .

если мы хотим сделать это для внешней DLL - здесь вопрос сложнее. Прежде всего - эта DLL может исправить ситуацию (загрузка базы> 0x80000000)? Хорошо. давай проверим это. любой API (включая самый низкий уровень LdrLoadDll) не позволяет указывать базовый адрес для DLL загрузки. здесь существует только хук-решение.

при загрузке библиотеки, внутренний всегда вызывал ZwMapViewOfSection и его 3-й параметр BaseAddress - указатель на переменную, которая получает базовый адрес представления. если мы установите эту переменную в 0 - система сама выберет загруженный адрес. если мы установим это на определенный адрес - представление системной карты ( DLL изображение в нашем случае) только по этому адресу или вернет ошибку STATUS_CONFLICTING_ADDRESSES.

рабочее решение - перехватить вызов ZwMapViewOfSection и заменить значение переменной, до которой BaseAddress. для поиска адреса> 0x80000000 мы можем использовать VirtualAlloc с опцией MEM_TOP_DOWN. примечание - несмотря на то, что ZwMapViewOfSection также позволяют использовать MEM_TOP_DOWN в параметре AllocationType, здесь это не будет иметь никакого эффекта - раздел в любом случае будет загружен по предпочтительному адресу или сверху вниз из 0x7FFFFFFF not от 0xFFFFFFFF. но с VirtualAlloc MEM_TOP_DOWN начинают поиск с 0xFFFFFFFF, если процесс использует 4 ГБ пространства пользователя. для информации - сколько памяти нужно для раздела - мы можем вызвать ZwQuerySection с SectionBasicInformation - несмотря на то, что это не документировано - для отладки и тестирования - это нормально.

для перехвата, конечно, можно использовать некоторый обходной путь, но можно сделать перехват с точкой останова DRx - установить для некоторого регистра Drx адрес NtMapViewOfSection. и установите AddVectoredExceptionHandler - который обрабатывает исключение. это будет идеальная работа, если процесс не под отладчиком. но при отладчике он ломается - большинство отладчиков всегда останавливались при одноэтапном исключении и обычно ни один вариант не обрабатывал их, а передавал в приложение. Конечно, мы можем запустить программу не под отладчиком, а прикрепить ее позже - после загрузки dll. или возможно сделать эту задачу в отдельном потоке и скрыть этот поток от отладчика. недостаток здесь - в этом случае отладчик не получает уведомления о загрузке DLL и не загружает символы для этого. однако для внешних (системных dll), для которых у вас нет кода src - это в большинстве случаев может быть не большой проблемой. поэтому решение выйдет, если мы сможем его реализовать). Возможный код:

PVOID pvNtMapViewOfSection;

LONG NTAPI OnVex(::PEXCEPTION_POINTERS ExceptionInfo)
{
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP &&
        ExceptionInfo->ExceptionRecord->ExceptionAddress == pvNtMapViewOfSection)
    {
        struct MapViewOfSection_stack 
        {
            PVOID ReturnAddress;
            HANDLE SectionHandle;
            HANDLE ProcessHandle;
            PVOID *BaseAddress;
            ULONG_PTR ZeroBits;
            SIZE_T CommitSize;
            PLARGE_INTEGER SectionOffset;
            PSIZE_T ViewSize;
            SECTION_INHERIT InheritDisposition;
            ULONG AllocationType;
            ULONG Win32Protect;
        } * stack = (MapViewOfSection_stack*)(ULONG_PTR)ExceptionInfo->ContextRecord->Esp;

        if (stack->ProcessHandle == NtCurrentProcess())
        {
            SECTION_BASIC_INFORMATION sbi;

            if (0 <= ZwQuerySection(stack->SectionHandle, SectionBasicInformation, &sbi, sizeof(sbi), 0))
            {
                if (PVOID pv = VirtualAlloc(0, (SIZE_T)sbi.Size.QuadPart, MEM_RESERVE|MEM_TOP_DOWN, PAGE_NOACCESS))
                {
                    if (VirtualFree(pv, 0, MEM_RELEASE))
                    {
                        *stack->BaseAddress = pv;
                    }
                }
            }
        }

        // RESUME_FLAG ( 0x10000) not supported by xp, but anyway not exist 64bit xp
        ExceptionInfo->ContextRecord->EFlags |= RESUME_FLAG;
        return EXCEPTION_CONTINUE_EXECUTION;
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

struct LOAD_DATA {
    PCWSTR lpLibFileName;
    HMODULE hmod;
    ULONG dwError;
};

ULONG WINAPI HideFromDebuggerThread(LOAD_DATA* pld)
{
    NtSetInformationThread(NtCurrentThread(), ThreadHideFromDebugger, 0, 0);

    ULONG dwError = 0;

    HMODULE hmod = 0;

    if (PVOID pv = AddVectoredExceptionHandler(TRUE, OnVex))
    {
        ::CONTEXT ctx = {};
        ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
        ctx.Dr7 = 0x404;
        ctx.Dr1 = (ULONG_PTR)pvNtMapViewOfSection;
        if (SetThreadContext(GetCurrentThread(), &ctx))
        {
            if (hmod = LoadLibraryW(pld->lpLibFileName))
            {
                pld->hmod = hmod;
            }
            else
            {
                dwError = GetLastError();
            }

            ctx.Dr7 = 0x400;
            ctx.Dr1 = 0;

            SetThreadContext(GetCurrentThread(), &ctx);
        }
        else
        {
            dwError = GetLastError();
        }
        RemoveVectoredExceptionHandler(pv);
    }
    else
    {
        dwError = GetLastError();
    }

    pld->dwError = dwError;

    return dwError;
}

HMODULE LoadLibHigh(PCWSTR lpLibFileName)
{
    BOOL bWow;
    HMODULE hmod = 0;
    if (IsWow64Process(GetCurrentProcess(), &bWow) && bWow)
    {
        if (pvNtMapViewOfSection = GetProcAddress(GetModuleHandle(L"ntdll"), "NtMapViewOfSection"))
        {
            LOAD_DATA ld = { lpLibFileName };

            if (IsDebuggerPresent())
            {
                if (HANDLE hThread = CreateThread(0, 0, (PTHREAD_START_ROUTINE)HideFromDebuggerThread, &ld, 0, 0))
                {
                    WaitForSingleObject(hThread, INFINITE);
                    CloseHandle(hThread);
                }
            }
            else
            {
                HideFromDebuggerThread(&ld);
            }

            if (!(hmod = ld.hmod))
            {
                SetLastError(ld.dwError);
            }
        }
    }
    else
    {
        hmod = LoadLibrary(lpLibFileName);
    }

    return hmod;
}
0 голосов
/ 09 мая 2018

Хорошо, мне потребовалось несколько попыток, но мне удалось придумать что-то работающее.

// cl /MT /Ox test.cpp /link /LARGEADDRESSAWARE
// occupy the 2 gigabytes!
#define ALLOCSIZE (64*1024)
#define TWOGB (2*1024ull*1024*1024)

#include <windows.h>
#include <stdio.h>

int main()
{
  int nallocs = TWOGB/ALLOCSIZE;
  for ( int i = 0; i < nallocs+200; i++ )
  {
   void * p = VirtualAlloc(NULL, ALLOCSIZE, MEM_RESERVE, PAGE_NOACCESS);
   if ( i%100 == 0)
   {
     if ( p != NULL )
       printf("%d: %p\n", i, p);
     else
     {
       printf("%d: failed!\n", i);
       break;
     }
   }
  }
  printf("finished VirtualAlloc. Loading  a DLL.\n");
  //getchar();
  HMODULE hDll = LoadLibrary("winhttp");

  printf("DLL base: %p.\n", hDll);
  //getchar();
  FreeLibrary(hDll);
}

На Win10 x64 выдает:

0: 00D80000
100: 03950000
200: 03F90000
[...]
31800: 7FBC0000
31900: 00220000
32000: 00860000
32100: 80140000
32200: 80780000
32300: 80DC0000
32400: 81400000
32500: 81A40000
32600: 82080000
32700: 826C0000
32800: 82D00000

32900: 83340000
finished VirtualAlloc. Loading  a DLL.
DLL base: 83780000.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...