我最近用C++编写了一个DLL注入器,其要求如下:
- 注入进程(以下简称“注入器”)和要注入的DLL(Injection)都有64位和32位版本。根据目标进程的情况,尝试注入匹配版本的注入器。 - 即使注入器以64位运行,也必须能够注入32位(WOW64)的目标进程。
很快我发现,在注入器中调用GetProcAddress("LoadLibraryA")会返回一个“无法使用”的句柄,因为32位目标进程加载了另一个kernel32.dll,而函数的地址也不同,因此注入失败(远程线程无法使用返回的地址/句柄启动)。此外,32位进程加载kernel32.dll的基址也不同,这使得创建远程线程更加困难。
具体来说,以下情况会发生:
- 注入器加载了kernel32.dll的64位版本,位于0x12340000 - 注入器从该kernel32.dll中检索出了LoadLibraryA句柄0x00005678 - 目标进程加载了kernel32.dll的32位版本,位于0xABCD0000 - 该kernel32.dll的LoadLibrary的句柄应为0x0000EFAB - 注入器尝试在目标进程中使用函数0x12345678启动远程线程,但期望值是0xABCDEFAB
当从64位进程注入64位进程以及从32位进程注入32位进程时,通常不会有问题,因为kernel32.dll(很可能)加载在相同的基址处,可以使用相同的函数地址。然而,在这种情况下,情况不同。
为了解决这个问题,我采取了以下步骤:
- 64位的注入器使用EnumProcessModulesEx()来检索32位目标进程加载的kernel32.dll的地址(应为0xABCD000) - 获取该kernel32.dll的文件名,分析PE头并获取LoadLibraryA的RVA(应为0x000EFAB) - 此时,我们知道了32位目标进程中kernel32.dll的加载位置和该DLL的函数地址。 - 64位注入器在32位目标进程中启动远程线程,使用ImageBase + Function RVA,此处是神奇的0xABCDEFAB。
这种方法实际上效果很好,但我无法摆脱它是完全开销的想法,并且必须有更简单的解决方案来从64位注入器中注入32位目标。
以下是我非常感谢您能在这里回答的两个问题:
1. 是否有更简单的方法来实现这种注入?
2. 我采取的方法是否存在可能尚未考虑的问题?
非常感谢任何答案!感谢!
编辑:哦天哪…我刚刚意识到,在我的初稿中,我描述了错误的情况。注入器为64位,目标为32位(最初是相反的,但我已经纠正了)。Ben Voigt下面的评论是完全正确的,调用EnumProcessModulesEx将失败。对于造成的混乱和困惑,我十分抱歉。
- 注入进程(以下简称“注入器”)和要注入的DLL(Injection)都有64位和32位版本。根据目标进程的情况,尝试注入匹配版本的注入器。 - 即使注入器以64位运行,也必须能够注入32位(WOW64)的目标进程。
很快我发现,在注入器中调用GetProcAddress("LoadLibraryA")会返回一个“无法使用”的句柄,因为32位目标进程加载了另一个kernel32.dll,而函数的地址也不同,因此注入失败(远程线程无法使用返回的地址/句柄启动)。此外,32位进程加载kernel32.dll的基址也不同,这使得创建远程线程更加困难。
具体来说,以下情况会发生:
- 注入器加载了kernel32.dll的64位版本,位于0x12340000 - 注入器从该kernel32.dll中检索出了LoadLibraryA句柄0x00005678 - 目标进程加载了kernel32.dll的32位版本,位于0xABCD0000 - 该kernel32.dll的LoadLibrary的句柄应为0x0000EFAB - 注入器尝试在目标进程中使用函数0x12345678启动远程线程,但期望值是0xABCDEFAB
当从64位进程注入64位进程以及从32位进程注入32位进程时,通常不会有问题,因为kernel32.dll(很可能)加载在相同的基址处,可以使用相同的函数地址。然而,在这种情况下,情况不同。
为了解决这个问题,我采取了以下步骤:
- 64位的注入器使用EnumProcessModulesEx()来检索32位目标进程加载的kernel32.dll的地址(应为0xABCD000) - 获取该kernel32.dll的文件名,分析PE头并获取LoadLibraryA的RVA(应为0x000EFAB) - 此时,我们知道了32位目标进程中kernel32.dll的加载位置和该DLL的函数地址。 - 64位注入器在32位目标进程中启动远程线程,使用ImageBase + Function RVA,此处是神奇的0xABCDEFAB。
这种方法实际上效果很好,但我无法摆脱它是完全开销的想法,并且必须有更简单的解决方案来从64位注入器中注入32位目标。
以下是我非常感谢您能在这里回答的两个问题:
1. 是否有更简单的方法来实现这种注入?
2. 我采取的方法是否存在可能尚未考虑的问题?
非常感谢任何答案!感谢!
编辑:哦天哪…我刚刚意识到,在我的初稿中,我描述了错误的情况。注入器为64位,目标为32位(最初是相反的,但我已经纠正了)。Ben Voigt下面的评论是完全正确的,调用EnumProcessModulesEx将失败。对于造成的混乱和困惑,我十分抱歉。