• X86架构解析及指令模拟流程


    前言:

    要进行指令模拟,我们先需要了解X86架构下的指令是长什么样子的。根据intel的编程手册我们找到了如下信息。

    Intel CPU的机器指令格式如下图所示:

    e.g.:图片位于intel开发手册第二卷第二章的2.1

    根据开发手册,一条指令由 指令前缀(Instruction Prefixes) + 操作码(Opcode) + ModR/M + SIB + 偏移(displacement) + 立即数(Immediate data)几部分组成,一条指令至少需要有Opcode,其它几部分,在不同指令中可能存在可能不存在。今天我们主要来看Opcode、Instruction Prefixes、Immediate data三部分在不同指令中的使用。

    一:x86指令解析

    1.1  指令前缀

    指令前缀分为四组,每组都有一组允许的前缀代码。对于每条指令,它仅在包含来自四个组(组 1、2、3、4)中的每一组的最多一个前缀代码时有用。第 1 组至第 4 组可以以任何相对于彼此的顺序放置。
    第一组:封锁和重复执行前缀

        F0H: LOCK前缀,封锁总线。在有数的指令(如ADD,ADC)前方时,使指令变为原子操作,并与被修饰的指令一起提供内存屏障效果。
        F2H:REPNE/REPNZ前缀(只位于字符串指令前)
        F3H:REP前缀(只位于字符串指令前)
        F3H:(与REP前缀同码)

    第二组:段前缀

        在32位汇编中,有8个段寄存器:ES、CS、SS、DS、FS、GS、LDTR、TR(顺序固定),不再用段寄存器寻址而只做权限控制。前缀和寄存器对应如下:

    2E - CS
    36 - SS
    3E - DS
    26 - ES
    64 - FS
    65 - GS

        使用前缀修饰后,指令(opcode)的默认段寄存器会被修改,如默认的MOV操作从DS段拿数据,加上36前缀后会基于SS段地址取数据。

    第三组:修改操作数默认长度

         66H,用来“反转”默认的16位或32位操作数宽度。例如,当默认的操作数宽度是32位时,可以用这个前缀选择16位宽度的操作数,或者反之。如下指令码和汇编对照:

    50:  PUSH EAX
    6650: PUSH AX
    第四组:修改默认地址长度

        67H,用来“反转”默认的16位或32位地址宽度。例如,当默认的地址宽度是32位时,可以用这个前缀选择16位宽度的地址,或者反之。如下指令码和汇编对照:

    8801:   MOV DS:[ECX],AL
    678801: MOV DS:[BX+DI], AL

    2.  指令码(opcode)

        主操作码的长度可以是 1、2 或 3 个字节。如果主操作码不能确定指令则会有额外的字段编辑倒ModR/M中。如果主操作码是0x0f开头则需要取第二字节,如果主操作码开头是0x0f38,0x0f3a开头则再取第三字节。

        从intel编程手册中截取一字节指令码格式如下图(附录A,第三章):

        

        部分释义:

      G:通用寄存器
      E:寄存器/内存
      b:字节
      v:worddouble wordquadword(16/32/64位,取决于CPU模式)

          opcode查看方法:

            如指令码0x48,查看第4行第0列,属于dec eax(REX是为64位指令集使用的,可自行搜索研究),此时指令为定长指令。

            再比如0x28(表示sub Eb, Gb),这里Gb表示通用寄存器,Eb表示还需要其他字段来确定这个参数,这就是下面要讲的ModR/M字段。此时指令为不定长指令。

    3.  ModR/m

         字段名称解析:

      Mod: 指明操作码中的E表示寄存器还是内存,11表示内存,其余表示寄存器
      Reg/Opcode:标记通用寄存器
      R/M:配合Mod字段标明取址方式,若为内存寻址则需要SIB字段辅助

        ModR/m格式表格:

     指令解析示例:88 01
      ①"88"我们知道其同通式是“mov Eb,Gb”,因此88是不定长指令,所以其后的一个字节**"01"即为ModR/M;
      ②我们将“01”按照ModR/M的格式拆分成三部分:01== 00 000 001三部分 ==> Mod=00=0,Eb即为byte ptr的内存;Reg/Opcode=000=0,即为eax/ax/al寄存器(Eb即byte则为al);R/M=001=1,即为ecx
      ③确定出“8801”的汇编指令为:mov byte ptr [ecx],al ==>mov byte ptr ds:[ecx],al(没有指令前缀则DS是默认的)

    3.  SIB

       SIB字段 引出:
              SIB是紧接着ModR/M的一个字节。不定长指令后必有ModR/M,而ModR/M的Mod不为"11"且R/M值为"100"(ESP)时则ModR/M后就有SIB。

        解析方式:

      SIB字段分三部分,如上图所示。
      该三部分均存在于[]的括号中,格式为:Base + Index*2^(Scale),Base为寄存器编号索引的寄存器,Index也是寄存器编号索引的寄存器,Scale为00~11,因此格式又为:Base + Index * 1/2/4/8所以格式形如:ds:[eax+ecx*4]。

     解析示例:"88 84 48 12 34 56 78":
      ①Opcode = 88 --> 指令格式:mov Eb,Gb
      ②ModR/M = 84 --> 10 000 100 -->[reg+disp32](普通格式), al,esp
      ③由于Mod为10,且R/M为100,则属于特殊情况,不遵循普通格式,所以下一个字节为SIB(可确定汇编指令为:mov byte ptr [–][–][disp32],al)
      ④[–][–]解析:SIB = 48H --> 01 001 000;Scale=1,Index=1(ECX),Base=0(EAX)
      ⑤得到汇编指令为:mov byte ptr [eax][ecx*2][78563412],al ==> **mov byte ptr [eax+ecx*2+78563412],al**

     二:指令模拟

        本文不对虚拟化的基础知识再进行展开,如需理解下面的内容须先 了解硬件辅助虚拟化的基础知识,如 KVM/GVM/HAXM的实现流程,网上讲基础的帖子很多,此处就不再展开。

        如下内容基于HAXM进行流程分析,假设大家都知道了一些基础知识(如VM_EXIT,VM_ENTRY,VMX)

        2.1 功能定位

            指令模拟在硬件辅助虚拟化环境下,所处的执行流程如下图:

        

         2.2 功能描述

      功能描述部分属于上图蓝色部分的展开,便于后续对代码的理解

        

         2.3 代码流程

         

  • 相关阅读:
    [script]判定dd是否成功
    [script]判定某一个脚本是否正确执行
    [wifi]wifi模块的测试
    [其他]设计开发
    [应用]Linux下" >/dev/null 2>&1 "
    [log]利用logrotate对Linux log进行管理
    [sz,rz]使用sz/rz在两台Linux设备之间传输数据
    [Kernel]内核版本添加字符和内核版本'+'解决
    [Linux应用]Linux应用程序输出数据重定向到文件中
    [systemd]How To Use Systemctl to Manage Systemd Services and Units
  • 原文地址:https://www.cnblogs.com/edver/p/15460077.html
Copyright © 2020-2023  润新知