C++ 了解如何手动发出处理器指令
我在读一篇关于如何构造的文章,作者基本上使用了以下代码:C++ 了解如何手动发出处理器指令,c++,linux,assembly,C++,Linux,Assembly,我在读一篇关于如何构造的文章,作者基本上使用了以下代码: // Processor instructions for: // mov eax, 0 // ret unsigned char code[] = {0xb8, 0x00, 0x00, 0x00, 0x00, 0xc3}; void *mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
// Processor instructions for:
// mov eax, 0
// ret
unsigned char code[] = {0xb8, 0x00, 0x00, 0x00, 0x00, 0xc3};
void *mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE, -1, 0);
memcpy(mem, code, sizeof(code));
int (*func)() = mem;
return func();
除了他知道如何手动将汇编指令映射到数字代码之外,我对代码了解得很透彻。为了理解这一点,我需要学习什么?参考文献()介绍了装配指令如何映射到实际机器代码上。当然,这将在CPU/环境之间进行-例如,上述操作在ARM系统上不起作用
或者,可以从现有生成的输出(如汇编程序的输出)复制这些值。您可以使用许多库来编写JIT。将帮助您发出机器代码指令(在x86上),将一些抽象指令集(或抽象语法树)转换为机器代码
记录处理器(在长文档中)。因为你可能需要阅读上千页。我是文章作者,希望你喜欢 为了构建这些价值观,我基本上做到了
$ cat test.S
.intel_syntax noprefix
mov eax, 0
ret
$ gcc -c -o test.o test.S
$ objdump -d -M intel test.o
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: b8 00 00 00 00 mov eax,0x0
5: c3 ret
$cat test.S
.intel_语法noprefix
mov-eax,0
ret
$gcc-c-o测试.o测试.S
$objdump-d-M intel test.o
test.o:文件格式elf64-x86-64
第节的分解。正文:
0000000000000000 :
0:B800 mov eax,0x0
5:c3 ret
您可以在左栏中看到指令的字节。除非你有很好的理由,否则我不建议你深入研究指令编码。它们非常复杂,汇编程序非常擅长生成这些东西。在这个层次上还有很多其他的东西需要学习,这将更好地利用你的大脑能量。阅读有一个良好的开端。这让我朝着正确的方向前进。我在这里找到了一个列表:。我唯一不明白的是,MOV(0xB8)似乎是第一条指令的开始,而它似乎是第二条指令的结束(RET:0xC3)。@sircodesalot不同的指令,如
MOV
有不同的模式(例如立即?寄存器?内存?),并采用不同的参数(即eax
,0
)。另一方面,ret
只有一种简单的无参数形式,这就是为什么它与mov
输出相比显得如此小(一个字节)的原因,后者还必须对所有其他数据进行编码。我想我需要继续研究计算机体系结构。谢谢因此,我已经对此进行了足够多的实验,以探索它是如何工作的,以及如何发出我自己的代码(非常棒,非常棒的文章)等等。有一件事我有一个问题,在文章中你提到,我们希望避免映射任何可写和可执行的内存。我想我从一个较高的层面上想知道:(1)风险是什么,(2)你如何正确地设置它?再次感谢,我喜欢你的文章@sircodesalot:风险在于,成功利用缓冲区溢出的人可以将自己的可执行指令写入该内存,然后跳转到该内存。基本上它是另一种攻击的载体。避免这种情况的方法是首先映射内存rw,然后像我在这段代码中所做的那样将映射更改为rx:很高兴你喜欢这篇文章!