手动 DLL 注入
Manual DLL injection
我正在尝试学习一些手动 dll 注入,但似乎无法执行 dll 代码。我是 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:\projects\injector\hack.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 与磁盘上的 .DLL 文件不同。部分布局不一样,您需要处理重定位、导入 table 和 PEB 加载模块列表。你基本上必须重新实现 NTDLL!Ldr*
.
在 LoadLibrary
上调用 CreateRemoteThread
是一种不同的技术,当您执行此操作时,线程参数需要指向远程进程中的 .DLL 路径,而不是入口点。
最基本的DLL注入形式是:
- 使用 VirtualAllocEx() 在目标进程中分配内存
- 使用 WriteProcessMemory 将 DLL 的路径写入该内存位置
- 在目标进程中通过 CreateRemoteThread() 调用 LoadLibrary()
- 将写入 DLL 路径的内存位置传递给该调用
您已经有了这个,但是您的目标是手动映射 DLL 并避免使用 LoadLibrary()。您提供的代码无法运行,还有大约 5 个步骤。您需要模拟 LoadLibrary() 通常所做的一切:
- 加载原始二进制数据
- 将部分映射到目标进程
- 注入加载程序 shellcode
- 搬迁
- 修复导入问题
- 执行 TLS 回调
- 调用 DllMain
- 清理
手动映射的好处是你会被ToolHelp32Snapshot()隐藏起来,走PEB和NtQueryVirtualMemory中的模块链表。
如果你想正确地进行错误检查,大约需要 350 行代码,而且会变得很复杂。这一切都是通过解析 PE header 完成的。
- 获取目标的进程ID
- 读取DLL文件
- 在目标进程中分配与 PE 中的 ImageBase 大小相同的内存 header
- 解析 PE 后遍历 PE 部分 header
- 以正确的相对地址将节写入内存
- 将shellcode写入目标进程
- 调用 CreateRemoteThread 并设置要执行的 shellcode
- 您的 shellcode 修复导入并进行重定位
- 您的 Shellcode 执行 TLS 回调
- 以上2步完成解析可选的DataDirectory header
- 使用 DLL_PROCESS_ATTACH 参数调用 DllMain()
现在您的 DLL 已加载并且 DLL_PROCESS_ATTACH 开关案例正在执行。显然它比那更复杂,但这就是想法。
如果没有我的朋友 Broihon 教我,我对此一无所知,所以我想把这个答案归功于他。祝你好运
我正在尝试学习一些手动 dll 注入,但似乎无法执行 dll 代码。我是 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:\projects\injector\hack.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 与磁盘上的 .DLL 文件不同。部分布局不一样,您需要处理重定位、导入 table 和 PEB 加载模块列表。你基本上必须重新实现 NTDLL!Ldr*
.
在 LoadLibrary
上调用 CreateRemoteThread
是一种不同的技术,当您执行此操作时,线程参数需要指向远程进程中的 .DLL 路径,而不是入口点。
最基本的DLL注入形式是:
- 使用 VirtualAllocEx() 在目标进程中分配内存
- 使用 WriteProcessMemory 将 DLL 的路径写入该内存位置
- 在目标进程中通过 CreateRemoteThread() 调用 LoadLibrary()
- 将写入 DLL 路径的内存位置传递给该调用
您已经有了这个,但是您的目标是手动映射 DLL 并避免使用 LoadLibrary()。您提供的代码无法运行,还有大约 5 个步骤。您需要模拟 LoadLibrary() 通常所做的一切:
- 加载原始二进制数据
- 将部分映射到目标进程
- 注入加载程序 shellcode
- 搬迁
- 修复导入问题
- 执行 TLS 回调
- 调用 DllMain
- 清理
手动映射的好处是你会被ToolHelp32Snapshot()隐藏起来,走PEB和NtQueryVirtualMemory中的模块链表。
如果你想正确地进行错误检查,大约需要 350 行代码,而且会变得很复杂。这一切都是通过解析 PE header 完成的。
- 获取目标的进程ID
- 读取DLL文件
- 在目标进程中分配与 PE 中的 ImageBase 大小相同的内存 header
- 解析 PE 后遍历 PE 部分 header
- 以正确的相对地址将节写入内存
- 将shellcode写入目标进程
- 调用 CreateRemoteThread 并设置要执行的 shellcode
- 您的 shellcode 修复导入并进行重定位
- 您的 Shellcode 执行 TLS 回调
- 以上2步完成解析可选的DataDirectory header
- 使用 DLL_PROCESS_ATTACH 参数调用 DllMain()
现在您的 DLL 已加载并且 DLL_PROCESS_ATTACH 开关案例正在执行。显然它比那更复杂,但这就是想法。
如果没有我的朋友 Broihon 教我,我对此一无所知,所以我想把这个答案归功于他。祝你好运