首页 文章

CPU如何正确解码可变长度指令?

提问于
浏览
1

在大多数体系结构中,指令都是固定长度的 . 这使得程序加载和执行变得简单 . 在x86 / x64上,指令是可变长度的,因此反汇编的程序可能如下所示:

File Type: EXECUTABLE IMAGE

  00401000: 8B 04 24           mov         eax,dword ptr [esp]
  00401003: 83 C4 04           add         esp,4
  00401006: FF 64 24 FC        jmp         dword ptr [esp-4]
  0040100A: 55                 push        ebp
  0040100B: E8 F0 FF FF FF     call        00401000
  00401010: 50                 push        eax
  00401011: 68 00 30 40 00     push        403000h
  00401016: E8 0D 00 00 00     call        00401028
  0040101B: 83 C4 08           add         esp,8
  0040101E: 33 C0              xor         eax,eax
  00401020: 5D                 pop         ebp
  00401021: 83 C4 04           add         esp,4
  00401024: FF 64 24 FC        jmp         dword ptr [esp-4]
  00401028: FF 25 00 20 40 00  jmp         dword ptr ds:[00402000h]

  Summary

        1000 .data
        1000 .rdata
        1000 .reloc
        1000 .text

似乎很难想象CPU "knows"如何结束一条指令并且下一条指令开始 . 例如,如果我将字节0x90( NOP )添加到 XOR EAX,EAX 操作码的中间,则程序将反汇编为:

File Type: EXECUTABLE IMAGE

  00401000: 8B 04 24           mov         eax,dword ptr [esp]
  00401003: 83 C4 04           add         esp,4
  00401006: FF 64 24 FC        jmp         dword ptr [esp-4]
  0040100A: 55                 push        ebp
  0040100B: E8 F0 FF FF FF     call        00401000
  00401010: 50                 push        eax
  00401011: 68 00 30 40 00     push        403000h
  00401016: E8 0D 00 00 00     call        00401028
  0040101B: 83 C4 08           add         esp,8
  0040101E: 33 90 C0 5D 83 C4  xor         edx,dword ptr [eax+C4835DC0h]
  00401024: 04 FF              add         al,0FFh
  00401026: 64 24 FC           and         al,0FCh
  00401029: FF
  0040102A: 25
  0040102B: 00 20              add         byte ptr [eax],ah
  0040102D: 40                 inc         eax

  Summary

    1000 .data
    1000 .rdata
    1000 .reloc
    1000 .text

可以预见的是,在运行时会崩溃 .

我很好奇指令解码器看到的那个额外的字节使得它认为 0040101E 行是6个字节长,而最初在 00401028 的行是四个单独的指令 .

1 回答

  • 6

    在获取指令时,CPU首先分析其第一个字节(操作码) . 有时知道指令的总长度就足够了 . 有时它会告诉CPU分析后续字节以确定长度 . 但总而言之,编码并不含糊 .

    是的,如果您在中间插入随机字节,命令流就会搞砸了 . 这是可以预料的;并非每个字节序列都构成有效的机器码 .

    现在,关于你的具体例子 . 原始命令是 XOR EAX, EAX (33 C0) . XOR的编码是那些依赖于第二字节的编码之一 . 第一个字节 - 33 - 表示异或 . 第二个字节是ModR / M字节 . 它对操作数进行编码 - 无论它插入的's a register pair, a register and a memory location, etc. The initial value C0 in 32-bit mode corresponds to operands EAX, EAX. The value 90 that you'对应于操作数EDX,[EAX偏移],它意味着ModR / M字节后跟32位偏移 . 命令流的后四个字节不再被解释为命令 - 它们是受损XOR命令中的偏移量 .

    因此,通过弄乱第二个字节,您将一个2字节的命令转换为6字节的命令 .

    然后CPU(和反汇编程序)在这四个之后恢复获取 . 它位于 ADD ESP, 4 指令的中间,但CPU无法知道 . 它从04字节开始,第三个在ADD编码中 . 那一点的前几个字节仍然有意义,但是由于你最终在中间,原始的指令序列完全丢失了 .

相关问题