mirror of
https://github.com/racoten/BetterNetLoader
synced 2026-06-08 16:54:45 +00:00
218 lines
8.8 KiB
C++
218 lines
8.8 KiB
C++
#include <Windows.h>
|
|
#include <tchar.h>
|
|
#include <stdio.h>
|
|
|
|
// Helper: Constructs an absolute path if lpFileName does not contain any path separator.
|
|
static void BuildFullPath(LPCTSTR lpFileName, LPTSTR fullPath, size_t fullPathSize)
|
|
{
|
|
// Check if lpFileName contains '\' or '/' or ':' to determine if it is a full path.
|
|
if (_tcschr(lpFileName, _T('\\')) || _tcschr(lpFileName, _T('/')) || _tcschr(lpFileName, _T(':')))
|
|
{
|
|
_tcsncpy_s(fullPath, fullPathSize, lpFileName, _TRUNCATE);
|
|
}
|
|
else
|
|
{
|
|
// If a singular name, use the system directory.
|
|
TCHAR sysDir[MAX_PATH] = { 0 };
|
|
if (GetSystemDirectory(sysDir, MAX_PATH))
|
|
_stprintf_s(fullPath, fullPathSize, _T("%s\\%s"), sysDir, lpFileName);
|
|
else
|
|
_tcsncpy_s(fullPath, fullPathSize, lpFileName, _TRUNCATE);
|
|
}
|
|
// Debug print to verify the full path.
|
|
_tprintf(_T("BuildFullPath: %s\n"), fullPath);
|
|
}
|
|
|
|
// A simple manual PE loader that mimics LoadLibraryEx.
|
|
// It reads the DLL file from disk, maps it into memory,
|
|
// copies headers and sections, applies base relocations, resolves imports,
|
|
// and finally calls DllMain with DLL_PROCESS_ATTACH.
|
|
// dwFlags may include LOAD_LIBRARY_AS_DATAFILE or DONT_RESOLVE_DLL_REFERENCES.
|
|
HMODULE MyLoadLibraryExWrapped(LPCTSTR lpFileName, HANDLE hFileIgnored, DWORD dwFlags)
|
|
{
|
|
TCHAR fullPath[MAX_PATH] = { 0 };
|
|
BuildFullPath(lpFileName, fullPath, MAX_PATH);
|
|
|
|
// Open the file.
|
|
HANDLE hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
_tprintf(_T("MyLoadLibraryExWrapped: CreateFile failed for %s\n"), fullPath);
|
|
return NULL;
|
|
}
|
|
|
|
// Create a file mapping for reading.
|
|
HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (!hFileMapping)
|
|
{
|
|
CloseHandle(hFile);
|
|
_tprintf(_T("MyLoadLibraryExWrapped: CreateFileMapping failed for %s\n"), fullPath);
|
|
return NULL;
|
|
}
|
|
|
|
LPVOID pFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
|
|
if (!pFileBase)
|
|
{
|
|
CloseHandle(hFileMapping);
|
|
CloseHandle(hFile);
|
|
_tprintf(_T("MyLoadLibraryExWrapped: MapViewOfFile failed for %s\n"), fullPath);
|
|
return NULL;
|
|
}
|
|
|
|
// Validate DOS header.
|
|
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)pFileBase;
|
|
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
|
{
|
|
UnmapViewOfFile(pFileBase);
|
|
CloseHandle(hFileMapping);
|
|
CloseHandle(hFile);
|
|
_tprintf(_T("MyLoadLibraryExWrapped: Invalid DOS signature in %s\n"), fullPath);
|
|
return NULL;
|
|
}
|
|
|
|
// Get NT headers.
|
|
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((LPBYTE)pFileBase + dosHeader->e_lfanew);
|
|
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
|
|
{
|
|
UnmapViewOfFile(pFileBase);
|
|
CloseHandle(hFileMapping);
|
|
CloseHandle(hFile);
|
|
_tprintf(_T("MyLoadLibraryExWrapped: Invalid NT signature in %s\n"), fullPath);
|
|
return NULL;
|
|
}
|
|
|
|
// Reserve memory for the image.
|
|
SIZE_T imageSize = pNTHeader->OptionalHeader.SizeOfImage;
|
|
LPVOID baseDll = VirtualAlloc((LPVOID)pNTHeader->OptionalHeader.ImageBase, imageSize,
|
|
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
|
if (!baseDll)
|
|
baseDll = VirtualAlloc(NULL, imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
|
if (!baseDll)
|
|
{
|
|
UnmapViewOfFile(pFileBase);
|
|
CloseHandle(hFileMapping);
|
|
CloseHandle(hFile);
|
|
_tprintf(_T("MyLoadLibraryExWrapped: VirtualAlloc failed for %s\n"), fullPath);
|
|
return NULL;
|
|
}
|
|
|
|
// Copy headers.
|
|
memcpy(baseDll, pFileBase, pNTHeader->OptionalHeader.SizeOfHeaders);
|
|
|
|
// Copy each section.
|
|
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNTHeader);
|
|
for (UINT i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
|
|
{
|
|
LPVOID dest = (LPBYTE)baseDll + pSection[i].VirtualAddress;
|
|
LPVOID src = (LPBYTE)pFileBase + pSection[i].PointerToRawData;
|
|
memcpy(dest, src, pSection[i].SizeOfRawData);
|
|
}
|
|
|
|
// Clean up file mapping.
|
|
UnmapViewOfFile(pFileBase);
|
|
CloseHandle(hFileMapping);
|
|
CloseHandle(hFile);
|
|
|
|
// If not loaded as data file, perform base relocations.
|
|
if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE) &&
|
|
(DWORD_PTR)baseDll != pNTHeader->OptionalHeader.ImageBase)
|
|
{
|
|
PIMAGE_DATA_DIRECTORY relocDir = &pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
|
if (relocDir->Size)
|
|
{
|
|
PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)baseDll + relocDir->VirtualAddress);
|
|
DWORD_PTR delta = (DWORD_PTR)baseDll - pNTHeader->OptionalHeader.ImageBase;
|
|
while (pReloc->VirtualAddress)
|
|
{
|
|
DWORD count = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
|
PWORD pRelocData = (PWORD)((LPBYTE)pReloc + sizeof(IMAGE_BASE_RELOCATION));
|
|
for (DWORD i = 0; i < count; i++)
|
|
{
|
|
WORD typeOffset = pRelocData[i];
|
|
WORD type = typeOffset >> 12;
|
|
WORD offset = typeOffset & 0x0FFF;
|
|
if (type == IMAGE_REL_BASED_HIGHLOW)
|
|
{
|
|
PDWORD pPatch = (PDWORD)((LPBYTE)baseDll + pReloc->VirtualAddress + offset);
|
|
*pPatch += (DWORD)delta;
|
|
}
|
|
#ifdef _WIN64
|
|
else if (type == IMAGE_REL_BASED_DIR64)
|
|
{
|
|
PULONGLONG pPatch = (PULONGLONG)((LPBYTE)baseDll + pReloc->VirtualAddress + offset);
|
|
*pPatch += delta;
|
|
}
|
|
#endif
|
|
}
|
|
pReloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)pReloc + pReloc->SizeOfBlock);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process imports (unless loading as data file or if flag to not resolve imports is set).
|
|
if (!(dwFlags & (LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES)))
|
|
{
|
|
PIMAGE_DATA_DIRECTORY importDir = &pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
|
if (importDir->Size)
|
|
{
|
|
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)baseDll + importDir->VirtualAddress);
|
|
while (pImportDesc->Name)
|
|
{
|
|
LPCTSTR importName = (LPCTSTR)((LPBYTE)baseDll + pImportDesc->Name);
|
|
// Load dependency using standard LoadLibrary (or recursively call MyLoadLibraryExWrapped if desired).
|
|
HMODULE hImport = LoadLibrary(importName);
|
|
if (!hImport)
|
|
{
|
|
VirtualFree(baseDll, 0, MEM_RELEASE);
|
|
_tprintf(_T("MyLoadLibraryExWrapped: Failed to load dependency %s\n"), importName);
|
|
return NULL;
|
|
}
|
|
|
|
PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((LPBYTE)baseDll + pImportDesc->OriginalFirstThunk);
|
|
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((LPBYTE)baseDll + pImportDesc->FirstThunk);
|
|
while (pOrigThunk->u1.AddressOfData)
|
|
{
|
|
FARPROC proc = NULL;
|
|
if (pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
|
|
{
|
|
WORD ordinal = IMAGE_ORDINAL(pOrigThunk->u1.Ordinal);
|
|
proc = GetProcAddress(hImport, (LPCSTR)ordinal);
|
|
}
|
|
else
|
|
{
|
|
PIMAGE_IMPORT_BY_NAME pImport = (PIMAGE_IMPORT_BY_NAME)((LPBYTE)baseDll + pOrigThunk->u1.AddressOfData);
|
|
proc = GetProcAddress(hImport, (LPCSTR)pImport->Name);
|
|
}
|
|
if (!proc)
|
|
{
|
|
VirtualFree(baseDll, 0, MEM_RELEASE);
|
|
_tprintf(_T("MyLoadLibraryExWrapped: Failed to resolve import in %s\n"), importName);
|
|
return NULL;
|
|
}
|
|
pThunk->u1.Function = (ULONG_PTR)proc;
|
|
pOrigThunk++;
|
|
pThunk++;
|
|
}
|
|
pImportDesc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call the DLL's entry point (DllMain) for DLL_PROCESS_ATTACH.
|
|
if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE))
|
|
{
|
|
if (pNTHeader->FileHeader.Characteristics & IMAGE_FILE_DLL)
|
|
{
|
|
typedef BOOL(WINAPI* DllMainProc)(HINSTANCE, DWORD, LPVOID);
|
|
DllMainProc pDllMain = (DllMainProc)((LPBYTE)baseDll + pNTHeader->OptionalHeader.AddressOfEntryPoint);
|
|
if (pDllMain)
|
|
pDllMain((HINSTANCE)baseDll, DLL_PROCESS_ATTACH, NULL);
|
|
}
|
|
}
|
|
|
|
// Return the base address as the module handle.
|
|
_tprintf(_T("MyLoadLibraryExWrapped: Successfully loaded %s at 0x%p\n"), fullPath, baseDll);
|
|
return (HMODULE)baseDll;
|
|
}
|