x86代码从x64进程注入x86进程

我意识到标题有点令人费解,所以让我解释一下我要做的事情:

我刚刚编写了一个简单的DLL注入器,用于我正在尝试编写的概念certificate。 该程序获取当前进程的快照,枚举进程树,并将DLL注入其直接父进程。 现在,在理想条件下,工作正常:32位版本的注入器可以注入到32位父进程中,64位版本的注入器可以注入到64位父进程中。

不过,我现在要做的是从x64注入器向32位父进程注入32位DLL。 一旦注入了DLL,我就希望再注入一个由注入的DLL导出的函数。 不过,我不确定是否真的可以做到。 (我已经汇总了一些代码来确定父进程是32位进程还是64位进程,所以这不是问题)

现在,我已经找到了一些代码,它们似乎通过将预编译的机器代码注入到流程中来完成第一部分。 (至少,我认为这就是它正在做的事情)通常,在注入对LoadLibraryW的调用之后,我将获得该调用返回的地址,将相对偏移量添加到我想要调用的导出函数,并注入一个调用function。 在这种情况下,我无法将32位库加载到我的64位注入器中,因此我无法像通常那样使用GetProcAddress找到函数的相对偏移量。 我通过以下方式解决了这个问题:

由于我无法使用常规方法找到32位DLL的函数偏移量,因此我正在将文件读入缓冲区,使用该缓冲区填充IMAGE_NT_HEADERS32结构,并枚举IMAGE_EXPORT_DIRECTORY以查找名称和相对偏移量所有导出的function。

所以在这一点上,我有以下内容:

  • 32位DLL加载到32位进程中
  • 在32位进程中运行以下代码时,该值等效于funcAddr:

码:

HMODULE hInjectedDLL = LoadLibrary("mydll.dll"); DWORD funcAddr = (DWORD)GetProcAddress(hInjectedDLL, "ExportedFunc") - (DWORD)hInjectedDLL; 

从理论上讲,我现在需要的只是hInjectedDLL的值,我应该可以调用该函数。 但遗憾的是,我对组装或机器代码知之甚少,知道如何获得该值。

有任何想法吗?

(另外,我知道通过编译两个版本的注入器可以省去很多麻烦,当父进程的处理器架构不匹配时,让一个运行另一个版本。我试图避免去不过这条路线。)

编辑 :想想它可能有助于解释我在这个概念certificate中实际想要完成的事情。

我正在尝试一个想法,我必须允许在当前控制台中执行子进程,而不需要原始进程等待子进程完成。 由于在控制台应用程序中没有用于执行此操作的内置API,因此您通常会遇到一个进程树,所有进程都在等待各自的子进程完成。 为了实现此function,我想执行以下操作:

注射

DLL注入器将扮演“执行进程”的角色。 (通常必须等到子进程完成的进程)运行时,它确定其父进程的平台,以及父进程是否甚至是基于控制台的应用程序。 如果不是,则该过程仅使用exec系列函数来运行所需的子进程,立即退出。 如果父进程是控制台应用程序,则进程确定要使用的DLL,挂起最初创建注入进程的线程,然后将DLL注入父进程。

解决我们的职能

