【问题标题】:Why does process loads modules(dlls) in different phases?为什么进程在不同阶段加载模块(dll)?
【发布时间】:2015-07-13 15:08:08
【问题描述】:

现象是这样的。

我正在尝试实现 dll 注入。我的应用程序创建进程处于“暂停”状态,(使用带有 CREATE_SUSPENDED 的 CreateProcess),等待加载一个重要的 dll(当然是 kernel32.dll),然后执行注入。注入后使用 ResumeThread 恢复暂停的进程。

我正在使用挂起的进程创建,希望在除 dll 加载之外的任何其他执行之前注入 dll。但在执行此操作时,我发现了一些有趣的事情。

我已经使用 notepad.exe 简单地测试了这种注入,它工作得很好。但是当我将可执行文件复制为 notepad2.exe 时,我的注入会等待无限时间。在进程的主线程恢复之前,Kernel32.dll 永远不会加载。

似乎有某种“经过身份验证”的可执行文件会预先加载模块(或者我的计算机中安装的一些已经使用 dll 注入的安全解决方案可能会导致这种不同的执行)。

有谁知道模块是否以不同的方式加载? (坦白说,我还是不明白windows进程的生命周期……)

【问题讨论】:

  • 根据我的经验,最初只映射了 ntdll.dll,当线程恢复时,APC 排队运行。这会调用ntdll!LdrpInitializeProcess,它会初始化执行环境(例如语言支持、堆、线程本地存储、KnownDlls 目录),加载 kernel32.dll 并获取BaseThreadInitThunk 的地址,执行静态 DLL 导入,中断对于附加的调试器,并运行 init 例程。然后执行跳转到ntdll!RtlUserThreadStart,调用kernel32!BaseThreadInitThunk,调用EXE的入口点如WinMainCRTStartup
  • 我看到 DLL 加载在进程启动期间以看似随机的方式表现不同,即使对于同一个可执行文件也是如此。我怀疑 Windows 试图基于未记录的启发式优化进程启动 - 例如,它可能在每次特定可执行文件启动时“做笔记”,并使用该信息尝试在下次更快地启动它。不幸的是,底线可能是没有可靠的方法来预测 kernel32.dll(或任何其他系统 DLL)是否会在进程恢复之前或之后加载。 (但我猜你可以排队 APC?)
  • @HarryJohnston,有一些服务,例如 prefetch/superfetch 在内存管理器级别工作,以优化系统范围内加载哪些 DLL 和 EXE 页面以加速应用程序加载。但我认为,从ntdll!LdrInitializeThunk 开始排队的初始 APC 所做的工作多年来一直相当一致。我总是在以下调试会话中看到初始化步骤和 DLL 加载顺序:cdb -xe cpr -c "bp ntdll!LdrInitializeThunk; g" notepad。也就是说,NtCreateUserProcess 有时可能会预映射 ntdll.dll 以外的 DLL。
  • 我想我得到了一些提示。我电脑上安装的安全解决方案使用Detour注入和hook windows API。而且似乎 Windows Detour 使您能够注入具有 CreateProcess 挂起状态的 DLL。我猜当我创建进程时,经过几次之后,Detour 会加载安全解决方案 dll,然后在此之前加载“kernel32.dll”。虽然我对这个假设没有信心,但我测试过的几个在主线程恢复之前成功加载 kernel32.dll 的可执行文件都产生了从 DebugViewer 查看的 Detour Debug 输出(解决方案制造商留下了消息!)
  • @SihuSong,这听起来很合理。你为什么不把它写成答案呢?

标签: windows dll process code-injection


【解决方案1】:

我想我找到了答案。答案是,虽然进程处于 SUSPENDED 状态,因此主线程已停止,但当该进程中的某个线程尝试访问模块时,会加载所需的模块(这意味着,该 windows 应用程序似乎 '延迟加载' 需要模块)

在评论中,我说受我PC上安装的某些安全解决方案保护的进程使用Detour注入DLL,因此在进程处于SUSPENDED状态时加载了某些进程中的模块。 但这不是Detour的具体案例,而是每个流程的普遍问题。

我傻了,我使用的注入技术是,列出目标进程中加载​​的所有模块,找到 kernel32.dll,计算 LoadLibraryW 的偏移量,然后使用远程线程调用该函数。在这种技术中,注入器枚举所有模块 BEFORE LoadLibrary 函数被调用,因此 LazyLoading 永远不会发生,因此我永远看不到 kernel32.dll 加载到进程中。

当我使用从当前进程中使用GetProcAddress的简单技术,获取LoadLibrary函数的地址,远程调用进程时,kernel32.dll加载成功,注入成功。

如果有几个应用程序通过“使用 Detour 的安全解决方案”保护,即使我暂停了进程,注入尝试确实发生了,这就是为什么 kernel32.dll 被加载到 notepad.exe(它位于安全解决方案),但未加载到 notepad2.exe(由于图像名称不同,它不在列表中)。

我只是愚蠢,试图使用更复杂的方法进行 dll 注入,这就是导致所有这些问题的原因。

非常感谢 cmets。

【讨论】:

    猜你喜欢
    • 2015-04-07
    • 1970-01-01
    • 2017-09-23
    • 1970-01-01
    • 2020-11-25
    • 2010-09-21
    • 2019-05-28
    • 2011-04-04
    • 2019-10-10
    相关资源
    最近更新 更多