我正在尝试学习一些手动dll注入,但似乎无法使dlls代码的执行正常工作。我是 Windows C++ 的新手,因此非常感谢有关改进代码的任何提示。我也只发布了相关代码。
喷油器程序:
hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, getPID(TARGET_NAME));
DWORD gotDLL = GetFullPathName(DLL_NAME, MAX_PATH, dllPath, NULL);
hFile = CreateFile(dllPath, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
dllFileSize = GetFileSize(hFile, NULL);
memAddrForDLL = VirtualAllocEx(hProcess, NULL, dllFileSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
loadedDLL = HeapAlloc(GetProcessHeap(), NULL, dllFileSize);
// Load dll into allocated memory in current process
ReadFile(hFile, loadedDLL, dllFileSize, &bytesRead, NULL))
// Find offset of dll entry point
IMAGE_NT_HEADERS* pOldNtHeader = reinterpret_cast<IMAGE_NT_HEADERS*>(reinterpret_cast<BYTE*>(loadedDLL) + reinterpret_cast<IMAGE_DOS_HEADER*>(loadedDLL)->e_lfanew);
IMAGE_OPTIONAL_HEADER* pOldOptHeader = &pOldNtHeader->OptionalHeader;
entryPointOffset = pOldOptHeader->AddressOfEntryPoint;
// Load dll into allocated memory in target process
WriteProcessMemory(hProcess, memAddrForDLL, loadedDLL, bytesRead, NULL)
LPTHREAD_START_ROUTINE entryPoint = (LPTHREAD_START_ROUTINE)((unsigned __int64)memAddrForDLL + entryPointOffset);
CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibrary, entryPoint, NULL, NULL)
.DLL:
DWORD WINAPI OnDllAttach(LPVOID base){
typedef void func(void);
func* f = (func*)0x00007FF605EC5835;
f();
FreeLibraryAndExitThread(static_cast<HMODULE>(base),1);
}
BOOL WINAPI OnDllDetach(){
return TRUE;
}
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDll,
_In_ DWORD fdwReason,
_In_opt_ LPVOID lpvReserved){
typedef void func(void);
func* f = (func*)0x00007FF605EC5835;
f();
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDll);
CreateThread(nullptr, 0, OnDllAttach, hinstDll, 0, nullptr);
return TRUE;
case DLL_PROCESS_DETACH:
if(lpvReserved == nullptr)
return OnDllDetach();
return TRUE;
default:
return TRUE;
}
}
目标程序包含以下函数:
void printer(){
cout << "test" << endl;
}
我的喷油器产生以下输出
1. Attempting to attatch to process target.exe
--- Got target.exe PID: 14640
--- Got target.exe Handle: 0x0000000000000084
2. Attempting to allocate memory
--- Found dll: D:projectsinjectorhack.dll
--- Got hack.dll Handle: 0x0000000000000088
--- Allocated memory in target.exe at 0x0000017BEB690000
3. Attempting to copy dll to target.exe
--- Allocated memory at 0x00000226A060FFE0
--- Loaded hack.dll in current process at 0x00000226A060FFE0
--- hack.dll is a valid DLL
--- Loaded hack.dll into target.exe at 0x0000017BEB690000
4. Attempting to execute dll
--- Offset from start of file to entrypoint: 0x3cf6
--- Began execution of hack.dll in target.exe at 0x0000017BEB693CF6
使用 Ghidra,我可以确认这是 dll 入口点的正确偏移量。但是当运行我的注射器时,目标进程中没有任何反应,我也尝试使用 cout 从 dll 打印消息,但我什么也没得到(我认为它甚至不起作用,因为没有任何东西被重新定位)
我正在使用
CreateRemoteThread(hProcess, NULL, NULL, entryPoint, memAddrForDLL, NULL, NULL)
之前,因为第 4 个参数称为 lpStartAddress,我认为这应该需要入口点,但它导致目标进程崩溃,我看到的每个示例都使用了我目前在我的代码中使用它的方式。
在我的 dll 中,我通过地址调用目标进程中的函数。
编辑:我正在我自己的控制台应用程序上对此进行测试。
DLL 注入的最基本形式是:
- 使用 VirtualAllocEx() 在目标进程中分配内存
- 使用 WriteProcessMemory 将 DLL 的路径写入该内存位置
- 在目标进程中通过 CreateRemoteThread() 调用 LoadLibrary()
- 将写入 DLL 路径的内存位置传递给该调用
你已经有了这个,但是,你的目标是手动映射DLL并避免使用LoadLibrary()。 您提供的代码不起作用,还有大约 5 个步骤。 你需要模拟 LoadLibrary() 通常所做的一切:
- 加载原始二进制数据
- 将部分映射到目标流程
- 注入加载器外壳代码
- 进行搬迁
- 修复导入
- 执行 TLS 回调
- 呼叫 DllMain
- 清理
手动映射的好处是,您将在 ToolHelp32Snapshot() 中隐藏,遍历 PEB 和 NtQueryVirtualMemory 中的模块链表。
如果你想做对,通过体面的错误检查,它大约有 350 行代码,而且会变得复杂。 这一切都是通过解析 PE 标头来完成的。
- 获取目标的进程 ID
- 读取 DLL 文件
- 在目标进程中从 PE 标头分配与 ImageBase 大小相同的内存
- 解析 PE 标头后遍历 PE 部分
- 以正确的相对地址将节写入内存
- 将外壳代码写入目标进程
- 调用 CreateRemoteThread 并设置要执行的 shellcode
- 您的shellcode修复导入并重新定位
- 您的 Shellcode 执行 TLS 回调
- 以上 2 个步骤已完成解析可选标头中的数据目录
- 调用 DllMain(),带有DLL_PROCESS_ATTACH参数
现在,您的 DLL 已加载,并且正在执行DLL_PROCESS_ATTACH开关大小写。 显然,它比这更复杂,但这就是想法。
如果没有我的朋友布罗伊洪教我,我对此一无所知,所以我想把这个答案归功于他。 祝你好运
内存中加载的.DLL与磁盘上的.DLL文件不同。部分布局不同,您需要处理重新定位,导入表和PEB加载的模块列表。你基本上必须重新实现NTDLL!Ldr*
.
在LoadLibrary
上调用CreateRemoteThread
是一种不同的技术,执行此操作时,线程参数需要指向 .远程进程中的 DLL 路径,而不是入口点。