如何从PIMAGE_EXPORT_DIRECTORY节,得到某个API函数代码地址

本文介绍了一种通过给定模块句柄和函数名或序号来获取API函数地址的方法。该方法首先从指定模块中找到导出目录,然后查找对应函数名的序号,最后根据序号获取到函数的实际地址。

//功能:得到某个API函数代码地址
//参数://hModule: 导入模块的句柄(如user32.dll模块的句柄,GetModuleHandle("user32.dll"))
//pProcName: 函数名或序号
const FARPROC GetExportFunctionFARPROC(HMODULE hModule, LPCSTR pProcName)
{
 PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)
  GetDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT);
 
 if ( pExport==NULL )
  return NULL;
 
 unsigned ord = 0;
 
 if ( (unsigned) pProcName < 0xFFFF ) // ordinal ?
  ord = (unsigned) pProcName;
 else
 {
  const DWORD * pNames = (const DWORD *) RVA2Ptr(pExport->AddressOfNames);
  const WORD  * pOrds  = (const WORD  *) RVA2Ptr(pExport->AddressOfNameOrdinals);
  
  // find the entry with the function name
  for (unsigned i=0; i<pExport->AddressOfNames; i++)
   if ( stricmp(pProcName, RVA2Ptr(pNames[i]))==0 )
   {
    // get the corresponding ordinal
    ord = pExport->Base + pOrds[i];
    break;
   }
 }
 
 if ( (ord<pExport->Base) || (ord>pExport->NumberOfFunctions) )
  return NULL;
 
 // use ordinal to get the address where export RVA is stored
 const char * pModule = (const char *) hModule;
 DWORD * pRVA = (DWORD *) (pModule + pExport->AddressOfFunctions) +
  ord - pExport->Base;
 
 // read original function address
 DWORD rslt = * pRVA;
 
 return (FARPROC) (pModule + rslt);
}

 

