SetThreadContext 只修改 x64 中 RIP 的最后 32 位

Posted

技术标签:

【中文标题】SetThreadContext 只修改 x64 中 RIP 的最后 32 位【英文标题】:SetThreadContext modifies only the last 32 bits of RIP in x64 【发布时间】:2017-12-29 09:24:55 【问题描述】:

由于未知原因,SetThreadContext 在 x64 进程中无法正常工作。我打算在挂起的线程上使用它来修改指令指针并使线程执行一些shellcode。 不幸的是,SetThreadContext 似乎只修改了 x64 指令指针 RIP 的最后 32 位

为了隔离问题,我做了如下小程序,提示用户输入要附加的线程ID,如果附加成功,用户可以输入地址手动修改指令指针。

#include <Windows.h>
#include <iostream>

using namespace std;

int main() 
    DWORD tid = 0;
    cout << "Thread ID: ";
    cin >> dec >> tid;

    HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid);
    if (hThread == NULL) 
        cout << "Failed." << endl;
        system("pause");
        return EXIT_FAILURE;
    

    DWORD64 newRip = 0;
    cout << "New RIP: 0x";
    cin >> hex >> newRip;

    CONTEXT tcNew;
    SecureZeroMemory(&tcNew, sizeof(tcNew));
    tcNew.ContextFlags = CONTEXT_FULL;
    tcNew.Rip = newRip;

    SuspendThread(hThread);
    SetThreadContext(hThread, &tcNew);
    ResumeThread(hThread);

    cout << "Done." << endl;
    system("pause");
    return EXIT_SUCCESS;

现在,如果我启动另一个附加到调试器的 x64 进程并使用诸如 Process Hacker 或 Sysinternals 的 Process Explorer 之类的工具来获取该进程的运行线程 ID,我可以简单地将这个程序附加到我想要的线程修改并输入我希望它执行的地址。

我用几个程序进行了测试(一些是用我的 Visual Studio 编译的,还有一些来自我的 Windows 的 x64 进程,例如记事本或 mspaint),如果我将新的 RIP 设置为 0x1234567890ABCDEF,那么我会执行访问冲突位置 0x0000000090ABCDEF

现在这很有趣,似乎 RIP 地址的最高有效 32 位在此过程中由于某种原因丢失了,我无法解释。 我想到了我的Visual Studio项目配置中的一些问题,所以我又从头开始了,但同样的事情。我还尝试安装另一个 IDE 和编译器(Code::Blocks 和 MinGW64),但由于某种原因问题仍然存在。 更有趣的是,GetThreadContext 效果很好,我成功地获得了 RIP 在挂起线程中的确切 64 位地址。 似乎只有 SetThreadContext 有问题。

如果您对为什么会发生这种情况有任何想法,或者有任何实验建议,我可以尝试让我获得有关如何解决此问题的新想法,请毫不犹豫地分享。

非常感谢。

【问题讨论】:

很可能,0x1234567890ABCDEF 地址超出了目标进程的 CS 选择器的范围。 @Malkocoglu 我显然也尝试将线程发送到可执行内存区域中存在适当 shellcode 的地址,并且它执行相同的操作。无论如何,指令指针应该指向我发送它的地址,它没有理由拒绝,即使它指向一个什么都没有的地址,它应该指向那里并且会在恢复时触发异常. SetThreadContext 100% 也适用于 x64。在您的代码中,您不会在调用之前初始化 SS 和 CS 寄存器。首先调用GetThreadContext,然后修改Rip并调用SetThreadContextRip 也必须具有规范形式 - 任何虚拟地址的最高有效 16 位,第 48 位到第 63 位,必须是第 47 位的副本 是的,这正是因为您有 SegCs = 0; 将其更改为正确的值 0x33 (最好先调用 GetThreadContext )并且您在 0x0000567890abcdef 地址处遇到异常。由于规范标准化,最高有效 16 位将为 0 如果您将 Rip 更改为 0x1234867890abcdef 您在 0xffff867890abcdef 遇到异常 【参考方案1】:

感谢@RbMm 在 CONTEXT 上调用 GetThreadContext,然后我用它来编辑 RIP 解决了这个问题。我将阅读有关此 SegC 的作用的更多信息,以更详细地了解此问题的原因。

【讨论】:

以上是关于SetThreadContext 只修改 x64 中 RIP 的最后 32 位的主要内容,如果未能解决你的问题,请参考以下文章

x64 CPU 上的原子 16 字节读取

CentOS 6.7 x64 Apache/PHP/Mariadb环境搭建

armwin11怎么在x64电脑运行

x64内核内存空间结构

wrpper x64 版本发布到centos

转移 MariaDB