C ++:从64位进程注入32位目标

时间:2012-01-08 09:03:04

标签: c++ winapi dll 32bit-64bit code-injection

我最近在C ++中编写了一个DLL-Injector,其要求如下

  • 64位和32位变体中存在注入过程(我们称之为'注入器')以及要注入的(注入)。根据目标,尝试注入匹配的注射版本。
  • 即使Injector以64位运行,也必须能够注入32位(WOW64)的目标进程

我很快注意到,Injector中 GetProcAddress(“LoadLibraryA”)的调用返回一个“不可用”句柄,因为32位目标已加载另一个kernel32.dll并且地址函数有不同,因此注入失败(远程线程无法使用返回的地址/句柄启动)。此外,32位进程将kernel32.dll加载到不同的基址,这使得创建远程线程更加不可能。

为了说清楚我的意思,会发生以下情况:

  • Injector有64位版本的 kernel32.dll 加载到0x12340000
  • Injector从此kernel32.dll检索 LoadLibraryA 0x00005678的句柄
  • Target在0xABCD0000
  • 处加载了32位版本的 kernel32.dll
  • 此kernel32.dll的 LoadLibrary 句柄应为0x0000EFAB
  • Injector尝试使用函数0x12345678在目标中启动远程线程,但是期望使用0xABCDEFAB

当从64位进程注入64位进程,从32位注入32位时,通常没有问题,因为kernel32.dll(很可能)在相同的基址加载,并且相同的函数地址可以使用 - 这是我到目前为止的解释。在这种情况下,条件不同。

为了克服这个问题,我做了以下步骤:

  • 64位Injector使用 EnumProcessModulesEx()(应该是0xABCD000)检索由32位目标加载的kernel32.dll的地址
  • 获取 kernel32.dll 的文件名,解析PE头并获得 LoadLibraryA 的RVA(应为0x000EFAB)
  • 此时,我们知道32位目标中加载 kernel32.dll 的位置以及此DLL中函数的地址。
  • 64位Injector使用 ImageBase + Function RVA 在32位目标中启动远程线程,在这种情况下是神奇的0xABCDEFAB

这种方法实际上运行得很好,但我无法摆脱这是总开销的想法,并且必须有一个更简单的解决方案来从64位注入器注入32位目标。

我有两个问题,如果可以在这里回答,我将非常感激:

  1. 有没有更简单的方法来实现这种注射?
  2. 我一直在考虑的方法是否存在问题?
  3. 非常感谢任何答案,谢谢!

    编辑:哦,我的天啊......我刚刚意识到,我在最初的帖子中描述了错误的情况。 INJECTOR是64位,TARGET是32位(最初是另一种方式,但我已经纠正了它)。 Ben Voigt在下面的评论完全正确,对EnumProcessModulesEx的调用将失败。这个混乱让人感到非常抱歉:(

3 个答案:

答案 0 :(得分:5)

我偶然发现这个线程正在寻找同样问题的解决方案。

到目前为止,我倾向于使用另一种更简单的解决方案。要获得32位内核proc地址,64位进程只需执行一个32位程序,它将为我们查找proc地址:

#include <Windows.h>

int main(int argc, const char**)
{
    if(argc > 1)
        return (int) LoadLibraryA;
    else
        return (int) GetProcAddress;
}

答案 1 :(得分:0)

这个答案解决了问题的早期版本,它与64位注入器的情况大多无关。


你是说这种方法有效吗?因为根据the documentation,您无法从WOW64获取有关64位进程的信息:

  

如果在WOW64下运行的32位应用程序调用该函数,则忽略dwFilterFlag选项,该函数提供与EnumProcessModules函数相同的结果。

EnumProcessModules进一步解释了限制)

  

如果从WOW64上运行的32位应用程序调用此函数,则它只能枚举32位进程的模块。如果进程是64位进程,则此函数失败,最后一个错误代码为ERROR_PARTIAL_COPY(299)。

但由于ASLR,您确实需要找到加载kernel32.dll的基地址。

答案 2 :(得分:0)

我认为您可以使用调试符号API来保存自己解析PE头和导出表。该路由应该产生32位注入器所需的信息;虽然我仍然不知道如何将64位地址传递给CreateRemoteThread,但64位目标情况也是如此。

通常这些调试符号函数需要一个.pdb或.sym文件才能运行,但是我很确定它们也从DLL导出表中获取信息(只是从调试器显示的文件中获取的信息) t有符号)。