如果我编辑线程的指令指针,如何跳回到(旧的)指令指针?

时间:2018-09-21 12:49:35

标签: c# assembly 64-bit pinvoke x86-64

我正在尝试编辑线程的指令指针,以使其在跳回旧的指令指针并按常规继续之前,调用某些已组装的x64程序集。在恢复假定的线程之后,我正在执行此操作的程序崩溃,表明该程序没有跳回到旧的指令指针。

当前,我正在尝试以下

获取线程的句柄

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenThread(ThreadPrivileges dwDesiredAccess, bool bInheritHandle, IntPtr dwThreadId);

public enum ThreadPrivileges
{
    SuspendResume = 0x02,
    GetContext = 0x08,
    SetContext = 0x010,
    AllAccess = SuspendResume | GetContext | SetContext
}

var threadId = Process.GetProcessesByName(processName)[0].Threads[0].Id;

var threadHandle = OpenThread(ThreadPrivileges.AllAccess, false, (IntPtr)threadId);

暂停线程

[DllImport("kernel32.dll", SetLastError = true)]
public static extern void SuspendThread(IntPtr hThread);

SuspendThread(threadHandle); 

获取线程的上下文

可以在此link

找到Context64和ContextFlags的结构。
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetThreadContext(IntPtr hThread, ref Context64 lpContext);

var context = new Context64() { ContextFlags = ContextFlags.ContextFull };
GetThreadContext(threadHandle, ref context);

试图将旧的指令指针写入堆栈

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, int lpNumberOfBytesWritten);

context.Rsp -= sizeof(ulong); // Allocate 8 bytes on the stack

WriteProcessMemory(processHandle, (IntPtr)context.Rsp, BitConverter.GetBytes(context.Rip), sizeof(ulong), 0);

覆盖指令指针

context.Rip = (ulong)myAssemblyPointer;

将线程上下文设置为已编辑的上下文

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetThreadContext(IntPtr hThread, ref Context64 lpContext);

SetThreadContext(threadHandle, ref context);

最后恢复线程

[DllImport("kernel32.dll", SetLastError = true)]
public static extern void ResumeThread(IntPtr hThread);

ResumeThread(threadHandle);

我不确定这是否有任何相关性,但是我正在使用的程序集如下

PUSHFQ
PUSH rax
PUSH rbx
PUSH rcx
PUSH rdx
PUSH r8
PUSH r9
PUSH r10
PUSH r11

sub RSP, 0x28
movabs RCX, 0x0000000000000000  ; Pointer 1
movabs RAX, 0x0000000000000000  ; Pointer 2
call RAX
add RSP, 0x28

POP r11
POP r10
POP r9
POP r8
POP rdx
POP rcx
POP rbx
POP rax
POPFQ

ret

为了尽量简化这篇文章,我试图排除尽可能多的代码(错误检查和清除),但是我所有的错误检查似乎都表明pinvoke方法正在按预期工作。我主要担心的是当我尝试将旧的指令指针写入堆栈时。尽管WriteProcessMemory不会失败,但是我有一种错误的感觉。如果有人可以向我解释我是否正确地将旧的指令指针正确地写入了堆栈,如果不能,我将如何实现它,我将不胜感激。

如果您要我提供其他信息,例如processHandlemyAssemblyPointer变量,请告诉我。

1 个答案:

答案 0 :(得分:0)

根据评论:

您只读取4个字节,需要读取8个字节,因为它是x64进程,并且这是内存地址指针所需的大小。 long只有4个字节,您需要使用long long。