如何将对ole32.dll的调用重定向到自己的代理DLL?

10

我正在尝试在某个进程中检测对CoCreateInstance的所有调用(理想情况下,我能够检测子进程中的调用)。

为了实现这个目标,我在Windows 7上使用Microsoft Visual Studio 2008创建了一个代理DLL,它将标准ole32.dll库中除了一个函数以外的所有函数都转发出去,具体做法可以参考各种文章,例如 Intercepted: Windows Hacking via DLL Redirection。生成的DLL看起来很好,但是我就是无法让已有的程序(测试应用程序我使用了标准的ActiveX Control Test Container (tstcon32.exe))加载我的代理DLL。不管我怎么做,这些程序似乎总是会加载C:\Windows\SysWow64\ole32.dll,根据Process Explorer的显示结果。到目前为止,我已经尝试了以下几件事情:

  1. 将包含我的代理DLL的目录添加到PATH环境变量的开头,然后调用程序;没有任何效果。
  2. 将我的代理DLL复制到被调用程序所在的目录中;也没有成功。
  3. 在调用程序的同一目录下创建一个名为.local的文件,就像Dynamic-Link Library Redirection文章中所描述的那样,并将代理DLL放入相同的目录中——也不起作用。但后来,我读到这在更近期的Windows版本上已经停止工作了。此外,ole32.dll是一个“已知的DLL”,根据HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs注册表设置,因此基于.local的重定向可能根本无法工作。
  4. 使用基于清单的重定向,例如在DLL重定向使用清单问题中所述,但似乎也没有任何效果。然而,这种方法似乎并不简单,所以很有可能是我做错了什么。

有没有人有使用桩DLL重定向标准DLL(如ole32.dll)的经验?你是如何强制应用程序选择你的桩DLL的?


1
我曾经使用过 Microsoft Detours,并且没有遇到你所描述的问题,我的体验非常愉快。 - ildjarn
@ildjarn:是的,我在其他地方也使用类似的东西。我的希望是,一个重定向 DLL(希望)也适用于给定程序的子进程。 - Frerich Raabe
也许会有点相关兴趣:http://www.darknet.org.uk/2010/08/windows-binary-planting-dll-preloadinghijacking-bug/ - sehe
对系统库(如OLE32)进行存根化可能会导致问题,除非您从系统目录动态加载原始库并转发所有调用。使用一个更容易注入的不同DLL将会有所帮助;然而,请不要使用Detours。它非常容易出错,并且无法处理递归或线程安全。如果您选择注入+重定向,其他库(如EasyHook)将会更好地满足您的需求。 - ssube
2个回答

5
我意识到可能有点晚,差不多晚了6个月,但我也在尝试同样的事情,并有一些额外的笔记:
  1. 您可以拥有并删除 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs 中的 ole32.dll。这使您能够绕过 Windows 锁定这些密钥的事实。
  2. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager 中创建一个键 SafeDllSearch,其值为 0应该改变搜索路径

应用了这两种技术并重新启动后,挂钩仍然无法正常工作。我更进一步,在 VM 中启动了我们的一个救援 CD(一个基于 Windows PE 的环境),并覆盖了 system32 中的文件。结果 Windows 无法启动 - 没有符号错误,但我从未达到 LogonUI.exe。可能是我的挂钩函数出了问题,所以这可能是原因。

无论如何,这产生了一个实际的、有形的挂钩效应——尽管这个效应呼喊着“破碎”!不幸的是,似乎很难调试,我可能会采用另一种挂钩方法,即IAT patching编辑 另一个我进行的实验是将DLL明确地加载到目标进程的地址空间中。一个执行此操作的代码片段看起来像这样:
wchar_t* TargetPath = argv[1];
wchar_t DllPath[] = L"N:\\experiments\\ole32.dll";
STARTUPINFOW si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(STARTUPINFOW));
memset(&pi, 0, sizeof(PROCESS_INFORMATION));

// create process suspended
BOOL bResult = CreateProcess(NULL, TargetPath, NULL, NULL, FALSE, 
    CREATE_SUSPENDED, NULL, NULL, &si, &pi);

// write DLL name to remote process
void* RemoteAddr = VirtualAllocEx(pi.hProcess, NULL, sizeof(DllPath)+1, 
    MEM_RESERVE | MEM_COMMIT, PAGE_READONLY);
WriteProcessMemory(pi.hProcess, RemoteAddr, DllPath, sizeof(DllPath), &BytesWritten);

// get handle to LoadLibraryW
PTHREAD_START_ROUTINE pfLoadLibrary = (PTHREAD_START_ROUTINE) 
    GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");

// create remote thread calling LoadLibraryW
HANDLE hThread = CreateRemoteThread(pi.hProcess, NULL, 
    0, pfLoadLibrary, RemoteAddr, 0, NULL);

// start remote process
ResumeThread(pi.hThread);

出于简洁考虑,错误处理已删除。

基本上,目标是在系统32加载ole32.dll之前将我的ole32.dll强制加载到目标地址空间中。在我的情况下,ole32.dll稍后会在应用程序的加载例程中被加载,因此理论上这应该起作用。但实际上并没有。我不确定为什么。

更新 我原来的代码失败了,因为DLL在运行时有未解析的符号警告。这种技术确实有效 因此,它似乎同时加载了我的ole32.dll和system32中的那个。为确保库成功加载,我在上面的代码中添加了一个LoadLibrary(DllPath)调用。


+1 鼓励你在六个月后回复(你的发现确实很有趣!)。 - Frerich Raabe

2
也许 winapioverride 能帮助你。它可以在不编写任何代码的情况下记录所有的 win api 调用。因此,它会向进行日志记录的进程注入 dll。如果我没记错的话,甚至可以在进程实际执行任何代码之前注入自定义的 dll。文档中提供了一些有关监听 com 对象的信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接