一旦DLL就位,注入器就会确定DLL导出的函数的地址。 (通常,我通过调用CreateRemoteThread来执行初始注入,然后在该线程上使用GetExitCodeThread来获取父进程中DLL的基址。一旦我有了这个,就可以通过简单的算法找到地址我们导出的函数,然后我可以使用它来注入该函数的第二次调用。

呼唤我们的function

导出的函数将类似于:

BOOL RewriteHProcess(HANDLE hProcess)

注入器将再次使用CreateRemoteThread从父进程的上下文调用此函数,其中hProcess是注入器进程的句柄。 在DLL方面,该函数会做两件事之一(我不太确定我的第一个想法是否可行,考虑到跨线程的内存访问的安全限制,所以我把第二个想法放在一起,如果首先没有成功。)

  1. RewriteHProcess将打开先前挂起的线程进行读写,并使用ReadProcessMemory ,它将在进程的内存中搜索HANDLE到我们的注入器进程。 (我们假设父进程当前正在阻止使用WaitForSingleObject函数进一步执行。我知道命令提示符至少会执行,而这是我目前关注的焦点)然后DLL调用内部函数创建我们想要的子进程,关闭旧句柄,并使用新子进程的句柄覆盖内存。 在这一点上,它清理它可以做什么,并返回。 然后,注入器将执行所需的任何剩余清理,恢复挂起的线程,关闭进程和线程的句柄,然后退出,让父进程在等待新的子进程结束时继续阻塞。

  2. 如果该路由不可行,我的后备是从注入器暂停阻塞线程,在注入的DLL中创建新的子进程,清理并退出注入器,并等待DLL直到子进程完成。 此时,DLL将清理,恢复挂起的线程,并卸载自己。 (但是,此路由的缺点是父进程从注入器返回的返回代码可能与我们的目标子进程的返回代码不同)

使用VirtualAllocEx()在目标进程内部分配可执行内存块,然后根据需要使用WriteProcessMemory()将x86或x64机器指令写入该内存块。 让这些指令调用LoadLibrary()GetProcAddress() ,根据需要调用导出的DLL函数。 然后使用CreateRemoteThread()执行内存块。 如果导入器在单独的进程中运行,则无法直接调用导出的DLL函数。 必须在目标进程的上下文中加载和调用导出的函数。 并且不要从GetProcAddress()的返回值中减去LoadLibrary()的返回值。 GetProcAddress()返回一个指向函数的直接内存指针,因此可以直接调用它。

更新:这种方法的一个变体是,当使用DLL_ATTACH_PROCESS原因调用时,将所有注入的代码放入DLL的入口点(或让入口点生成一个线程来运行代码)。 因此无需从DLL导出任何函数。 然后,您可以使用VirtualAllocEx()WriteProcessMemory()将DLL的路径存储到目标进程中,然后使用CreateRemoteThread() LoadLibrary()直接调用LoadLibrary() 。 内核函数在进程间始终具有相同的内存地址,因此您的注入过程可以在其自己的地址空间中调用GetProcAddress()以获取LoadLibrary()的地址,然后将该指针传递给CreateRemoteThread()lpStartAddress参数。 这样,您不必担心编写任何x86 / x64汇编代码。

本文第3节中更详细地描述了此技术:

将代码注入另一个进程的三种方法

如果有人发现了这个问题并且在阅读完评论后仍然感到困惑,那么你可以在这里找到一个(当然是丑陋的 )概念certificate。 假设您已经知道DLL注入的正常策略,这里解释了您需要采取哪些不同的做法。 ( 附有相关代码 ):


找到我们的出口 ( injdll32_64.c )

通常我们可以使用LoadLibrary/GetProcAddress路由,但由于这两个模块针对不同的处理器,我们需要采取不同的方式。 正如Remy Lebeau在他的回答中所说 ,我们可以在assembly方面完全做到这一点。 但是,从我的角度来看,编写程序集以找到kernel32.dll的基地址,找到导出表,并找到LoadLibraryGetProcAddress似乎是一个痛苦的屁股。 相反,我通过读取每个DLL,在IMAGE_EXPORT_DIRECTORY搜索必要的导出,并存储它们的RVA以供以后使用,在C端处理它。


通过RVA调用我们的出口 ( injdll32.c , x86.final.asm )

为了我的目的,我需要在目标应用程序的主线程中执行注入的DLL。 为此,我暂停了目标应用程序的主线程,为我预先组装的机器代码分配了内存,用适当的导出RVAs填充了占位符,通过调用GetThreadContext/SetThreadContext更改了目标应用程序的EIP,并恢复了悬挂的线程。 (不一定按顺序)不幸的是,我从来没有编写过一种机制来释放我们在目标应用程序中VirtualAlloc的内存,但这样做的一个解决方案是实现一个机制来通知注入器何时目标进程返回其原始EIP。 此时,为VirtualFree注入分配的内存是安全的。 (或者,你总能找到一个合适的代码洞穴,甚至可以省去释放内存的麻烦)