好吧,我试图创建一个创建shellcode的函数。
我在解决rex / mod东西时遇到很多问题。
我当前的代码工作正常。
到目前为止,如果regs小于R8,则可以正常工作。
如果我使用一个较小的reg,那么R8就可以了。
问题是一旦我必须将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的话,那将很好。
答案 0 :(得分:1)
仅供参考,大多数人是通过使用NASM之类的常规汇编程序进行汇编来创建Shellcode的,然后将该二进制文件十六进制转储为C字符串。编写自己的汇编器可能是一个有趣的项目,但基本上是一个单独的项目。
您的encode_rex
看起来有些明智,为这四个位取了四个args。但是mov
中调用它的代码有时会传递一个2
,它将截断为0
!
此外,用于reg-reg移动的2个相关扩展位(b和x)有4种可能。但是您的if / else if / else链仅覆盖了其中的3条,而忽略了dest>=8 && src >= 8
=> x:b = 3
由于这两个位是正交的,因此您应该像这样分别计算它们:
put<uint8_t>(encode_rex(1, 0, dest>=8, src>=8));
SIB索引x
字段应始终为0
,因为您没有SIB字节,只有RegRM mov
的ModRM。
您在encode_rex
中混合了结构初始化程序,其中extend_modrm_reg
为第二,它将初始化x
字段而不是r
。您的位字段名称匹配https://wiki.osdev.org/X86-64_Instruction_Encoding#Encoding,但是您使用错误的C ++变量对其进行了初始化。请参阅该链接以获取描述。
根据您使用的是mov r/m, r
还是mov r, r/m
操作码,可能我有dest,src的顺序倒退。我没有仔细检查哪个。
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*)&result;
是UB的严格别名,在MSVC之外并不安全。
使用memcpy安全键入旁遮普字符。 (或者是一个联合;与ISO C ++不同,大多数现实世界中的C ++编译器(包括gcc / clang / MSVC都确实定义了C99中联合类型的行为)。