用c++写一个反射dll注入 (0) 1 打开dll文件(CreateFile),获取dll长度(GetFileSize) (1) 2 分配内存(HealAlloc),读取文件(ReadFile) (0) 3 打开目标进程(OpenProcess) (2,3) 4 调用反射注入函数(LoadLibraryR.c>LoadRemoteLibraryR) (2) 5 获取反射加载函数的文件偏移(LoadLibraryR.c>GetReflectiveLoaderOffset) (2,3) 6 在目标进程中分配空间(VirtualAllocEx),写入dll(WriteProcessMemory) (6) 7 修改目标进程中的空间为可执行(VirtualProtectEx) (5,7) 8 创建远程线程,执行反射加载函数(CreateRemoteThread) ======================= 反射加载函数(运行在被注入进程的新建线程中): ======================= 1 获取基地址 2 获取需要的kernel32.dll及ntdll.dll的函数的va 3 分配空间作为映像空间,并复制pe头到新的位置 4 复制所有段到映像的对应位置 5 处理导入表,填写iat 6 重定位 7 跳转到ep(_DllMainCRTStartup) 8 返回entry point地址注入器 主要流程 首先打开dll文件,获取长度,并在堆中分配空间读取文件。 1 2 3 hFile = CreateFileW(dllPathname, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); dwDllLen = GetFileSize(hFile, 0); lpDll = HeapAlloc(GetProcessHeap(), 0, dwDllLen); 然后找到dll中ReflectiveLoader的入口点。 最后以RW申请空间,写入dll后改成RX,然后以ReflectiveLoader作为线程函数创建远程线程。 计算fa LoadLibraryR中有个函数Rva2Offset用于获取rva对应的fa。 原理是遍历区块获取区块的section_rva和section_fa,然后比较rva和section_rva找到rva所在的section,最后计算出fa。再用fa+baseAddr得到内存中的位置。 1 2 3 4 5 6 7 8 9 10 DWORD Rva2Fa(DWORD rva, PIMAGE_SECTION_HEADER sections, int sectionNum) { for (int i = 0; i < sectionNum; i++) { int sectionVa = sections[i].VirtualAddress; if ((rva >= sectionVa) && ((sectionVa + sections[i].SizeOfRawData) > rva)) return rva - (sectionVa - sections[i].PointerToRawData); } return 0; } 获取ReflectivelLoader位置(输出表) 通过nt头,计算出sections的fa,以及通过nt头的optionalheader获取输出表的rva。 然后遍历section找到输出表的fa,接着遍历输出表的函数名字rva表,计算出rva对应fa得到导出函数名字,与需要的导出函数做对比,确定要找的函数函数名表中的下标。用此下标在序号表中找到序号,最后再用序号去地址表找到地址。 反射加载函数 1. 获取基址 首先获取代码的位置,然后再往前找dos头。 _ReturnAddress()返回当前调用函数的返回地址。所以在loader中调用一个函数,该函数再调用_ReturnAddress(),返回调用函数的返回地址,即loader中调用函数的下一条语句的地址。 其中 __declspec(noinline) 用于防止编译器优化该函数成内联函数,否则返回的就是loader的返回地址。 使用_ReturnAddress需要intrin.h,并使用#pragma intrinsic防止内联优化。 1 2 3 4 5 6 #include<intrin.h> #pragma intrinsic(_ReturnAddress) __declspec(noinline) PVOID NextAddr() { return (PVOID)_ReturnAddress(); } 根据pe格式可知,dos头(IMAGE_DOS_HEADER)中有有一个e_magic标志,值是0x5A4D(MZ)。 所以向前遍历内存,直到找到MZ标志,再检查pe头的PE标志,这样就找到dos头了。 需要注意的是,检查PE标志时要检查pe头偏移是否正确,防止错误的内存访问。 1 2 3 4 5 6 7 8 9 10 11 while (TRUE) { if (dosHeadAddr->e_magic == 0x5A4D) { LONG e_lfanew = dosHeadAddr->e_lfanew; if (e_lfanew >= sizeof(IMAGE_DOS_HEADER) && e_lfanew < 1024) { ntHeadAddr = (PIMAGE_NT_HEADERS)((PVOID)dosHeadAddr + (PVOID)e_lfanew); if (ntHeadAddr->Signature == 0x4550) break; } } dosHeadAddr--; } 这里也可以取巧,远程线程是可以传递一个参数的,对于我们这个简单的dll,imagebase实际上就是分配空间的首地址,可以作为参数传入。 2. 获取需要的内核导出函数的va 目标 接下来的步骤中需要用到一些ntdll.dll,kernel32.dll中的导出函数,所以需要先找到这些函数的va。这些系统模块都是已经加载了的,可以在peb中找到其加载的位置。 这里利用hash避免直接比较字符串。 我们需要LoadLibraryA、GetProcAddress加载导入表中的dll的对应的函数。 需要VirtualAlloc分配内存给我们把pe文件加载到其中。 需要NtFlushInstructionCache刷新指令缓存。 LDR_DATA_TABLE_ENTRY InMemoryOrderModuleList对应的链表是一个环形双向链表,且有一个头点(或者说哨兵点)。InMemoryOrderModuleList的Flink指向链表的第一个点,Blink指向链表最后一个点。头点的Flink是第一个点,可以以此为跳出条件遍历该链表。 这里借用一张网图。 ldr链 思路 首先从peb中找到ldr,然后遍历InMemoryOrderModuleList,通过hash(BaseName)找到kernel32.dll和ntdll.dll对应的LDR_DATA_TABLE_ENTRY结构。 找到dll对应的LDR_DATA_TABLE_ENTRY后,获取其imagebase,然后解析pe头,计算出导出表位置。同样利用hash比较字符串找到所需的导出函数,并计算出va。 实现代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // 找pLdrDataTableEnrty DWORD pPeb = __readfsdword(0x30); DWORD pLdr = *(DWORD*)(pPeb + 0xc); DWORD pInMemoryOrderModuleList = pLdr + 0x14; // 第一个点的二级指针 DWORD pLdrDataTableEnrty = *(DWORD*)(pInMemoryOrderModuleList + 0); // 遍历LdrDataTableEnrty do{ WCHAR* name = (WCHAR*)*(DWORD*)(pLdrDataTableEnrty + 0x24 + 0x4); hash = YourHashFun(name); // 使用你自己的函数计算hash值 if(hash == DLLHASH) { // DLLHASH由你自己的函数计算得出 DWORD baseAddr = *(DWORD*)(pLdrDataTableEnrty + 0x10); // 解析pe头过程省略 for(int i = 0; i < funcNum; i++) { // funcNum是导出函数的个数 char* name = (char*)(baseAddr + ((DWORD*)nameRvas)[i]); DWORD hash = YourHashFun(name); if (hash == FUNCHASH) { pFunc = (FUNC)(baseAddr + ((DWORD*)funcRvas)[((WORD*)ordRvas)[i]]); } } } }while(*(DWORD*)(pLdrDataTableEnrty) != *(DWORD*)(pInMemoryOrderModuleList)) 3. 给映像分配空间,并加载pe头 新分配大小等于sizeOfImga的内存作为映像加载的空间,然后把pe头复制到新内存里,这里我只更新了新nt头的imagebase地址。太简单就不贴代码了。 4. 加载段 遍历section_header获取fa和rva,计算出section在旧内存中的va和新内存中的va。然后复制section到新内存中的对应位置。 1 2 oldVA = oldImageBase + sections[i].PointerToRawData; newVA = newImageBase + sections[i].VirtualAddress; 5. 处理导入表 目标 找到导入表,然后遍历导入表,依次加载对应的dll,及需要的dll的导出函数,并填写对应iat。 导入表结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // winnt.h typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; // PBYTE DWORD Function; // PDWORD DWORD Ordinal; DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME (补充一下,这是个rva) } u1; } IMAGE_THUNK_DATA32; typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32; typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; CHAR Name[1]; } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; 和导出表不同,导入表是一个结构体数组。它不提供结构体数量,最后一个结构体仅作为结束标志,不包含导入信息,其成员Characteristics为0,这可以作为遍历的退出条件。 对于每个导入表,在文件中时OriginalFirstThunk和FirstThunk都是RVA,指向同一个IMAGE_THUNK_DATA结构体数组。 当加载到内存时,FirstThunk改为函数的VA,即iat。 文件中时,OriginalFirstThunk和FirstThunk指向的结构体数组中,每一个IMAGE_THUNK_DATA的成员u1都被解释为Ordinal,若该函数应该通过序号导入,则Ordinal的最高位会被置为1。 思路 见实现代码注释。 实现代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pNewDosHeader + pNewNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); for (; pImportDescriptor->Characteristics; pImportDes++) { // 加载dll HMODULE libraryAddress = pLoadLibraryA((LPCSTR)((DWORD)pNewDosHeader + pImportDes->Name)); if (!libraryAddress) continue; // parsing pe structure PIMAGE_THUNK_DATA32 pOriginalThunk = (PIMAGE_THUNK_DATA32)((DWORD)pNewDosHeader + pImportDes->OriginalFirstThunk); PIMAGE_THUNK_DATA32 pThunk = (PIMAGE_THUNK_DATA32)((DWORD)pNewDosHeader + pImportDes->FirstThunk); PIMAGE_NT_HEADERS32 pLibNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)libraryAddress + ((PIMAGE_DOS_HEADER)libraryAddress)->e_lfanew); PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((DWORD)libraryAddress + pLibNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); PDWORD funcRvas = (PDWORD)((DWORD)libraryAddress + pExportDir->AddressOfFunctions); while (*(DWORD*)pThunk) { if (pOriginalThunk && pOriginalThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) { // import by ord WORD ord = pOriginalThunk->u1.Ordinal - pExportDir->Base; *(DWORD*)pThunk = ((DWORD)libraryAddress + funcRvas[ord]); } else { // import by name (this is a rva) *(DWORD*)pThunk = (DWORD)pGetProcAddress(libraryAddress, ((PIMAGE_IMPORT_BY_NAME)((DWORD)pNewDosHeader + pThunk->u1.AddressOfData))->Name); } pThunk++; if (pOriginalThunk) pOriginalThunk++; } } 6. 重定位 目标 完成重定位过程。 重定位表结构 重定位表是一个结构体数组,DataDirectory中的重定位表项保存着第一个重定位表的rva,遍历每一个重定位表,并遍历重定位表中的表项,根据其重定位类型,执行重定位操作。 1 2 3 4 5 6 7 8 9 typedef struct { WORD offset : 12; WORD type : 4; } RELOC; typedef struct { DWORD VA; DWORD size; // RELOC reloc[]; } IMAGE_BASE_RELOCATION; 其中每一个重定位表保存着一个rva,重定位实际上就是遍历IMAGE_BASE_RELOCATION的成员reloc,然后执行*(rva+baseAddr+reloc[i].offset) += baseAddr - ImageBase。 思路 两层循环,遍历重定位表,再遍历每个表的 RELOC reloc[]。然后根据重定位类型进行重定位。 实现代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 // 解析pe,并计算offset PIMAGE_DATA_DIRECTORY pDDBaseReloc = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; PIMAGE_BASE_RELOCATION pBaseRelocation; ULONG_PTR offset = (ULONG_PTR)pNewDosHeader - (ULONG_PTR)pNtHeaders->OptionalHeader.ImageBase; if (pDDBaseReloc->Size) { DWORD size = pDDBaseReloc->Size; pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pNewDosHeader + pDDBaseReloc->VirtualAddress); // 遍历重定位表结构体 while (size && pBaseRelocation->SizeOfBlock) { DWORD va = (DWORD)pNewDosHeader + pBaseRelocation->VirtualAddress; DWORD num = (pBaseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC); // 计算reloc[]大小 PIMAGE_RELOC reloc = (PIMAGE_RELOC)((DWORD)pBaseRelocation + sizeof(IMAGE_BASE_RELOCATION)); // 遍历reloc[],根据重定位类型重定位 while (num--) { DWORD type = reloc->type; if (type == IMAGE_REL_BASED_HIGH) { *(WORD*)(va + reloc->offset) += HIWORD(offset); } else if (type == IMAGE_REL_BASED_LOW) { *(WORD*)(va + reloc->offset) += LOWORD(offset); } else if (type == IMAGE_REL_BASED_HIGHLOW) { *(DWORD*)(va + reloc->offset) += (DWORD)offset; } reloc++; } size -= pBaseRelocation->SizeOfBlock; pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock); } } 7. 跳转到ep 跳转到dll的ep。实际上就是执行dll原本的_DllMainCRTStartup函数。该函数会完成一些初始化工作并转到dllMain,让我们的dllMain像正常dllmain那样运行,但又不在peb中留下dll加载的痕迹。 1 2 3 4 5 6 7 typedef BOOL(WINAPI* DLLMAIN)(HINSTANCE, DWORD, LPVOID); PVOID entryPoint = (PVOID)((DWORD)pNewDosHeader + pNewNtHeaders->OptionalHeader.AddressOfEntryPoint); pNtFlushInstructionCache((HANDLE)-1, NULL, 0); ((DLLMAIN)entryPoint)((HMODULE)pNewDosHeader, DLL_PROCESS_ATTACH, lpParameter);
11-18
#include <windows.h> #include <iostream> #include <fstream> #include <vector> // 反射式加载器函数类型 typedef void(__stdcall* ReflectiveLoader)(HMODULE hModule); // 查找 ReflectiveLoader 函数地址 FARPROC FindReflectiveLoader(const char* dllBuffer) { PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)dllBuffer; PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(dllBuffer + dosHeader->e_lfanew); PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)(dllBuffer + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); DWORD* nameRVAs = (DWORD*)(dllBuffer + exportDirectory->AddressOfNames); WORD* nameOrdinals = (WORD*)(dllBuffer + exportDirectory->AddressOfNameOrdinals); DWORD* functionRVAs = (DWORD*)(dllBuffer + exportDirectory->AddressOfFunctions); for (DWORD i = 0; i < exportDirectory->NumberOfNames; ++i) { const char* functionName = dllBuffer + nameRVAs[i]; if (strcmp(functionName, "ReflectiveLoader") == 0) { WORD ordinal = nameOrdinals[i]; return (FARPROC)(dllBuffer + functionRVAs[ordinal]); } } return nullptr; } int main() { // 打开 DLL 文件 std::ifstream dllFile("ReflectiveDll.dll", std::ios::binary | std::ios::ate); if (!dllFile) { std::cerr << "Failed to open DLL file." << std::endl; return 1; } // 获取文件大小 std::streamsize size = dllFile.tellg(); dllFile.seekg(0, std::ios::beg); // 读取 DLL 数据到缓冲区 std::vector<char> dllBuffer(size); if (!dllFile.read(dllBuffer.data(), size)) { std::cerr << "Failed to read DLL file." << std::endl; return 1; } dllFile.close(); // 查找 ReflectiveLoader 函数地址 ReflectiveLoader reflectiveLoader = (ReflectiveLoader)FindReflectiveLoader(dllBuffer.data()); if (!reflectiveLoader) { std::cerr << "ReflectiveLoader function not found." << std::endl; return 1; } // 打开目标进程 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); if (!hProcess) { std::cerr << "Failed to open target process." << std::endl; return 1; } // 在目标进程中分配内存 LPVOID remoteDllBuffer = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!remoteDllBuffer) { std::cerr << "Failed to allocate memory in target process." << std::endl; CloseHandle(hProcess); return 1; } // 将 DLL 数据写入目标进程内存 if (!WriteProcessMemory(hProcess, remoteDllBuffer, dllBuffer.data(), size, NULL)) { std::cerr << "Failed to write DLL data to target process." << std::endl; VirtualFreeEx(hProcess, remoteDllBuffer, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } // 计算 ReflectiveLoader 在目标进程中的地址 DWORD remoteReflectiveLoaderOffset = (DWORD)reflectiveLoader - (DWORD)dllBuffer.data(); ReflectiveLoader remoteReflectiveLoader = (ReflectiveLoader)((DWORD)remoteDllBuffer + remoteReflectiveLoaderOffset); // 创建远程线程执行反射式加载器 HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteReflectiveLoader, NULL, 0, NULL); if (!hThread) { std::cerr << "Failed to create remote thread." << std::endl; VirtualFreeEx(hProcess, remoteDllBuffer, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } // 等待线程结束 WaitForSingleObject(hThread, INFINITE); // 清理资源 CloseHandle(hThread); VirtualFreeEx(hProcess, remoteDllBuffer, 0, MEM_RELEASE); CloseHandle(hProcess); return 0; } 仔细讲解这些函数的作用和执行流程。仔细讲我是新手
11-18
// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h" #include <windows.h> #include <iostream> // 反射加载函数 extern "C" __declspec(dllexport) void __stdcall ReflectiveLoader(HMODULE hModule) { // 获取基地址 PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule; PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD)hModule + dosHeader->e_lfanew); // 获取内核导出函数的 VA HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); FARPROC pLoadLibraryA = GetProcAddress(hKernel32, "LoadLibraryA"); FARPROC pGetProcAddress = GetProcAddress(hKernel32, "GetProcAddress"); // 分配空间加载 PE 头 LPVOID newBase = VirtualAlloc(NULL, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!newBase) { return; } // 复制 PE 头 memcpy(newBase, hModule, ntHeaders->OptionalHeader.SizeOfHeaders); // 加载段 PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeaders); for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i) { memcpy((LPVOID)((DWORD)newBase + sectionHeader->VirtualAddress), (LPVOID)((DWORD)hModule + sectionHeader->PointerToRawData), sectionHeader->SizeOfRawData); sectionHeader++; } // 处理导入表 PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)newBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); while (importDescriptor->Name) { char* dllName = (char*)((DWORD)newBase + importDescriptor->Name); HMODULE hDll = (HMODULE)pLoadLibraryA(dllName); PIMAGE_THUNK_DATA originalFirstThunk = (PIMAGE_THUNK_DATA)((DWORD)newBase + importDescriptor->OriginalFirstThunk); PIMAGE_THUNK_DATA firstThunk = (PIMAGE_THUNK_DATA)((DWORD)newBase + importDescriptor->FirstThunk); while (originalFirstThunk->u1.AddressOfData) { if (IMAGE_SNAP_BY_ORDINAL(originalFirstThunk->u1.Ordinal)) { WORD ordinal = IMAGE_ORDINAL(originalFirstThunk->u1.Ordinal); firstThunk->u1.Function = (DWORD)pGetProcAddress(hDll, (LPCSTR)ordinal); } else { PIMAGE_IMPORT_BY_NAME importByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)newBase + originalFirstThunk->u1.AddressOfData); firstThunk->u1.Function = (DWORD)pGetProcAddress(hDll, importByName->Name); } originalFirstThunk++; firstThunk++; } importDescriptor++; } // 重定位 PIMAGE_BASE_RELOCATION baseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)newBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); DWORD delta = (DWORD)newBase - ntHeaders->OptionalHeader.ImageBase; while (baseRelocation->VirtualAddress) { WORD* relocationInfo = (WORD*)((DWORD)baseRelocation + sizeof(IMAGE_BASE_RELOCATION)); for (DWORD i = 0; i < (baseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); ++i) { if (relocationInfo[i] >> 12 == IMAGE_REL_BASED_HIGHLOW) { DWORD* address = (DWORD*)((DWORD)newBase + baseRelocation->VirtualAddress + (relocationInfo[i] & 0x0FFF)); *address += delta; } } baseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)baseRelocation + baseRelocation->SizeOfBlock); } // 跳转到 EP typedef int(__stdcall* DllEntryPoint)(HMODULE, DWORD, LPVOID); DllEntryPoint entryPoint = (DllEntryPoint)((DWORD)newBase + ntHeaders->OptionalHeader.AddressOfEntryPoint); entryPoint((HMODULE)newBase, DLL_PROCESS_ATTACH, NULL); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: std::cout << "DLL attached." << std::endl; break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: std::cout << "DLL detached." << std::endl; break; } return TRUE; } 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0140 函数调用中的参数太多 Dll1 C:\Users\17116\source\repos\Dll1\Dll1\dllmain.cpp 40 错误(活动) E0140 函数调用中的参数太多 Dll1 C:\Users\17116\source\repos\Dll1\Dll1\dllmain.cpp 48 错误(活动) E0140 函数调用中的参数太多 Dll1 C:\Users\17116\source\repos\Dll1\Dll1\dllmain.cpp 52 错误 C2197 “FARPROC”: 用于调用的参数太多 Dll1 C:\Users\17116\source\repos\Dll1\Dll1\dllmain.cpp 40 错误 C2197 “FARPROC”: 用于调用的参数太多 Dll1 C:\Users\17116\source\repos\Dll1\Dll1\dllmain.cpp 48 错误 C2197 “FARPROC”: 用于调用的参数太多 Dll1 C:\Users\17116\source\repos\Dll1\Dll1\dllmain.cpp 52
最新发布
11-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值