【发布时间】:2016-09-12 03:45:39
【问题描述】:
我正在开发一个应用程序,它保存自定义进程的状态,然后从停止的点恢复它。
现在有以下问题。当系统重新启动时,系统模块的所有基地址都是随机的(ntdll.dll、kernelbase.dll、kernel32.dll 等)。操作系统是 Windows 7 x64 企业版。在重新启动后尝试恢复应用程序时,它显然会崩溃(但在重新启动前从恢复点开始工作正常)。我看到了 2 个解决方案:
- 在应用程序中加载系统模块时将其地址设为静态
- 修补应用程序中对目标模块的所有引用(堆栈上的函数地址、可能保存在堆中的地址等)
显然,第二种方式是困难的。我们要做完整的应用分析,不同的代码和数据,不同的数据,从引用到系统模块可能是简单的整数......所以我选择了第一种方式。
在虚拟内存中加载模块时,操作系统首先检查物理内存中是否已经存在具有此名称的模块。如果有,它只是将此模块映射到虚拟地址空间。在这种情况下,模块从其初始位置加载,如果是ntdll.dll - 从C:\Windows\System32\ntdll.dll。即使您将此模块复制到应用程序目录,禁用 ASLR 标志并将图像库修补为某个值,该模块仍将从系统目录加载。需要某种重定向方式。
从MSDN 我们了解到以下内容(“桌面应用程序的搜索顺序”部分):
桌面应用程序可以通过指定完整路径、使用DLL redirection 或使用manifest 来控制加载DLL 的位置。如果没有使用这些方法,系统会在加载时搜索 DLL,如本节所述。
所以至少有两种方法可以实现目标。
在关于 DLL 重定向的页面上我们看到:
无法重定向已知的 DLL。有关已知 DLL 的列表,请参阅以下注册表项:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs。系统使用 Windows 文件保护来确保诸如此类的系统 DLL 不会被更新或删除,除非通过操作系统更新(例如服务包)。
我要重定向的模块是已知的 DLL,因此无法应用此变体。
清单已留下。有一个很好的 article 关于使用它们。文章的作者创建了存根 DLL 来替换原来的 (user32.dll),然后让应用程序加载这个替换的 DLL。关键是这个DLL是在搜索导入函数时加载的,而不是在应用程序一开始就加载,比如ntdll.dll。
更具体的,我们参考“Windows Internals, Sixth Edition”, p. 359,其中解释了进程创建的细节。有7个阶段:
- 转换和验证参数和标志
- 打开要执行的镜像
- 创建 Windows 执行进程对象
- 创建初始线程及其堆栈和上下文
- 执行 Windows 子系统 - 特定的后期 初始化
- 开始执行初始线程
- 在上下文中执行进程初始化 新流程
这里最重要的几点:
ntdll.dll的映射发生在第 3 阶段;在第 5 阶段开始时,我们阅读(重点是我的):
此时,Kernel32.dll 向 Windows 子系统发送一条消息,以便它可以设置 SxS 信息(有关并排组件的更多信息,请参见本节末尾),例如 清单文件、DLL 重定向路径,以及新进程的进程外执行。
这意味着ntdll.dll 在重定向有机会发生之前被加载。关于重定向的主题有很多关于 SO 的问题,但它们都是关于在搜索导入函数时加载的 DLL 或通过LoadLibrary 显式加载的,当应用程序中可能发生重定向时。
但是,ntdll.dll 无论如何都是在很早的阶段就被加载到应用程序中的。
使用到目前为止我收集到的所有信息,我能够进行应用程序搜索,例如,在应用程序目录中搜索kernel32.dll(使用本段中的链接)。但我无法重定向搜索ntdll.dll。
在用户模式下有可能吗?
附:有一种全局禁用 ASLR 的方法,请参阅 post。但是,这是一种非常粗鲁的方式,Internet Explorer、Adobe Reader 之类的应用程序无法运行,整个操作系统都存在漏洞。
【问题讨论】:
-
ntdll.dll 包含进程引导代码。它必须是映射到流程对象的第一个模块。你不能改变它,除非重写内核。
标签: redirect dll windows-7-x64 ntdll