LoadLibrary 將 DLL 映射至進程位址空間,回傳基址 (HMODULE)GetProcAddress 依匯出名稱/序數查詢函數虛擬位址核心洞見:若能手動在記憶體中重建 DLL 映射映像,並解析其匯出表,即可直接呼叫函數,無需磁碟檔案。
RT_RCDATAIDR_DATA1資源編譯器 → 加入資源 → 選擇 DLL 檔案
LPVOID pRawDll = NULL;
HMODULE hSelf = GetModuleHandle(NULL);
HRSRC hRes = FindResource(hSelf, MAKEINTRESOURCE(IDR_DATA1), RT_RCDATA);
if (hRes) {
HGLOBAL hLoaded = LoadResource(hSelf, hRes);
if (hLoaded) {
DWORD size = SizeofResource(hSelf, hRes);
pRawDll = LockResource(hLoaded); // 取得資源在記憶體位址
}
}
LoadPbDllFromMemory - 手動映射 DLL此函數執行完整的 PE 載入器邏輯:
DWORD LoadPbDllFromMemory(LPVOID lpRawDll, LPVOID* lpImageDll) {
// 1. 驗證 DOS/PE 標頭
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)lpRawDll;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return 2;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)dos + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) return 3;
// 2. 計算所需記憶體大小 (對齊區塊)
SYSTEM_INFO si; GetSystemInfo(&si);
DWORD maxLen = 0;
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
DWORD end = sec[i].VirtualAddress + sec[i].SizeOfRawData;
if (end > maxLen) maxLen = end;
}
DWORD pages = (maxLen + si.dwPageSize - 1) / si.dwPageSize;
*lpImageDll = VirtualAlloc(NULL, pages * si.dwPageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!*lpImageDll) return 6;
// 3. 複製標頭與所有區塊至新位址
DWORD hdrSize = (DWORD)sec - (DWORD)dos + sizeof(IMAGE_SECTION_HEADER) * nt->FileHeader.NumberOfSections;
CopyMemory(*lpImageDll, lpRawDll, hdrSize);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
DWORD dst = (DWORD)*lpImageDll + sec[i].VirtualAddress;
DWORD src = (DWORD)lpRawDll + sec[i].PointerToRawData;
CopyMemory((LPVOID)dst, (LPVOID)src, sec[i].SizeOfRawData);
}
// 4. 修正重定位表
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)
((DWORD)*lpImageDll + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
if (reloc) {
while (reloc->VirtualAddress) {
PWORD entry = (PWORD)((BYTE*)reloc + sizeof(IMAGE_BASE_RELOCATION));
WORD* end = (WORD*)((BYTE*)reloc + reloc->SizeOfBlock);
while (entry < end) {
if ((*entry >> 12) == IMAGE_REL_BASED_HIGHLOW) {
DWORD* fixup = (DWORD*)((BYTE*)*lpImageDll + reloc->VirtualAddress + (*entry & 0xFFF));
*fixup = (DWORD)*lpImageDll + (*fixup - nt->OptionalHeader.ImageBase);
}
entry++;
}
reloc = (PIMAGE_BASE_RELOCATION)((BYTE*)reloc + reloc->SizeOfBlock);
}
}
// 5. 解析匯入表 (載入依賴 DLL、填寫 IAT)
PIMAGE_IMPORT_DESCRIPTOR imp = (PIMAGE_IMPORT_DESCRIPTOR)
((DWORD)*lpImageDll + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (imp->Name) {
char* dllName = (char*)((DWORD)*lpImageDll + imp->Name);
HMODULE hDep = GetModuleHandleA(dllName) ?: LoadLibraryA(dllName);
if (!hDep) return 9;
DWORD* thunkRef = (DWORD*)((DWORD)*lpImageDll + imp->OriginalFirstThunk);
DWORD* thunkAddr = (DWORD*)((DWORD)*lpImageDll + imp->FirstThunk);
while (*thunkRef) {
if (*thunkRef & IMAGE_ORDINAL_FLAG) {
*thunkAddr = (DWORD)GetProcAddress(hDep, (LPCSTR)(*thunkRef & 0xFFFF));
} else {
PIMAGE_IMPORT_BY_NAME ibn = (PIMAGE_IMPORT_BY_NAME)((DWORD)*lpImageDll + *thunkRef);
*thunkAddr = (DWORD)GetProcAddress(hDep, ibn->Name);
}
if (!*thunkAddr) return 10;
thunkRef++; thunkAddr++;
}
imp++;
}
// 6. 設定區塊保護屬性
// ... 略 (依 Characteristics 設定 PAGE_READONLY/EXECUTE_READ/READWRITE)
// 7. 呼叫 DllMain(DLL_PROCESS_ATTACH)
DLL_ENTRY_POINT entry = (DLL_ENTRY_POINT)((DWORD)*lpImageDll + nt->OptionalHeader.AddressOfEntryPoint);
entry((HINSTANCE)*lpImageDll, DLL_PROCESS_ATTACH, NULL);
return 0;
}
GetProcAddressDirectly
DWORD GetProcAddressDirectly(PIMAGE_DOS_HEADER dos, LPCSTR funcName) {
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)dos + dos->e_lfanew);
DWORD expRVA = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (!expRVA) return 0;
PIMAGE_EXPORT_DIRECTORY exp = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)dos + expRVA);
DWORD* names = (DWORD*)((BYTE*)dos + exp->AddressOfNames);
WORD* ordinals = (WORD*)((BYTE*)dos + exp->AddressOfNameOrdinals);
DWORD* funcs = (DWORD*)((BYTE*)dos + exp->AddressOfFunctions);
for (DWORD i = 0; i < exp->NumberOfNames; i++) {
char* name = (char*)((BYTE*)dos + names[i]);
if (strcmp(name, funcName) == 0) {
WORD ord = ordinals[i];
return (DWORD)dos + funcs[ord];
}
}
return 0;
}
// 使用範例
typedef void (__stdcall *PFN_GETBIOS)(char* buffer);
PFN_GETBIOS pfn = (PFN_GETBIOS)GetProcAddressDirectly((PIMAGE_DOS_HEADER)hMemDll, "getbios");
if (pfn) {
char bios[256];
pfn(bios);
MessageBox(bios);
}
// 卸載
void UnloadPbDllFromMemory(PIMAGE_DOS_HEADER dos) {
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)dos + dos->e_lfanew);
DLL_ENTRY_POINT entry = (DLL_ENTRY_POINT)((BYTE*)dos + nt->OptionalHeader.AddressOfEntryPoint);
entry((HINSTANCE)dos, DLL_PROCESS_DETACH, NULL);
VirtualFree(dos, 0, MEM_RELEASE);
}
附帶 hardware.dll 提供硬體資訊查詢:
getmac - 網卡 MAC 位址getserial - 硬碟序號getcpuid - CPU IDgetbios - 主機板 BIOS (僅支援 AWARD BIOS,讀取 0xF0000 區域)changeres - 變更螢幕解析度DPGetDefaultPrinter - 取得預設印表機VirtualAlloc 保護屬性