手动 DLL 注入

Manual DLL injection

本文关键字:注入 DLL 手动      更新时间:2023-10-16

我正在尝试学习一些手动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 标头来完成的。

  1. 获取目标的进程 ID
  2. 读取 DLL 文件
  3. 在目标进程中从 PE 标头分配与 ImageBase 大小相同的内存
  4. 解析 PE 标头后遍历 PE 部分
  5. 以正确的相对地址将节写入内存
  6. 将外壳代码写入目标进程
  7. 调用 CreateRemoteThread 并设置要执行的 shellcode
  8. 您的shellcode修复导入并重新定位
  9. 您的 Shellcode 执行 TLS 回调
  10. 以上 2 个步骤已完成解析可选标头中的数据目录
  11. 调用 DllMain(),带有DLL_PROCESS_ATTACH参数

现在,您的 DLL 已加载,并且正在执行DLL_PROCESS_ATTACH开关大小写。 显然,它比这更复杂,但这就是想法。

如果没有我的朋友布罗伊洪教我,我对此一无所知,所以我想把这个答案归功于他。 祝你好运

内存中加载的.DLL与磁盘上的.DLL文件不同。部分布局不同,您需要处理重新定位,导入表和PEB加载的模块列表。你基本上必须重新实现NTDLL!Ldr*.

LoadLibrary上调用CreateRemoteThread是一种不同的技术,执行此操作时,线程参数需要指向 .远程进程中的 DLL 路径,而不是入口点。