【问题标题】:creating shellcode problems with mov reg to reg使用 mov reg to reg 创建 shellcode 问题
【发布时间】:2020-04-29 01:31:38
【问题描述】:

好的,我正在尝试创建一个创建 shellcode 的函数。

我在处理 rex / mod 的东西时遇到了很多问题。

我当前的代码工作。

到目前为止,如果 regs 比 R8 小,它可以正常工作。

如果我使用一个小于 R8 的 reg 就可以了。

问题是一旦我必须 regs 小于 r8 并且相同,或者如果 src 更小我就会遇到问题

enum Reg64 : uint8_t {
    RAX = 0, RCX = 1, RDX = 2, RBX = 3,
    RSP = 4, RBP = 5, RSI = 6, RDI = 7,
    R8 = 8, R9 = 9, R10 = 10, R11 = 11,
    R12 = 12, R13 = 13, R14 = 14, R15 = 15
};

inline uint8_t encode_rex(uint8_t is_64_bit, uint8_t extend_sib_index, uint8_t extend_modrm_reg, uint8_t extend_modrm_rm) {
    struct Result {
        uint8_t b : 1;
        uint8_t x : 1;
        uint8_t r : 1;
        uint8_t w : 1;
        uint8_t fixed : 4;
    } result{ extend_modrm_rm, extend_modrm_reg, extend_sib_index, is_64_bit, 0b100 };
    return *(uint8_t*)&result;
}
inline uint8_t encode_modrm(uint8_t mod, uint8_t rm, uint8_t reg) {
    struct Result {
        uint8_t rm : 3;
        uint8_t reg : 3;
        uint8_t mod : 2;
    } result{ rm, reg, mod };
    return *(uint8_t*)&result;
}

    inline void mov(Reg64 dest, Reg64 src) {
        if (dest >= 8)
            put<uint8_t>(encode_rex(1, 2, 0, 1));
        else if (src >= 8)
            put<uint8_t>(encode_rex(1, 1, 0, 2));
        else
            put<uint8_t>(encode_rex(1, 0, 0, 0));

        put<uint8_t>(0x89);

        put<uint8_t>(encode_modrm(3, dest, src));
    }

    //c.mov(Reg64::RAX, Reg64::RAX); // works
    //c.mov(Reg64::RAX, Reg64::R9); // works
    //c.mov(Reg64::R9, Reg64::RAX); // works
    //c.mov(Reg64::R9, Reg64::R9); // Does not work returns (mov r9,rcx)

另外,如果没有所有 if 的情况下有更短的方法来做到这一点,那就太好了。

【问题讨论】:

  • 仅供参考,大多数人通过使用像 NASM 这样的普通汇编程序进行汇编来创建 shellcode,然后将该二进制文件十六进制转储为 C 字符串。编写自己的汇编程序可能是一个有趣的项目,但不是您通常需要做的事情。

标签: c++ assembly x86-64 machine-code


【解决方案1】:

仅供参考,大多数人通过使用像 NASM 这样的普通汇编程序进行汇编来创建 shellcode,然后将该二进制文件十六进制转储为 C 字符串。编写自己的汇编程序可能是一个有趣的项目,但基本上是一个单独的项目。


您的encode_rex 看起来有些明智,四个参数为四个位。但是mov 中调用它的代码有时会传递一个2,它会截断为0

此外,您用于 reg-reg 移动的 2 个相关扩展位(b 和 x)有 4 种可能性。但是你的 if/else if/else 链只覆盖了其中的 3 个,忽略了 dest&gt;=8 &amp;&amp; src &gt;= 8 => x:b = 3 的可能性

由于这两个位是正交的,您应该像这样分别计算它们:

put<uint8_t>(encode_rex(1, 0, dest>=8, src>=8));

SIB 索引 x 字段应始终为 0,因为您没有 SIB 字节,只有 ModRM 用于 reg-reg mov

你的结构初始化器在 encode_rex 混淆了,extend_modrm_reg 是第二个,它将初始化 x 字段而不是 r 你的位字段名称匹配 https://wiki.osdev.org/X86-64_Instruction_Encoding#Encoding ,但你有错误的 C++ 变量初始化它们。有关说明,请参见该链接。


可能我的 dest 和 src 顺序倒退,这取决于您使用的是mov r/m, r 还是mov r, r/m 操作码。我没有仔细检查哪个是哪个。

来自 NASM 的健全性检查:我与 nasm -felf64 -l/dev/stdout 集合以获取列表:

     1 00000000 4889C8                  mov rax, rcx
     2 00000003 4889C0                  mov rax, rax
     3 00000006 4D89C0                  mov r8, r8
     4 00000009 4989C0                  mov r8, rax
     5 0000000C 4C89C0                  mov rax, r8

您正在使用与 NASM 相同的 0x89 操作码,因此您的 REX 前缀应该匹配。


return *(uint8_t*)&amp;result; 是严格别名 UB,在 MSVC 之外不安全。

使用 memcpy 安全地键入双关语。 (或联合;大多数现实世界的 C++ 编译器,包括 gcc/clang/MSVC 都定义了联合类型双关语的行为,就像在 C99 中一样,与 ISO C++ 不同)。

【讨论】:

  • 当我只使用 put&lt;uint8_t&gt;(encode_rex(1, 0, dest&gt;=8, src&gt;=8)); 时,即使切换它们也会得到奇怪的结果
  • @werico4026:我认为这是因为您的encode_rex 已损坏,使用错误顺序的函数 args 作为位字段的初始值设定项。我将答案的那部分加粗。使用调试器查看您获得的实际二进制值,而不仅仅是反汇编。
  • 我真的很困惑这是如何工作的,我不确定如何改进我的 encode_rex
  • @werico4026:更新以更清楚地解释它并链接一些文档。
猜你喜欢
  • 2010-11-11
  • 2021-03-06
  • 1970-01-01
  • 1970-01-01
  • 2016-02-16
  • 2013-03-17
相关资源
最近更新 更多