汇编中的线程函数是什么样的?(LPTHREAD_START_ROUTINE,CreateRemoteThread)

逆向工程 视窗 部件 字节码
2021-06-14 13:24:13

使用CreateRemoteThread我们可以调用另一个进程的特定类型的函数。它必须是应用程序定义的并且是LPTHREAD_START_ROUTINE 类型
如果目标进程中不存在这样的函数,您必须注入自己的函数,这就是我需要您帮助的地方。
使用VirtualAllocExWriteProcessMemory我想注入这样一个函数作为字节码。所以我想知道,这样的函数在原始汇编中是什么样子的。

1个回答

这个问题有点过于宽泛,没有更多信息就无法回答,但我将介绍一些关于我认为您正在寻找的内容的一般观点。为简单起见,我还将假设 Windows 上的 x86 调用约定。

唯一真正决定一个函数在汇编中“看起来像”什么的是它的调用约定,但是对于函数可以做什么没有限制(特别是编写原始字节代码)。在使用 CreateRemoteThread 的情况下,唯一的限制是您不会破坏堆栈(这可能会使目标进程崩溃)

例如,我可以在汇编中编写一个如下所示的函数:

nop
nop
nop
nop
ret

它将是 100% 有效的。但就更复杂的功能而言,让我们看看 CreateRemoteThread 的 MSDN:

LPTHREAD_START_ROUTINE 是以下函数定义的#define:

DWORD WINAPI ThreadProc(
  _In_  LPVOID lpParameter
);

其中 WINAPI 是 stdcall 调用约定的另一个 #define。您可以在此处查看 wiki,但重要的部分(如果您正在编写原始程序集”)是对于 WINAPI/stdcall 函数,被调用函数会清理堆栈,这意味着您编写的函数在远程进程中运行将不得不清理它的参数。这是通过 ret N 指令集完成的。例如,如果你有:

WINAPI foo(int x)

然后 foo 将不得不清理堆栈上的一个参数,因此该函数的汇编将以一条ret 4指令结束,因为有一个参数意味着需要清理堆栈上的 4 个字节。

同样,如果您有:

WINAPI bar(int x, int y)

然后汇编将以一条ret 8指令结束,因为现在有 2 个参数 = 8 个字节。同样重要的是要注意,如果我有类似的东西:

WINAPI foobar(char x, char y, int z)

它仍然会以ret 0C. 即使 char 参数只有 1 个字节的数据,堆栈仍将以 4 个字节的增量对齐。

回到 CreateRemoteThread 的 API,您可以看到其中一个参数是

LPVOID lpParameter

这是将参数传递给远程函数的指针。这意味着在您的“远程”函数中,您必须使用 ret 4 指令清除堆栈上的一个参数。如果您执行一些技巧,您可以用 C 代码编写您的 WINAPI 函数,让编译器为您编译它(使编码更容易,您不必担心自己清理),并在运行时找到该函数进行复制它关了。