для вашей собственной 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;
}