我意识到标题有点复杂,所以让我解释一下我想要做什么:
我刚刚完成了一个简单的 DLL 注入器的编写,用于验证我正在尝试编写的概念。该程序拍摄当前进程的快照,枚举进程树,并将 DLL 注入其直接父进程。现在,在理想条件下,效果很好:32 位版本的注入器可以注入到 32 位父进程中,64 位版本的注入器可以注入到 64 位父进程中。
不过,我现在想做的是将 32 位 DLL 从 x64 注入器注入到 32 位父进程中。注入该 DLL 后,我希望注入对所注入 DLL 导出的函数之一的调用。但我不确定这是否真的可行。 (我已经编写了一些代码来识别父进程是 32 位进程还是 64 位进程,所以这不会成为问题)
现在,我已经找到了一些似乎可以完成第一部分的代码将预编译的机器代码注入进程中。 (至少,我认为这就是它正在做的事情)通常,在注入对 LoadLibraryW 的调用之后,我会获取该调用返回的地址,将相对偏移量添加到我想要调用的导出函数,然后注入对该函数的调用功能。但在这种情况下,我无法将 32 位库加载到我的 64 位注入器中,因此我无法使用以下命令找到函数的相对偏移量获取进程地址就像我平常会做的那样。我通过执行以下操作解决了这个问题:
由于我无法使用正常方法找到 32 位 DLL 的函数偏移量,因此我当前正在将文件读入缓冲区,并使用该缓冲区填充IMAGE_NT_HEADERS32struct,并枚举 IMAGE_EXPORT_DIRECTORY 以查找所有导出函数的名称和相对偏移量。
所以此时,我有以下几点:
- 加载到32位进程中的32位DLL
- 在 32 位进程中运行以下代码时,该值相当于 funcAddr:
Code:
HMODULE hInjectedDLL = LoadLibrary("mydll.dll");
DWORD funcAddr = (DWORD)GetProcAddress(hInjectedDLL, "ExportedFunc") - (DWORD)hInjectedDLL;
理论上,我现在需要的只是 hInjectedDLL 的值,并且我应该能够调用该函数。但不幸的是,我对汇编或机器代码了解不够,不知道如何获取该值。
有任何想法吗?
(另外,我知道,只需编译两个版本的注入器,并在父进程的处理器架构不匹配时让一个版本运行另一个版本,我就可以省去很多麻烦。我试图避免去不过这条路线。)
Edit:我认为这可能有助于解释我在这个概念验证中实际想要完成的任务。
我正在尝试一个想法,我必须允许在当前控制台中执行子进程,而不需要原始进程等待子进程完成。由于没有内置 API 可以在控制台应用程序中执行此操作,因此您通常会陷入进程树中,所有进程都在等待各自的子进程完成。为了促进此功能,我想要执行以下操作:
注射
DLL注入器将扮演“执行进程”的角色。 (通常必须等到子进程完成的进程)运行时,它确定其父进程的平台,以及父进程是否是基于控制台的应用程序。如果不是,该过程简单地使用exec 函数族运行所需的子进程,立即退出。如果父进程是控制台应用程序,则注入器确定使用哪个DLL,挂起最初创建注入器进程的线程,然后将DLL注入到父进程中。
解决我们的函数
一旦 DLL 就位,注入器就会确定 DLL 导出的函数的地址。 (通常,我会通过调用来做到这一点创建远程线程进行初始注射,然后使用获取退出代码线程在该线程上获取父进程中 DLL 的基地址。一旦我有了它,就可以通过简单的算术找到导出函数的地址,然后我可以用它来注入对该函数的第二次调用。
调用我们的函数
导出的函数将类似于:
BOOL RewriteHProcess(HANDLE hProcess)
注入器将再次使用 CreateRemoteThread 从父进程的上下文中调用此函数,其中 hProcess 是注入器进程的句柄。在 DLL 方面,该函数将执行两件事之一(考虑到跨线程内存访问的安全限制,我不太确定我的第一个想法是否可行,因此我将第二个想法放在一起进行回退,如果首先是行不通的。)
RewriteHProcess会打开之前挂起的线程进行读写,并使用读取进程内存,它将在进程的内存中搜索我们的注入器进程的句柄。 (我们假设父进程当前正在阻止进一步执行等待单个对象功能。我知道命令提示符至少可以做到这一点,这也是我目前关注的焦点)DLL 然后调用一个内部函数来创建我们想要的子进程,关闭旧句柄,并用新句柄覆盖内存子进程。此时,它会清理它能清理的内容,然后返回。然后,注入器将执行所需的任何剩余清理工作,恢复挂起的线程,关闭进程和线程的句柄,然后退出,使父进程在等待新子进程结束时继续阻塞。
如果该路线不可行,我的后备方案是挂起注入器的阻塞线程,在注入的 DLL 中创建新的子进程,清理并退出注入器,然后在 DLL 中等待,直到子进程完成。此时,DLL 将进行清理、恢复挂起的线程并自行卸载。 (不过,此路线的缺点是父进程从注入器返回的返回代码可能与目标子进程的返回代码不同)