手动 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 完成的。

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

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

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