【问题标题】:Manual DLL injection手动 DLL 注入
【发布时间】:2019-04-19 21:59:41
【问题描述】:

我正在尝试学习一些手动 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 中,我通过地址调用目标进程中的函数。

编辑:我正在我自己的控制台应用程序上对此进行测试。

【问题讨论】:

  • 您只是真正证明了 std::cout 不起作用。这是完全正常的,最糟糕的证明注入有效的方法。可能不是您要注入的控制台应用程序。注入的进程可能不使用您正在使用的相同 C++ 库。使用保证相同的函数,例如 CreateFile()。
  • 在我的测试用例中,它是一个控制台程序,它将使用与我编译它们相同的库。我将编辑帖子以明确
  • 不要在 DllMain 中创建线程
  • @Anders 我刚刚在其他示例中看到了这一点。但无论如何,这并不能解释为什么调用该函数不起作用

标签: c++ windows dll dll-injection


【解决方案1】:

DLL注入最基本的形式是:

  • 使用 VirtualAllocEx() 在目标进程中分配内存
  • 使用 WriteProcessMemory 将 DLL 的路径写入该内存位置
  • 在目标进程中通过 CreateRemoteThread() 调用 LoadLibrary()
  • 将写入 DLL 路径的内存位置传递给该调用

您已经有了这个,但您的目标是手动映射 DLL 并避免使用 LoadLibrary()。您提供的代码不会成功,还有大约 5 个步骤。您需要模拟 LoadLibrary() 通常所做的一切:

  • 加载原始二进制数据
  • 将部分映射到目标进程中
  • 注入加载器shellcode
  • 搬迁
  • 修复导入
  • 执行 TLS 回调
  • 调用 DllMain
  • 清理

手动映射的好处是你将被隐藏在 ToolHelp32Snapshot() 之外,遍历 PEB 和 NtQueryVirtualMemory 中的模块链表。

如果你想把它做好,并进行适当的错误检查,它大约需要 350 行代码,而且会变得很复杂。这一切都是通过解析PE头来完成的。

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

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

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

【讨论】:

    【解决方案2】:

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

    LoadLibrary 上调用CreateRemoteThread 是一种不同的技术,当您这样做时,线程参数需要指向远程进程中的.DLLs 路径,而不是入口点。

    【讨论】:

    • 所以调用 LoadLibrary 是为了自动加载 dll。如果我想手动映射 DLL,我必须编写自己的 LoadLibrary 版本,对吗?
    • 是的。大多数人只是使用 LoadLibrary,因为它与线程函数的签名相匹配。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-19
    • 2017-07-28
    • 1970-01-01
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多