最近一段时间,看了《加密与解密》3版里的虚拟机,感觉挺有意思,现将自已的分析发表如下(里面有些是原作者的分析,但大部分我已作更详细分析),与大家共享
本想将其发表至《加密与解密》版块中,但自已是新人,只好发表在这里了

// 发表的内容以文件为单元(因为比较容易,懒得去整理所有的思路了)

// ----------------------------------------------- 源程序文件部分 -------------------------------------------------------


// ------------------------------------------ VCommand.cpp --------------------------------------

// 虚拟机的一部分指令,用于模拟某些汇编指令

#include "stdafx.h"
#include "VCommand.h"

// 需手工编写的虚拟机指令

/**
 * 进入虚拟机
 */
void _declspec(naked) VStartVM()  // *
{
  _asm
  {
    // 将寄存器压入堆栈
    push eax
    push ebx
    push ecx
    push edx
    push esi
    push edi
    push ebp
    pushfd
    // edi -> VMContext
    // esi -> 当前字节码地址
    // ebp -> 真实堆栈
    mov    esi, [esp+0x20]      // 伪代码开始的地址
    mov     ebp, esp        // ebp保存原堆栈指针
    sub    esp, 0x200
    mov     edi, esp        // edi指向VMContext
    sub    esp, 0x40        // esp现在是vm用的堆栈了
    mov    ebx, esi        // 把伪代码base_addr做key

    movzx  eax, byte ptr [esi]    // 获得bytecode
    lea    esi, [esi+1]      // 跳过这个字节
    
    VM_END              // VM结束标记
  }
}

/**
 * 检测真实堆栈是否已接近VMContext所在的位置,如果是
 * 则将VMContext结构复制到更远的位置存放
 */
void DCheckESP()  // *
{
  _asm
  {
    // edi -> 指向VMContext
    // ebp -> 指向真实的堆栈

    // 判断VMContext往上0x100处是否已接近真实堆栈
    lea     eax, dword ptr [edi + 0x100]
    cmp     eax, ebp

    nop
    nop

    // 计算VMContext与虚拟机堆栈之间的长度
    mov     edx, edi
    mov     ecx, esp
    sub     ecx, edx

    push  esi            // 保存虚拟指令IP指针
    mov    esi, esp
    sub    esp, 0x60
    mov    edi, esp
    push  edi            // 保存新的edi基地址
    sub    esp, 0x40
    cld
    rep movsb            // 开始复制

    // 恢复IP指针
    pop edi
    pop esi
    VM_END              // VM结束标记
  }
}

/**
 * 将32位寄存器压入堆栈中
 */
void _declspec(naked) DPushReg32()  // *
{
  _asm
  {
    mov    eax, dword ptr [esi]  // 得到寄存器偏移
    add    esi, 4
    mov    eax, dword ptr [edi + eax]  // 得到寄存器的值
    push  eax              // 压入寄存器
    VM_END              // VM结束标记
  }
}

/**
 * 将32位直接数压入堆栈中
 */
void _declspec(naked) DPushImm32()  // *
{
  _asm
  {
    // 取得直接数
    mov    eax, dword ptr [esi]
    add    esi, 4
    push  eax
    VM_END              // VM结束标记
  }
}

/**
 * 将32位内存数压入堆栈中
 *
 * 格式:[rax + rcx * 8 + 0x11223344]
 */
void _declspec(naked) DPushMem32()    // *
{
  _asm
  {
    mov    edx, 0
    mov    ecx, 0
    mov    eax, dword ptr [esp]  // 第1个寄存器偏移
    test   eax, eax

    // edx = 第1个寄存器值
    cmovge  edx, dword ptr [edi + eax]  // 如果不是负数则赋值
    mov    eax, dword ptr [esp + 4]  // 第2个寄存器偏移
    test   eax, eax

    // ecx = 第2个寄存器值
    cmovge  ecx, dword ptr [edi + eax]
    imul  ecx, dword ptr [esp + 8]  // 第2个寄存器的乘积
    add    ecx, dword ptr [esp + 0x0C]    // 第三个为常量
    add    edx, ecx
    add    esp, 0x10        // 释放参数

    // 压入该数
    // 问题:这里压入的似乎不是内存数,而是该数的地址

    push  edx
    VM_END              // VM结束标记
  }
}

/**
 * 将堆栈中的数据弹回寄存器
 */
void _declspec(naked) DPopReg32()  // *
{
  _asm
  {
    mov    eax, dword ptr [esi]  // 得到reg偏移
    add    esi, 4
    pop    dword ptr [edi + eax]  // 弹回寄存器
    VM_END              // VM结束标记
  }
}

/**
 * 释放堆栈
 */
void _declspec(naked) DFree()  // *
{
  _asm
  {
    add esp, 4
    VM_END              // VM结束标记
  }
}

// ------------------------------------------------ comm.cpp ------------------------------------------------------------

// 此文件包含一些公共的函数
#include "stdafx.h"
#include "comm.h"
/**
 * 显示提示信息对话框
 *
 * content: 显示的提示信息
 * nType: 显示的提示图标的类型
 */
void MsgBox(char * content, UINT nType)    // *
{
  if (!content)
    return;
  MessageBoxA(NULL, content, VMPACKERTITLE, nType);
}

/**
 * 去除str字符串中的空格,并将其能守pstr返回
 *
 * 返回值: 成功返回TRUE,否则FALSE
 */
BOOL Trim(char * pstr, char * str)    // *
{
  int i, j = 0;

  // 检测参数是否有效
  if (!pstr || !str)
    return FALSE;

  // 将pstr复制至str并计算其长度
  strcpy(str, pstr);

  j = strlen(str);
  i = 0;
  // 遍历str中的字符
  for (i = 0; i < j; i ++)
  {
    // 如果i字符是换行或回车,则替换为0
    if ((str[i] == 0x0a) || (str[i] == 0x0d))
    {
      str[i] = 0;
    }
  }
  j = strlen(str);
  i = 0;
  // 统计str头部的空格数
  while (str[i] == ' ')
    i++;
  // 如果str中都是空格,则处理失败
  if (i >= j)
  {
    return FALSE;
  }
  // 去除头部的空格
  memmove(str, str + i, j - i + 1);
  j = strlen(str);
  i = 0;
  // 统计str尾部的空格数
  while (str[i] == ' ')
    i--;
  if (i)
    str[i] = '\0';
  if (i >= j)
  {
    return FALSE;
  }
  return TRUE;
}

/**
 * 将指定字符串strSource转为16进制数并返回
 *
 * strSource: 欲转为16进制数的数字字符串
 *
 * 返回值:成功返回转换后的结果,否则-1
 */
DWORD StringToHex(char * strSource)    // *

  DWORD nTemp = 0;
  char strTemp[64];

  // 如果字符串为空,则不需要转,直接返回
  if (strSource == NULL)
    return -1;

  // 将源数据复制到临时变量中
  strcpy(strTemp, strSource);

  // 16进制只能包含A、B、C、D、E、F字符,所以,如果包含除此以外其他字符则出错返回
  for (char cc = 'G', dd = 'g'; cc <= 'Z', dd <= 'z';  cc++, dd++)
  {
    if (strchr(strTemp, cc) != NULL || strchr(strTemp, dd) != NULL)
    {
      return -1;
    }
  }

  // 遍历strSource中所有的字符
  for (int i = 0; i<(int)::strlen(strSource); i++)
  {
    int nDecNum;
    // 根据每个字符进行处理
    switch (strSource[i])
    {
    case 'a':
    case 'A':
      nDecNum = 10;
      break;
    case 'b':
    case 'B':
      nDecNum = 11;
      break;
    case 'c':
    case 'C':
      nDecNum = 12;
      break;
    case 'd':
    case 'D':
      nDecNum = 13;
      break;
    case 'e':
    case 'E':
      nDecNum = 14;
      break;
    case 'f':
    case 'F':
      nDecNum = 15;
      break;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      nDecNum = strSource[i] - '0';
      break;
    default:
      return 0;
    }
    nTemp += (DWORD)nDecNum * (long double)pow((long double)16, (int)(strlen(strSource) - i - 1));
  }
  return nTemp;
}

/**
 * 按指定的某种格式输出字符串str
 */
void OutPutStr(char *str, ...)  // *
{
  // #if _DEBUG
  char strbuf[1024]; 

  // 处理可变参数
  va_list  vl;
  va_start(vl, str);
  vsprintf(strbuf, str, vl);
  va_end(vl);

  OutputDebugString(strbuf);
  // #endif
}

// ------------------------------------------------ main.cpp:------------------------------------------------------------

// 此文件主要是读取要保护的文件的PE信息,加载跟文件有关的MAP符号信息,在该PE文件的最后区块后加多一个
// 区块,此区块将用于保存虚拟机的各种信息,如虚拟机指令,跳转表等。接下来,对文件的代码进行反编译后,根
// 据生成的汇编指令进行虚拟机保护,最后生成受虚拟机保护的新PE文件

#include "stdafx.h"
#include "CCodeILFactory.h"
#include ".\pestructure.h"

/**
 * 程序入口
 */
int main()    // *
{
  // PE文件信息
  CPEStructure pestruct;
  CCodeILFactory codefactory;
  list<CodeNode *> CodeList;
  list<CodeNode *> CodeList1;

  // 打开文件,读取头部,并读取区块数据
  pestruct.OpenFileName("G:\\项目\\VMPacker\\ConvertCrackMe\\Release\\ConvertCrackMe.exe");

  // 读取map文件,解析其中的符号信息并存于pestruct.MapVector中
  // 注:MAP文件是一个文本文件,记录了程序的入口地址、基地址、符号及其对应的段、文件偏址等信息
  pestruct.LoadMap("G:\\项目\\VMPacker\\ConvertCrackMe\\Release\\ConvertCrackMe.map");
  getchar();

  // 为PE文件pestruct分配一个新的区块,并对该区块进行初始化,并生成某
  // 些虚拟机核心指令,并将指令代码复制到该区块的某个子块中
  // 注:具体此函数分新的区块结构怎样,可查看“PE新区块结构图.jpg”
  codefactory.Init(0x400000 + pestruct.GetNewSection());

  // 查找CrackMe()函数的符号信息
  MapStructrue * stu = pestruct.GetMap("CrackMe(struct HWND__ *)");
  // 找不到,则直接返回
  if (!stu)
    return 0;

  // 得到符号的基地址
  char * Base_Addr = pestruct.image_section[stu->Segment - 1] + stu->Offset;

  // 反汇编Base_Addr的代码,即函数CrackMe()的代码
  codefactory.DisasmFunction(&CodeList, Base_Addr, stu->VirtualAddress);

  // 
  list<CodeNode*>::iterator itr;
  
  itr = CodeList.begin();
  // 获取CodeList指令列表中起始指令
  CodeNode * code = *itr;
  itr = CodeList.end();
  itr--;
  // 获取CodeList指令列表中结束指令
  CodeNode * endcode = *itr;

  // 计算CodeList的总代码长度
  int asmlen = endcode->disasm.ip + endcode->disasm.codelen - code->disasm.ip;

  // 组织一段进入虚拟机的指令(即jmp VStartVM),存储于Base_Addr
  codefactory.VMFactory.CompileEnterStubCode(Base_Addr, code->disasm.ip, asmlen);

  // 将文件各头部与区块数据写回至PE文件中
  pestruct.UpdateHeadersSections(FALSE);

  char errtext[255] = {0};
  // 将CodeList指令列表编译为虚拟机字节码
  codefactory.BuildCode(Base_Addr, &CodeList, errtext);

  // 计算存放虚拟机各个部件的虚存空间长度
  int len = codefactory.m_JumpTable.m_addrlen + codefactory.m_CodeEngine.m_addrlen +
        codefactory.m_EnterStub.m_addrlen + codefactory.m_VMEnterStubCode.m_addrlen + 
        codefactory.m_VMCode.m_addrlen;
  // 分配足够的空间
  char * newdata = new char[len];
  char * p = newdata;

  // 将跳转表复制到p空间中
  memcpy(p, codefactory.m_JumpTable.m_BaseAddr, codefactory.m_JumpTable.m_addrlen);
  p += codefactory.m_JumpTable.m_addrlen;
  // 将虚拟机其他部分复制到p空间中
  memcpy(p, codefactory.m_CodeEngine.m_BaseAddr, codefactory.m_CodeEngine.m_addrlen);
  p += codefactory.m_CodeEngine.m_addrlen;
  memcpy(p, codefactory.m_EnterStub.m_BaseAddr, codefactory.m_EnterStub.m_addrlen);
  p += codefactory.m_EnterStub.m_addrlen;
  memcpy(p, codefactory.m_VMEnterStubCode.m_BaseAddr, codefactory.m_VMEnterStubCode.m_addrlen);
  p += codefactory.m_VMEnterStubCode.m_addrlen;
  memcpy(p, codefactory.m_VMCode.m_BaseAddr, codefactory.m_VMCode.m_addrlen);
  p += codefactory.m_VMCode.m_addrlen;

  // 为PE文件添加.bug的新区块,即虚拟机
  pestruct.AddSection(newdata,len,".bug");

  pestruct.UpdateHeaders(FALSE);
  pestruct.UpdateHeadersSections(TRUE);
  pestruct.UpdateHeadersSections(FALSE);
  // 创建一个新的可执行文件
  pestruct.MakePE("G:\\项目\\VMPacker\\ConvertCrackMe\\Release\\ConvertCrackMe.vm.exe", len);

  return 0;
}


// ----------------------------------------------------- PEStructure.cpp ----------------------------------------------------------

// 此文件中的代码用于对PE文件的各种操作,包括读取等

#include "StdAfx.h"
#include ".\pestructure.h"

#include <ImageHlp.h>
#pragma comment(lib, "ImageHlp")  // 链接到ImageHlp.lib

CPEStructure::CPEStructure(void)
{
  Init();
}

CPEStructure::~CPEStructure(void)
{
  Free();
}

/**
 * 打开指定的文件并读取文件头,区块数据
 *
 * FileName: 欲打开的文件路径名
 *
 * 返回值:FALSE表示失败,否则成功
 */
BOOL CPEStructure::OpenFileName(char * FileName)    // *
{
  // 已读取文件的字节数
  DWORD dwBytesRead = 0;

  // 释放分配的内存资源
  Free();

  // 打开FileName文件
  hFile = CreateFileA(FileName, GENERIC_READ,  FILE_SHARE_WRITE | FILE_SHARE_READ,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  // 打开失败,则显示提示信息并直接返回
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MsgBox("打开文件失败", MB_ICONERROR);
    return FALSE;
  }

  // 获取文件大小
  dwFsize = GetFileSize(hFile, 0);
  // 文件不存在内容,则关闭文件并提示错误信息
  if (dwFsize == 0)
  {
    CloseHandle(hFile);
    MsgBox("错误的文件长度", MB_ICONERROR);
    return FALSE;
  }

  // 打开成功,则将文件名复制至m_FileName中
  strcpy_s(m_FileName, 256, FileName);

  // 计算文件与壳的长度和,以便分配足够的内存空间
  dwOutPutSize = dwFsize + PackerCode_Size + ALIGN_CORRECTION;

  // 分配内存,并将其清0
  pMem = (char*)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, dwOutPutSize);

  // 内存分配失败,则关闭文件并提示错误信息
  if (pMem == NULL)
  {
    CloseHandle(hFile);
    MsgBox("申请内存出错", MB_ICONERROR);
    return FALSE;
  }

  // 将文件读取至pMem中,dwBytesRead表示真正读取的数据长度
  ReadFile(hFile, pMem, dwFsize, &dwBytesRead, NULL);

  // 读取完文件后,就可以关闭文件了
  CloseHandle(hFile);

  // 将PE文件的DOS头部复制到image_dos_header头部中
  CopyMemory(&image_dos_header, pMem, sizeof(IMAGE_DOS_HEADER));

  // 检测是否是一个有效的PE文件,不是则提示错误信息
  if (image_dos_header.e_magic != IMAGE_DOS_SIGNATURE)
  {
    MsgBox("不是一个有效的PE文件", MB_ICONERROR);
    return FALSE;
  }

  // 保存DOS头部长度
  ReservedHeaderRO = sizeof(IMAGE_DOS_HEADER);

  // 保存保留头部长度值
  // 注:e_lfanew是PE头部的偏移,所以,这里的保留头部指的是DOS-PE头部之间的部分
  ReservedHeaderSize = image_dos_header.e_lfanew - sizeof(IMAGE_DOS_HEADER);
  reservedheader = new char[ReservedHeaderSize];

  // 复制PE头部
  CopyMemory(&image_nt_headers, pMem + image_dos_header.e_lfanew, sizeof(IMAGE_NT_HEADERS));

  // PE文件区块表在文件中的偏移
  dwRO_first_section = image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS);

  // 读取文件的头部信息与区块数据
  UpdateHeadersSections(TRUE);
  
  return TRUE;
}



//----------------------------------------------------------------
void CPEStructure::UpdateHeaders(BOOL bSaveAndValidate)
{
  DWORD SectionNum = image_nt_headers.FileHeader.NumberOfSections;

  if( bSaveAndValidate )//TRUE = 保存数据
  {
    CopyMemory(&image_dos_header,pMem,sizeof(IMAGE_DOS_HEADER));
    ReservedHeaderSize=image_dos_header.e_lfanew - sizeof(IMAGE_DOS_HEADER);
    if( ( ReservedHeaderSize & 0x80000000 ) == 0x00000000)
    {
      CopyMemory(reservedheader, pMem+ReservedHeaderRO, ReservedHeaderSize);
    }
    CopyMemory(&image_nt_headers,
      pMem+image_dos_header.e_lfanew,
      sizeof(IMAGE_NT_HEADERS));
    dwRO_first_section = image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS);
    CopyMemory(&image_section_header, pMem+dwRO_first_section, SectionNum * sizeof(IMAGE_SECTION_HEADER));
  }
  else        //FALSE = 恢复数据
  {
    CopyMemory(pMem, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
    ReservedHeaderSize=image_dos_header.e_lfanew - sizeof(IMAGE_DOS_HEADER);
    if( (ReservedHeaderSize & 0x80000000) == 0x00000000)
    {
      CopyMemory(pMem+ReservedHeaderRO,reservedheader,ReservedHeaderSize);
    }
    CopyMemory(pMem+image_dos_header.e_lfanew,
      &image_nt_headers,
      sizeof(IMAGE_NT_HEADERS));
    dwRO_first_section=image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS);
    CopyMemory(pMem+dwRO_first_section, &image_section_header, SectionNum * sizeof(IMAGE_SECTION_HEADER));
  }
}

/**
 * 更新PE文件的头部与区块数据
 *
 * bSaveAndValidate: true表示读取PE文件中的各头部与区块数据
 *           false表示是将文件各头部与区块数据写回至PE文件中
 */
void CPEStructure::UpdateHeadersSections(BOOL bSaveAndValidate)    // *
{
  DWORD i = 0;
  // 文件区块数
  DWORD SectionNum = image_nt_headers.FileHeader.NumberOfSections;

  // 读取文件头部与区块数据
  if (bSaveAndValidate)
  {
    // 复制文件DOS头部
    CopyMemory(&image_dos_header, pMem, sizeof(IMAGE_DOS_HEADER));

    // 复制文件保留头部
    ReservedHeaderSize = image_dos_header.e_lfanew - sizeof(IMAGE_DOS_HEADER);
    // 0x80000000: 10000000000000000000000000000000b,即32位最高位置1
    if ((ReservedHeaderSize & 0x80000000) == 0x00000000)
    {
      CopyMemory(reservedheader, pMem + ReservedHeaderRO, ReservedHeaderSize);
    }
    // 复制文件PE头部
    CopyMemory(&image_nt_headers, pMem + image_dos_header.e_lfanew,  sizeof(IMAGE_NT_HEADERS));

    // 区块表在文件中的偏移
    dwRO_first_section = image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS);

    // 复制区块表
    CopyMemory(&image_section_header, pMem + dwRO_first_section, SectionNum * sizeof(IMAGE_SECTION_HEADER));
    // 遍历所有的区块
    for (i = 0; i < SectionNum; i++)
    {
      // 为每个区块分配内存
      image_section[i] = (char*)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
        PEAlign(image_section_header[i].SizeOfRawData, image_nt_headers.OptionalHeader.FileAlignment));

      // 复制该区块数据
      CopyMemory(image_section[i], pMem + image_section_header[i].PointerToRawData,
        image_section_header[i].SizeOfRawData);
    }
    SectionAssigned = TRUE;    // 已经为区块分配空间
  }
  // 将数据写回文件头部与区块数据
  else
  {
    // 写回DOS头部
    CopyMemory(pMem, &image_dos_header, sizeof(IMAGE_DOS_HEADER));

    ReservedHeaderSize = image_dos_header.e_lfanew - sizeof(IMAGE_DOS_HEADER);
    if ((ReservedHeaderSize & 0x80000000 ) == 0x00000000)
    {
      CopyMemory(pMem + ReservedHeaderRO, reservedheader, ReservedHeaderSize);
    }
    CopyMemory(pMem + image_dos_header.e_lfanew, &image_nt_headers,  sizeof(IMAGE_NT_HEADERS));
    dwRO_first_section = image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS);
    CopyMemory(pMem + dwRO_first_section, &image_section_header, SectionNum * sizeof(IMAGE_SECTION_HEADER));

    for (i = 0; i < SectionNum; i++)
    {
      CopyMemory(pMem + image_section_header[i].PointerToRawData,
        image_section[i], image_section_header[i].SizeOfRawData);
    }
  }
}

/**
 * 根据dwAlignTo对齐值对dwTarNum进行对齐操作
 *
 * dwTarNum: 欲进行对齐的值
 * dwAlignTo: 进行对齐的参照值
 */
DWORD CPEStructure::PEAlign(DWORD dwTarNum, DWORD dwAlignTo)    // *
{  
  DWORD dwtemp;
  dwtemp = dwTarNum / dwAlignTo;
  if ((dwTarNum % dwAlignTo) != 0)
  {
    dwtemp++;
  }
  dwtemp = dwtemp * dwAlignTo;
  return(dwtemp);
}


/**
 * 获取strAddress中的段与文件偏移信息
 *
 * StrAddress:如0001:00000003
 * Segment: 用于返回段信息,即0001
 * FileOffset: 用于返回文件偏移量,即00000003
 *
 * 返回值:成功返回TRUE,否则FALSE
 */
BOOL CPEStructure::GetFileAddr(char * StrAddress, int * Segment, int * FileOffset)  // *
{
  // 检测参数是否有效
  if (!StrAddress || !Segment || !FileOffset)
    return FALSE;
  char StrSegment[4 + 1] = {0};
  char StrOffset[8 + 1] = {0};
  // 解析StrAddress
  memcpy(StrSegment, StrAddress, 4);
  memcpy(StrOffset, StrAddress + 5, 8);
  // 获取段与文件偏移信息
  *Segment = StringToHex(StrSegment);
  *FileOffset = StringToHex(StrOffset);
  return TRUE;
}

/**
 * 读取指定pmapfilename文件,解析其中的符号信息,将每个符号的信息存放在MapVector中
 *
 * pmapfilename: MAP文件的路径名
 *
 * 注:MAP文件是一个文本文件,记录了程序的入口地址、基地址、符号及其对应的段、偏址等信息
 */
void CPEStructure::LoadMap(char * pmapfilename)    // *
{
  if (!pmapfilename)
    return;
  FILE * mapfile = NULL;

  // 打开MAP文件
  mapfile = fopen(pmapfilename, "r+");
  if (!mapfile)
    return;

  // 释放MapVector所有符号信息占用的资源
  FreeMapVector();

  char linestr[512] = {0};  // 行数据
  int  readcnt = 0;    // 已读取的行数
  BOOL bBegin = FALSE;    // 开始

  // 循环读取MAP文件,直至文件结束
  while (!feof(mapfile))
  {
    // 清0行数据
    memset(linestr, 0, 512);
    // 读取MAP文件的每一行数据,如果读取不到数据,则结束
    if (fgets(linestr, 512, mapfile) == NULL)
    {
      break;
    }
    readcnt++;  // 累计读取的行数

    // 如果该行是模块入口地址数据,则跳过
    if (strstr(linestr, "entry point at") != NULL)
    {
      continue;
    }
    // 如果该行表示行号数据,则跳过
    if (strstr(linestr, "Line numbers for") != NULL)
    {
      break;
    }

    // 符号信息包括符号在节内的偏移地址、加载地址及符号出处等,其格式如下:
    // Address      Publics by Value      Rva+Base  Lib:Object
    // 0000:00000003  ___safe_se_handler_count  00000003  <absolute>

    // 或者搜索静态符号

    if ((strstr(linestr, "Publics by Value") != NULL && strstr(linestr, "Rva+Base") != NULL) ||
      (strstr(linestr, "Static symbols") != NULL ))
    {
      // 如果搜索到,则标注为开始
      bBegin = TRUE;
      continue;
    }

    char StrLine[512] = {0};

    // 将linestr去掉空格后放到StrLine中,如果该行是空行,则跳过
    if (!Trim(linestr, StrLine))
    {
      continue;
    }

    // 开始分析StrLine符号行数据信息
    if (bBegin)
    {
      // 这里的地址指的是文件中的地址
      char StrAddress[16] = {0};  // 段: 文件偏移
      char StrSymbol[512] = {0};  // 符号名
      char StrVirtualAddr[16] = {0};  // 虚拟地址
      char StrLibObject[512] = {0};  // 输入文件名
      int Segment = 0;
      int FileOffset = 0;

      // 复制符号地址,如:0001:00000003
      memcpy(StrAddress, StrLine, 13);
      int nstrlen = strlen(StrLine);

      memmove(StrLine, StrLine + 13, nstrlen - 13 + 1);  // 跳过段偏移

      // 获取符号所在的段与文件偏移
      GetFileAddr(StrAddress, &Segment, &FileOffset);

      // 段是不能为0的
      if (Segment == 0)
        continue;

      // 将剩余内容去掉空格
      Trim(StrLine, StrLine);

      // 复制符号名
      strcpy_s(StrSymbol, 512, StrLine);

      // 符号名结束
      *strchr(StrSymbol, ' ') = '\0';
      // 计算符号名的长度
      int nlen = strlen(StrSymbol);

      // 计算剩余部分的长度
      nstrlen = strlen(StrLine);
      memmove(StrLine, StrLine + nlen, strlen(StrLine) - nlen + 1);  // 跳过符号名

      // 去除空格
      Trim(StrLine, StrLine);

      // 获取符号的虚拟地址
      memcpy(StrVirtualAddr, StrLine, strchr(StrLine, ' ') - StrLine);

      nstrlen = strlen(StrLine);
      memmove(StrLine, StrLine + 8, nstrlen - 8 + 1);    // 跳过VirtualAddress

      nstrlen = strlen(StrLine);
      memmove(StrLine, StrLine + 5, nstrlen - 5 + 1);  // 跳过f i ...
      // 输入文件名
      strcpy(StrLibObject, StrLine);

      // 分配符号信息结构,将获取的符号信息保存起来
      MapStructrue * mapstruct = new MapStructrue;
      mapstruct->Segment = Segment;        // 段
      mapstruct->Offset = FileOffset;        // 偏移
      strcpy(mapstruct->SymbleName, StrSymbol);    // 符号名
      mapstruct->VirtualAddress = StringToHex(StrVirtualAddr);  // 线性地址
      strcpy(mapstruct->LibObject, StrLibObject);    // 输入文件名

      // 添加文件的符号信息
      MapVector.push_back(mapstruct);
    }
  }

  // 关闭文件
  fclose(mapfile);
  mapfile = NULL;

  // 遍历MapVector中的所有符号信息
  for (vector<MapStructrue*>::iterator itr = MapVector.begin(); itr != MapVector.end(); itr++)
  {
    MapStructrue * stu = * itr;
    // 获取符号段、文件偏移信息
    int a = stu->Segment;
    int b = stu->Offset;
    int nlen = strlen("__ehhandler$");
    // 如果符号名存在__e...,则将其截掉
    if (strstr(stu->SymbleName, "__ehhandler$"))
      memmove(stu->SymbleName, stu->SymbleName + nlen, strlen(stu->SymbleName) - nlen + 1);

    // 将修饰后名称转换成函数签名,失败则直接跳出循环
    // 注: 由于不同的编译器采用不同的名字修饰方法,必然会导致由不同编译器编译产生的目标
    //    文件无法正常相互链接,这是导致不同编译器之间不能互操作的主要原因之一
    if (!UnDecorateSymbolName(stu->SymbleName, stu->SymbleName, 512, 0xFFF))  // no UNDNAME_COMPLETE
    {
      // UnDecorateSymbolName failed
      DWORD error = GetLastError();
      char strerr[255] = {0};
      sprintf(strerr, "解析符号名时出现错误 %d\n", error);
      MsgBox(strerr, MB_ICONERROR);
      break;
    }

    // 输出符号信息,主要为了调试
    OutPutStr("%s %08X %s\n", stu->SymbleName, stu->VirtualAddress, stu->LibObject);
  }
}

/**
 * 根据函数名funcname获取相应的符号信息
 *
 * funcname:欲获取符号信息的函数(符号)名
 *
 * 返回值:找到相应符号信息则返回,否则返回NULL
 */
MapStructrue* CPEStructure::GetMap(char* funcname)  // *
{
  // 遍历文件所有符号信息
  for (vector<MapStructrue*>::iterator itr = MapVector.begin(); itr != MapVector.end(); itr++)
  {
    MapStructrue * stu = * itr;
    // 检测是否是欲查找符号信息
    if (_stricmp(stu->SymbleName, funcname) == 0)
      return stu;
  }
  return NULL;
}

/**
 * 为文件新区块分配一个虚拟区间,并返回其虚拟区间起始地址
 */
DWORD CPEStructure::GetNewSection()    // *
{
  // 获取PE文件的区块数
  DWORD SectionNum = image_nt_headers.FileHeader.NumberOfSections;
  return PEAlign(image_section_header[SectionNum - 1].VirtualAddress + image_section_header[SectionNum - 1].Misc.VirtualSize,
      image_nt_headers.OptionalHeader.SectionAlignment);
}






//----------------------------------------------------------------
// return values:
// 0 - no room for a new section
// 1 - file already encrypted
// else: returns a pointer to the IMAGE_SECTION_HEADER struct of the new section

/**
 * 在PE文件中添加新的区块
 *
 * Base:新区块的数据基址
 * len:新区块的数据长度
 * SectionName:新区块的名称
 *
 * 返回值:成功返回新区块的结构信息,否则NULL
 */
PIMAGE_SECTION_HEADER CPEStructure::AddSection(char * Base, int len, char * SectionName)  // *
{
  DWORD newSectionOffset;
  // 获取PE文件区块数
  DWORD SectionNum = image_nt_headers.FileHeader.NumberOfSections;
  // 计算新区块在区块表中的偏移
  newSectionOffset = dwRO_first_section + image_nt_headers.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);

  // 检测是否有足够空间在放置新区块
  if (image_nt_headers.OptionalHeader.SizeOfHeaders < (newSectionOffset + sizeof(IMAGE_SECTION_HEADER)))
  {
    return NULL;
  }

  // 创建一个新的区块

  // 遍历前面所有区块,设置每个区块为可写状态
  for (DWORD i = 0; i < (SectionNum - 1); i++)
  {
    image_section_header[i].Characteristics |= IMAGE_SCN_MEM_WRITE;
  }

  // 创建一个新的区块,将最后区块表中的记录复制作为新区块的记录
  CopyMemory(&image_section_header[SectionNum], &image_section_header[SectionNum - 1], sizeof(IMAGE_SECTION_HEADER));

  // 设置新区块的虚拟地址
  image_section_header[SectionNum].VirtualAddress = PEAlign(image_section_header[SectionNum - 1].VirtualAddress +
    image_section_header[SectionNum - 1].Misc.VirtualSize, image_nt_headers.OptionalHeader.SectionAlignment);

  // 设置新区块的长度
  image_section_header[SectionNum].Misc.VirtualSize = len;
  image_section_header[SectionNum].SizeOfRawData = PEAlign(image_section_header[SectionNum].Misc.VirtualSize,
                           image_nt_headers.OptionalHeader.FileAlignment);  // bughoho
  dwOutPutSize += image_section_header[SectionNum].SizeOfRawData;
  
  // 计算区块名长度
  int l = (int)strlen(SectionName);
  FillMemory(image_section_header[SectionNum].Name, 8, 0x00);
  // 设置区块名
  CopyMemory(image_section_header[SectionNum].Name, SectionName, l);
  
  // 设置新区块的特性
  image_section_header[SectionNum].Characteristics = IMAGE_SCN_MEM_EXECUTE |
                          IMAGE_SCN_MEM_WRITE | 
                          IMAGE_SCN_MEM_READ |
                          IMAGE_SCN_MEM_EXECUTE |
                          IMAGE_SCN_CNT_UNINITIALIZED_DATA |
                          IMAGE_SCN_CNT_INITIALIZED_DATA |
                          IMAGE_SCN_CNT_CODE;

  // RawOffset
  image_section_header[SectionNum].PointerToRawData = PEAlign(image_section_header[SectionNum-1].PointerToRawData
                            + image_section_header[SectionNum - 1].SizeOfRawData,
                            image_nt_headers.OptionalHeader.FileAlignment);
  // 递增PE文件区块数
  image_nt_headers.FileHeader.NumberOfSections++;
  
  // 重新设置PE文件映像长度
  image_nt_headers.OptionalHeader.SizeOfImage = image_section_header[SectionNum].VirtualAddress +
                          image_section_header[SectionNum].Misc.VirtualSize;

  // 复制新区块的数据
  memcpy(pMem + image_section_header[SectionNum].PointerToRawData, Base, len);
  return ((PIMAGE_SECTION_HEADER)&image_section_header[SectionNum]);
}

/**
 * PE文件初始化操作
 */
void CPEStructure::Init()  // *
{
  // 未分配区块内存
  SectionAssigned       = FALSE;

  // 未分配PE保留头部
  reservedheader        = NULL;
  // 保留头部长度为0
  ReservedHeaderSize    = 0;
  // DOS头部长度为0
  ReservedHeaderRO      = 0;
  hFile                 = 0;
  dwFsize               = 0;
  dwOutPutSize          = 0;

  // 清0文件名
  memset(m_FileName, 0, 255);
  memset(m_MapFileName, 0, 255);
}

/**
 * 释放MapVector中存储的符号信息占用的资源
 *
 * 注:MapVector存放PE文件的符号信息
 */
void CPEStructure::FreeMapVector()  // *
{
  // 遍历PE文件的符号信息
  for (vector<MapStructrue*>::iterator itr = MapVector.begin(); itr != MapVector.end(); itr++)
  {
    MapStructrue * stu = * itr;
    if (stu)
    {
      delete stu;
    }
  }
  MapVector.clear();
}

/**
 * 释放为PE文件分配的资源
 */
void CPEStructure::Free()  // *
{
  // 如果PE文件的保留头部内存占用着,则释放之
  if (reservedheader)
  {
    delete[] reservedheader;
    reservedheader = NULL;
  }
  // 如果未为PE文件分配区块内存,则不需要释放
  if (!SectionAssigned)
    return;

  // 获取PE文件区块数
  DWORD SectionNum = image_nt_headers.FileHeader.NumberOfSections;

  // 遍历所有的区块,释放为其分配的内存
  for (DWORD i = 0 ;i < SectionNum; i++)
  {
    GlobalFree(image_section[i]);
    image_section[i] = NULL;
  }
  Init();
}





/**
 * 根据指定的文件名filename,创建一个新的文件,并写入虚拟机的数据
 *
 * filename:欲创建的新文件名
 * len:文件中数据的长度
 */
void CPEStructure::MakePE(char * filename, int len)    // *
{
  HANDLE handle;
  DWORD Num;
  // 根据文件名创建一个新文件
  handle= ::CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

  // 文件打开失败,则出错返回
  if (INVALID_HANDLE_VALUE == handle)
  {
    MessageBox(0, "打开文件失败!", "错误", MB_OK);
    return;
  }
  ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
  // 将虚拟机写回文件
  ::WriteFile(handle, pMem, dwOutPutSize, &Num, NULL);
  ::CloseHandle(handle);
}

// ------------------------------------------------------- 相关数据结构 --------------------------------------------------------

// ------------------------------------------------------- PEStructure.h ------------------------------------------------------

// 文件中包含PE文件相关的结构信息


// #pragma once:用来防止某个头文件被多次include
#pragma once
#include "comm.h"

#include <math.h>

#include <vector>
using namespace std;

// PE文件最大区块数
#define MAX_SECTION_NUM 20

const DWORD PackerCode_Size = 0;  // 壳代码的长度
const DWORD ALIGN_CORRECTION = 0;  // 文件对齐值

/**
 * 代表PE文件的信息、数据及其相应MAP文件的信息
 *
 * 注:MAP文件是一个文本文件,记录了程序的入口地址、基地址、
 *     符号所在段、偏移信息的文本文件
 */
class CPEStructure
{
public:
  CPEStructure(void);
  ~CPEStructure(void);
private:

  // PE文件保留头部指的是DOS头部与PE头部之间的部分

  DWORD   ReservedHeaderSize;    // PE文件保留头部长度
  DWORD   ReservedHeaderRO;    // PE文件DOS头部长度

  // 表明是否已为PE文件的区块分配了存储数据的内存空间
  BOOL    SectionAssigned;
  HANDLE  hFile;          // PE文件句柄
  DWORD   dwFsize;        // PE文件长度
  DWORD   dwOutPutSize;      // 对PE文件处理后的输出长度,为PE文件长度 + 壳长度

  // 指向PE文件的缓存区,该缓存区用于存文PE文件处理后的结果
  char    * pMem;
private:
  char    m_FileName[256];    // PE文件名
  char    m_MapFileName[256];    // PE文件对应的MAP文件名

public:
  // 存储PE文件的符号信息,其每个元素是一个MapStructure,代表PE文件的符号信息
  vector<MapStructrue*> MapVector;

  // 加载并解析MAP文件的符号信息
  void LoadMap(char * pmapfilename);

  // 用于根据StrAddress符号地址得到符号的段号与文件偏移
  BOOL GetFileAddr(char * StrAddress, int * Segment, int * FileOffset);
public:
  MapStructrue * GetMap(char * funcname);
public:
  // PE文件区块表在文件中的偏移
  DWORD          dwRO_first_section;

  // DOS头部
  IMAGE_DOS_HEADER    image_dos_header;

  // 指向PE文件的保留头部信息
  char          * reservedheader;

  // PE文件头部
  IMAGE_NT_HEADERS    image_nt_headers;

  // 区块表,最多20项
  IMAGE_SECTION_HEADER  image_section_header[MAX_SECTION_NUM];

  // 为区块分配的内存,每个元素指向区块内存
  char          * image_section[MAX_SECTION_NUM];
  BOOL OpenFileName(char * FileName);
  void UpdateHeaders(BOOL bSaveAndValidate);             // 更新PE头
  void UpdateHeadersSections(BOOL bSaveAndValidate);         // 更新PE节
  DWORD PEAlign(DWORD dwTarNum, DWORD dwAlignTo);           // PE节对齐
  PIMAGE_SECTION_HEADER AddSection(char * Base, int len, char * SectionName);  // 添加节
  DWORD GetNewSection();                       // 获得新节的VirtualAddress地址
  void Init();
  void Free();
  void FreeMapVector();
  void MakePE(char * filename, int len);    // 制造一个PE
};



// 还有大量分析,等续

  • 标 题:再发
  • 作 者:shecx
  • 时 间:2010-07-13 09:42:20

// --------------------------------------------- CVirtualMemory.cpp -----------------------------

// 此文件代表了某段虚存空间,我们需要在PE文件中,为存放虚拟机的指令分配虚存空间

// VMPackLib

#include "stdafx.h"
#include "CVirtualMemory.h"

CVirtualMemory::CVirtualMemory()
{
    m_BaseAddr = NULL;
    m_addrlen  = 0;
    m_VirtualBaseAddress = 0;
    CurrentAddress = NULL;
}

CVirtualMemory::CVirtualMemory(DWORD VirtualAddress,
                 int len)
{
    m_BaseAddr = NULL;
    m_addrlen  = 0;
    m_VirtualBaseAddress = 0;
    CurrentAddress = NULL;
    CreateVirtualMemory(VirtualAddress,len);
}

CVirtualMemory::~CVirtualMemory()
{
  if( m_BaseAddr ) delete[] m_BaseAddr;m_BaseAddr = NULL;
}

/**
 * 根据指定虚拟地址分配一块内存空间
 *
 * VirtualAddress:欲分配内存的虚拟地址
 * len:分配内存的长度
 */
void CVirtualMemory::CreateVirtualMemory(DWORD VirtualAddress,
                     int len)  // *
{
  // 内存空间存在,则先释放之
  if (m_BaseAddr)
    delete[] m_BaseAddr;
  m_BaseAddr = NULL;
  // 分配内存空间并清0
  m_BaseAddr = new char[len];
  memset(m_BaseAddr, 0, len);
  m_addrlen  = len;
  m_VirtualBaseAddress = VirtualAddress;
  CurrentAddress = m_BaseAddr;
}

/**
 * 根据内存地址addr计算出相应的虚拟地址并返回
 *
 * addr:欲换算的内存地址
 *
 * 返回值:换算后的虚拟地址,失败则为-1
 */
DWORD CVirtualMemory::GetVirtualAddress(char * addr)  // *
{
  // 检测地址合法性
  if (addr < m_BaseAddr)
    return -1;
  return m_VirtualBaseAddress + (DWORD)(addr - m_BaseAddr);
}

// 根据虚拟地址计算出内存空间地址
char* CVirtualMemory::GetAddrOfVirtualAddr(DWORD VirtualAddress)
{
  return m_BaseAddr + VirtualAddress - m_VirtualBaseAddress;
}

/**
 * 将指定数据src复制到虚拟内存区间
 *
 * src:欲复制的数据
 * len:欲复制数据的长度
 *
 * 返回值:成功返回复制数据的起始虚址,否则返回0
 */
DWORD CVirtualMemory::WriteData(char * src, int len)  // *
{
  // 检测参数是否OK
  if (!src || !len)
  {
    MessageBox(0, "内部错误", "err", MB_OK);
    return 0;
  }
  // 如果虚存空间不足,则出错返回
  if (m_addrlen - (CurrentAddress - m_BaseAddr) < len)
  {
    MessageBox(0, "[memory to small]内部错误", "err", MB_OK);
    return 0;
  }
  DWORD StartVirtualAddress = 0;

  // 将src复制到当前地址位置
  memcpy(CurrentAddress, src, len);

  // 取得当前地址对应的虚址,即复制数据所在的虚址
  StartVirtualAddress = m_VirtualBaseAddress + (DWORD)(CurrentAddress - m_BaseAddr);
  CurrentAddress += len;
  return StartVirtualAddress;
}


//复制数据到指令虚拟内存地址
DWORD CVirtualMemory::WriteData(DWORD VirtualCode,char* src,int len)
{
  if( !src || !len )
    return 0;
  if( m_BaseAddr + m_addrlen - CurrentAddress < len )//空间不够
    return 0;
  DWORD StartVirtualAddress = 0;
  memcpy(GetAddrOfVirtualAddr(VirtualCode),src,len);
  return VirtualCode;
}
// 清空内存
void CVirtualMemory::ClearMemory()
{
  memset(m_BaseAddr,0,m_addrlen);
  CurrentAddress = m_BaseAddr;
}
// 获得还未使用的空间
char* CVirtualMemory::GetCurrentAddr(void)
{
  return CurrentAddress;
}

/**
 * 获取虚拟内存区间当前可用的虚拟地址
 */
DWORD CVirtualMemory::GetCurrentVirtualAddress(void)  // *
{
  return GetVirtualAddress(CurrentAddress);
}



// ----------------------------------------------- CVirtualMemory.h ------------------------------

// VMPackLib

#ifndef __CVIRTUALMEMORY__
#define __CVIRTUALMEMORY__


/**
 * 代表一段虚拟内存
 */
class CVirtualMemory
{
public:
  // 虚拟地址所对应的内存空间基址
  char * m_BaseAddr;
  // 内存空间的长度
  int m_addrlen;
  // 此段内存的虚拟地址,将对应m_BaseAddr所指向的内存空间
  DWORD m_VirtualBaseAddress;

private:
  // 此内存空间中当前可以使用的地址
  // 初始化为m_BaseAddr
  char * CurrentAddress;
public:

  CVirtualMemory();
  CVirtualMemory(DWORD VirtualAddress,
         int len);
  ~CVirtualMemory();

  // 创建一片对应虚拟地址的内存空间
  void  CreateVirtualMemory(DWORD VirtualAddress,
              int len);

  // 根据内存地址换算出虚拟线性地址
  DWORD  GetVirtualAddress(char * addr);

  // 根据虚拟地址计算出内存空间地址
  char *  GetAddrOfVirtualAddr(DWORD VirtualAddress);

  // 复制数据到虚拟内存, 返回数据的起始虚拟地址.
  DWORD  WriteData(char* src, int len);
  // 复制数据到指令虚拟内存地址
  DWORD  WriteData(DWORD VirtualCode, char * src, int len);
  // 清空内存
  void  ClearMemory();

  // 获得当前可用空间
  char *  GetCurrentAddr(void);
  // 获得当前可用的虚拟地质
  DWORD  GetCurrentVirtualAddress(void);
};

#endif // __CVIRTUALMEMORY__


// ----------------------------------- InterpretHandler.cpp ------------------------------------

// 每个普通的汇编指令,一般都有一个虚拟机指令对应着,而每个虚拟机指令,实际上又对应着某段普通的汇编代码,换句话说,一个普通的汇编指令就由另一段汇编代码来模拟完成了
// 此文件就是用于为某个汇编指令相应的虚拟机指令生成相应的汇编代码
// 对于普通汇编指令与虚拟机指令的对应关系由一张表定义着(即vmtable,见vmserv.cpp中定义)

#include "StdAfx.h"
#include "InterpretHandler.h"
#include "asm/disasm.h"

CInterpretHandler::CInterpretHandler(void)
{
}

CInterpretHandler::~CInterpretHandler(void)
{
}

/**
 * 获取指定寄存器RegType在VMContext虚拟机上下文中的偏移
 *
 *    针对不同的寄存器,偏移值不同,如
 *      32位寄存器:EAX
 *      16位寄存器:AX
 *      低8位寄存器:AL
 *    以上情况直接取得偏移
 *      高8位寄存器:AH
 *    则需要对偏移进行 + 1,以便得到高8位值
 *
 * RegType:指明要取得偏移的寄存器,取值可见枚举RegType
 */
int CInterpretHandler::GetRegisterOffset(int RegType)  // *
{
  if (RegType < 0)
    return RegType;
  int offset = m_RegisterIdx[RegType] * 4;

  // 判断是否取寄存器高8位
  if (RegType >= 20)
    offset++;
  return offset;
}

/**
 * 初始化操作
 */
BOOL CInterpretHandler::Init()    // *
{
  // 打乱寄存器偏移的索引
  RandListIdx(m_RegisterIdx, REGCOUNT);

  return TRUE;
}

/**
 * 为指定虚拟机指令table设置所需操作数
 *
 * table: 包含某个虚拟机指令与汇编指令相关信息
 * asmtext:保存生成的汇编代码
 * len: asmtext缓存的长度
 */
void CInterpretHandler::SetArg(VMTable * table, char * asmtext, int len)  // *
{
  // 操作数不超过3个
  for (int i = 0; i < 3; i++)
  {
    // 欲处理操作数超限制,则跳出
    // OperandNum: 操作数个数
    if (i >= table->OperandNum)
      break;
    
    // 以下将生成:mov EAX, [esp + (0 | 4 | 8)]
    // 所以相当于将堆栈中压入的操作数复制到EAX寄存器中以作为虚拟机的操作数使用
    sprintf_s(asmtext, len, "%smov %s,[esp+%02x]\n", asmtext, ArgReg[i], i * 4);
  }
}

/**
 * 将指定虚拟机指令table的操作数恢复到堆栈中(操作数一般都被压入栈)
 *
 * table: 包含某个虚拟机指令与汇编指令相关信息
 * asmtext:保存生成的汇编代码
 * len: asmtext缓存的长度
 */
void CInterpretHandler::RestoreArg(VMTable * table, char * asmtext, int len)  // *
{
  for (int i = 0; i < 3; i++)
  {
    if (i >= table->OperandNum)
      break;
    sprintf_s(asmtext, len, "%smov [esp+%02x],%s\n", asmtext, i * 4, ArgReg[i]);
  }
}

/**
 * 将VMContext上下文保存的EFlag寄存器值恢复到EFlag寄存器中
 *
 * asmtext:保存恢复所需的汇编代码
 * len: asmtext缓存的长度
 */
void CInterpretHandler::RestoreFlag(char * asmtext, int len)  // *
{
  sprintf_s(asmtext, len, "%sPush [edi+0x%02x]\nPopfd\n", asmtext, GetRegisterOffset(RT_EFlag));
}

/**
 * 将EFlag寄存器值保存到VMContext上下文的EFlag中
 *
 * asmtext:保存恢复所需的汇编代码
 * len: asmtext缓存的长度
 */
void CInterpretHandler::SaveFlag(char * asmtext, int len)  // *
{
  sprintf_s(asmtext, len, "%sPushfd\nPop [edi+0x%02x]\n", asmtext, GetRegisterOffset(RT_EFlag));
}

/**
 * 根据指定虚拟机指令-汇编指令信息table为虚拟机指令生成相应的汇编代码
 *
 * table: 包含某个虚拟机指令与汇编指令相关信息
 * asmtext:保存生成的汇编代码
 * len: asmtext缓存的长度
 *
 * 返回值:成功返回TRUE,否则FALSE
 */
BOOL CInterpretHandler::InterpretASMStr(VMTable * table, char * asmtext, int len)  // *
{
  // 检测参数的合法性
  if (strcmp(table->VMInstrName, "") == 0 || asmtext == NULL)
    return FALSE;

  // 清0 asmtext缓存
  memset(asmtext, 0, len);

  // 为虚拟机指令设置操作数
  SetArg(table, asmtext, len);

  // 某些汇编指令在执行时,需要用到某些寄存器的值来控制其执行,那么,对于相应
  // 的虚拟指令,当然也是需要,所以,现在需要恢复所需寄存器的值了

  // 为虚拟机指令设置所需的寄存器
  for (int i = 0; i < 4; i++)
  {
    // 判断虚拟机指令是否需要用 i 寄存器
    if (table->NeedReg[i] != NONE && table->NeedReg[i] < 14)
    {
      // edi -> 指向VMContext

      // 由于虚拟机将所有寄存器的值保存在VMContext中,所以,
      // 现在将保存的值复制到所需寄存器中以便指令使用
      sprintf_s(asmtext, len, "%smov %s,[edi+0x%02x]\n", asmtext,
        vregname[2][table->NeedReg[i]], GetRegisterOffset(table->NeedReg[i]));
    }
  }

  BOOL After = FALSE;

  // 为vBegin指令生成汇编代码
  if (_stricmp(table->strInstruction, "vBegin") == 0)
  {
    After = InterpretvBegin(table, asmtext, len);
  }
  // 为vtoReal指令生成汇编代码,vtoReal用于跳转至真实指令
  else if (_stricmp(table->strInstruction, "vtoReal") == 0)
  {
    After = InterpretvtoReal(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "push") == 0)
  {
    After = InterpretPush(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "pop") == 0)
  {
    After = InterpretPop(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "pushfd") == 0)
  {
    After = InterpretPushfd(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "popfd") == 0)
  {
    After = InterpretPopfd(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "pushad") == 0)
  {
    After = InterpretPushad(table, asmtext, len);
  }
  else if(_stricmp(table->strInstruction, "popad") == 0)
  {
    After = InterpretPopad(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "enter") == 0)
  {
    After = InterpretEnter(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "leave") == 0)
  {
    After = InterpretLeave(table, asmtext, len);
  }
  // 为jcxz指令生成相应的汇编代码
  // 注:jcxz指令,该指令实现当寄存器CX的值等于0时转移到标号,否则顺序执行
  else if (_stricmp(table->strInstruction, "jcxz") == 0)
  {
    // 先将VMContext上下文的EFlag寄存器恢复到EFlag标志寄存器
    RestoreFlag(asmtext, len);

    After = InterpretJCXZ(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "jmp") == 0)
  {
    After = InterpretJMP(table, asmtext, len);
  }
  // 不是jcxz和jmp, 则说明是条件跳转指令
  else if (table->strInstruction[0] == 'J' || table->strInstruction[0] == 'j')
  {
    RestoreFlag(asmtext, len);
    After = InterpretJCC(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "loope") == 0)
  {
    RestoreFlag(asmtext, len);
    After = InterpretLoope(table, asmtext, len);
    SaveFlag(asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "retn") == 0)
  {
    After = InterpretRetn(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "call") == 0)
  {
    After = InterpretCall(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "SaveEsp") == 0)
  {
    After = InterpretSaveEsp(table, asmtext, len);
  }
  else if (_stricmp(table->strInstruction, "RestoreEsp") == 0)
  {
    After = InterpretRestoreEsp(table, asmtext, len);
  }
  else
  {
    After = CommonInstruction(table, asmtext, len);
  }
  
  // 某些指令执行完成后,将会影响到某些寄存器的值,这里所做的,就是将
  // 指令执行后改变的寄存器值保存回VMContext上下文中的各个寄存器中
  for (int i = 0; i < 3; i++)
  {
    // 如果需保存i寄存器,即i寄存器被改变了
    if (table->SaveReg[i] != NONE)
    {
      sprintf_s(asmtext, len, "%smov [edi+0x%02x],%s\n", asmtext,
        GetRegisterOffset(table->NeedReg[i]), vregname[2][table->NeedReg[i]]);
    }
  }

  if (After)
  {
    RestoreArg(table, asmtext, len);
  }

  // 将最终为指令生成的汇编代码转为大写
  _strupr_s(asmtext, ASMTEXTLEN);
  return TRUE;
}

/**
 * 根据操作数的位数bit,获取相应的修饰符
 *
 * bit: 操作数的位数
 * scalestr:用于返回得到的修饰符
 *
 * 返回值:返回操作数大小索引:
 *    8位: 0
 *    16位: 1
 *    32位:2
 */
int GetScalestr(int bit, OUT char * scalestr)  // *
{
  int sizeidx = 0;
  if (bit == 8)
  {
    sizeidx = 0;
    strcpy_s(scalestr, 6, "byte");
  }
  else if (bit == 16)
  {
    sizeidx = 1;
    strcpy_s(scalestr, 6, "word");
  }
  else if (bit == 32)
  {
    sizeidx = 2;
    strcpy_s(scalestr, 6, "dword");
  }
  return sizeidx;
}

/**
 * 为普通指令生成相应的汇编代码
 *
 * table: 存放某个普通指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回TRUE
 */
BOOL CInterpretHandler::CommonInstruction(VMTable * table, char * asmtext, int len)    // *
{
  char scalestr[6] = {0};
  int sizeidx = 0;

  char stroperand[1024] = {0};

  // 处理指令的操作数
  for (int i = 0; i < 3; i++)
  {
    // 操作数超限制
    if (i >= table->OperandNum)
      break;

    // 获取i操作数的修饰符
    sizeidx = GetScalestr(table->bitnum[i], scalestr);

    // 如果i操作数是一个内存数
    if (table->optype[i] == MEMTYPE )
    {
      sprintf_s(stroperand, 1024, "%s%s ptr %s[%s],", stroperand, scalestr, GetSegStr(table->Segment), vregname[2][i]);
    }
    // 立即数和寄存器
    else
    {
      // 得到操作数
      sprintf_s(stroperand, 1024, "%s%s,", stroperand, vregname[sizeidx][i]);
    }
  }
  // 去掉最后操作数的逗号
  if (table->OperandNum > 0)
    stroperand[strlen(stroperand) - 1] = '\0';

  RestoreFlag(asmtext, len);
  // 执行指令
  sprintf_s(asmtext,len, "%s%s %s\n", asmtext, table->strInstruction, stroperand);
  SaveFlag(asmtext, len);
  return TRUE;
}

/**
 * 根据段号Segment获得段前缀信息
 *
 * segment: 段号,例如SEG_FS代表FS段寄存器
 *
 * 返回值:返回获取的段前缀
 */
char * CInterpretHandler::GetSegStr(int Segment)  // *
{
  static char segstr[10] = "";
  memset(segstr, 0, 10);
  if (Segment == SEG_FS)
    strcpy_s(segstr, 10, "fs:");
  else if (Segment == SEG_GS)
    strcpy_s(segstr, 10, "gs:");
  return segstr;
}


/**
 * 虚拟机在执行时,并不会破坏真实堆栈中保存的寄存器,而是将真正堆栈中的寄存器值
 * 复制至VMContext中,VMContext即为虚拟机上下文,以后,虚拟机用到的寄存器值都从
 * 其中取得
 */

/**
 * 此函数的使用即是将堆栈中的寄存器复制至VMContext上下文中以便虚拟机使用,虚拟机将
 * 这种操作称为堆栈平衡
 *
 * table: 保存vBegin虚拟指令的相关信息
 * asmtext:保存完成操作的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE值
 */
BOOL CInterpretHandler::InterpretvBegin(VMTable * table, char * asmtext, int len)  // *
{
  int s_reg[8] = {RT_EFlag, RT_Ebp, RT_Edi, RT_Esi, RT_Edx, RT_Ecx, RT_Ebx, RT_Eax};

  // ebp -> 指向真正的堆栈
  // edi -> 指向VMContext

  // 将真实堆栈中保存的寄存器值复制至VMContext虚拟机上下文中
  for (int i = 0; i < 8; i++)
  {  
    // 先将堆栈中的寄存器值复制至eax中
    sprintf_s(asmtext, len, "%smov eax,dword ptr [ebp]\n", asmtext);
    // 再将eax的值复制到VMContext的某个寄存器中
    sprintf_s(asmtext, len,"%smov [edi+%02X],eax\n", asmtext, GetRegisterOffset(s_reg[i]));
    
    sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
  }
  sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);

  // 将ebp(即真正esp)保存到VMContext的esp
  sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));

  return FALSE;
}

/**
 * 为指令vtoReal生成汇编代码
 *
 * vtoReal:用于跳转至真实指令
 *
 * table:包含vtoReal指令的信息
 * asmtext:保存生成的汇编代码
 * len: asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 */
BOOL CInterpretHandler::InterpretvtoReal(VMTable * table, char * asmtext, int len)  // *
{
  int s_reg[9] = {RT_Esp, RT_EFlag, RT_Ebp, RT_Edi, RT_Esi, RT_Edx, RT_Ecx, RT_Ebx, RT_Eax};

  // 模拟pushimm32 xxxx指令,其中xxxx是真实指令的地址

  // 将真实指令的地址复制至eax中
  sprintf_s(asmtext, len, "%smov eax,dword ptr [esi]\n", asmtext);
  sprintf_s(asmtext, len, "%sadd esi,4\n", asmtext);
  // 在真实堆栈中分配4个字节并将真实指令的地址复制进去
  sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
  sprintf_s(asmtext, len, "%smov dword ptr [ebp],eax\n", asmtext);

  // 将ebp(即真正esp)保存到VMContext的esp中
  sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));

  // 将VMContext寄存器中的值压入栈
  for (int i = 0; i < 9; i++)
  {
    sprintf_s(asmtext, len, "%spush [edi+%02X]\n", asmtext, GetRegisterOffset(s_reg[i]));
  }
  // 再将压入栈的值复制至相应寄存器中
  sprintf_s(asmtext, len, "%spop eax\n", asmtext);
  sprintf_s(asmtext, len, "%spop ebx\n", asmtext);
  sprintf_s(asmtext, len, "%spop ecx\n", asmtext);
  sprintf_s(asmtext, len, "%spop edx\n", asmtext);
  sprintf_s(asmtext, len, "%spop esi\n", asmtext);
  sprintf_s(asmtext, len, "%spop edi\n", asmtext);
  sprintf_s(asmtext, len, "%spop ebp\n", asmtext);
  sprintf_s(asmtext, len, "%spopfd\n",   asmtext);
  sprintf_s(asmtext, len, "%spop esp\n", asmtext);

  // 至此,在堆栈中保存了真实的地址,并且寄存器环境也恢复了,所以,现在已组成一个调用环境了

  // 返回,跳转到真正的地址
  sprintf_s(asmtext, len, "%sretn\n", asmtext);
  return FALSE;
}

/**
 * 为指令push生成相应的汇编代码
 *
 * table: 存放push指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回TRUE
 *
 * 此函数生成的代码类似如下:
 * mov  eax,dword ptr [esp]
 * sub  ebp,4
 * 内存数
 * mov  eax,[eax]
 * mov  word ptr [ebp],ax
 */
BOOL CInterpretHandler::InterpretPush(VMTable * table, char * asmtext, int len)    // *
{
  char scalestr[6] = {0};
  int sizeidx = 0;

  // 根据操作数位数设置修饰符

  // 8位操作数
  if (table->bitnum[0] == 8)
  {
    sizeidx = 0;
    strcpy_s(scalestr, 6, "byte");
  }
  // 16位操作数
  else if (table->bitnum[0] == 16)
  {
    sizeidx = 1;
    strcpy_s(scalestr, 6, "word");
  }
  // 32位操作数
  else if (table->bitnum[0] == 32)
  {
    sizeidx = 2;
    strcpy_s(scalestr, 6, "dword");
  }

  // 从堆栈中分配空间用于存放操作数
  sprintf_s(asmtext, len, "%ssub ebp,%d\n", asmtext, table->bitnum[0] / 8);

  // 至此,将要将该数据放到堆栈中以模拟压栈过程
  // 对于该数有两种情况:
  // 1、如果该数不是一个内存数,则该数已经被放到EAX寄存器中了
  // 2、如果该数是个内存数,则EAX寄存器中存放的不是该数,而是该数所在的偏移量
  if (table->optype[0] == MEMTYPE)
  {
    // 得到该内存数的值
    sprintf_s(asmtext, len, "%smov %s,%s ptr %s[eax]\n", asmtext, vregname[sizeidx][RT_Eax], scalestr, GetSegStr(table->Segment));
  }
  // 将内存数复制到堆栈中完成压栈操作
  sprintf_s(asmtext, len, "%smov %s ptr [ebp],%s\n", asmtext, scalestr, vregname[sizeidx][RT_Eax]);
  return TRUE;
}


/**
 * 为指令pop生成相应的汇编代码
 *
 * table: 存放pop指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回TRUE
 *
 * 此函数生成的代码类似如下:
 *  mov  eax,dword ptr [ebp]
 *  add  ebp,4
 *  mov  eax,[eax]
 */
BOOL CInterpretHandler::InterpretPop(VMTable * table, char * asmtext, int len)  // *
{
  char scalestr[6] = {0};
  int sizeidx = 0;

  // 根据操作数位数设置修饰符

  // 8位操作数
  if (table->bitnum[0] == 8)
  {
    sizeidx = 0;
    strcpy_s(scalestr, 6, "byte");
  }
  else if ( table->bitnum[0] == 16 )
  {
    sizeidx = 1;
    strcpy_s(scalestr, 6, "word");
  }
  else if( table->bitnum[0] == 32 )
  {
    sizeidx = 2;
    strcpy_s(scalestr, 6, "dword");
  }

  // 得到堆栈ebp( ebp -> 真实堆栈 )的值
  sprintf_s(asmtext, len, "%smov %s,%s ptr [ebp]\n", asmtext, vregname[sizeidx][RT_Ecx], scalestr);
  // 释放操作数占用的空间
  sprintf_s(asmtext, len, "%sadd ebp,%d\n", asmtext, table->bitnum[0] / 8);



  // 如果是内存数
  if (table->optype[0] == MEMTYPE)
  {
    // 得到内存数的值
    sprintf_s(asmtext, len, "%smov %s,%s ptr %s[eax]\n", asmtext, vregname[sizeidx][RT_Eax], scalestr, GetSegStr(table->Segment));
  }

  // 将值给mem(只有mem,因为无论是reg还是mem在vm中都是内存地址)
  sprintf_s(asmtext, len, "%smov eax,ecx\n", asmtext);

  // 问题:对于上面判断是否内存数的操作似乎是多余的,或说是不对的,个人理解应把代码改为如下:
  // if (table->optype[0] == MEMTYPE)
  // {
    // sprintf_s(asmtext, len, "%smov%s ptr %s[eax],%s\n", asmtext, scalestr, GetSegStr(table->Segment), vregname[sizeidx][RT_Ecx]);
  // }
  // else
  // {
    // sprintf_s(asmtext, len, "%smov eax,ecx\n", asmtext);
  // }

  return TRUE;
}

/**
 * 为指令pushfd生成相应的汇编代码
 *
 * table: 存放pushfd指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回TRUE
 */
BOOL CInterpretHandler::InterpretPushfd(VMTable * table, char * asmtext, int len)  // *
{
  // 在堆栈中分配4字节空间用于存放标志寄存器值
  sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
  // 将VMContext上下文中EFlag标志寄存器值放入eax寄存器
  sprintf_s(asmtext, len, "%smov eax,[edi+%02x]\n", asmtext, GetRegisterOffset(RT_EFlag));
  // 再将其放入堆栈以实现压栈
  sprintf_s(asmtext, len, "%smov dword ptr [ebp],eax\n", asmtext);
  return TRUE;
}

/**
 * 为指令popfd生成相应的汇编代码
 *
 * table: 存放popfd指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回TRUE
 */
BOOL CInterpretHandler::InterpretPopfd(VMTable * table, char * asmtext, int len)  // *
{
  // 将堆栈中的EFLAG寄存器值复制到eax寄存器中
  sprintf_s(asmtext, len, "%smov eax,dword ptr [ebp]\n", asmtext);
  // 释放堆栈中占用的4字节空间
  sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
  // 再将eax寄存器中的EFLAG值复制到VMContext上下文中实现出栈
  sprintf_s(asmtext, len, "%smov [edi+%02x],eax\n", asmtext, GetRegisterOffset(RT_EFlag));
  return TRUE;
}


/**
 * 为指令pushad生成相应的汇编代码
 *
 * table: 存放pushad指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 注:pushad指令用于实现将多个通用寄存器压入堆栈中
 *
 * 返回值:只返回TRUE
 */
BOOL CInterpretHandler::InterpretPushad(VMTable * table, char * asmtext, int len)  // *
{
  // 循环压栈通用寄存器
  for (int i = 0; i < 8; i++)
  {
    // 为i寄存器分配堆栈空间
    sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
    // 再将VMContext上下文中寄存器值复制到eax中
    sprintf_s(asmtext, len, "%smov eax,[edi+%02x]\n", asmtext, GetRegisterOffset(i));
    // 最后再将eax复制到堆栈中以完成压栈工作
    sprintf_s(asmtext, len, "%smov dword ptr [ebp],eax\n", asmtext);
  }
  return TRUE;
}

/**
 * 为指令popad生成相应的汇编代码
 *
 * table: 存放popad指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 注:popad指令用于实现将多个通用寄存器弹出堆栈
 *
 * 返回值:只返回TRUE
 */
BOOL CInterpretHandler::InterpretPopad(VMTable * table, char * asmtext, int len)  // *
{
  // 可见InterpretPushad()操作
  for (int i = 8; i > 0; i--)
  {
    sprintf_s(asmtext, len, "%smov eax,dword ptr [ebp]\n", asmtext);
    sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
    sprintf_s(asmtext, len, "%smov [edi+%02x],eax\n", asmtext, GetRegisterOffset(i));
  }
  return TRUE;
}

/**
 * 为指令enter生成相应的汇编代码
 *
 * table: 存放enter指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回TRUE
 *
 * 注:enter指令用于操作子过程栈框架,代码如下:
 *    push ebp
 *    mov ebp,esp
 *    sub esp,(L - 1) * 4  ; L > 0才有这步操作,用来存储嵌套的L - 1个子过程的栈框架指针(注意是指针)
 *    push ebp ; 当前过程的栈框架指针
 *    sub esp,N
 */
BOOL CInterpretHandler::InterpretEnter(VMTable * table, char * asmtext, int len)  // *
{
  // 模拟 push ebp

  // 将VMContext上下文中ebp寄存器的值复制到edx中
  sprintf_s(asmtext, len, "%smov edx,[edi+%02x]\n", asmtext, GetRegisterOffset(RT_Ebp));
  // 然后,在真实堆栈中分配4字节空间用于放置ebp的值
  sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
  sprintf_s(asmtext, len, "%smov dword ptr [ebp],edx\n", asmtext);

  // 将真实堆栈的esp值复制到edx中,并保存到VMContext上下文的esp中
  sprintf_s(asmtext, len, "%smov edx,ebp\n", asmtext);
  sprintf_s(asmtext, len, "%smov [edi+%02x],edx\n", asmtext, GetRegisterOffset(RT_Esp));

  // 至此,ebp指向的就是真实堆栈,所以是不需要模拟mov ebp,esp的
  
  // 模拟 sub esp,(L - 1) * 4  ; L > 0才有这步操作

  // 将真实堆栈esp的值复制到edx中
  sprintf_s(asmtext, len, "%smov edx,ebp\n", asmtext);
  // ecx = ecx * 4
  // ecx寄存器中存放的是 L 嵌套层数
  sprintf_s(asmtext, len, "%slea ecx,[ecx*4]\n", asmtext);
  // 在真实堆栈中分配 (L - 1) * 4 的空间存放子过程的栈框架信息
  sprintf_s(asmtext, len, "%ssub edx,ecx\n", asmtext);
  // 再减4
  sprintf_s(asmtext, len, "%ssub edx,4\n", asmtext);
  // 检测ecx是否为0
  sprintf_s(asmtext, len, "%stest ecx,ecx\n", asmtext);
  // 不为0则意味着确实需要在堆栈中分空栈框架空间,即ebp = ebp - edx
  sprintf_s(asmtext, len, "%scmovne ebp,edx\n", asmtext);

  // 模拟 push ebp

  sprintf_s(asmtext, len, "%smov edx,[edi+%02x]\n", asmtext, GetRegisterOffset(RT_Ebp));
  sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
  sprintf_s(asmtext, len, "%smov dword ptr [ebp],edx\n", asmtext);

  // 模拟 sub esp,N

  sprintf_s(asmtext, len, "%ssub ebp,eax\n", asmtext);
  return TRUE;
}

/**
 * 为指令leave生成相应的汇编代码
 *
 * table: 存放leave指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回TRUE
 *
 * 注:enter指令用于操作子过程栈框架,代码如下:
 *    mov ebp,esp
 *    pop ebp
 */
BOOL CInterpretHandler::InterpretLeave(VMTable * table, char * asmtext, int len)  // *
{
  // 因为ebp已指向真实堆栈,所以是不需要模拟 mov ebp,esp 指令的

  // 先将真实堆栈esp的值保存到VMContext上下文中
  sprintf_s(asmtext, len, "%smov edx,ebp\n", asmtext);
  sprintf_s(asmtext, len, "%smov [edi+%02x],edx\n", asmtext,GetRegisterOffset(RT_Esp));

  // 模拟 pop ebp

  // 将真实堆栈中的值复制到eax中
  sprintf_s(asmtext, len, "%smov eax,[ebp]\n", asmtext);
  // 释放堆栈占用的空间
  sprintf_s(asmtext,len, "%sadd ebp,4\n", asmtext);
  // 将eax复制到VMContext上下文的ebp中
  sprintf_s(asmtext,len, "%smov [edi+%02x],eax\n", asmtext, GetRegisterOffset(RT_Ebp));
  return TRUE;
}

/**
 * 为指令jmp生成相应的汇编代码
 *
 * table: 存放jmp指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 */
BOOL CInterpretHandler::InterpretJMP(VMTable * table, char * asmtext, int len)  // *
{
  // 将跳转地址复制到esi,改变esi就相当于改变执行流程
  sprintf_s(asmtext, len, "%smov esi,eax\n", asmtext);
  sprintf_s(asmtext, len, "%sadd esp,4\n", asmtext);
  return FALSE;
}

/**
 * 为指令jcxz生成相应的汇编代码
 *
 * table: 存放jcxz指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 *
 * 注:jcxz指令,该指令实现当寄存器CX的值等于0时转移到标号,否则顺序执行
 */
BOOL CInterpretHandler::InterpretJCXZ(VMTable * table, char * asmtext, int len)    // *
{
  // 检测ecx是否为0
  sprintf_s(asmtext, len, "%stest ecx,ecx\n", asmtext);

  // 如果为0,则将eax复制到esi中,由于eax中存放的是跳转指令地址,所以,将
  // esi的值改变为跳转指令地址,则相当于改变了虚拟机的执行流程
  sprintf_s(asmtext, len, "%sCMOVZ esi,eax\n", asmtext);
  // 
  sprintf_s(asmtext, len, "%sadd esp,4\n", asmtext);
  return FALSE;
}

/**
 * 为条件跳转指令jcc生成相应的汇编代码
 *
 * table: 存放jcc指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 */
BOOL CInterpretHandler::InterpretJCC(VMTable * table, char * asmtext, int len)  // *
{
  // 先根据指令得到跳转的条件后缀
  char strPostfix[16] = {0};
  strcpy_s(strPostfix, 16, &table->strInstruction[1]);
  // 如果条件成立,则将esp的地址复制到esi中以改变流程,否则不复制,即流程不改变
  sprintf_s(asmtext, len, "%scmov%s esi,[esp]\n", asmtext, strPostfix);
  sprintf_s(asmtext,len, "%sadd esp,4\n", asmtext);
  return FALSE;
}

/**
 * 为指令loope生成相应的汇编代码
 *
 * table: 存放loope指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 */
BOOL CInterpretHandler::InterpretLoope(VMTable * table, char * asmtext, int len)  // *
{
  // 将标志寄存器压入堆栈中
  sprintf_s(asmtext, len, "%spushfd\n", asmtext);
  // 测试ecx是否为0
  sprintf_s(asmtext, len, "%stest ecx,ecx\n", asmtext);
  // 如果不为0,则说明需要继续循环,则将循环指令地址复制到edx中
  sprintf_s(asmtext, len, "%scmovne edx,eax\n", asmtext);
  sprintf_s(asmtext, len, "%spopfd\n", asmtext);
  sprintf_s(asmtext, len, "%scmovne edx,esi\n", asmtext);

  sprintf_s(asmtext, len, "%scmove edx,eax\n", asmtext);
  // 释放堆栈
  sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
  return FALSE;
}

/**
 * 为指令retn生成相应的汇编代码
 *
 * table: 存放retn指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 *
 * 注:retn在虚拟机里被认为是一个退出函数
 */
BOOL CInterpretHandler::InterpretRetn(VMTable * table, char * asmtext, int len)    // *
{
  int s_reg[9] = {RT_Esp, RT_EFlag, RT_Ebp, RT_Edi, RT_Esi, RT_Edx, RT_Ecx, RT_Ebx, RT_Eax};

  // 如果指令带有一个操个数,即 retn XX
  if (table->OperandNum == 1)
  {
    // 先将返回地址复制到edx中
    // 问题:此语句似乎是错误的,应该如下:
    //     sprintf_s(asmtext, len, "%smov edx,[ebp]\n", asmtext);
    sprintf_s(asmtext, len, "%smov edx,ebp\n", asmtext);

    // 释放堆栈占用的空间XX
    sprintf_s(asmtext, len, "%sadd ebp,eax\n", asmtext);

    // 再将返回地址放入堆栈中
    sprintf_s(asmtext, len, "%smov [ebp],edx\n", asmtext);
    // 将ebp(即真实esp)保存到VMContext上下文的esp中
    sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));
  }
  // 将保存在VMContext上下文的寄存器压入堆栈
  for (int i = 0; i < 9; i++)
  {
    sprintf_s(asmtext, len, "%spush [edi+%02X]\n", asmtext, GetRegisterOffset(s_reg[i]));
  }
  // 再将其恢复到各个寄存器中
  sprintf_s(asmtext, len, "%spop eax\n", asmtext);
  sprintf_s(asmtext, len, "%spop ebx\n", asmtext);
  sprintf_s(asmtext, len, "%spop ecx\n", asmtext);
  sprintf_s(asmtext, len, "%spop edx\n", asmtext);
  sprintf_s(asmtext, len, "%spop esi\n", asmtext);
  sprintf_s(asmtext, len, "%spop edi\n", asmtext);
  sprintf_s(asmtext, len, "%spop ebp\n", asmtext);
  sprintf_s(asmtext, len, "%spopfd\n", asmtext);
  // 将esp恢复到真实堆栈
  sprintf_s(asmtext, len, "%spop esp\n", asmtext);

  // 正式返回
  sprintf_s(asmtext, len, "%sretn\n", asmtext);
  return FALSE;
}

/**
 * 为指令call生成相应的汇编代码
 *
 * table: 存放call指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 *
 * 注:call anotherfunc调用子函数,会将控制权移交给另处的代码,这些代码是不受
 *     虚拟机控制的,所以碰到call指令,必须退出虚拟机, 让子函数在真实CPU中执
 *     行后再交回给虚拟机执行一下指令
 */
BOOL CInterpretHandler::InterpretCall(VMTable * table, char * asmtext, int len)    // *
{
  int s_reg[9] = {RT_Esp, RT_EFlag, RT_Ebp, RT_Edi, RT_Esi, RT_Edx, RT_Ecx, RT_Ebx, RT_Eax};
  
  sprintf_s(asmtext, len, "%smov edx,[esi]\n", asmtext);
  // 在堆栈中分配4字节空间
  sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
  // 将call的返回地址压入堆栈中
  sprintf_s(asmtext, len, "%smov [ebp],edx\n", asmtext);
  // 再分配4字节空间
  sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);

  // 如果call调用地址是一个内存数
  if (table->optype[0] == MEMTYPE)
  {
    sprintf_s(asmtext, len, "%smov eax,dword ptr [eax]\n", asmtext);
  }
  // 将call的地址压入堆栈中
  sprintf_s(asmtext, len, "%smov [ebp],eax\n", asmtext);

  // 将ebp(即真正esp)保存到VMContext上下文的esp中
  sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));

  // 压入保存在VMContext的寄存器值
  for (int i = 0; i < 9; i++)
  {
    sprintf_s(asmtext, len, "%spush [edi+%02X]\n", asmtext, GetRegisterOffset(s_reg[i]));
  }
  // 再将各寄存器值弹出,恢复到各寄存器中,以恢复环境
  sprintf_s(asmtext, len, "%spop eax\n", asmtext);
  sprintf_s(asmtext, len, "%spop ebx\n", asmtext);
  sprintf_s(asmtext, len, "%spop ecx\n", asmtext);
  sprintf_s(asmtext, len, "%spop edx\n", asmtext);
  sprintf_s(asmtext, len, "%spop esi\n", asmtext);
  sprintf_s(asmtext, len, "%spop edi\n", asmtext);
  sprintf_s(asmtext, len, "%spop ebp\n", asmtext);
  sprintf_s(asmtext, len, "%spopfd\n", asmtext);
  // 恢复到真实的堆栈
  sprintf_s(asmtext, len, "%spop esp\n", asmtext);

  // 返回,直接回到call的地址执行
  sprintf_s(asmtext, len, "%sretn\n", asmtext);

  return FALSE;
}

/**
 * 为指令saveesp生成相应的汇编代码,用于保存堆栈
 *
 * table: 存放saveesp指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 */
BOOL CInterpretHandler::InterpretSaveEsp(VMTable * table, char * asmtext, int len)  // *
{
  // 将ebp(即真正esp)保存到VMContext上下文的esp中
  sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));
  return FALSE;
}

/**
 * 为指令restoreesp生成相应的汇编代码,用于恢复堆栈
 *
 * table: 存放restoreesp指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
 * asmtext: 保存生成的汇编代码
 * len:asmtext缓存的长度
 *
 * 返回值:只返回FALSE
 */
BOOL CInterpretHandler::InterpretRestoreEsp(VMTable * table, char * asmtext, int len)  // *
{
  // 将VMContext上下文中esp恢复到ebp真实堆栈指针中
  sprintf_s(asmtext, len, "%smov ebp,[edi+%02x]\n", asmtext, GetRegisterOffset(RT_Esp));
  return FALSE;
}


// ------------------------------------- vmserv.cpp ---------------------------------------------
// 此文件定义了如《汇编指令-虚拟机指令》对应表vmtable等信息
// 这里,只摘取了其中一部分
#include "stdafx.h"
#include "VCommand.h"
#include "asm\disasm.h"
#include "vmdisasm.h"

/**
 * 寄存器名
 */
const char * vregname[3][14] = 
{
  { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH", "CS","DS","ES","FS","GS","SS"  },
  { "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI", "CS","DS","ES","FS","GS","SS" },
  { "EAX","ECX","EDX","EBX","ESP","EBP","ESI","EDI","CS","DS","ES","FS","GS","SS" } 
};

/**
 * 可用于存放虚拟指令操作数的寄存器
 */
const char ArgReg[3][4] = {"EAX","ECX","EDX"};

/**
 * 虚拟机有两部分的指令:
 * 1、是需要手工编写生成的指令
 * 2、是可以由程序动态分析生成的指令
 */


/**
 * 需要手工编写生成的核心指令
 */
VM_DName vm_dname[COREHANDLERLEN] = 
{
  {"VStartVM", VStartVM},    // 启动函数
  {"DCheckESP", DCheckESP},  // 检查ESP是否重复

  {"DPushReg32", DPushReg32},  // dpush eax
  {"DPushImm32", DPushImm32},  // dpush 1234
  {"DPushMem32", DPushMem32},  // dpush 1234
  {"DPopReg32", DPopReg32},  // dpop  ebx
  {"DFree", DFree},      // daddesp 4
  {"", NULL},

  {"", NULL},
};

/**
 * 数组包含了虚拟机指令与相应的普通汇编指令的信息,根据这些信息,虚拟机
 * 可以为虚拟机指令生成相应的汇编代码以代替原来对应的普通汇编指令
 */
VMTable vmtable[VMTABLEMAXLEN] = 
{
  // 信息格式如下:
  // 虚拟机指令名、相对的汇编指令、操作数个数、段前缀、操作数类型、操作数位数、执行指令时需要使用的寄存器、执行指令后需要保存的寄存器、第2个寄存器是否恢复(一般为0不恢复)

  {"VBEGIN","vBegin",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE,    NONE,NONE,NONE,NONE },
  {"VTOREAL","vtoReal",1, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE,    NONE,NONE,NONE,NONE },
  {"VRETN","RETN",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE,    NONE,NONE,NONE,NONE },
  {"VRETNIMM32","RETN",1, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE,    NONE,NONE,NONE,NONE },
  // 保存堆栈
  {"VSAVEESP","SAVEESP",0, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE,    NONE,NONE,NONE,NONE },
  // 恢复堆栈
  {"VRESTOREESP","RESTOREESP",0, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE,    NONE,NONE,NONE,NONE },

  // 堆栈操作指令
  {"VPUSH_IMM32","PUSH",1, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE,    NONE,NONE,NONE,NONE },
  {"VPUSH_REG16","PUSH",1, SEG_UNDEF, REGTYPE, NONETYPE,16,0, NONE,NONE,NONE,NONE,    NONE,NONE,NONE,NONE },
};


// ----------------------------------- InterpretHandler.h ---------------------------------------
#pragma once
#include "vmdisasm.h"

class CInterpretHandler
{
public:
  CInterpretHandler(void);
public:
  ~CInterpretHandler(void);
private:
  // 寄存器数组索引
  // 前15个元素是15个不超15的随机数
  int      m_RegisterIdx[STACKLEN];
public:
  // 初始化
  BOOL Init();
public:
  // 获得寄存器的偏移
  int GetRegisterOffset(int RegType);
  // 获得段前缀
  char* GetSegStr(int Segment);
  // 根据结构声称ASM字符串
  BOOL  InterpretASMStr(VMTable* table,char* asmtext,int len);
private:
  //设置参数
  void SetArg(VMTable* table,char* asmtext,int len);
  //恢复参数
  void RestoreArg(VMTable* table,char* asmtext,int len);
  //恢复标志
  void RestoreFlag(char* asmtext,int len);
  //保存标志
  void SaveFlag(char* asmtext,int len);
private:
  // 首先执行的指令
  BOOL  InterpretvBegin(VMTable* table,char* asmtext,int len);
  // 跳转到真实指令
  BOOL  InterpretvtoReal(VMTable* table,char* asmtext,int len);
  // *********************堆栈类********************************* //
  // 解释push
  BOOL  InterpretPush(VMTable* table,char* asmtext,int len);
  // 解释pop
  BOOL  InterpretPop(VMTable* table,char* asmtext,int len);
  // 解释pushfd
  BOOL  InterpretPushfd(VMTable* table,char* asmtext,int len);
  // 解释popfd
  BOOL  InterpretPopfd(VMTable* table,char* asmtext,int len);
  // 解释pushad
  BOOL  InterpretPushad(VMTable* table,char* asmtext,int len);
  // 解释popad
  BOOL  InterpretPopad(VMTable* table,char* asmtext,int len);
  // 解释enter
  BOOL  InterpretEnter(VMTable* table,char* asmtext,int len);
  // 解释leave
  BOOL  InterpretLeave(VMTable* table,char* asmtext,int len);
  // *********************流程类********************************* //
  // 解释jmp
  BOOL  InterpretJMP(VMTable* table,char* asmtext,int len);
  // 解释jcxz\jecxz
  BOOL  InterpretJCXZ(VMTable* table,char* asmtext,int len);
  // 解释jcc
  BOOL  InterpretJCC(VMTable* table,char* asmtext,int len);
  // 解释loope
  BOOL InterpretLoope(VMTable* table,char* asmtext,int len);

  // 解释返回
  BOOL InterpretRetn(VMTable* table,char* asmtext,int len);
  // 解释子调用
  BOOL InterpretCall(VMTable* table,char* asmtext,int len);

  // 普通指令
  BOOL CommonInstruction(VMTable* table,char* asmtext,int len);
  // 解释保护堆栈Handler
  BOOL InterpretSaveEsp(VMTable* table,char* asmtext,int len);
  // 解释恢复堆栈Handler
  BOOL InterpretRestoreEsp(VMTable* table,char* asmtext,int len);
};


// ----------------------------------------------- vmdisasm.h -------------------------------------

////////////////////////////////////////////////////////////////////////////////////
// 新段各部分空间的长度

#define JumpTableLen    (HANDLERMAXLEN * 4)
#define CodeEngineLen    (4096 * 4)      // 虚拟机引擎代码长度
#define EnterStubAddrLen  (4096)        // 重新进入VM的Stub,长度1024字节
#define VMEnterCodeLen    (4096 * 2)      // 空间存放重新进入的vmcode

#define VMCodeLen      (10240 * 2)      // 2K的空间存放VMCode
#define ASMTEXTLEN      1024        // 汇编文本长度

////////////////////////////////////////////////////////////////////////////////////

//8086指令字符串长度
#define X86NAMELEN          32

// 虚拟机指令名的长度限制
#define VMNAMELEN          32

//Handler的最大Handler长度
#define CODEMAXLEN          512

// 最上面存放寄存器, 后来的一些就当vm堆栈用了
#define STACKLEN          0x200    // 512

// 寄存器最大个数
#define REGCOUNT          15

// 命令Handler最大个数
// 伪指令最大限制数
#define HANDLERMAXLEN        0xFF

//VMTABEL表的个数
#define VMTABLEMAXLEN        0x400

// 核心伪指令个数
#define COREHANDLERLEN        10

// 没有用到寄存器
#define NONE  -1

// 寄存器类型
enum RegType
{
  RT_Eax,
  RT_Ecx,
  RT_Edx,
  RT_Ebx,
  RT_Esp,
  RT_Ebp,
  RT_Esi,
  RT_Edi,
  RT_CS,
  RT_DS,
  RT_ES,
  RT_FS,
  RT_GS,
  RT_SS,

  // 必须是最后一个(为什么?我忘了)
  RT_EFlag,

  RT_AH = 20,
  RT_CH,
  RT_DH,
  RT_BH,
};

/**
 * 指令操作数类型
 */
enum optype
{
  NONETYPE,
  // 立即数
  IMMTYPE,
  // 寄存器数
  REGTYPE,
  // 内存数
  MEMTYPE,
  CSTYPE,
  DSTYPE,
  ESTYPE,
  SSTYPE,
  FSTYPE,
  GSTYPE,
};

/**
 * 虚拟机指令对应着相应的普通汇编指令,此结构的作用,
 * 用于存放虚拟机指令与普通汇编指令之间的相关信息
 */
struct VMTable
{
  char  VMInstrName[VMNAMELEN];  // 虚拟机指令名
  char  strInstruction[16];    // 相对的汇编指令
  int    OperandNum;      // 操作数个数
  int    Segment;      // 段前缀
  int    optype[2];      // 操作数类型(寄存器,立即数,内存数)
  int    bitnum[2];      // 操作数位数

  // 某些汇编指令在执行过程中,需要根据某些寄存器的值来控制执行

  int    NeedReg[4];      // 执行指令时需要使用的寄存器
  int    SaveReg[4];      // 执行指令后需要保存的寄存器
  BOOL  Reg2Esp;      // 第2个寄存器是否恢复,一般为0不恢复
};

/**
 * 存放虚拟机指令处理代码信息
 */
struct VHandler
{
  // 虚拟机指令名称,例如:VStartVM等
  char  VMInstrName[VMNAMELEN];

  // 应该是虚拟机指令相应代码所在的虚拟地址
  DWORD  VirtualAddress;

  // 指令的汇编代码,或放置编译后的机器码
  char    AssembleCode[CODEMAXLEN];

  // 指令对应的处理代码(函数)长度
  int     CodeLen;
  VHandler()
  {
    memset(this, 0, sizeof(VHandler));
  }
};

/**
 * 指令处理代码(即函数)定义
 */
typedef void (*HandlerFunc)();

//核心的一些手工VM函数名称和对应的函数
struct VM_DName
{
  char    vm_dname[VMNAMELEN];
  HandlerFunc  FuncAddr;
};

extern VM_DName  vm_dname[COREHANDLERLEN];

//描述一些标准Handler的行为表
extern VMTable  vmtable[VMTABLEMAXLEN];
//寄存器对应表
extern const char *vregname[3][14];
extern const char ArgReg[3][4];

// 虚拟机为每个指令产生一个索引号,虚拟机的执行是基于一张跳转表,跳转表中
// 存储每个虚拟机指令处理代码的地址,而当执行某个虚拟机指令时,就是根据虚
// 拟机指令索引号计算出在跳转表中的下标进行跳转操作的

// 而且,该索引号也将作为虚拟机指令的字节码使用

/**
 * 保存虚拟机指令名与对应的索引号信息
 */
struct VM_RandomTable
{
  char  VMInstrName[VMNAMELEN];  // 虚拟机指令名
  int    idx;  // 对应的索引
};

/**
 * 存放虚拟机字节码指令节点(即字节码)
 */
struct VCodeNode 
{
  // 原指令原来所在的地址
  DWORD  InAddress;

  // 虚拟机指令所在的地址
  DWORD  VMAddress;

  char  VMInstrName[VMNAMELEN];    //VM命令名称

  // 虚拟机指令的字节码,HexCode[0]存放的是指令在跳转表中的索引号
  BYTE  HexCode[32];
  // 字节码长度
  int    HexLen;
  // 指令操作数个数
  int    OperandNum;

  // 操作数:立即数(寄存器, 立即数, 内存数)
  int    immconst[2];
  int    bitnum[2];          //位数
  VCodeNode()
  {
    InAddress = 0;
    VMAddress = 0;
    memset(VMInstrName,0,VMNAMELEN);
    memset(HexCode,0,32);
    HexLen = 0;
    OperandNum = 0;
    immconst[0] = immconst[1] = bitnum[0] = bitnum[1] = 0;
  }
};

// 为一个索引数组随机排序.
void  RandListIdx(int* idx,int cout);

  • 标 题:再发
  • 作 者:shecx
  • 时 间:2010-07-13 09:45:26

// ---------------------------------------- CCodeILFactory.cpp ---------------------------------

// VMPackLib

#include "stdafx.h"
#include "CCodeILFactory.h"

CCodeILFactory::CCodeILFactory()
{
}

/**
 * 中间字节码指令工厂初始化,主要是生成某些虚拟机核心指令机器代码,
 * 并将该代码复制到m_CodeEngine虚拟内存中
 *
 * VirtualAddress:虚拟内存相应的虚拟地址
 */
void CCodeILFactory::Init(DWORD VirtualAddress)    // *
{
  // 计算与虚拟机相关代码的各虚拟地址与空间长度
  int nJumpTableAddr  = VirtualAddress;
  int nCodeEngineAddr = nJumpTableAddr + JumpTableLen;
  int nEnterStubAddr  = nCodeEngineAddr + CodeEngineLen;
  int mVMEnterStubCodeAddr = nEnterStubAddr + EnterStubAddrLen;
  int nVMCodeAddr    = mVMEnterStubCodeAddr + VMEnterCodeLen;

  // 为每段代码分配相关的内存空间
  m_JumpTable.CreateVirtualMemory(nJumpTableAddr, JumpTableLen);
  m_CodeEngine.CreateVirtualMemory(nCodeEngineAddr, CodeEngineLen);
  m_EnterStub.CreateVirtualMemory(nEnterStubAddr, EnterStubAddrLen);
  m_VMEnterStubCode.CreateVirtualMemory(mVMEnterStubCodeAddr, VMEnterCodeLen);
  m_VMCode.CreateVirtualMemory(nVMCodeAddr, VMCodeLen);

  // 将各内存空间信息传递给VMFactory虚拟机工厂
  VMFactory.SetupVirtualMemory(&m_JumpTable, &m_CodeEngine, &m_EnterStub, &m_VMEnterStubCode, &m_VMCode);

  // 初始化虚拟机
  VMFactory.InitVM();
}

/**
 * 反汇编指定代码
 *
 * CodeList:用于返回反汇编后所得的每个指令
 * base_addr:欲反汇编代码的基地址,一般是某个函数的基址
 * VirtualAddress:欲反汇编代码所在的虚拟地址
 */
void CCodeILFactory::DisasmFunction(list<CodeNode *> * CodeList, char * base_addr, DWORD VirtualAddress)  // *
{
  // 函数起始地址
  DWORD Start_VirtualAddress = VirtualAddress;
  // 函数结束地址
  DWORD End_VirtualAddress = 0;

  // 用于保存反汇编结果
  t_disasm da;
  CodeNode * code = NULL;
  int len =0;
  int KeepNum = 0;

  // 计算出函数的结尾
  while (TRUE)
  {
    code = new CodeNode;

    // 反汇编base_addr的代码
    len = Disasm(base_addr, MAXCMDSIZE, VirtualAddress, &da, DISASM_CODE);

    // 将反汇编结果da复制到disasm中,并加入到CodeList列表中
    memcpy(&code->disasm, &da, sizeof(t_disasm));
    CodeList->push_back(code);

    // 递减所剩代码长度
    if (KeepNum >= len && KeepNum != 0 )
      KeepNum -= len;

    // 如果da指令是跳转指令
    if (IsJumpCmd(&da))
    {
      // 并且跳转的目标地址是向前跳转,则计算从当前指令至目标地址的代码长度
      if (da.jmpconst && da.jmpconst > VirtualAddress)
      {
        KeepNum = da.jmpconst - VirtualAddress;
      }
    }

    // 如果是int3指令,即断点指令,则直接跳出
    if (_stricmp(da.result, "int3") == 0)
    {
      break;
    }
    // 如果找到返回指令
    if (_stricmp(da.result, "retn") == 0)
    {
      // 并且已没有更多的代码,则说明函数结束
      if (KeepNum - len <= 0)
        break;
    }
    base_addr += len;
    VirtualAddress += len;
  }
  End_VirtualAddress = VirtualAddress;

  // 确定每个指令的类型等信息
  BOOL NextJMCALL = FALSE;

  list<CodeNode*>::iterator itr;
  // 遍历CodeList中的每个语句
  for (itr = CodeList->begin(); itr != CodeList->end(); itr++)
  {
    CodeNode * code = * itr;
    t_disasm t_da;
    // 得到语句的反汇编结果
    memcpy(&t_da, &code->disasm, sizeof(t_disasm));
    
    // 真是一个奇怪的判断
    if (code)
    {
      if (NextJMCALL)
      {
        NextJMCALL = FALSE;
        // 表示code指令是JMP或CALL指令后的指令
        code->IsJmcNextType = TRUE;
      }
      // 如果是跳转指令
      if (IsJumpCmd(&t_da))
      {
        NextJMCALL = TRUE;
        // 如果存在跳转地址
        if (t_da.jmpconst)
        {
          // 跳转的范围在CodeList中
          if (t_da.jmpconst > Start_VirtualAddress && t_da.jmpconst < End_VirtualAddress)
          {
            // 搜索code跳转指令跳转的目标
            CodeNode * tmpNode = SearchAddrAsList(CodeList, code);

            // 确实找到跳转目标
            if (tmpNode)
            {
              // 标明tmpNode指令是从其他地方跳转过来的
              tmpNode->IsJmcFromType = TRUE;
            }
          }
          else
          {
            // 为跳到外部的指令
            code->IsJmcBeSideType = TRUE;
          }
        }
        // 没有跳转地址,则表示是动态跳转
        else
        {
          code->IsJmcDynamicType = TRUE;
        }
      }
      // 是函数调用指令
      else if (IsCallCmd(&t_da))
      {
        NextJMCALL = TRUE;
        code->IsCallType = TRUE;
      }
    }
  }
}

/**
 * 检测da中反汇编指令是否是跳转指令
 *
 * 返回值:是则返回TRUE,否则FALSE
 */
BOOL CCodeILFactory::IsJumpCmd(t_disasm * da)    // *
{
  // CALL不属于跳转指令
  if ((da->cmdtype & C_CAL) == C_CAL)
  {
    return FALSE;
  }
  if ((da->cmdtype & C_JMP) == C_JMP)
  {
    return TRUE;
  }
  if ((da->cmdtype & C_JMC) == C_JMC)
  {
    return TRUE;
  }
  return FALSE;
}

/**
 * 检测da中反汇编指令是否是函数调用指令
 *
 * 返回值:是则返回TRUE,否则FALSE
 */
BOOL CCodeILFactory::IsCallCmd(t_disasm * da)    // *
{
  if ((da->cmdtype & C_CAL ) == C_CAL)
  {
    return TRUE;
  }
  return FALSE;
}


BOOL CCodeILFactory::IsRetnCmd(t_disasm* da)
{
  char strretn[32] = "RETN\0";
  _strlwr_s(da->result,TEXTLEN);
  _strlwr_s(strretn,32);
  if( strstr(strretn,da->result) )
  {
    return TRUE;
  }
  return FALSE;
}

/**
 * 在指令List列表中,搜索跳转指令node跳转的目标指令
 *
 * List:指令列表
 * node: 跳转指令
 *
 * 返回值:找到node是跳转向List中的哪个指令,则返回该目标指令,否则NULL
 */
CodeNode * CCodeILFactory::SearchAddrAsList(list<CodeNode*> * List, CodeNode * node)  // *
{
  // node是一个跳转指令,现在获取其跳转地址
  DWORD GotoAddr = node->disasm.jmpconst;
  DWORD LastAddr = 0;
  list<CodeNode*>::iterator itr;
  // 遍历List中的每个语句
  for (itr = List->begin(); itr != List->end(); itr++)
  {
    CodeNode * code = * itr;
    t_disasm t_da;
    // 获取语句反汇编代码
    memcpy(&t_da, &code->disasm, sizeof(t_disasm));

    // 检测node是否欲跳转至t_da
    if (GotoAddr == t_da.ip)
    {
      node->IsJmcType = TRUE;
      return code;
    }
    // 否则,如果跳转地址在t_da指令之后,则保存t_da的地址
    else if (GotoAddr > t_da.ip)
    {
      LastAddr = t_da.ip;
    }
    // 否则,如果跳转地址在t_da指令之前
    else if (GotoAddr < t_da.ip)
    {
      if (LastAddr)
      {
        // 指令跳转地址不明确
        node->IsJmcUndefineType = TRUE;  
      }
    }
  }
  return NULL;
}

/**
 * 将指定指令列表CodeList编译为虚拟机字节码
 *
 * baseaddr:代码基址
 * CodeList:欲编译的指令
 * ErrText:返回出错信息
 *
 * 返回值:成功返回TRUE,否则FALSE
 */
BOOL CCodeILFactory::BuildCode(char * baseaddr, list<CodeNode*> * CodeList, char * ErrText)    // *
{
  return VMFactory.BuildVMCode(baseaddr, CodeList, ErrText);
}


// ----------------------------------- CCodeILFactory.h ----------------------------------------
// VMPackLib

#ifndef __CCODEILFACTORY__
#define __CCODEILFACTORY__

#include <Windows.h>
#include <stdio.h>
#include "CVirtualMemory.h"
#include "CVMFactory.h"
#include "asm\disasm.h"

/**
 * 中间语言字节码工厂
 */
class CCodeILFactory
{
public:
  CVirtualMemory m_JumpTable;    // JMP表
  CVirtualMemory m_CodeEngine;  // VM指令引擎
  CVirtualMemory m_EnterStub;    // 重新进入vm的代码
  CVirtualMemory m_VMEnterStubCode;  // 重新进入的vmcode
  CVirtualMemory m_VMCode;    // 通常的vmcode
public:
  // 虚拟机工厂
  CVMFactory VMFactory;
public:
  CCodeILFactory();
public:
  void  Init(DWORD VirtualAddress);
public:
  // 编译汇编代码为VM字节码,返回成功失败
  BOOL  BuildCode(char* baseaddr,list<CodeNode*> *CodeList,char* ErrText);
public:

  // 反汇编代码,识别出函数结束的地方.添加到提供的链表中
  // CodeList : 输出汇编链表
  // base_addr: 2进制代码
  // VirtualAddress : 虚拟地址
  void  DisasmFunction(list<CodeNode*> *CodeList,char* base_addr, DWORD VirtualAddress);


private:
  // 是否是跳转指令
  BOOL  IsJumpCmd(t_disasm* da);
  // 是否是函数调用指令
  BOOL  IsCallCmd(t_disasm* da);
  // 是否是返回指令
  BOOL  IsRetnCmd(t_disasm* da);
  //从链表中搜索一个地址的节点
  CodeNode*  SearchAddrAsList( list<CodeNode*>* List,CodeNode* node );

};// END CLASS DEFINITION CCodeILFactory

#endif // __CCODEILFACTORY__

// ------------------------------------------ VCommand.h----------------------------------------
// 漏发的文件
// "VMEND"结束标记
#define    VM_END          __asm    push    esi \
                  __asm    dec     ebp \
                  __asm    inc     ebp \
                  __asm    dec     esi \
                  __asm    inc     esp

#define    INTCC          __asm    _emit  0xCC
// 进入虚拟机
void VStartVM();
//检测堆栈是否覆盖
void DCheckESP();

//带D的为内部Handler
//内部push pop只做32位操作,段寄存器也当寄存器处理
void DPushReg32();
void DPushImm32();
void DPushMem32();

void DPopReg32();//弹回寄存器
void DFree();//释放堆栈

  • 标 题:答复
  • 作 者:shecx
  • 时 间:2010-07-13 09:50:25

很多文件都已发表了,就只剩下跟《编译》与《反编译》有关部分,这一部分也很有意思,不过要理解它,需要先了解机器指令的格式,这一部分随便在网上找找都有,因为篇幅问题,对机器指令格式就不细说

实际上,《编译》与《反编译》部分的源代码是属于OllyDBG的,接下来将会发表《编译》与《反编译》部分的内容,如果可以,会将整个程序源码作为附件发送,待续......

  • 标 题:编译部分
  • 作 者:shecx
  • 时 间:2010-07-13 12:47:14

// ----------------------------------------- assemble.cpp ------------------------------------------

// 此文件用于扫描汇编指令,得到扫描的符号,并检测操作数的合法性,最后可将某个语句编译成机器码

// 理解此文件需要理解机器码的格式

// 这是一个很有意思的文件

// Free Disassembler and Assembler -- Assembler
//
// Copyright (C) 2001 Oleh Yuschuk
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// 16.01.2002 - corrected error in processing of immediate constants.


#define STRICT

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//#include <dir.h>
#include <math.h>
#include <float.h>
#pragma hdrstop

#include "disasm.h"

#pragma   warning(disable:4996)    // 关闭旧函数声明警告
////////////////////////////////////////////////////////////////////////////////
///////////////////////////// ASSEMBLER FUNCTIONS //////////////////////////////

// Scanner modes.
#define SA_NAME        0x0001          // Don't try to decode labels
#define SA_IMPORT      0x0002          // Allow import pseudolabel

// Types of input tokens reported by scanner.

// 表示指令行结束
#define SCAN_EOL       0               // End of line

// 扫描到8位寄存器
#define SCAN_REG8      1               // 8-bit register

// 扫描到16位寄存器
#define SCAN_REG16     2               // 16-bit register

// 扫描到32位寄存器
#define SCAN_REG32     3               // 32-bit register

// 扫描到段寄存器
#define SCAN_SEG       4               // Segment register

// 扫描到FPU寄存器
#define SCAN_FPU       5               // FPU register

// 扫描到MMX寄存器
#define SCAN_MMX       6               // MMX register

// 扫描到控制寄存器
#define SCAN_CR        7               // Control register

// 扫描到调试寄存器
#define SCAN_DR        8               // Debug register

// 扫描到操作数据长度
#define SCAN_OPSIZE    9               // Operand size modifier
#define SCAN_JMPSIZE   10              // Jump size modifier
#define SCAN_LOCAL     11              // Address on stack in form LOCAL.decimal
#define SCAN_ARG       12              // Address on stack in form ARG.decimal
#define SCAN_PTR       20              // PTR in MASM addressing statements
#define SCAN_REP       21              // REP prefix
#define SCAN_REPE      22              // REPE prefix
#define SCAN_REPNE     23              // REPNE prefix
#define SCAN_LOCK      24              // LOCK prefix
#define SCAN_NAME      25              // Command or label

// 扫描到16进制常数
#define SCAN_ICONST    26              // Hexadecimal constant

// 扫描到10进制数
#define SCAN_DCONST    27              // Decimal constant

// 扫描到不明确的常量值
#define SCAN_OFS       28              // Undefined constant

// 扫描到浮点数
#define SCAN_FCONST    29              // Floating-point constant
#define SCAN_EIP       30              // Register EIP
#define SCAN_SIGNED    31              // Keyword "SIGNED" (in expressions)
#define SCAN_UNSIGNED  32              // Keyword "UNSIGNED" (in expressions)
#define SCAN_CHAR      33              // Keyword "CHAR" (in expressions)
#define SCAN_FLOAT     34              // Keyword "FLOAT" (in expressions)
#define SCAN_DOUBLE    35              // Keyword "DOUBLE" (in expressions)
#define SCAN_FLOAT10   36              // Keyword "FLOAT10" (in expressions)
#define SCAN_STRING    37              // Keyword "STRING" (in expressions)
#define SCAN_UNICODE   38              // Keyword "UNICODE" (in expressions)
#define SCAN_MSG       39              // Pseudovariable MSG (in expressions)

// 扫描到其他符号,如','
#define SCAN_SYMB      64              // Any other character
#define SCAN_IMPORT    65              // Import pseudolabel

// 扫描出错
#define SCAN_ERR       255             // Definitely bad item

// Definition used by Assembler to report command matching errors.
#define MA_JMP         0x0001          // Invalid jump size modifier

// 指令操作数个数有误
#define MA_NOP         0x0002          // Wrong number of operands
// 操作数类型有误
#define MA_TYP         0x0004          // Bad operand type
#define MA_NOS         0x0008          // Explicit operand size expected
// 操作数长度有误
#define MA_SIZ         0x0010          // Bad operand size
// 操作数大小不同
#define MA_DIF         0x0020          // Different operand sizes
#define MA_SEG         0x0040          // Invalid segment register
#define MA_RNG         0x0080          // Constant out of expected range

/**
 * 用于存储汇编指令中某个操作数的相关信息
 */
typedef struct t_asmoperand
{
  // 操作数类型
  // REG:表示操作数是通用寄存器
  // RST:表示操作数是FPU寄存器
  // IMM:表示操作数是一个立即数
  // JMF:表示操作数是jmp或call的地址
  // SGM:表示操作数是一个段寄存器
  // MXL:表示操作数是一个XLAT操作数  // XLAT operand ([EBX+AL])
  // MRG:表示操作数包含 ModRM 寻址的信息
  int            type;                 // Operand type, see beginning of file

  // 表明操作数的长度,如8位寄存器则为1,
  // 16位寄存器则为2,32位寄存器则为4
  int            size;                 // Operand size or 0 if yet unknown

  // 例如当type为某个寄存器时,指明操作相应寄存器的下标
  int            index;                // Index or other register
  int            scale;                // Scale
  int            base;                 // Base register if present

  // 当操作数是一个立即数时,存储该立即数
  // 当操作数是通过如OFFSET计算出的偏移量时,存放偏移量,但实际上偏移量的值是不明确的
  long           offset;               // Immediate value or offset

  // 表示offset的数存在,但其值不明确(即存放的可能是某个偏移量)
  int            anyoffset;            // Offset is present but undefined

  // 当操作数是 seg:offset 时,offset存储其偏移,segment存储seg段信息
  int            segment;              // Segment in address if present

  // 指定跳转指令的跳转地址修饰符,如FAR、NEAR、SHORT、LONG等
  // SHORT:1,LONG:2,NEAR:4,FAR:8
  int            jmpmode;              // Specified jump size
} t_asmoperand;

// 指向以0字符结束、要编译的源代码指令
static char      * asmcmd;              // Pointer to 0-terminated source line

/**
 * 表明扫描的元素类型
 * SCAN_REG8:表明是8位通用寄存器
 * SCAN_FPU:表明是FPU寄存器
 * SCAN_OPSIZE:表明是操作数长度修饰符,如BYTE
 * SCAN_JMPSIZE:表明是jmp跳转指令跳转长度修饰符,如SHORT
 * SCAN_REP:表明扫描到指令前缀:REP等
 * SCAN_LOCK:表明扫描到指令前缀:LOCK
 * SCAN_PTR:表明扫描到 PTR 修饰符
 *
 * SCAN_SIGNED: 表明扫描到各种数据类型修饰符
 * SCAN_UNSIGNED
 * SCAN_CHAR
 * SCAN_FLOAT
 * SCAN_DOUBLE
 *
 * SCAN_NAME:表明扫描到标识符名称、指令名等
 */
static int       scan;                 // Type of last scanned element

// 当如idata为 || 等运算符时,prio代表该运算符的优先级
static int       prio;                 // Priority of operation (0: highest)

// 存储最后扫描到的名字(如标识名,指令名等)
static char      sdata[TEXTLEN];       // Last scanned name (depends on type)

/**
 * 保存最后扫描到的元素值:
 * 1、如果扫描到的是寄存器,则保存该寄存器编号
 * 2、如果扫描到的是长度修饰符(见数组sizename[]),则是修饰符下标
 * 3、如果扫描到的是jmp指令跳转长度修饰符,则如SHORT为1、LONG为2
 * 4、如果扫描到的是SCAN_LOCAL,则表示 Local.decimal 后的 decimal
 * 5、如果扫描到的是SCAN_ARG,则表示 Arg.decimal 后的 decimal
 * 6、如果扫描到的是标识符名称、指令名等,则表示该名称的长度
 * 7、如果扫描到的是数字,则保存数字值
 * 8、如果扫描到的是字符,则保存该字符的ASCII码
 *    实际上,当扫描到字符时,scan中存放的是SCAN_ICONST,表示扫描
 *    到常数,由此可见,扫描到字符是以扫描到常数看待的
 * 9、如果扫描到的是运算符,则存储之,例如 idata = '||'
 * 10、如果扫描到的是SCAN_IMPORT,则存储import name
 */
static long      idata;                // Last scanned value

// 用于存储扫描到的浮点大数
static long      double fdata;         // Floating-point number
// 指向出错的内容
static char      *asmerror;            // Explanation of last error, or NULL

// Simple and slightly recursive scanner shared by Assemble(). The scanner is
// straightforward and ineffective, but high speed is not a must here. As
// input, it uses global pointer to source line asmcmd. On exit, it fills in
// global variables scan, prio, sdata, idata and/or fdata. If some error is
// detected, asmerror points to error message, otherwise asmerror remains
// unchanged.

/**
 * 对汇编指令进行扫描,扫描结果存储至全局变量中:
 * scan:存放扫描到的元素类型
 * prio:如果扫描到的是运算符,则存储其优先级
 * sdata:存放扫描元素的字符串
 * idata:存放扫描元素的相关值
 * fdata:同idata,当扫描到的元素是一浮点数时,保存该值
 * asmerror:存放扫描过程中出现的错误信息
 *
 * mode:指明扫描模式
 *
 * 返回值:SCAN_ERR表明扫描中出现错误
 *       SCAN_EOL表明扫描结束
 *       ...
 */
static void Scanasm(int mode)    // *
{
  int i, j, base, maxdigit;
  long decimal, hex;
  long double floating, divisor;
  char s[TEXTLEN], * pcmd;
  sdata[0] = '\0';
  idata = 0;

  // 如果汇编指令为空,则出错
  if (asmcmd == NULL)
  {
    asmerror = "NULL input line";
    scan = SCAN_ERR;
    return;
  };

  // 跳过汇编指令前的空格
  while (*asmcmd == ' ' || *asmcmd == '\t')
    asmcmd++;      // Skip leading spaces

  // 扫描到汇编指令结束符,则直接返回
  if (*asmcmd == '\0' || *asmcmd == ';')
  {
    scan = SCAN_EOL;
    return;
  };

  // 判断asmcmd是否以英文字母,或下划线,或@开头,是则表明可能是指令名、寄存器名或标识符名称...
  if (isalpha(*asmcmd) || *asmcmd == '_' || *asmcmd == '@')
  {
    // 先保存首字符
    sdata[0] = *asmcmd++;
    // Some keyword or identifier
    i = 1;

    // 注:isalnum()用于判断字符是否是数字或字母

    // 将指令后续内容复制到sdata[]中
    while ((isalnum(*asmcmd) || *asmcmd == '_' || *asmcmd == '@') && i < sizeof(sdata))
      sdata[i++] = *asmcmd++;

    // 如果标识符长度超限制,则出错返回
    if (i >= sizeof(sdata))
    {
      asmerror = "Too long identifier";
      scan = SCAN_ERR; 
      return;
    };
    // 给扫描到的sdata名称添加结束符
    sdata[i] = '\0';

    // 再次跳过空格
    while (*asmcmd == ' ' || *asmcmd=='\t')
      // Skip trailing spaces
      asmcmd++;

    // 将sdata复制至s中,并转为大写字母
    strcpy(s, sdata);
    _strupr_s(s);

    // 至此,s可能是指令名、寄存器名或标识符名称...

    // 判断s是否是低8位寄存器,如AL、CL、DL等
    for (j = 0; j <= 8; j++)
    {
      // j == 8 means "any register"
      if (strcmp(s, regname[0][j]) != 0)
        continue;
      idata = j;

      // 表示扫描结果是一个8位寄存器
      scan = SCAN_REG8;    // 8-bit register
      return;
    };
    // 判断s是否是16位寄存器,如AX、CX、DX等
    for (j = 0; j <= 8; j++)
    {
      if (strcmp(s, regname[1][j]) != 0)
        continue;
      idata = j;
      scan = SCAN_REG16;    // 16-bit register
      return;
    };
    // 判断s是否是32位寄存器,如EAX、ECX、EDX等
    for (j = 0; j <= 8; j++)
    {
      if (strcmp(s, regname[2][j]) != 0)
        continue;
      idata = j; 
      scan = SCAN_REG32;    // 32-bit register
      return;
    };
    // 判断s是否是段寄存器,如CS、SS等
    for (j = 0; j < 6; j++)
    {
      if (strcmp(s, segname[j]) != 0)
        continue;
      idata = j;
      scan = SCAN_SEG;    // Segment register
      // 跳过空格
      // 问题:进入此判断前已经跳过一次了,怎么还需跳过 [??]
      while (*asmcmd == ' ' || *asmcmd == '\t')
        asmcmd++;      // Skip trailing spaces
      return;
    };

    // 判断是否扫描到FPU寄存器
    if (strcmp(s, "ST") == 0)
    {
      // 先保存原汇编指令指针,以便需要时恢复
      pcmd = asmcmd;
      // 递归调用Scanasm()继续扫描
      Scanasm(SA_NAME);    // FPU register

      // 后面的内容非 '(',即非"ST(",则不处理之,
      // 并且将原扫描的ST当作FPU寄存器处理
      if (scan != SCAN_SYMB || idata != '(')
      {
        // 恢复为原汇编指令
        asmcmd = pcmd;    // Undo last scan
        idata = 0;
        scan = SCAN_FPU;
        return;
      };

      // 至此,说明扫描到的是 ST(,则继续扫描"("后的内容
      Scanasm(SA_NAME);

      // j 存储该扫描到的内容的值
      j = idata;
      // 检测在ST后是否是 0 - 7 的数 [ST0, ST1...ST7], 如果不是,则说明该FPU寄存器出错
      if ((scan != SCAN_ICONST && scan != SCAN_DCONST) || idata < 0 || idata > 7)
      {
        asmerror = "FPU registers have indexes 0 to 7";
        scan = SCAN_ERR;
        return;
      };
      // 至此,说明前面扫描到的是ST([0-7]),则继续扫描
      Scanasm(SA_NAME);
      // 扫描不到")",说明不是ST([0-7]),则出错返回
      if (scan != SCAN_SYMB || idata != ')')
      {
        asmerror = "Closing parenthesis expected";
        scan = SCAN_ERR;
        return;
      };
      // 至此,说明扫描到的是 ST([0-7])
      idata = j;
      scan = SCAN_FPU;
      return;
    };

    // 判断是否是FPU寄存器,格式:ST[0 - 7]
    for (j = 0; j <= 8; j++)
    {
      if (strcmp(s, fpuname[j]) != 0)
        continue;
      idata = j;
      scan = SCAN_FPU;    // FPU register (alternative coding)
      return; 
    };

    // 判断是否是MMX寄存器,如MM0、MM1
    for (j = 0; j <= 8; j++)
    {
      if (strcmp(s, mmxname[j]) != 0) 
        continue;
      idata = j;
      scan = SCAN_MMX;    // MMX register
      return;
    };

    // 判断是否是控制寄存器,如CR0、CR1
    for (j = 0; j <= 8; j++)
    {
      if (strcmp(s, crname[j]) != 0)
        continue;
      idata = j;
      scan = SCAN_CR;      // Control register
      return;
    };

    // 判断是否调试寄存器,如DR0、DR1
    for (j = 0; j <= 8; j++) 
    {
      if (strcmp(s, drname[j]) != 0) 
        continue;
      idata = j;
      scan = SCAN_DR;      // Debug register
      return;
    };

    // 判断是否是数据长度修饰符
    for (j = 0; j < sizeof(sizename) / sizeof(sizename[0]); j++) 
    {
      if (strcmp(s, sizename[j]) != 0)
        continue;
      pcmd = asmcmd;
      // 如果是数据长度修饰符,则继续扫描看是否带有PTR
      Scanasm(SA_NAME);
      
      // 如果不是如BYTE PTR格式,则恢复
      if (scan != SCAN_PTR)    // Fetch non-functional "PTR"
        asmcmd = pcmd;
      idata = j;
      scan = SCAN_OPSIZE;    // Operand (data) size in bytes
      return;
    };

    // 判断是否是EIP寄存器
    if (strcmp(s, "EIP") == 0)
    {
      // Register EIP
      scan = SCAN_EIP;
      idata = 0;
      return;
    };

    // 判断是否是JMP指令跳转长度修饰符
    if (strcmp(s, "SHORT") == 0) 
    {
      // Relative jump has 1-byte offset
      scan = SCAN_JMPSIZE;
      idata = 1;
      return;
    };
    if (strcmp(s, "LONG") == 0)
    {
      // Relative jump has 4-byte offset
      scan = SCAN_JMPSIZE;
      idata = 2;
      return;
    };
    if (strcmp(s, "NEAR") == 0) 
    {
      // Jump within same code segment
      scan = SCAN_JMPSIZE;
      idata = 4;
      return;
    };
    if (strcmp(s, "FAR") == 0) 
    {
      // Jump to different code segment
      scan = SCAN_JMPSIZE;
      idata = 8;
      return; 
    };

    // LOCAL是定义局部变量用的.用这个关键字是为了让编译器
    // 给变量分配内存.
    // 格式是:
    //    LOCAL  变量名:变量类型
    
    // LOCAL.decimal
    if (strcmp(s, "LOCAL") == 0 && *asmcmd == '.')
    {
      // 跳过"."
      asmcmd++;
      // 跳过空格
      while (*asmcmd == ' ' || *asmcmd == '\t')
        asmcmd++;        // Skip trailing spaces
      // 判断在LOCAL后是否是数字, LOCAL.后面必须是数字
      if (!isdigit(*asmcmd))
      {
        asmerror = "Integer number expected";
        scan = SCAN_ERR;
        return;
      };
      // 获取 LOCAL. 后面的数字信息
      while (isdigit(*asmcmd))  // LOCAL index is decimal number!
        idata = idata * 10 + (*asmcmd++) - '0';
      scan = SCAN_LOCAL;
      return; 
    };

    // ARG.decimal
    if (strcmp(s, "ARG") == 0 && *asmcmd == '.')
    {
      asmcmd++;
      while (*asmcmd == ' ' || *asmcmd == '\t')
        asmcmd++;        // Skip trailing spaces
      if (!isdigit(*asmcmd))
      {
        asmerror = "Integer number expected";
        scan = SCAN_ERR;
        return;
      };
      while (isdigit(*asmcmd))  // ARG index is decimal number!
        idata = idata * 10 + (*asmcmd++) - '0';
      scan = SCAN_ARG;
      return;
    };

    // 判断是否是REP前缀
    if (strcmp(s, "REP") == 0) 
    {
      scan = SCAN_REP;
      return; 
    };
    // 判断是否是REPE前缀
    if (strcmp(s, "REPE") == 0 || strcmp(s, "REPZ") == 0)
    {
      scan = SCAN_REPE;
      return;
    };
    // 判断是否是REPNE前缀
    if (strcmp(s, "REPNE") == 0 || strcmp(s, "REPNZ") == 0) 
    {
      scan = SCAN_REPNE;
      return;
    };
    // 判断是否是LOCK前缀
    if (strcmp(s, "LOCK") == 0) 
    {
      scan = SCAN_LOCK;
      return;
    };
    // 判断是否PTR
    if (strcmp(s, "PTR") == 0)
    {
      scan = SCAN_PTR;
      return;
    };

    // Undefined constant
    // Present but undefined offset/constant
    // OFFSET:在汇编中用于得到变量的偏址,偏址相当于一个常量,但该常量值并不明确多少(在哪)
    if (strcmp(s, "CONST") == 0 || strcmp(s, "OFFSET") == 0)
    {
      scan = SCAN_OFS;
      return;
    };

    // Keyword "SIGNED" (in expressions)
    // 判断是否是有符号数
    if (strcmp(s, "SIGNED") == 0)
    {
      scan = SCAN_SIGNED;
      return; 
    };
    // Keyword "UNSIGNED" (in expressions)
    // 判断是否无符号数
    if (strcmp(s, "UNSIGNED") == 0)
    {
      scan = SCAN_UNSIGNED;
      return;
    };
    // Keyword "CHAR" (in expressions)
    // 判断是数据类型CHAR
    if (strcmp(s, "CHAR") == 0)
    {
      scan = SCAN_CHAR;
      return;
    };
    // 判断是数据类型FLOAT
    // Keyword "FLOAT" (in expressions)
    if (strcmp(s, "FLOAT") == 0) 
    {
      scan = SCAN_FLOAT;
      return;
    };
    // Keyword "DOUBLE" (in expressions)
    // 判断是数据类型DOUBLE
    if (strcmp(s, "DOUBLE") == 0) 
    {
      scan = SCAN_DOUBLE;
      return; 
    };
    // Keyword "FLOAT10" (in expressions)
    // 判断是数据类型FLOAT10
    if (strcmp(s, "FLOAT10") == 0)
    {
      scan = SCAN_FLOAT10; 
      return;
    };
    // Keyword "STRING" (in expressions)
    // 判断是数据类型STRING
    if (strcmp(s, "STRING") == 0) 
    {
      scan = SCAN_STRING;
      return; 
    };
    // Keyword "UNICODE" (in expressions)
    // 判断是数据类型UNICODE
    if (strcmp(s, "UNICODE") == 0)
    {
      scan = SCAN_UNICODE;
      return;
    };
    // Pseudovariable MSG (in expressions)
    if (strcmp(s, "MSG") == 0)
    {
      scan = SCAN_MSG;
      return;
    };

    // 至此,说明s是标签了,包括标识符名称、指令名等,无需对标签进行解码,则直接返回
    if (mode & SA_NAME) 
    {
      idata = i;
      // Don't try to decode symbolic label
      scan = SCAN_NAME;
      return;
    }

    // 至此,说明此标识符无效,出错返回
    asmerror = "Unknown identifier";
    scan = SCAN_ERR;
    return; 
  }
  // 否则,如果扫描到的是数字
  else if (isdigit(*asmcmd))
  {
    // Constant
    // 数字基数
    base = 0;
    // 该数所属进制数最大数,如十进制为9,十六进制为15
    maxdigit = 0;
    // 存储该数字
    decimal = hex = 0L;
    floating = 0.0;

    // Force hexadecimal number
    // 数字以0X/0x开头,则是16进制数
    if (asmcmd[0] == '0' && toupper(asmcmd[1]) == 'X')
    {
      // 基数为16
      base = 16;
      // 跳过数字的0X/0x
      asmcmd += 2;
    };
    // 获取该数字
    while (1) 
    {
      // 如果是数字
      if (isdigit(*asmcmd))
      {
        decimal = decimal * 10 + (*asmcmd) - '0';
        floating = floating * 10.0 + (*asmcmd) - '0';
        hex = hex * 16 + (*asmcmd) - '0';
        // 默认最大数为9,即以十进制数算
        if (maxdigit == 0)
          maxdigit = 9;
        asmcmd++;
      }
      // 否则,如果是16进制数的字母部分
      else if (isxdigit(*asmcmd)) 
      {
        hex = hex * 16 + toupper(*asmcmd++) - 'A' + 10;
        maxdigit = 15; 
      }
      else
        break;
    };
    // 如果进制的最大数为0,那就奇怪了
    if (maxdigit == 0)
    {
      asmerror = "Hexadecimal digits after 0x... expected";
      scan = SCAN_ERR;
      return;
    };
    // 如果数字后为H字母,则也是16进制数
    if (toupper(*asmcmd) == 'H')
    {
      // Force hexadecimal number
      if (base == 16)
      {
        // 16进制数格式不应该为:0xXXXXH
        asmerror = "Please don't mix 0xXXXX and XXXXh forms";
        scan = SCAN_ERR;
        return;
      };
      // 跳过 'H' 字符
      asmcmd++;
      // 保存该数字
      idata = hex;
      // 扫描到16进制常数
      scan = SCAN_ICONST;
      while (*asmcmd == ' ' || *asmcmd == '\t')
        asmcmd++;
      return;
    };
    // 可能是浮点数,必须是一个10进制数
    if (*asmcmd == '.') 
    {
      // Force decimal number
      // 但如果不是10进制数,则出错
      if (base == 16 || maxdigit > 9) 
      {
        asmerror = "Not a decimal number";
        scan = SCAN_ERR;
        return;
      };
      asmcmd++;
      // 如果带有小数,或是科学计数法
      if (isdigit(*asmcmd) || toupper(*asmcmd) == 'E')
      {
        divisor = 1.0;    // 除数
        // 计算小数部分
        while (isdigit(*asmcmd))
        {
          // Floating-point number
          divisor /= 10.0;
          floating += divisor * (*asmcmd - '0');
          asmcmd++; 
        };
        // 科学记数法
        if (toupper(*asmcmd) == 'E')
        {
          asmcmd++;
          if (*asmcmd == '-')
          {
            base = -1;
            asmcmd++;
          }
          else 
            base = 1;
          if (!isdigit(*asmcmd))
          {
            asmerror = "Invalid exponent";
            scan = SCAN_ERR; 
            return;
          };
          decimal = 0;
          while (isdigit(*asmcmd))
          {
            if (decimal < 65536L)
              decimal = decimal * 10 + (*asmcmd++) - '0';
          };

          floating *= powl(decimal * base, 1);
        };
        fdata = floating;
        // 扫描到一浮点数
        scan = SCAN_FCONST;
        return;
      }
      // 扫描到一10进制数
      else
      {
        idata = decimal;
        scan = SCAN_DCONST;
        while (*asmcmd == ' ' || *asmcmd == '\t') 
          asmcmd++;
        return;
      };
    };
    // 
    idata = hex;
    // Default is hexadecimal
    // 默认情况下用16进制数
    scan = SCAN_ICONST;
    while (*asmcmd == ' ' || *asmcmd == '\t') 
      asmcmd++;
    return;
  }
  // 否则,如果以 ' 开始,表示扫描到字符
  else if (*asmcmd=='\'') 
  {
    // Character constant
    // 字符常量,跳过 ' 符号
    asmcmd++;
    if (*asmcmd == '\0' || (*asmcmd == '\\' && asmcmd[1] == '\0'))
    {
      asmerror = "Unterminated character constant";
      scan = SCAN_ERR;
      return;
    };
    // 空字符,出错返回
    // 问题:为何不能使用空字符呢 [??]
    if (*asmcmd == '\'')
    {
      asmerror = "Empty character constant";
      scan = SCAN_ERR; 
      return;
    };
    // 跳过转义字符的 "\"
    if (*asmcmd == '\\')
      asmcmd++;

    // 获取该字符的ASCII码
    idata = *asmcmd++;

    // 从这里看得出来,似乎只有一个字符

    // 扫描结束字符
    if (*asmcmd != '\'')
    {
      asmerror = "Unterminated character constant";
      scan = SCAN_ERR;
      return;
    };
    asmcmd++;
    // 跳过空格部分
    while (*asmcmd == ' ' || *asmcmd == '\t') 
      asmcmd++;

    // 扫描到字符,相当于扫描到ASCII码,所以,
    // 也将扫描结果作为常量看待
    scan = SCAN_ICONST;
    return;
  }
  // 否则,扫描到的可能是一些运算符
  else
  {
    // Any other character or combination
    // 存储首字符
    idata = sdata[0] = *asmcmd++;
    sdata[1] = sdata[2] = '\0';
    // 判断是否逻辑运算符
    if (idata == '|' && *asmcmd == '|')
    {
      // '||'
      // 问题:'||'是一个字符吗 [??]
      idata = '||';
      prio = 10;
      sdata[1] = *asmcmd++;
    }
    else if (idata == '&' && *asmcmd == '&')
    {
      // '&&'
      idata = '&&';
      prio = 9;
      sdata[1] = *asmcmd++;
    }
    else if (idata == '=' && *asmcmd == '=') 
    {
      // '=='
      idata = '==';
      prio = 5;
      sdata[1] = *asmcmd++; 
    }
    else if (idata == '!' && *asmcmd == '=')
    {
      // '!='
      idata = '!=';
      prio = 5;
      sdata[1] = *asmcmd++;
    }
    else if (idata == '<' && *asmcmd == '=')
    {
      // '<='
      idata = '<=';
      prio = 4;
      sdata[1] = *asmcmd++;
    }
    else if (idata == '>' && *asmcmd == '=') 
    {
      // '>='
      idata = '>=';
      prio = 4;
      sdata[1] = *asmcmd++;
    }
    else if (idata == '<' && *asmcmd == '<') 
    {
      // '<<'
      idata = '<<';
      prio = 3;
      sdata[1] = *asmcmd++; 
    }
    else if (idata == '>' && *asmcmd == '>')
    {
      // '>>'
      idata = '>>';
      prio = 3;
      sdata[1] = *asmcmd++; 
    }
    // '|'
    else if (idata == '|')
      prio = 8;
    // 异或运算符
    // '^'
    else if (idata == '^') 
      prio = 7;
    else if (idata == '&') 
      prio = 6;       // '&'

    // 似乎表示是否是 include < 指令的 < 部分
    else if (idata == '<')
    {
      if (*asmcmd == '&')
      {
        // Import pseudolabel (for internal use)
        if ((mode & SA_IMPORT) == 0)
        {
          asmerror = "Syntax error";
          scan = SCAN_ERR;
          return;
        };
        asmcmd++;
        i = 0;
        // 获取import指令的import name
        while (*asmcmd != '\0' && *asmcmd != '>')
        {
          sdata[i++] = *asmcmd++;
          if (i >= sizeof(sdata))
          {
            asmerror = "Too long import name"; 
            scan = SCAN_ERR; 
            return;
          };
        };
        // import 指令必须 <&XXXX>
        if (*asmcmd != '>') 
        {
          asmerror = "Unterminated import name";
          scan = SCAN_ERR; 
          return;
        };
        asmcmd++;
        // 设置name的结束符
        sdata[i] = '\0';
        scan = SCAN_IMPORT;
        return;
      }
      // 只是一个 < 号
      else
        prio = 4;
    }
    else if (idata == '>')
      prio = 4;    // '>'
    else if (idata == '+')
      prio = 2;    // '+'
    else if (idata == '-')
      prio = 2;    // '-'
    else if (idata == '*') 
      prio = 1;    // '*'
    else if (idata == '/')
      prio = 1;    // '/'
    else if (idata == '%') 
      prio = 1;    // '%'
    else if (idata == ']')
    {
      pcmd = asmcmd; 
      Scanasm(SA_NAME);
      if (scan != SCAN_SYMB || idata != '[')
      {
        idata = ']';
        asmcmd = pcmd; 
        prio = 0;
      }
      // 将 ][ 符号解析为 + 号
      else
      {
        idata = '+';
        prio = 2;  // Translate '][' to '+'
      };
    }
    // 否则,扫描到其他符号
    else
      prio = 0;    // Any other character

    // 其他符号
    scan = SCAN_SYMB;
    return;
  };
};

// Fetches one complete operand from the input line and fills in structure op
// with operand's data. Expects that first token of the operand is already
// scanned. Supports operands in generalized form (for example, R32 means any
// of general-purpose 32-bit integer registers).

/**
 * 对汇编指令操作数进行解析
 *
 * op: 用于返回解析的操作数
 */
static void Parseasmoperand(t_asmoperand * op)    // *
{
  int i, j, bracket, sign, xlataddr;
  int reg, r[9];
  long offset;

  // No or bad operand
  // 扫描不到操作数,或出错,则直接返回
  if (scan == SCAN_EOL || scan == SCAN_ERR)
    return;

  // Jump or call address may begin with address size modifier(s) SHORT, LONG,
  // NEAR and/or FAR. Not all combinations are allowed. After operand is
  // completely parsed, this function roughly checks whether modifier is
  // allowed. Exact check is done in Assemble().

  // 跳转或call指令的地址可以用修饰符修饰,如SHORT、LONG、NEAR和FAR。并不是所有组
  // 合都是允许的。此函数只是大要检测修饰是否允许,而更精确的检测在Assemble()中

  // 如果扫描到Jump地址修饰符,如SHORT
  if (scan == SCAN_JMPSIZE)
  {
    j = 0;
    // 扫描并获取所有地址修饰符
    while (scan == SCAN_JMPSIZE)
    {
      j |= idata;    // Fetch all size modifiers
      // 往下继续扫描看是否有修饰符
      Scanasm(0);
    };

    // SHORT:1,LONG:2,NEAR:4,FAR:8

    // 如果修饰符即有SHORT,又掺杂了LONG,则出错
    if (((j & 0x03) == 0x03) ||    // Mixed掺杂 SHORT and LONG
      ((j & 0x0C) == 0x0C) ||    // Mixed NEAR and FAR
      ((j & 0x09) == 0x09))    // Mixed FAR and SHORT
    {
      asmerror = "Invalid combination of jump address modifiers";
      scan = SCAN_ERR;
      return;
    };
    // 如果未指定FAR,则强制为NEAR
    if ((j & 0x08) == 0)
      j |= 0x04;      // Force NEAR if not FAR

    // 保存跳转地址修饰符(模式)
    op->jmpmode = j;
  };

  // Simple operands are either register or constant, their processing is
  // obvious and straightforward.

  // 最简单的操作数为寄存器操作数或立即数,他们的处理是显而易见和易懂的

  // 扫描到8位或16位或32位通用寄存器
  if (scan == SCAN_REG8 || scan == SCAN_REG16 || scan == SCAN_REG32)
  {
    // 表示操作数类型为寄存器操作
    op->type = REG;
    // 指明操作的目标寄存器的下标
    op->index = idata;    // Integer general-purpose register

    // 保存操作数的长度值
    if (scan == SCAN_REG8)
      op->size = 1;
    else if (scan == SCAN_REG16)
      op->size = 2;
    else 
      op->size = 4;
  }
  // 否则,如果是FPU寄存器操作
  else if (scan == SCAN_FPU)
  {
    // FPU register
    op->type = RST;
    op->index = idata;
  }
  // 否则,如果是MMX寄存器
  else if (scan == SCAN_MMX) 
  {
    // MMX or 3DNow! register
    op->type = RMX;
    op->index = idata;
  }
  // 否则,如果是CR控制寄存器
  else if (scan == SCAN_CR)
  {
    // Control register
    op->type = CRX;
    op->index = idata;
  }
  // 否则,如果是调试寄存器
  else if (scan == SCAN_DR) 
  {
    // Debug register
    op->type = DRX;
    op->index = idata;
  }

  // 至此,说明扫描到的不是寄存器操作数

  // 否则,如果是其他符号,负数...
  else if (scan == SCAN_SYMB && idata == '-')
  {
    // 负数
    Scanasm(0);      // Negative constant

    // 扫描到的不是数字,则出错
    if (scan != SCAN_ICONST && scan != SCAN_DCONST && scan != SCAN_OFS)
    {
      asmerror = "Integer number expected";
      scan = SCAN_ERR;
      return;
    };
    // 操作类型为直接数操作
    op->type = IMM;

    // 保存直接数或偏移
    op->offset = -idata;
    if (scan == SCAN_OFS)
      // 表示offset数存在,但该值不明确
      op->anyoffset = 1;
  }
  // 操作数是一个 正数
  else if (scan == SCAN_SYMB && idata == '+') 
  {
    // 扫描是否是一个正数
    Scanasm(0);      // Positive constant
    // 如果扫描不到一整数,则出错返回
    if (scan != SCAN_ICONST && scan != SCAN_DCONST && scan != SCAN_OFS)
    {
      asmerror = "Integer number expected";
      scan = SCAN_ERR;
      return;
    };
    // 操作数是一直接数
    op->type = IMM;
    op->offset = idata;
    // 如果该操作数是一个偏移地址
    if (scan == SCAN_OFS)
      op->anyoffset = 1;
  }
  // 扫描到直接数或偏移地址(值不明确的直接数)
  else if (scan == SCAN_ICONST || scan == SCAN_DCONST || scan == SCAN_OFS)
  {
    j = idata;
    // 扫描到的是偏移
    if (scan == SCAN_OFS)
      // 表示该直接数的值不明确
      op->anyoffset = 1;

    // 扫描直接数后的内容
    Scanasm(0);
    // 扫描是否 seg:offset 格式地址
    if (scan == SCAN_SYMB && idata == ':')
    {
      Scanasm(0);      // Absolute long address (seg:offset)
      // 如果 seg: 后不是一偏移地址,则出错
      if (scan != SCAN_ICONST && scan != SCAN_DCONST && scan != SCAN_OFS) 
      {
        asmerror = "Integer address expected";
        scan = SCAN_ERR;
        return;
      };
      // 操作数是一跳转地址
      op->type = JMF;
      // 偏移地址
      op->offset = idata;
      // 段地址
      op->segment = j;
      if (scan == SCAN_OFS) 
        op->anyoffset = 1;
    }
    // 否则,说明操作数是一个直接数
    else
    {
      op->type = IMM;
      // offset保存该直接数
      op->offset = j;      // Constant without sign
      return;          // Next token already scanned
    };
  }
  // 否则,如操作数是一个浮点数,命令是不能带浮点数的,所以出错返回
  else if (scan == SCAN_FCONST)
  {
    asmerror = "Floating-point numbers are not allowed in command";
    scan = SCAN_ERR;
    return;
  }

  // 指令格式: mov dword ptr es:[eax + ecx * 8 + 0x11223344], 0x12345678

  // Segment register or address.
  // 段寄存器或地址
  else if (scan == SCAN_SEG || scan == SCAN_OPSIZE || (scan == SCAN_SYMB && idata == '['))
  {
    // Segment register or address
    bracket = 0;    // 括弧

    // 段寄存器
    if (scan == SCAN_SEG)
    {
      // 保存段寄存器编号
      j = idata;
      Scanasm(0);
      // 判断是否为 seg:offset,如果不是,则说明扫描到的只是一个段寄存器
      if (scan != SCAN_SYMB || idata != ':')
      {
        // Segment register as operand
        // 操作数是段寄存器
        op->type = SGM;
        op->index = j;
        return;    // Next token already scanned
      };
      // 以 seg:offset 出现,则保存段寄存器编号
      op->segment = j;
      // 扫描以获得 offset
      Scanasm(0);
    };

    // Scan 32-bit address. This parser does not support 16-bit addresses.
    // First of all, get size of operand (optional可选), segment register (optional)
    // and opening bracket (required).
    while (1)
    {
      // 扫描到 [ 
      if (scan == SCAN_SYMB && idata == '[')
      {
        // 如果前面已扫描到此符号,则出错返回
        if (bracket)
        {
          // Bracket
          asmerror = "Only one opening bracket allowed";
          scan = SCAN_ERR;
          return; 
        };
        // 表示扫描到 [
        bracket = 1;
      }
      // 扫描到操作数大小修饰符,如BYTE
      else if (scan == SCAN_OPSIZE)
      {
        // 操作数大小修饰符重复
        if (op->size != 0)
        {
          // Size of operand
          asmerror = "Duplicated size modifier修饰语";
          scan = SCAN_ERR;
          return;
        };
        op->size = idata;
      }
      // 扫描到段寄存器
      else if (scan == SCAN_SEG)
      {
        // 段寄存器重复
        if (op->segment != SEG_UNDEF)
        {
          // Segment register
          asmerror = "Duplicated segment register";
          scan = SCAN_ERR;
          return;
        };
        // 保存段寄存器编号
        op->segment = idata;
        Scanasm(0);
        if (scan != SCAN_SYMB || idata != ':') 
        {
          asmerror = "Semicolon分号 expected";
          scan = SCAN_ERR;
          return;
        };
      }
      else if (scan == SCAN_ERR)
        return;
      else 
        break;      // None of expected address elements
      Scanasm(0);
    };
    // 地址必须包含有 [ 符号
    if (bracket == 0)
    {
      asmerror = "Address expression requires brackets";
      scan = SCAN_ERR;
      return;
    };

    // Assembling a 32-bit address may be a kind of nightmare恶梦, due to a large
    // number of allowed forms. Parser collects immediate offset in op->offset
    // and count for each register in array r[]. Then it decides whether this
    // combination is valid and determines scale, index and base. Assemble()
    // will use these numbers to select address form (with or without SIB byte,
    // 8- or 32-bit offset, use segment prefix or not). As a useful side effect
    // of this technique, one may specify, for example, [EAX*5] which will
    // correctly assemble to [EAX*4+EAX].

    // 编译一个32位地址简直是一个恶梦,因为对于地址有大量的格式可以使用。解析程序
    // 将收集直接数偏移地址存放于op->offset中,与寄存器相关的地址信息则统计到 r[]
    // 数组中。并决定该地址组合是否有效,确定scale,index与base的值。Assemble()函
    // 数将会使用这些值进行确定地址格式(是否需要SIB字节来进行寻址,8位还是32位偏
    // 址,是否使用段前缀等)。作为一个非常有效的技术,举例:[EAX * 5] 可以被正确
    // 编译为 [EAX * 4 + EAX]



















    // 初始化寄存器值,该数组用于统计在地址中对寄存器的使用情况
    for (i = 0; i <= 8; i++)
      r[i] = 0;

    // 默认符号
    sign = '+';      // Default sign for the first operand
    xlataddr = 0;
    while (1)
    {
      // Get SIB and offset
      // 扫描到 符号,则保存之
      if (scan == SCAN_SYMB && (idata == '+' || idata == '-'))
      {
        sign = idata;

        // 往下扫描符号后面的数
        Scanasm(0);
      };
      // 出错,直接返回
      if (scan == SCAN_ERR)
        return;
      // 语法错误
      if (sign == '?')
      {
        asmerror = "Syntax error"; 
        scan = SCAN_ERR;
        return;
      };

      // XLAT:查表指令 XLAT 
      // 指令格式:XLAT TABLE 其中TABLE为一待查表格的首地址。 
      // 指令功能:把待查表格的一个字节内容送到AL累加器中。在执行该指令前,
      //    应将TABLE先送至BX寄存器中,然后将待查字节与其在表格中距表首
      //    地址位移量送AL,即(AL)<--((BX)+(AL)). 执行XLAT将使待查内容送
      //    到累加器。
      // 本指令不影响状态标位,表格长度不超过256字节。

      // Register AL appears as part of operand of (seldom很少 used) command XLAT.
      // 因为作为操作数使用8位AL寄存器是很少见的,所以如果出现,则可能使用了XLAT指令
      if (scan == SCAN_REG8 && idata == REG_EAX)
      {
        // 寄存器前不能使用 - 符号
        if (sign == '-')
        {
          asmerror = "Unable to subtract register";
          scan = SCAN_ERR;
          return;
        };
        if (xlataddr != 0)
        {
          asmerror = "Too many registers"; 
          scan = SCAN_ERR; 
          return;
        };
        // 假设使用了XLAT指令
        xlataddr = 1;
        Scanasm(0);
      }
      // 不支持16位寄存器作为地址
      else if (scan == SCAN_REG16)
      {
        asmerror = "Sorry, 16-bit addressing is not supported";
        scan = SCAN_ERR;
        return;
      }
      // 32位寄存器地址
      else if (scan == SCAN_REG32)
      {
        if (sign == '-') 
        {
          asmerror = "Unable to subtract register";
          scan = SCAN_ERR;
          return;
        };
        // 保存寄存器编号
        reg = idata;

        // 继续扫描
        Scanasm(0);

        // 如果是如上格式 EAX * 4,*后面的数字称为scale
        if (scan == SCAN_SYMB && idata == '*')
        {
          // 扫描 scale
          Scanasm(0);    // Try index*scale
          if (scan == SCAN_ERR)
            return;
          // 不明确的scale是不允许使用不明确的值
          if (scan == SCAN_OFS) 
          {
            asmerror = "Undefined scale is not allowed"; 
            scan = SCAN_ERR; 
            return;
          };
          // 如果扫描到的不是 常量值 ,则不能作为scale使用,语法错误
          if (scan != SCAN_ICONST && scan != SCAN_DCONST)
          {
            asmerror = "Syntax error";
            scan = SCAN_ERR; 
            return;
          };
          // scale的值只能是:1,2,4,8。当在地址中出现如5的scale时,将
          // 其当成4使用,但如果出现6、7等,则不能使用
          if (idata == 6 || idata == 7 || idata > 9)
          {
            asmerror = "Invalid scale";
            scan = SCAN_ERR;
            return;
          };
          // 计算reg寄存器的总 scale
          r[reg] += idata;
          Scanasm(0);
        }
        // 否则,说明 scale = 1
        else
          r[reg]++;
      }      // Simple register


      // 以下两个部分不太理解



      // LOCAL是定义局部变量用的.用这个关键字是为了让编译器
      // 给变量分配内存. 
      // 格式是: LOCAL 变量名:变量类型
      else if (scan == SCAN_LOCAL)
      {
        r[REG_EBP]++;
        // 在堆栈上分配 idata * 4 空间用于存放局部变量 [??]
        op->offset -= idata * 4;
        Scanasm(0);
      }
      // ARG r_Kernel32 : DWORD
      // push r_Kernel32
      // call Whereis_GPA
      // 伪操作符arg的作用是,容许你输入一段字符或者数字作为
      // 一个函数的参数(只在 pwb环境中)
      else if (scan == SCAN_ARG)
      {
        r[REG_EBP]++;
        op->offset += (idata + 1) * 4;
        Scanasm(0);
      }
      // 扫描到直接数,则地址可能以scale * index出现
      else if (scan == SCAN_ICONST || scan == SCAN_DCONST)
      {
        offset = idata;
        Scanasm(0);
        if (scan == SCAN_SYMB && idata == '*')
        {
          // 尝试是否 4 * EAX 等格式
          Scanasm(0);        // Try scale * index
          if (scan == SCAN_ERR)
            return;
          // 寄存器前不能加 - 号
          if (sign == '-')
          {
            asmerror = "Unable to subtract register"; 
            scan = SCAN_ERR;
            return;
          };
          // 不支持16位寄存器地址
          if (scan == SCAN_REG16) 
          {
            asmerror = "Sorry, 16-bit addressing is not supported";
            scan = SCAN_ERR;
            return;
          };
          // 必须是32位寄存器
          if (scan != SCAN_REG32)
          {
            asmerror = "Syntax error";
            scan = SCAN_ERR; 
            return;
          };
          // 无效的scale
          if (offset == 6 || offset == 7 || offset > 9)
          {
            asmerror = "Invalid scale";
            scan = SCAN_ERR;
            return; 
          };
          // 统计该寄存器的scale值
          r[idata] += offset;
          // 继续扫描后面的部分
          Scanasm(0);
        }
        // 在scale后面没有 * 寄存器,则说明是偏移量
        else 
        {
          if (sign == '-') 
            op->offset -= offset;
          else
            op->offset += offset;
        };
      }
      else if (scan == SCAN_OFS) 
      {
        Scanasm(0);
        // 如果ofs作为scale用,但因为ofs不明确,所以是不允许的
        if (scan == SCAN_SYMB && idata == '*')
        {
          asmerror = "Undefined scale is not allowed";
          scan = SCAN_ERR;
          return;
        }
        // 否则,说明不是用于scale,则表示是一偏移
        else
        {
          op->anyoffset = 1;
        };
      }
      // 不属于以上情况,则跳地址无关,跳出循环
      else
        break;        // None of expected address elements

      // 如果已扫描到地址结束符 ],则结束
      if (scan == SCAN_SYMB && idata == ']')
        break;

      // 至此,说明上面所有扫描无法得到有效结果,
      // 则将sign置为 ? 表示上面的扫描有问题
      sign = '?';
    };

    // 扫描出错,直接返回
    if (scan == SCAN_ERR) 
      return;
    // 扫描不到 ']',则语法错误
    if (scan != SCAN_SYMB || idata != ']') 
    {
      asmerror = "Syntax error";
      scan = SCAN_ERR;
      return;
    };
    // Process XLAT address separately.
    // 如果确实扫描到XLAT地址,则检测其格式是否OK
    if (xlataddr != 0)
    {
      // XLAT address in form [EBX+AX]
      // 这里似乎只是为检测除EBX寄存器外,是否还使用了其他寄存器
      // 作为地址使用,当然,对XLAT,基址只使用EBX,所以,如果有
      // 其他寄存器作为地址使用,或还指明了偏址,那将是错误的
      for (i = 0; i <= 8; i++)
      {
        // Check which registers used
        if (i == REG_EBX)
          continue;
        if (r[i] != 0)
          break;
      };
      if (i <= 8 || r[REG_EBX] != 1 || op->offset != 0 || op->anyoffset != 0) 
      {
        asmerror = "Invalid address";
        scan = SCAN_ERR;
        return;
      };
      // XLAT操作数
      op->type = MXL;
    }
    // Determine scale, index and base.
    // 否则,说明是真正的地址了,现在需要确定scale,index与base了
    else
    {
      j = 0;    // Number of used registers
      for (i = 0; i <= 8; i++)
      {
        // i寄存器未被使用作为地址部分,跳过
        if (r[i] == 0)
          continue;    // Unused register
        // 如果scale值为3、5或9
        if (r[i] == 3 || r[i] == 5 || r[i] == 9)
        {
          // 如果index存在,或基址存在
          if (op->index >= 0 || op->base >= 0)
          {
            if (j == 0)
              asmerror = "Invalid scale";
            else 
              asmerror = "Too many registers";
            scan = SCAN_ERR;
            return;
          };
          op->index = op->base = i;
          // scale 只能是:1、2、4、8
          op->scale = r[i] - 1;
        }
        else if (r[i] == 2 || r[i] == 4 || r[i] == 8)
        {
          // 已经有用于index的寄存器
          if (op->index >= 0) 
          {
            if (j <= 1) 
              asmerror = "Only one register may be scaled";
            else 
              asmerror = "Too many registers";
            scan = SCAN_ERR;
            return; 
          };
          op->index = i;
          op->scale = r[i];
        }
        // 如果 i 寄存器使用计数为1,则可能是base,也可能是index
        else if (r[i] == 1)
        {
          // 如果base还不存在,则将其作为base,这种可能性更高
          if (op->base < 0)
            op->base = i;
          // 否则,index未指定,则作为index用,并且scale为1
          else if (op->index < 0)
          {
            op->index = i;
            op->scale = 1;
          }
          // 而如果既指定了index又指定了base,那么说明扫描到太多的寄存器,出错
          else
          {
            asmerror = "Too many registers";
            scan = SCAN_ERR;
            return;
          };
        }
        // 错误的scale
        else 
        {
          asmerror = "Invalid scale";
          scan = SCAN_ERR;
          return;
        };
        // 统计用于地址的寄存器数
        j++;
      };
      // 操作数包含了 ModRM 寻址信息
      op->type = MRG;
    }; 
  }
  // 否则,说明是系统不支持的操作数
  else 
  {
    asmerror = "Unrecognized operand";
    scan = SCAN_ERR;
    return;
  };
  // In general, address modifier is allowed only with address expression which
  // is a constant, a far address or a memory expression. More precise check
  // will be done later in Assemble().
  // 如果是一个JMP指令,并且操作数不是直接数,并且也不是地址,并且也不未包含 ModRM 的地址,则出错
  if (op->jmpmode != 0 && op->type != IMM && op->type != JMF && op->type != MRG)
  {
    asmerror = "Jump address modifier is not allowed";
    scan = SCAN_ERR; 
    return;
  };
  // 扫描下一符号
  Scanasm(0);    // Fetch next token from input line
};

// Function assembles text into 32-bit 80x86 machine code. It supports imprecise不精确的,不确定的
// operands操作数 (for example, R32 stays for any general-purpose 32-bit register).
// This allows to search for incompletei不完全的 commands. Command is precise when all
// significant bytes in model.mask are 0xFF. Some commands have more than one
// decoding. By calling Assemble() with attempt=0,1... and constsize=0,1,2,3 one
// gets also alternative variants (bit 0x1 of constsize is responsible for size
// of address constant and bit 0x2 - for immediate data). However, only one
// address form is generated ([EAX*2], but not [EAX+EAX]; [EBX+EAX] but not
// [EAX+EBX]; [EAX] will not use SIB byte; no DS: prefix and so on). Returns
// number of bytes in assembled code or non-positive number in case of detected
// error. This number is the negation of the offset in the input text where the
// error encountered. Unfortunately, BC 4.52 is unable to compile the switch
// (arg) in this code when any common subexpression optimization is on. The
// next #pragma statement disables all optimizations.

//#pragma option -Od                     // No optimizations, or BC 4.52 crashes

// 将cmd编译为机器指令并通过model返回

// errtext:返回编译的出错信息

// 返回值:指令长度

int Assemble(char * cmd, ulong ip, t_asmmodel * model, int attempt, int constsize, char * errtext)
{
  // bytesize:为1表示只允许操作数为1个字节长度
  int i, j, k, namelen, nameok, arg, match, datasize, addrsize, bytesize, minop, maxop;
  int rep, lock, segment, jmpsize, jmpmode, longjump;
  int hasrm, hassib, dispsize, immsize;
  int anydisp, anyimm, anyjmp;
  long l, displacement, immediate, jmpoffset = 0;

  // name:存储指令完整助记符,包括指令前缀(如REP)与助记符
  // nameend:指向指令助记符(指令名)的末尾
  char name[32], * nameend;

  // 存储编译后的指令机器码,长度不超15字节
  char tcode[MAXCMDSIZE], tmask[MAXCMDSIZE];

  // 存储指令3个操作数信息,指令最多可以有3个操作数
  t_asmoperand aop[3], *op;      // Up to 3 operands allowed
  const t_cmddata * pd;

  // 代码长度为0
  if (model != NULL) 
    model->length = 0;
  // 检测参数的合法性
  if (cmd == NULL || model == NULL || errtext == NULL)
  {
    // 参数出错,则保存错误信息并返回
    if (errtext != NULL) 
      strcpy(errtext, "Internal OLLYDBG error");
    return 0;
  };        // Error in parameters
  // 保存欲编译的汇编指令
  asmcmd = cmd;

  // 指令是否有REP或LOCK前缀
  rep = lock = 0;
  errtext[0] = '\0';

  // 扫描指令
  Scanasm(SA_NAME);

  // 指令结束,则不需要做任何操作
  if (scan == SCAN_EOL)    // End of line, nothing to assemble
    return 0;

  // 扫描指令前缀
  while (1)
  {
    // Fetch all REPxx and LOCK prefixes
    // 如果是REP或REPE或REPNE前缀,则...
    if (scan == SCAN_REP || scan == SCAN_REPE || scan == SCAN_REPNE) 
    {
      // 指令的REP前缀重复,出错返回
      if (rep != 0)
      {
        strcpy(errtext, "Duplicated REP prefix"); 
        goto error;
      };
      rep = scan;
    }
    // 否则,如果是LOCK前缀(用于加锁总线)
    else if (scan == SCAN_LOCK)
    {
      // 同样,如果LOCK前缀重复,也出错返回
      if (lock != 0)
      {
        strcpy(errtext, "Duplicated LOCK prefix");
        goto error;
      };
      lock = scan;
    }
    else
      // 未发现更多前缀,跳出循环
      // No more prefixes
      break;
    // 往下扫描
    Scanasm(SA_NAME);
  };

  // 扫描不到指令助记符(即指令名),则出错
  if (scan != SCAN_NAME || idata > 16)
  {
    strcpy(errtext,"Command mnemonic助记符 expected");
    goto error;
  };

  // 指向指令结束符并将指令转为大写
  nameend = asmcmd;
  strupr(sdata);

  // Prepare full mnemonic (including repeat prefix, if any).
  // 与前缀组合成完整助记符
  if (rep == SCAN_REP)
    sprintf(name, "REP %s", sdata);
  else if (rep == SCAN_REPE)
    sprintf(name, "REPE %s", sdata);
  else if (rep == SCAN_REPNE)
    sprintf(name, "REPNE %s", sdata);
  else 
    strcpy(name, sdata);

  // 至此,name可能如:REPX 指令助记符

  // 往下扫描指令的操作数
  Scanasm(0);

  // 接下来需要解析命令的操作数(最多3个)

  // Parse command operands (up to 3). Note: jump address is always
  // the first (and only) operand in actual command set.
  // 初始化3个操作数结构信息
  for (i = 0; i < 3; i++) 
  {
    aop[i].type = NNN;      // No operand
    aop[i].size = 0;      // Undefined size
    aop[i].index = -1;      // No index
    aop[i].scale = 0;      // No scale
    aop[i].base = -1;      // No base
    aop[i].offset = 0;      // No offset
    aop[i].anyoffset = 0;    // No offset
    aop[i].segment = SEG_UNDEF;  // No segment
    aop[i].jmpmode = 0;
  };  // No jump size modifier

  // 解析第一个操作数
  Parseasmoperand(aop + 0);

  // 获取跳转模式
  jmpmode = aop[0].jmpmode;
  // 如果存在跳转模式,则表示是一跳转指令,最高位置1
  if (jmpmode != 0)
    jmpmode |= 0x80;  // 10000000b

  // 在Parseasmoperand()函数中会调用Scanasm()往下扫描指令

  // 第1个操作数后是 ','符号,则扫描第2个操作数并解析之
  if (scan == SCAN_SYMB && idata == ',')
  {
    // 扫描第2个操作数并解析之
    Scanasm(0);
    Parseasmoperand(aop + 1);

    // 还存在第3个操作数,则继续扫描之
    if (scan == SCAN_SYMB && idata == ',') 
    {
      // 扫描第3个操作数并解析之
      Scanasm(0);
      Parseasmoperand(aop + 2);
    };
  };
  // 扫描出错,获取出错信息并返回
  if (scan == SCAN_ERR)
  {
    strcpy(errtext, asmerror);
    goto error;
  };
  // 操作数结束后,指令还存在其他内容,那就奇怪了
  if (scan != SCAN_EOL)
  {
    strcpy(errtext, "Extra input after operand");
    goto error;
  };

  // If jump size is not specified, function tries to use short jump. If
  // attempt fails, it retries with long form.

  // 如果跳转指令的跳转地址大小(如SHORT,FAR)未指令,则函数尝试使用SHORT
  // 如果尝试失败,则尝试LONG跳转

  longjump = 0;      // Try short jump on the first pass
retrylongjump:
  nameok = 0;
  // Some commands allow different number of operands. Variables minop and
  // maxop accumulate their minimal and maximal counts. The numbers are not
  // used in assembly process but allow for better error diagnostics.
  // 指令最多、最少操作数数量,用于限制指令的操作数个数
  minop = 3;
  maxop = 0;
  // Main assembly loop: try to find the command which matches all operands,
  // but do not process operands yet.

  // 现在开始检测上面扫描到的指令与操作数是否匹配,是否OK

  // 指令长度
  namelen = (int)strlen(name);

  // 遍历处理器有效的(机器)指令列表,以检测上面扫描的指令是否OK
  for (pd = cmddata; pd->mask != 0; pd++)
  {
    // 如果指令助记符以&开始,则对助记符进行译码依赖于操作数长度
    // 例如:
    //    &PUSHA*
    //    &CBW:CWDE
    if (pd->name[0] == '&')
    {
      // Mnemonic助记符 depends on operand size
      // 跳过助记符前的&符号
      j = 1;
      // 数据长度默认为16位
      datasize = 2;
      // 地址长度默认为32位
      addrsize = 4;
      while (1)
      {
        // 某些指令可以有多个助记符,每个助记符以 :分开,所以检测时需对所有助记符进行匹配
        // Try all mnemonics (separated by ':')
        for (i = 0; pd->name[j] != '\0' && pd->name[j] != ':'; j++)
        {
          // 指令助记符名称中,可以包含有字符'W'或'D',用于表示指令的操作数长度,这种指令
          // 的译码结果往往操作数的长度起到很重要的决定作用

          // 如果助记符有*符号,则表示该助记符中在*号位
          // 置可能包含'W'或'D'以指示操作数长度
          if (pd->name[j] == '*')
          {
            // 如果扫描到的助记符包含'W',说明操作数长度为16位(字)
            if (name[i] == 'W')
            {
              datasize = 2;
              i++;
            }
            // 如果扫描到的助记符包含'D',说明操作数长度为32位(双字)
            else if (name[i] == 'D')
            {
              datasize = 4;
              i++;
            }
            // 如果没通过'W'或'D'指明操作数长度,则根据sizesens处理
            else if (sizesens == 0) 
              datasize = 2;
            else
              datasize = 4;
          }
          // 检测pd助记符与扫描的助记符是否匹配
          else if (pd->name[j] == name[i])
            i++;
          else
            break;
        };
        // 如果扫描的助记符与pd列表助记符相符,则找到指令信息,跳出循环
        if (name[i] == '\0' && (pd->name[j] == '\0' || pd->name[j] == ':'))
          break;        // Bingo!

        // 否则,说明助记符与pd不符,则需要搜索下一pd,如上例子
        // &CBW:CWDE,搜索CBW不符后,则需要搜索CWDE指令看是否相符
        while (pd->name[j] != '\0' && pd->name[j] != ':')
          j++;

        // 如果助记符确实以&CBW:CWDE出现,则需要检测:后的助记符是否与扫描到的助记符是否相符
        // 注:以 : 作为助记符分割的指令,一般前部分是16位指令,而后部分是32位指令
        if (pd->name[j] == ':')
        {
          j++;
          datasize = 4;
        }           // Retry with 32-bit mnenonic
        // 否则,只好搜索下一pd指令了
        else
        {
          i = 0; 
          break;                  // Comparison failed
        };
      };
      if (i == 0) 
        continue;
    }
    // 否则,如果指令助记符以 $ 开始,则对助记符进行译码依赖于地址长度
    // 此处操作同上,略
    else if (pd->name[0] == '$')  // Mnemonic depends on address size
    {
      // 跳过 $ 符号
      j = 1;
      datasize=0;
      addrsize=2;
      while (1)
      {
        // Try all mnemonics (separated by ':')
        for (i = 0; pd->name[j] != '\0' && pd->name[j] != ':'; j++)
        {
          if (pd->name[j] == '*')
          {
            // 如果地址大小为 16位
            if (name[i] == 'W')
            {
              addrsize = 2;
              i++;
            }
            // 否则,如果为32位
            else if (name[i] == 'D') 
            { 
              addrsize = 4;
              i++;
            }
            else if (sizesens == 0) 
              addrsize = 2;
            else 
              addrsize = 4; 
          }
          else if (pd->name[j] == name[i]) 
            i++;
          else 
            break;
        };
        if (name[i] == '\0' && (pd->name[j] == '\0' || pd->name[j] == ':'))
          break;            // Bingo!
        while (pd->name[j] != '\0' && pd->name[j] != ':')
          j++;
        if (pd->name[j] == ':')
        {
          j++;
          addrsize = 4;
        }              // Retry with 32-bit mnenonic
        else
        {
          i = 0;
          break;          // Comparison failed
        };
      };
      if (i == 0)
        continue;
    }
    // 否则,匹配其他指令助记符
    else    // Compare with all synonimes
    {
      j = k = 0;
      datasize = 0;      // Default settings
      addrsize = 4;
      // 检测扫描的助记符是否与pd匹配
      while (1)
      {
        // 有些指令有多个助记符,用','号分开
        // 例如:JLE,JNG--以逗号分割两个指令
        while (pd->name[j] != ',' && pd->name[j] != '\0') 
          j++;
        if (j - k == namelen && strnicmp(name, pd->name + k, namelen) == 0)
          break;
        // 如果前一助记符不符,则比较后面的助记符是否相符
        k = j + 1;
        // 已经比较完整个pd指令,则结束本环比较
        if (pd->name[j] == '\0')
          break;
        j = k;
      };
      if (k > j)
        continue;
    };

    // 至此,说明找到扫描助记符name的pd指令信息

    // For error diagnostics错误诊断法 it is important to know whether mnemonic exists.
    // 累计找到的pd指令信息
    nameok++;

    // 统计助记符的操作数个数

    // 根据pd指令信息,判断该指令的操作数个数
    if (pd->arg1 == NNN || pd->arg1 >= PSEUDOOP)
      minop = 0;
    // 否则,如果只有1个操作数
    else if (pd->arg2 == NNN || pd->arg2 >= PSEUDOOP)
    {
      if (minop > 1)
        minop = 1;
      if (maxop < 1)
        maxop = 1;
    }
    // 两个操作数
    else if (pd->arg3 == NNN || pd->arg3 >= PSEUDOOP)
    {
      // 限制其只有两个操作数
      if (minop > 2)
        minop = 2;
      if (maxop < 2)
        maxop = 2;
    }
    // 最多只能有3个操作数
    else
      maxop = 3;

    // Determine default and allowed operand size(s).

    // 在汇编语言中,数据长度有两个概念:默认长度与有效长度

    // 确定默认和允许的操作数长度
    // Forced 16-bit size
    // FF:表示强制16位大小
    if (pd->bits == FF)
      datasize = 2;    // Forced 16-bit size

    // 检测指令是否只允许使用1字节长度的操作数
    // 注:此处的检测逻辑可借鉴指令的编码构造文章
    if (pd->bits == WW || pd->bits == WS || pd->bits == W3 || pd->bits == WP)
      bytesize = 1;      // 1-byte size allowed
    else
      bytesize = 0;      // Word/dword size only

    // Check whether command operands match specified. If so, variable match
    // remains zero, otherwise it contains kind of mismatch. This allows for
    // better error diagnostics错误诊断法.
    // 检测命令操作数是否OK,如OK,则match保持为0,否则,包含不匹配的信息
    match = 0;
    // 检测扫描到的操作数是否OK
    for (j = 0; j < 3; j++)    // Up to 3 operands
    {
      // 获取操作数 j
      op = aop + j;
      // 获取指令pd的操作数类型
      if (j == 0)
        arg = pd->arg1;
      else if (j == 1)
        arg = pd->arg2;
      else 
        arg = pd->arg3;

      // 如果指令pd没有操作数j
      if (arg == NNN || arg >= PSEUDOOP)
      {
        // 而助记符name却带有该操作数,则出错
        if (op->type != NNN)    // No more arguments
          // 表示助记符操作数个数有误
          match |= MA_NOP;
        break;
      };
      // 至此,则说明指令pd有操作数j,但助记符name却没带操作数
      if (op->type == NNN)
      {
        match |= MA_NOP;
        break;
      };    // No corresponding operand

      // 否则,至此,说明带操作数,并且相符,则需要进一步操作了

      // 根据所带操作数处理,主要是根据pd操作数检测扫描的助记符操作数是否OK
      switch (arg)
      {
        // 寄存器操作数
      case REG:          // Integer register in Reg field
      case RCM:          // Integer register in command byte
        // 使用累加器寄存器
      case RAC:          // Accumulator (AL/AX/EAX, implicit)
        // 如果助记符使用的不是寄存器操作数,则类型出错
        if (op->type != REG)
          match |= MA_TYP;
        // 如果pd指令操作数使用累加寄存器(即AL、AX等),而扫描到的助记符使用的
        // 操作数寄存器有误,则说明操作数类型有误
        if (arg == RAC && op->index != REG_EAX && op->index != 8)
          match |= MA_TYP;

        // bytesize == 0 -> Word/dword size only
        // 如果pd指令只使用word/dword操作数,而助记符却使用byte,则操作数大小出错
        if (bytesize == 0 && op->size == 1)
          match |= MA_SIZ;
        // 如果pd没有指定数据长度,则设置为扫描到的助记符的操作数长度
        if (datasize == 0)
          datasize = op->size;
        // 操作数长度不同
        if (datasize != op->size)
          match |= MA_DIF;
        break;

        // 4字节长度的整型寄存器
      case RG4:          // Integer 4-byte register in Reg field
        // 如果助记符操作数不是寄存器,则类型出错
        if (op->type != REG)
          match |= MA_TYP;
        // 如果操作数长度匹配,则出错
        if (op->size != 4)
          match |= MA_SIZ;
        // 保存操作数长度值
        if (datasize == 0)
          datasize = op->size;
        if (datasize != op->size)
          match |= MA_DIF;
        break;

        // AX寄存器,16位
      case RAX:          // AX (2-byte, implicit)
        // 如果扫描到的助记符操作数不是EAX寄存器则出错
        if (op->type != REG || (op->index != REG_EAX && op->index != 8))
          match |= MA_TYP;
        // 不是16位寄存器,即不是AX,则出错
        if (op->size != 2) 
          match |= MA_SIZ;
        if (datasize == 0) 
          datasize = op->size;
        if (datasize != op->size)
          match |= MA_DIF;
        break;

        // DX寄存器,一般作为端口地址
      case RDX:          // DX (16-bit implicit port address)
        // 判断同上
        if (op->type != REG || (op->index != REG_EDX && op->index != 8))
          match |= MA_TYP;
        if (op->size != 2)
          match |= MA_SIZ; 
        break;

        // CL寄存器,一般作为移位用
      case RCL:          // Implicit CL register (for shifts)
        if (op->type != REG || (op->index != REG_ECX && op->index != 8))
          match |= MA_TYP;
        if (op->size != 1) 
          match |= MA_SIZ;
        break;

        // FPU堆栈顶寄存器
      case RS0:          // Top of FPU stack (ST(0))
        if (op->type != RST || (op->index != 0 && op->index != 8))
          match |= MA_TYP;
        break;

        // FPU堆栈寄存器
      case RST:          // FPU register (ST(i)) in command byte
        if (op->type != RST)
          match |= MA_TYP;
        break;

        // MMX寄存器
      case RMX:          // MMX register MMx
        // 3D寄存器
      case R3D:          // 3DNow! register MMx
        if (op->type != RMX)
          match |= MA_TYP;
        break;

        // 操作数是内存操作数或寄存器操作数,并且该操作数由ModRM提供寻址
        // 例如:MOV MRG, REG
      case MRG:          // Memory/register in ModRM byte
        // 如果扫描到的助记符操作数不是MRG,并且也不是寄存器,则出错
        if (op->type != MRG && op->type != REG) 
          match |= MA_TYP;
        // 如果操作数只允许使用16位或32位,而扫描到的助记符使用1字节,而长度出错
        if (bytesize == 0 && op->size == 1)
          match |= MA_SIZ;
        if (datasize == 0)
          datasize = op->size;
        if (op->size != 0 && op->size != datasize) 
          match |= MA_DIF;
        break;

        // 同上,操作数由ModRM提供寻址,并且长度为1字节
      case MR1:          // 1-byte memory/register in ModRM byte
        if (op->type != MRG && op->type != REG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 1)
          match |= MA_SIZ;
        break;

        // 同上,操作数由ModRM提供寻址,并且长度为2字节
      case MR2:          // 2-byte memory/register in ModRM byte
        if (op->type != MRG && op->type != REG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 2)
          match |= MA_SIZ;
        break;

        // 同上,操作数由ModRM提供寻址,并且长度为4字节
      case MR4:          // 4-byte memory/register in ModRM byte
        if (op->type != MRG && op->type != REG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 4)
          match |= MA_SIZ;
        break;

        // 操作数是4字节的寄存器
      case RR4:          // 4-byte memory/register (register only)
        if (op->type != REG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 4)
          match |= MA_SIZ;
        break;
      
        // 操作数是JMP的跳转目标地址,并且由ModRM提供寻址
        // 例如:CALL MRJ
      case MRJ:          // Memory/reg in ModRM as JUMP target
        if (op->type != MRG && op->type != REG)
          match |= MA_TYP;
        // 长度必须为4个字节
        if (op->size != 0 && op->size != 4) 
          match |= MA_SIZ;
        // Invalid jump size modifier
        if ((jmpmode & 0x09) != 0)
          match |= MA_JMP;
        jmpmode &= 0x7F;    // 1111111b
        break;

        // 对于8字节操作数不关心
      case MR8:          // 8-byte memory/MMX register in ModRM
      case MRD:          // 8-byte memory/3DNow! register in ModRM
        if (op->type != MRG && op->type != RMX)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 8) 
          match |= MA_SIZ;
        break;
      case RR8:          // 8-byte MMX register only in ModRM
      case RRD:          // 8-byte memory/3DNow! (register only)
        if (op->type != RMX)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 8) 
          match |= MA_SIZ;
        break;

      case MMA:          // Memory address in ModRM byte for LEA
        if (op->type != MRG)
          match |= MA_TYP;
        break;
      case MML:          // Memory in ModRM byte (for LES)
        if (op->type != MRG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 6)
          match |= MA_SIZ;
        if (datasize == 0)
          datasize = 4;
        else if (datasize != 4) 
          match |= MA_DIF;
        break;
      case MMS:          // Memory in ModRM byte (as SEG:OFFS)
        if (op->type != MRG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 6)
          match |= MA_SIZ;
        // MA_JMP: Invalid jump size modifier
        if ((jmpmode & 0x07) != 0)
          match |= MA_JMP;
        jmpmode &= 0x7F;
        break;
      case MM6:          // Memory in ModRm (6-byte descriptor)
        if (op->type != MRG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 6)
          match |= MA_SIZ;
        break;
      case MMB:          // Two adjacent memory locations (BOUND)
        if (op->type != MRG)
          match |= MA_TYP;
        k = op->size; 
        if (ideal == 0 && k > 1) 
          k /= 2;
        if (k != 0 && k != datasize)
          match |= MA_DIF;
        break;
      case MD2:          // Memory in ModRM byte (16-bit integer)
      case MB2:          // Memory in ModRM byte (16-bit binary)
        if (op->type != MRG) 
          match |= MA_TYP;
        if (op->size != 0 && op->size != 2)
          match |= MA_SIZ;
        break;
      case MD4:          // Memory in ModRM byte (32-bit integer)
      case MF4:          // Memory in ModRM byte (32-bit float)
        if (op->type != MRG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 4) 
          match |= MA_SIZ;
        break;
      case MD8:          // Memory in ModRM byte (64-bit integer)
      case MF8:          // Memory in ModRM byte (64-bit float)
        if (op->type != MRG)
          match |= MA_TYP;
        if (op->size != 0 && op->size != 8)
          match |= MA_SIZ;
        break;
      case MDA:          // Memory in ModRM byte (80-bit BCD)
      case MFA:          // Memory in ModRM byte (80-bit float)
        if (op->type != MRG) 
          matc h|= MA_TYP;
        if (op->size != 0 && op->size != 10)
          match |= MA_SIZ;
        break;
      case MFE:          // Memory in ModRM byte (FPU environment)
      case MFS:          // Memory in ModRM byte (FPU state)
      case MFX:          // Memory in ModRM byte (ext. FPU state)
        if (op->type != MRG) 
          match |= MA_TYP;
        if (op->size != 0)
          match |= MA_SIZ;
        break;
      case MSO:          // Source in string operands ([ESI])
        if (op->type != MRG || op->base != REG_ESI || op->index != -1 ||
          op->offset != 0 || op->anyoffset != 0)
          match |= MA_TYP;
        if (datasize == 0)
          datasize = op->size;
        if (op->size != 0 && op->size != datasize) 
          match |= MA_DIF;
        break;
      case MDE:          // Destination in string operands ([EDI])
        if (op->type != MRG || op->base != REG_EDI || op->index != -1 ||
          op->offset != 0 || op->anyoffset != 0)
          match |= MA_TYP;
        if (op->segment != SEG_UNDEF && op->segment != SEG_ES)
          match |= MA_SEG;
        if (datasize == 0)
          datasize = op->size;
        if (op->size != 0 && op->size != datasize)
          match |= MA_DIF;
        break;
      case MXL:          // XLAT operand ([EBX+AL])
        if (op->type != MXL) 
          match |= MA_TYP;
        break;

        // 操作数是一直接数
      case IMM:          // Immediate data (8 or 16/32)
      case IMU:          // Immediate unsigned data (8 or 16/32)
        if (op->type != IMM)
          match |= MA_TYP;
        break;

      case VXD:          // VxD service (32-bit only)
        if (op->type != IMM)
          match |= MA_TYP;
        if (datasize == 0)
          datasize = 4;
        if (datasize != 4) 
          match |= MA_SIZ;
        break;

        // jump/call的直接数地址
      case JMF:          // Immediate absolute far jump/call addr
        if (op->type != JMF)
          match |= MA_TYP;
        // 地址修饰出错
        if ((jmpmode & 0x05) != 0)
          match |= MA_JMP;
        jmpmode &= 0x7F;
        break;
      case JOB:          // Immediate byte offset (for jumps)
        if (op->type != IMM || longjump)
          match |= MA_TYP;
        if ((jmpmode & 0x0A) != 0)
          match |= MA_JMP;
        jmpmode &= 0x7F;
        break;
      case JOW:          // Immediate full offset (for jumps)
        if (op->type != IMM)
          match |= MA_TYP;
        if ((jmpmode & 0x09) != 0) 
          match |= MA_JMP;
        jmpmode &= 0x7F;
        break;
      case IMA:          // Immediate absolute near data address
        if (op->type != MRG || op->base >= 0 || op->index >= 0)
          match |= MA_TYP;
        break;
      case IMX:          // Immediate sign-extendable byte
        if (op->type != IMM)
          match |= MA_TYP;
        if (op->offset < -128 || op->offset > 127) 
          match |= MA_RNG;
        break;
      case C01:          // Implicit constant 1 (for shifts)
        if (op->type != IMM || (op->offset != 1 && op->anyoffset == 0))
          match |= MA_TYP;
        break;

      case IMS:          // Immediate byte (for shifts)
      case IM1:          // Immediate byte
        if (op->type != IMM)
          match |= MA_TYP;
        // 常数范围有误
        if (op->offset < -128 || op->offset > 255)
          match |= MA_RNG;
        break;
      case IM2:          // Immediate word (ENTER/RET)
        if (op->type != IMM)
          match |= MA_TYP;
        if (op->offset < 0 || op->offset > 65535)
          match |= MA_RNG;
        break;
        // 段寄存器
      case SGM:          // Segment register in ModRM byte
        if (op->type != SGM)
          match |= MA_TYP;
        if (datasize == 0) 
          datasize = 2;
        if (datasize != 2) 
          match |= MA_DIF;
        break;
        //
      case SCM:          // Segment register in command byte
        if (op->type != SGM)
          match |= MA_TYP;
        break;
        // 控制寄存器
      case CRX:          // Control register CRx
      case DRX:          // Debug register DRx
        if (op->type != arg)
          match |= MA_TYP;
        if (datasize == 0) 
          datasize = 4;
        if (datasize != 4) 
          match |= MA_DIF;
        break;
      case PRN:          // Near return address (pseudooperand)
      case PRF:          // Far return address (pseudooperand)
      case PAC:          // Accumulator (AL/AX/EAX, pseudooperand)
      case PAH:          // AH (in LAHF/SAHF, pseudooperand)
      case PFL:          // Lower byte of flags (pseudooperand)
      case PS0:          // Top of FPU stack (pseudooperand)
      case PS1:          // ST(1) (pseudooperand)
      case PCX:          // CX/ECX (pseudooperand)
      case PDI:          // EDI (pseudooperand in MMX extentions)
        break;
        // 错误操作数
      default:          // Undefined type of operand
        strcpy(errtext,"Internal Assembler error");
        goto error;
      };    // End of switch (arg)

      // 在switch(arg)中会对jmpmode进行 & 0x7F的计算,所以其0x80
      // 位正常应该为0,所以,如果不为0,则说明出错了
      if ((jmpmode & 0x80) != 0)
        match |= MA_JMP;
      // 如果操作数不匹配,则出错
      if (match != 0)
        break;      // Some of the operands doesn't match
    };    // End of operand matching loop

    // 如果所有操作数都匹配
    if (match == 0)      // Exact match found
    {
      // 允许尝试多次
      if (attempt > 0)
      {
        --attempt;
        nameok = 0;
      }    // Well, try to find yet another match
      else
        break;
    };
  };    // End of command search loop : for (pd = cmddata; pd->mask != 0; pd++)

  // 至此,对操作数的验证就结束了

  // Check whether some error was detected. If several errors were found
  // similtaneously, report one (roughly in order of significance).
  // 如果助记符无效,则出错
  if (nameok == 0)  // Mnemonic unavailable
  {
    strcpy(errtext,"Unrecognized command");
    asmcmd = nameend;
    goto error; 
  };

  // 操作数不匹配,则出错
  if (match != 0)    // Command not found
  {
    if (minop > 0 && aop[minop - 1].type == NNN)
      strcpy(errtext, "Too few operands");
    else if (maxop < 3 && aop[maxop].type != NNN)
      strcpy(errtext, "Too many operands");
    // 指令包含了不止一个命令
    else if (nameok > 1)      // More that 1 command
      strcpy(errtext, "Command does not support given operands");
    else if (match & MA_JMP)
      strcpy(errtext, "Invalid jump size modifier");
    else if (match & MA_NOP)
      strcpy(errtext, "Wrong number of operands");
    else if (match & MA_TYP)
      strcpy(errtext, "Command does not support given operands");
    else if (match & MA_NOS)
      strcpy(errtext, "Please specify operand size");
    else if (match & MA_SIZ)
      strcpy(errtext, "Bad operand size");
    else if (match & MA_DIF)
      strcpy(errtext, "Different size of operands");
    else if (match & MA_SEG)
      strcpy(errtext, "Invalid segment register");
    else if (match & MA_RNG)
      strcpy(errtext, "Constant out of expected range");
    else
      strcpy(errtext, "Erroneous command");
    goto error;
  };

  // Exact match found. Now construct the code.

  // 至此,命令是OK的,包括操作数,则需要解码该指令了

  // 指令是否包含ModR/M字节
  hasrm = 0;      // Whether command has ModR/M byte
  // 指令是否包含SIB字节
  hassib = 0;      // Whether command has SIB byte
  // displacement的长度(如果有)
  dispsize = 0;    // Size of displacement (if any)
  // immediate直接数的长度(如果有)
  immsize = 0;    // Size of immediate data (if any)
  // 指令段前缀
  segment = SEG_UNDEF;  // Necessary segment prefix
  // 
  jmpsize = 0;    // No relative jumps

  // tcode:应是用于存储解码后的机器指令,最长指令不超15字节
  memset(tcode, 0, sizeof(tcode));

  // 获取指令机器码
  *(ulong *)tcode = pd->code & pd->mask;
  memset(tmask, 0, sizeof(tmask));

  *(ulong *)tmask = pd->mask;
  // 指令最后字节的编号
  // len:指令的长度(不包括操作数),一般为单字节指令
  i = pd->len - 1;      // Last byte of command itself

  // 如果指令包含REP前缀,则长度 + 1
  if (rep)
    i++;        // REPxx prefixes count as extra byte

  // 指令w位说明:
  // 指令的最低位为w位或最低3位为w
  //    当 w = 0 时:operand size 是 byte 
  //    当 w = 1 时:operand size 是 default operand size

  // In some cases at least one operand must have explicit size declaration (as
  // in MOV [EAX],1). This preliminary check does not include all cases.
  // 在一些情况下,操作数必须明确指定其大小,但并不是所有情况都需要
  // 如果是最低位为w位
  if (pd->bits == WW || pd->bits == WS || pd->bits == WP)
  {
    // 如果是 WW || WS || WP,则需要指定操作数大小
    if (datasize == 0)
    {
      strcpy(errtext, "Please specify operand size");
      goto error;
    }
    // 操作数是 word / dword
    else if (datasize > 1)
      // 置w位为 1,表示操作数大小为 default operand size
      tcode[i] |= 0x01;    // WORD or DWORD size of operands
    // 表示 i 字节将会是操作数长度 [??]
    tmask[i] |= 0x01;
  }
  // 第3位为w位
  // Bit W at position 3
  else if (pd->bits == W3)
  {
    if (datasize == 0)
    {
      strcpy(errtext, "Please specify operand size");
      goto error;
    }
    // 置w位为 1,表示操作数大小为 default operand size
    else if (datasize > 1)
      tcode[i] |= 0x08;  // WORD or DWORD size of operands
    tmask[i] |= 0x08;
  };

  // Present suffix后缀 of 3DNow! command as immediate byte operand.
  if ((pd->type & C_TYPEMASK) == C_NOW)
  {
    immsize = 1;
    immediate = (pd->code >> 16) & 0xFF;
  };

  // Process operands again, this time constructing the code.
  // 再次处理操作数,并构建指令

  anydisp = anyimm = anyjmp = 0;
  
  for (j = 0; j < 3; j++)    // Up to 3 operands
  {
    // 获取操作数
    op = aop + j;
    if (j == 0)
      arg = pd->arg1;
    else if
      (j == 1)
      arg = pd->arg2;
    else
      arg = pd->arg3;

    // 所有的操作数都处理了,则跳出循环
    if (arg == NNN)
      break;      // All operands processed

    // 指令的格式:
    // Legacy Prefix + REX prefix + Opcode + ModRM + SIB + Displacement + Immediate

    // 根据操作数处理
    switch (arg)
    {
      // 处理寄存器操作数
    case REG:      // Integer register in Reg field
    case RG4:      // Integer 4-byte register in Reg field
    case RMX:      // MMX register MMx
    case R3D:      // 3DNow! register MMx
    case CRX:      // Control register CRx
    case DRX:      // Debug register DRx
      // 只要出现寄存器操作数,那么指令就会包含ModRM字节
      // 注:ModRM的格式:mod reg r/m 
      hasrm = 1;
      // 指定明确的寄存器作为操作数
      if (op->index < 8)
      {
        // 组织ModRM字节以指明寄存器操作数
        tcode[i + 1] |= (char)(op->index << 3);
        tmask[i + 1] |= 0x38;  // 111000
      };
      break;

      // 整数寄存器或FPU寄存器,则可以将寄存器编码至指令中
      // 这种指令使用低3位来指明指令使用的寄存器
    case RCM:      // Integer register in command byte
    case RST:      // FPU register (ST(i)) in command byte
      // 
      if (op->index < 8)
      {
        // 将寄存器号嵌入指令中
        tcode[i] |= (char)op->index; 
        tmask[i] |= 0x07;
      };
      break;

    case RAC:      // Accumulator (AL/AX/EAX, implicit)
    case RAX:      // AX (2-byte, implicit)
    case RDX:      // DX (16-bit implicit port address)
    case RCL:      // Implicit CL register (for shifts)
    case RS0:      // Top of FPU stack (ST(0))
    case MDE:      // Destination in string op's ([EDI])
    case C01:      // Implicit constant 1 (for shifts)
      break;      // Simply skip implicit operands

    case MSO:      // Source in string op's ([ESI])
    case MXL:      // XLAT operand ([EBX+AL])
      if (op->segment != SEG_UNDEF && op->segment != SEG_DS)
        segment = op->segment;
      break;

    case MRG:      // Memory/register in ModRM byte
    case MRJ:      // Memory/reg in ModRM as JUMP target
    case MR1:      // 1-byte memory/register in ModRM byte
    case MR2:      // 2-byte memory/register in ModRM byte
    case MR4:      // 4-byte memory/register in ModRM byte
    case RR4:      // 4-byte memory/register (register only)
    case MR8:      // 8-byte memory/MMX register in ModRM
    case RR8:      // 8-byte MMX register only in ModRM
    case MRD:      // 8-byte memory/3DNow! register in ModRM
    case RRD:      // 8-byte memory/3DNow! (register only)
      // 表示存在ModRM字节
      hasrm = 1;
      // 如果非MRG,则表示是寄存器寻址
      if (op->type != MRG)  // Register in ModRM byte
      {
        // 11000000:代表寄存器寻址
        tcode[i + 1] |= 0xC0;  // 11000000
        tmask[i + 1] |= 0xC0;
        // 明确指明寄存器,则设置寄存器号
        if (op->index < 8)
        {
          tcode[i + 1] |= (char)op->index;
          tmask[i + 1] |= 0x07;
        };
        break;
      };    // Note: NO BREAK, continue with address

      // 注意:这里没有break,继续处理地址信息

    case MMA:        // Memory address in ModRM byte for LEA
    case MML:        // Memory in ModRM byte (for LES)
    case MMS:        // Memory in ModRM byte (as SEG:OFFS)
    case MM6:        // Memory in ModRm (6-byte descriptor)
    case MMB:        // Two adjacent memory locations (BOUND)
    case MD2:        // Memory in ModRM byte (16-bit integer)
    case MB2:        // Memory in ModRM byte (16-bit binary)
    case MD4:        // Memory in ModRM byte (32-bit integer)
    case MD8:        // Memory in ModRM byte (64-bit integer)
    case MDA:        // Memory in ModRM byte (80-bit BCD)
    case MF4:        // Memory in ModRM byte (32-bit float)
    case MF8:        // Memory in ModRM byte (64-bit float)
    case MFA:        // Memory in ModRM byte (80-bit float)
    case MFE:        // Memory in ModRM byte (FPU environment)
    case MFS:        // Memory in ModRM byte (FPU state)
    case MFX:        // Memory in ModRM byte (ext. FPU state)
      hasrm = 1;
      // 获取操作数的偏移地址
      displacement = op->offset;
      // 地址中是否存在displacement值
      anydisp = op->anyoffset;
      
      // 地址没有包含base与index部分
      if (op->base < 0 && op->index < 0)
      {
        // 则直接数作为地址长度只有32位的情况了
        dispsize = 4;    // Special case of immediate address
        // 如果段前缀使用非默认段(即DS)
        if (op->segment != SEG_UNDEF && op->segment != SEG_DS)
          segment = op->segment;

        // 0x05:101,表示地址为 [disp32]
        tcode[i + 1] |= 0x05;
        tmask[i + 1] |= 0xC7;
      }
      // 如果地址没有包含index部分,并且base也不是esp寄存器,则不需要SIB字节来辅助寻址
      // 格式:[base + disp]
      else if (op->index < 0 && op->base != REG_ESP)
      {
        // 0xC0:11000000b
        tmask[i + 1] |= 0xC0;    // SIB byte unnecessary
        // 如果未提供offset(即是disp),除非非EBP寄存器,否则是有问题的
        if (op->offset == 0 && op->anyoffset == 0 && op->base != REG_EBP)
          ;            // [EBP] always requires offset
        else if ((constsize & 1) != 0 && ((op->offset >= -128 && op->offset < 128)
          || op->anyoffset != 0) )
        {
          // 0x40:1000000b,表示disp为8位数
          tcode[i + 1] |= 0x40;  // Disp8
          dispsize = 1;
        }
        else
        {
          // 否则,说明是32位disp数
          tcode[i + 1] |= 0x80;  // Disp32
          dispsize = 4;
        };
        // 明确指定基址寄存器
        if (op->base < 8)
        {
          // 如果指明操作数的段前缀与该基址的默认段不一致,则使用操作数指明的段作为前缀
          if (op->segment != SEG_UNDEF && op->segment != addr32[op->base].defseg)
            segment = op->segment;
          // 设置操作数的基址
          tcode[i + 1] |= (char)op->base;    // Note that case [ESP] has base<0.
          tmask[i + 1] |= 0x07;
        }
        // 否则,直接使用操作数指明的段寄存器
        else
          segment = op->segment;
      }
      // 地址包含index寄存器部分,则需要SIB寻址
      else    // SIB byte necessary
      {
        // 表明指令包含SIB字节
        hassib = 1;
        // 如果基址使用EBP寄存器,那么需要提供偏移量,所以,如果操作数未提供偏移量
        // 而且又提供了index寄存器,并且其scale为1,为了优先,可以将index 寄存器作
        // 为基址,而EBP作为index寄存器来使用
        if (op->base == REG_EBP &&      // EBP as base requires offset, optimize
          op->index >= 0 && op->scale == 1 && op->offset == 0 && op->anyoffset == 0)
        {
          op->base = op->index;
          op->index = REG_EBP;
        };
        // ESP寄存器不能作为index使用
        if (op->index == REG_ESP &&      // ESP cannot be an index, reorder
          op->scale <= 1) 
        {
          op->index = op->base;
          op->base = REG_ESP;
          op->scale = 1;
        };
        // 如果操作数不存在基址,而index寄存器存在,并且scale为2,并且其偏移量为8位,
        // 例如[index * 2 + XX],可以优化为:[base + index +XX]
        if (op->base < 0 &&          // No base means 4-byte offset, optimize
          op->index >= 0 && op->scale == 2 &&
          op->offset >= -128 && op->offset < 128 && op->anyoffset == 0)
        {
          op->base = op->index;
          op->scale = 1;
        };
        // 至此,如果index还是ESP,则出错
        if (op->index == REG_ESP)    // Reordering was unsuccessfull
        {
          strcpy(errtext, "Invalid indexing mode");
          goto error;
        };
        // 确实未指定基址
        if (op->base < 0)
        {
          tcode[i + 1] |= 0x04;
          dispsize = 4;
        }
        // 地址未包含displacement值
        else if (op->offset == 0 && op->anyoffset == 0 && op->base != REG_EBP)
          tcode[i + 1] |= 0x04;    // No displacement
        // 包含8位的displacement值
        else if ((constsize & 1) != 0 && ((op->offset >= -128 && op->offset < 128)
          || op->anyoffset != 0) )
        {
          tcode[i + 1] |= 0x44;    // Disp8
          dispsize = 1;
        }
        // 包含32位的displacement值
        else
        {
          tcode[i + 1] |= 0x84;    // Disp32
          dispsize = 4;
        }
        // 至此,ModRM字节OK了,现在处理SIB字节
        tmask[i + 1] |= 0xC7;      // ModRM completed, proceed with SIB

        // 处理完指令的ModRM,则继续处理SIB字节
        if (op->scale == 2)
          tcode[i + 2] |= 0x40;
        else if (op->scale == 4)
          tcode[i + 2] |= 0x80;
        else if (op->scale == 8)
          tcode[i + 2] |= 0xC0;
        tmask[i + 2] |= 0xC0;

        // 明确指定index寄存器
        if (op->index < 8) 
        {
          // 未指定index
          if (op->index < 0)
            op->index = 0x04;
          // 将index寄存器编码进SIB字节中
          tcode[i + 2] |= (char)(op->index << 3);
          tmask[i + 2] |= 0x38;
        };
        // 明确指定base寄存器
        if (op->base < 8) 
        {
          if (op->base < 0)
            op->base = 0x05;
          if (op->segment != SEG_UNDEF && op->segment != addr32[op->base].defseg)
            segment = op->segment;
          // 将base寄存器编码进SIB字节中
          tcode[i + 2] |= (char)op->base;
          tmask[i + 2] |= 0x07;
        }
        else 
          segment = op->segment; 
      };
      break;
    case IMM:        // Immediate data (8 or 16/32)
    case IMU:        // Immediate unsigned data (8 or 16/32)
    case VXD:        // VxD service (32-bit only)
      if (datasize == 0 && pd->arg2 == NNN && (pd->bits == SS || pd->bits == WS))
        datasize = 4;
      if (datasize == 0)
      {
        strcpy(errtext, "Please specify operand size");
        goto error;
      };
      immediate = op->offset;
      anyimm = op->anyoffset;
      if (pd->bits == SS || pd->bits == WS) 
      {
        if (datasize > 1 && (constsize & 2) != 0 && ((immediate >= -128 && immediate < 128)
          || op->anyoffset != 0)) 
        {
          immsize = 1;
          tcode[i] |= 0x02;
        }
        else
          immsize = datasize;
        tmask[i] |= 0x02;
      }
      else
        immsize = datasize;
      break;
    case IMX:          // Immediate sign-extendable byte
    case IMS:          // Immediate byte (for shifts)
    case IM1:          // Immediate byte
      if (immsize == 2)      // To accomodate ENTER instruction
        immediate = (immediate & 0xFFFF) | (op->offset << 16);
      else
        immediate = op->offset;
      anyimm |= op->anyoffset;
      immsize++;
      break;
    case IM2:          // Immediate word (ENTER/RET)
      immediate = op->offset;
      anyimm = op->anyoffset;
      immsize = 2; 
      break;
    case IMA:          // Immediate absolute near data address
      if (op->segment != SEG_UNDEF && op->segment != SEG_DS)
        segment = op->segment;
      displacement = op->offset;
      anydisp = op->anyoffset;
      dispsize = 4;
      break;
    case JOB:          // Immediate byte offset (for jumps)
      jmpoffset = op->offset;
      anyjmp = op->anyoffset;
      jmpsize = 1; 
      break;
    case JOW:          // Immediate full offset (for jumps)
      jmpoffset = op->offset;
      anyjmp = op->anyoffset;
      jmpsize = 4;
      break;
    case JMF:          // Immediate absolute far jump/call addr
      displacement = op->offset;
      anydisp = op->anyoffset;
      dispsize = 4;
      immediate = op->segment;
      anyimm = op->anyoffset; 
      immsize = 2;
      break;
    case SGM:          // Segment register in ModRM byte
      hasrm = 1;
      if (op->index < 6)
      {
        tcode[i + 1] |= (char)(op->index << 3); 
        tmask[i + 1] |= 0x38;
      };
      break;
    case SCM:          // Segment register in command byte
      if (op->index == SEG_FS || op->index == SEG_GS) 
      {
        tcode[0] = 0x0F;
        tmask[0] = 0xFF;
        i = 1;
        if (strcmp(name, "PUSH") == 0)
          tcode[i] = (char)((op->index << 3) | 0x80);
        else
          tcode[i] = (char)((op->index << 3) | 0x81);
        tmask[i] = 0xFF;
      }
      else if (op->index < 6)
      {
        if (op->index == SEG_CS && strcmp(name, "POP") == 0)
        {
          strcpy(errtext, "Unable to POP CS");
          goto error;
        };
        tcode[i] = (char)((tcode[i] & 0xC7) | (op->index << 3));
      }
      else
      {
        tcode[i] &= 0xC7;
        tmask[i] &= 0xC7;
      };
      break;
    case PRN:          // Near return address (pseudooperand)
    case PRF:          // Far return address (pseudooperand)
    case PAC:          // Accumulator (AL/AX/EAX, pseudooperand)
    case PAH:          // AH (in LAHF/SAHF, pseudooperand)
    case PFL:          // Lower byte of flags (pseudooperand)
    case PS0:          // Top of FPU stack (pseudooperand)
    case PS1:          // ST(1) (pseudooperand)
    case PCX:          // CX/ECX (pseudooperand)
    case PDI:          // EDI (pseudooperand in MMX extentions)
      break;          // Simply skip preudooperands
    default:          // Undefined type of operand
      strcpy(errtext, "Internal Assembler error");
      goto error;
    };
  };

  // 至此,说明指令中对操作数的处理已OK

  // Gather parts of command together in the complete command.
  j = 0;

  // 如果指令包含LOCK前缀
  if (lock != 0)      // Lock prefix specified
  {
    // 将LOCK前缀(即0xF0)编入指令中
    model->code[j] = 0xF0;
    model->mask[j] = 0xFF; 
    j++;
  };
  // 如果指令需使用数组长度前缀
  if (datasize == 2 && pd->bits != FF)  // Data size prefix necessary
  {
    // 将其编入指令中
    model->code[j] = 0x66;
    model->mask[j] = 0xFF; 
    j++;
  };
  // 同上
  if (addrsize == 2)    // Address size prefix necessary
  {
    model->code[j] = 0x67;
    model->mask[j] = 0xFF;
    j++;
  };
  // 指定段前缀,将段前缀编码入指令中
  if (segment != SEG_UNDEF)  // Segment prefix necessary
  {
    if (segment == SEG_ES)
      model->code[j] = 0x26;
    else if (segment == SEG_CS) 
      model->code[j] = 0x2E;
    else if (segment == SEG_SS)
      model->code[j] = 0x36;
    else if (segment == SEG_DS)
      model->code[j] = 0x3E;
    else if (segment == SEG_FS)
      model->code[j] = 0x64;
    else if (segment == SEG_GS)
      model->code[j] = 0x65;
    // 无效的段前缀
    else
    { 
      strcpy(errtext, "Internal Assembler error");
      goto error;
    };
    model->mask[j] = 0xFF; 
    j++;
  };
  // 如果displacement存在,则同样编码入指令中
  if (dispsize > 0)
  {
    memcpy(tcode + i + 1 + hasrm + hassib, &displacement, dispsize);
    if (anydisp == 0) 
      memset(tmask + i + 1 + hasrm + hassib, 0xFF, dispsize);
  };








  // 至此,所有的信息足够生成机器指令了



  // 指令拥有直接数
  if (immsize > 0)
  {
    if (immsize == 1)
      l = 0xFFFFFF00L;
    else if (immsize == 2)
      l = 0xFFFF0000L;
    else 
      l = 0L;
    // 
    if ((immediate & l) != 0 && (immediate & l) != l)
    {
      strcpy(errtext, "Constant does not fit into operand");
      goto error;
    };
    memcpy(tcode + i + 1 + hasrm + hassib + dispsize, &immediate, immsize);
    if (anyimm == 0)
      memset(tmask + i + 1 + hasrm + hassib + dispsize, 0xFF, immsize);
  };
  i = i + 1 + hasrm + hassib + dispsize + immsize;
  jmpoffset = jmpoffset - (i + j + jmpsize);
  model->jmpsize = jmpsize;
  model->jmpoffset = jmpoffset;
  model->jmppos = i + j;
  if (jmpsize != 0)
  {
    if (ip != 0)
    {
      jmpoffset = jmpoffset - ip;
      // 判断跳转范围(有长短两种)
      if (jmpsize == 1 && anyjmp == 0 && (jmpoffset < -128 || jmpoffset >= 128))
      {
        if (longjump == 0 && (jmpmode & 0x03) == 0)
        {
          longjump = 1;
          goto retrylongjump;
        };
        sprintf(errtext, "Relative jump out of range, use %s LONG form", name);
        goto error;
      };
      memcpy(tcode + i, &jmpoffset, jmpsize);
    };
    if (anyjmp == 0)
      memset(tmask + i, 0xFF, jmpsize);
    i += jmpsize;
  };
  // 将解码后的机器码复制到model中
  memcpy(model->code + j, tcode, i);
  memcpy(model->mask + j, tmask, i);
  i += j;
  // 指令长度
  model->length = i;

  return i;        // Positive value: length of code
error:
  model->length = 0;
  return (int)(cmd - asmcmd);    // Negative value: position of error
};

//#pragma option -O.                     // Restore old optimization options



// ------------------------------------------------- asmserv.cpp -----------------------------------

// assemble.cpp文件中使用到的结构信息

// 文件太长,只摘取一部分
// Free Disassembler and Assembler -- Command data and service routines
//
// Copyright (C) 2001 Oleh Yuschuk
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// 05.03.2002: Corrected error, FSTSW AX assembled with data size prefix

#define STRICT

#include "stdafx.h"

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//#include <dir.h>
#include <math.h>
#include <float.h>
#pragma hdrstop

#pragma   warning(disable:4996)//关闭旧函数声明警告

#include "disasm.h"

/**
 * 存储寄存器名称
 *
 * 类型:低8位寄存器名
 *     高8位寄存器名
 *     32位寄存器名
 */
const char * regname[3][9] = {
  { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH", "R8"  },
  { "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI", "R16" },
  { "EAX","ECX","EDX","EBX","ESP","EBP","ESI","EDI","R32" } };

 /**
  * 存储段寄存器名称
  */
const char * segname[8] = {
  "ES","CS","SS","DS","FS","GS","SEG?","SEG?" };

// 数据长度修饰符 
const char * sizename[11] = {
  "(0-BYTE)", "BYTE", "WORD", "(3-BYTE)",
  "DWORD", "(5-BYTE)", "FWORD", "(7-BYTE)",
  "QWORD", "(9-BYTE)", "TBYTE" };

const t_addrdec addr16[8] = {
  { SEG_DS,"BX+SI" }, { SEG_DS,"BX+DI" },
  { SEG_SS,"BP+SI" }, { SEG_SS,"BP+DI" },
  { SEG_DS,"SI" },    { SEG_DS,"DI" },
  { SEG_SS,"BP" },    { SEG_DS,"BX" } };

/**
 * 作为地址的两部分:基址与段选择子
 *    一般当使用某个寄存器作为基址寄存器时,则会
 *    有一个默认的段寄存器作为其选择子用,此数组
 *    存储的就是某基址寄存器与相应默认段寄存器的
 *    相对信息
 */
const t_addrdec addr32[8] = {
  { SEG_DS,"EAX" }, { SEG_DS,"ECX" },
  { SEG_DS,"EDX" }, { SEG_DS,"EBX" },
  { SEG_SS,"" },    { SEG_SS,"EBP" },
  { SEG_DS,"ESI" }, { SEG_DS,"EDI" } };


// FPU相关寄存器说明:
//    8个80位浮点寄存器(数据),16位状态寄存器,16位控制寄存器,16为标识寄存器。 
//    使用FPU指令对这些寄存器进行操作,这些寄存器构成一个循环栈,st7栈底,st0栈顶, 
//    当一个值被压入时,被存入st0,原来st0中的值被存入st7 
const char * fpuname[9] = {
  "ST0","ST1","ST2","ST3","ST4","ST5","ST6","ST7","FPU" };

// MMX寄存器
const char * mmxname[9] = {
  "MM0","MM1","MM2","MM3","MM4","MM5","MM6","MM7","MMX" };

// 探制寄存器
const char * crname[9] = {
  "CR0","CR1","CR2","CR3","CR4","CR5","CR6","CR7","CRX" };

const char *drname[9] = {
  "DR0","DR1","DR2","DR3","DR4","DR5","DR6","DR7","DRX" };

// List of available processor commands with decoding, types of parameters and
// other useful information. Last element has field mask=0. If mnemonic助记符 begins
// with ampersand ('&'), its mnemonic decodes differently depending on operand
// size (16 or 32 bits). If mnemonic begins with dollar ('$'), this mnemonic
// depends on address size. Semicolon (':') separates 16-bit form from 32-bit,
// asterisk ('*') will be substituted by either W (16), D (32) or none (16/32)
// character. If command is of type C_MMX or C_NOW, or if type contains C_EXPL
// (=0x01), Disassembler must specify explicit size of memory operand.

/**
 * 存储处理器有效的(机器)指令信息,包括操作数和其他有用信息
 */
const t_cmddata cmddata[] = {

  // mask,code,len,bits,arg1,arg2,arg3,type,name
  
  // 指令机器码的获取:code & mask
  { 0x0000FF, 0x000090, 1,00,  NNN,NNN,NNN, C_CMD+0,        "NOP" },
  { 0x0000FE, 0x00008A, 1,WW,  REG,MRG,NNN, C_CMD+0,        "MOV" },
  { 0x0000F8, 0x000050, 1,00,  RCM,NNN,NNN, C_PSH+0,        "PUSH" },
  { 0x0000FE, 0x000088, 1,WW,  MRG,REG,NNN, C_CMD+0,        "MOV" },
  { 0x0000FF, 0x0000E8, 1,00,  JOW,NNN,NNN, C_CAL+0,        "CALL" },
  { 0x0000FD, 0x000068, 1,SS,  IMM,NNN,NNN, C_PSH+0,        "PUSH" },
    { 0x0000FF, 0x000060, 1,00,  NNN,NNN,NNN, C_CMD+C_RARE+0, "&PUSHA*" },
  { 0x0038FF, 0x00008F, 1,00,  MRG,NNN,NNN, C_POP+1,        "POP" },
  { 0x0000FF, 0x000061, 1,00,  NNN,NNN,NNN, C_CMD+C_RARE+0, "&POPA*" },
};

// ------------------------------------- disasm.cpp ------------------------------------------

// 用于反编译机器码

// 此文件分析不够详细,但如能看懂assemble.cpp ,则此文件相对简单了很多
// Free Disassembler and Assembler -- Disassembler
//
// Copyright (C) 2001 Oleh Yuschuk
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


#define STRICT

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//#include <dir.h>
#include <math.h>
#include <float.h>
#pragma hdrstop

#pragma   warning(disable:4996)//关闭旧函数声明警告

#include "disasm.h"

//bughoho. 文件扩展名改为cpp.把外部扩展变量在cpp中添加声明
int       ideal;                // Force IDEAL decoding mode
int       lowercase;            // Force lowercase display
int       tabarguments;         // Tab between mnemonic and arguments
int       extraspace;           // Extra space between arguments
int       putdefseg;            // Display default segments in listing
int       showmemsize;          // Always show memory size
int       shownear;             // Show NEAR modifiers
int       shortstringcmds;      // Use short form of string commands

// 某些指令助记符中含有'W'或'D'以指明其操作数长度,这种指令的机器码一般依赖于操作数的长度
// 而当这些指令没有指明'W'或'D'时,则根据sizesens决定其操作数长度:
//    0:操作数为16位(字)
//    1:操作数为32位(双字)
int       sizesens;             // How to decode size-sensitive mnemonics
int       symbolic;             // Show symbolic addresses in disasm
int       farcalls;             // Accept far calls, returns & addresses
int       decodevxd;            // Decode VxD calls (Win95/98)
int       privileged;           // Accept privileged commands
int       iocommand;            // Accept I/O commands
int       badshift;             // Accept shift out of range 1..31
int       extraprefix;          // Accept superfluous prefixes
int       lockedbus;            // Accept LOCK prefixes
int       stackalign;           // Accept unaligned stack operations
int       iswindowsnt;          // When checking for dangers, assume NT
////////////////////////////////////////////////////////////////////////////////
//////////////////////////// DISASSEMBLER FUNCTIONS ////////////////////////////

// Work variables of disassembler
static ulong     datasize;             // Size of data (1,2,4 bytes)

// 地址大小
static ulong     addrsize;             // Size of address (2 or 4 bytes)
static int       segprefix;            // Segment override prefix or SEG_UNDEF
static int       hasrm;                // Command has ModR/M byte
// 指令是否包含SIB字节
static int       hassib;               // Command has SIB byte
// displacement的长度,1表示1字节,2表示2字节
static int       dispsize;             // Size of displacement (if any)
static int       immsize;              // Size of immediate data (if any)
static int       softerror;            // Noncritical disassembler error

// 代码已完成dump的长度
static int       ndump;                // Current length of command dump

// 登记当前已反汇编的指令长度
static int       nresult;              // Current length of disassembly
static int       addcomment;           // Comment value of operand

// Copy of input parameters of function Disasm()
static char      *cmd;                 // Pointer to binary data
static char      *pfixup;              // Pointer to possible fixups or NULL
static ulong     size;                 // Remaining size of the command buffer
static t_disasm  *da;                  // Pointer to disassembly results
static int       mode;                 // Disassembly mode (DISASM_xxx)

// Disassemble name of 1, 2 or 4-byte general-purpose integer register and, if
// requested and available, dump its contents. Parameter type changes decoding
// of contents for some operand types.
// static void DecodeRG(int index,int datasize,int type)
// bughoho void 改为 int 返回解码得到的寄存器索引
// 
static int DecodeRG(int index, int datasize, int type)
{
  int sizeindex;
  char name[9];

  // 无需解码
  if (mode < DISASM_DATA)
    return -1;    // No need to decode

  sprintf_s(da->vm_name, "%s_REG%02d", da->vm_name, datasize * 8);

  // 操作数类型为寄存器
  da->optype[stoperand] = Reg;

  // 0x07: 00000111b
  index &= 0x07;    //得到寄存器索引
  if (datasize == 1)
    sizeindex = 0;
  else if (datasize == 2)
    sizeindex = 1;
  else if (datasize == 4)
    sizeindex = 2;
  else
  {
    da->error = DAE_INTERN;
    return -1;
  }
  // 反汇编
  if (mode >= DISASM_FILE)
  {
    // name中将存储寄存器名称
    strcpy(name, regname[sizeindex][index]);
    if (lowercase)
      strlwr(name);
    // 不是一个假操作数
    if (type < PSEUDOOP)    // Not a pseudooperand
      // 将操作数组合至result结果中
      nresult += sprintf(da->result + nresult,"%s",name);

    // bughoho
    if (sizeindex == 0 && (index > 3 && index < 7))
    {
      // 高8字节寄存器:ah bh ch dh
      da->highbit[stoperand] = true;
    }
  }

  // 存储寄存器编号
  da->reg[stoperand] = index;
  return index;
}

// Disassemble name of 80-bit floating-point register and, if available, dump
// its contents.
// 对index编号的FPU寄存器ST[XX]进行反汇编码
static void DecodeST(int index, int pseudoop)
{
  int i;
  char s[32];
  // 不需反汇编
  if (mode < DISASM_FILE)
    return;    // No need to decode
  // 0x07: 00000111b
  index &= 0x07;
  // s = ST0 | ST1 | ST2 | ...
  i = sprintf(s, "%s(%i)", (lowercase ? "st" : "ST"), index);
  if (pseudoop == 0)
  {
      strcpy(da->result + nresult, s);
      nresult += i;
  }
}

// Disassemble name of 64-bit MMX register.
// 对index编号的MMX寄存器进行反汇编码
static void DecodeMX(int index)
{
  char * pr;
  if (mode < DISASM_FILE)
    return;    // No need to decode
  index &= 0x07;
  pr = da->result + nresult;
  nresult += sprintf(pr, "%s%i", (lowercase ? "mm" : "MM"), index);
}

// Disassemble name of 64-bit 3DNow! register and, if available, dump its
// contents.
static void DecodeNR(int index)
{
  char * pr;
  if (mode < DISASM_FILE)
    return;    // No need to decode
  index &= 0x07;
  pr = da->result + nresult;
  nresult += sprintf(pr, "%s%i", (lowercase ? "mm" : "MM"), index);
}

// Service function, adds valid memory adress in MASM or Ideal format to
// disassembled string. Parameters: defseg - default segment for given
// register combination, descr - fully decoded register part of address,
// offset - constant part of address, dsize - data size in bytes. If global
// flag 'symbolic' is set, function also tries to decode offset as name of
// some label.
// Memadr(seg, "", addr, dsize);
// defseg: 默认段寄存器
// descr: 
// offset:偏址
// dsize: 数据长度
static void Memadr(int defseg, const char *descr,long offset,int dsize)
{
  int i, n, seg;
  char * pr;
  char s[TEXTLEN];
  if (mode < DISASM_FILE || descr == NULL)
    return;      // No need or possibility to decode

  char segstr[32] = {0};
  if (da->segment != SEG_UNDEF)
  {
    if (da->segment == SEG_FS)
      strcpy(segstr, "FS");
    else if (da->segment != SEG_UNDEF && da->segment == SEG_GS)
      strcpy(segstr, "GS");
  }

  sprintf_s(da->vm_name, "%s_%sMEM%02d", da->vm_name, segstr, dsize * 8);
  da->optype[stoperand] = Mem;

  // 指向反汇编结果缓存区
  pr = da->result + nresult;
  n = 0;
  if (segprefix != SEG_UNDEF) 
    seg = segprefix; 
  else
    seg = defseg;
  if (ideal != 0) 
    pr[n++] = '[';

  // In some cases Disassembler may omit省略 size of memory operand. Namely即是, flag
  // showmemsize must be 0, type bit C_EXPL must be 0 (this bit namely means
  // that explicit operand size is necessary) and type of command must not be
  // C_MMX or C_NOW (because bit C_EXPL has in these cases different meaning).
  // Otherwise, exact size must be supplied.
  // 如果需要显示内存大小,或是MMX或NOW指令,或C_EXPL设置,则指令不能省略操作数大小
  if (showmemsize != 0 || (da->cmdtype & C_TYPEMASK) == C_MMX ||
      (da->cmdtype & C_TYPEMASK) == C_NOW || (da->cmdtype & C_EXPL) != 0)
  {
      if (dsize < sizeof(sizename) / sizeof(sizename[0]))
      n += sprintf(pr + n, "%s %s", sizename[dsize], (ideal == 0 ? "PTR " : ""));
    else
      n += sprintf(pr + n, "(%i-BYTE) %s", dsize, (ideal == 0 ? "PTR " : ""));
  }

  // BYTE PTR ES:      [??]

  // putdefseg: Display default segments in listing
  // 如果需要显示默认段寄存器,或seg段本身不是默认段,则需要显示
  if ((putdefseg != 0 || seg != defseg) && seg != SEG_UNDEF)
    n += sprintf(pr + n, "%s:", segname[seg]);
  if (ideal == 0)
    pr[n++] = '[';
  n += sprintf(pr + n, "%s", descr);
  if (lowercase)
    strlwr(pr);
  // 未指定偏移地址
  if (offset == 0L) 
  {
    // 并且descr是空字符串,则偏移假设为0
    if (*descr == '\0')
      pr[n++] = '0';
  }
  else
  {
    // symbolic: Show symbolic addresses in disasm
    if (symbolic && mode >= DISASM_CODE)
      // 空操作,并且返回值为0
      i = Decodeaddress(offset, s, TEXTLEN - n - 24, NULL);
    else
      i = 0;
    if (i > 0)    // Offset decoded in symbolic form
    {
      if (*descr != '\0')
        pr[n++] = '+';
        strcpy(pr + n, s);
        n += i; 
    }
    else if (offset < 0 && offset > -16384 && *descr != '\0')
      n += sprintf(pr + n, "-%lX", -offset);
    else
    {
      if (*descr != '\0') 
        pr[n++] = '+';
      n += sprintf(pr + n, "%lX", offset);
    };
  };
  pr[n++] = ']';
  pr[n] = '\0';
  nresult += n;
};

// Disassemble memory/register from the ModRM/SIB bytes and, if available, dump
// address and contents of memory.
static void DecodeMR(int type)
{
  int j, memonly, inmemory, seg;
  int c, sib;
  ulong dsize, regsize, addr;
  char s[TEXTLEN];
  if (size < 2)
  {
    da->error = DAE_CROSS;
    return;
  }    // ModR/M byte outside the memory block
  hasrm = 1;
  // 默认数据大小
  dsize = regsize = datasize;      // Default size of addressed reg/memory
  memonly = 0;            // Register in ModM field is allowed

  // Size and kind of addressed memory or register in ModM has no influence on对...有影响
  // the command size, and exact calculations are omitted if only command size
  // is requested. If register is used, optype will be incorrect and we need
  // to correct it later.
  // 0xC7: 01111100b,取得ModM字节中Mod与M信息
  c = cmd[1] & 0xC7;          // Leave only Mod and M fields
  if (mode >= DISASM_DATA) 
  {
    // 0xC0:11000000b
    // 寄存器寻址
    if ((c & 0xC0) == 0xC0)      // Register operand
      inmemory = 0;
    else            // Memory operand
      inmemory = 1;
    // 判断是内存操作数还是寄存器操作数, 并确定长度
    switch (type)
    {
    case MRG:        // Memory/register in ModRM byte
      // 内存操作数
      if (inmemory)
      {
        if (datasize == 1) 
          da->memtype = DEC_BYTE;
        else if (datasize == 2) 
          da->memtype = DEC_WORD;
        else
          da->memtype = DEC_DWORD;
      }
      break;
    case MRJ:        // Memory/reg in ModRM as JUMP target
      if (datasize != 2 && inmemory)
        da->memtype = DEC_DWORD;
      if (mode >= DISASM_FILE && shownear != 0)
        nresult += sprintf(da->result + nresult, "%s ", (lowercase ? "near" : "NEAR"));
      break;
    case MR1:        // 1-byte memory/register in ModRM byte
      dsize = regsize = 1;
      if (inmemory)
        da->memtype = DEC_BYTE;
      break;
    case MR2:        // 2-byte memory/register in ModRM byte
      dsize = regsize = 2;
      if (inmemory)
        da->memtype = DEC_WORD;
      break;
    case MR4:        // 4-byte memory/register in ModRM byte
    case RR4:        // 4-byte memory/register (register only)
      dsize = regsize = 4;
      if (inmemory)
        da->memtype = DEC_DWORD;
      break;
    case MR8:        // 8-byte memory/MMX register in ModRM
    case RR8:        // 8-byte MMX register only in ModRM
      dsize = 8;
      if (inmemory)
        da->memtype = DEC_QWORD;
      break;
    case MRD:        // 8-byte memory/3DNow! register in ModRM
    case RRD:        // 8-byte memory/3DNow! (register only)
      dsize = 8;
      if (inmemory)
        da->memtype = DEC_3DNOW;
      break;
    case MMA:        // Memory address in ModRM byte for LEA
      memonly = 1; 
      break;
    case MML:        // Memory in ModRM byte (for LES)
      dsize = datasize + 2;
      memonly = 1;
      if (datasize == 4 && inmemory)
        da->memtype = DEC_FWORD;
      da->warnings |= DAW_SEGMENT;
      break;
    case MMS:        // Memory in ModRM byte (as SEG:OFFS)
      dsize = datasize + 2; 
      memonly = 1;
      if (datasize == 4 && inmemory)
        da->memtype = DEC_FWORD;
      if (mode >= DISASM_FILE)
        nresult += sprintf(da->result + nresult, "%s ", (lowercase? "far" : "FAR"));
      break;
    case MM6:        // Memory in ModRM (6-byte descriptor)
      dsize = 6;
      memonly = 1;
      if (inmemory)
        da->memtype = DEC_FWORD;
      break;
    case MMB:        // Two adjacent memory locations (BOUND)
      dsize = (ideal ? datasize : datasize * 2);
      memonly = 1;
      break;
    case MD2:        // Memory in ModRM byte (16-bit integer)
    case MB2:        // Memory in ModRM byte (16-bit binary)
      dsize = 2;
      memonly = 1;
      if (inmemory)
        da->memtype = DEC_WORD;
      break;
    case MD4:        // Memory in ModRM byte (32-bit integer)
      dsize = 4;
      memonly = 1;
      if (inmemory)
        da->memtype = DEC_DWORD;
      break;
    case MD8:        // Memory in ModRM byte (64-bit integer)
      dsize = 8;
      memonly = 1;
      if (inmemory)
        da->memtype = DEC_QWORD;
      break;
    case MDA:        // Memory in ModRM byte (80-bit BCD)
      dsize = 10;
      memonly = 1;
      if (inmemory)
        da->memtype = DEC_TBYTE;
      break;
    case MF4:        // Memory in ModRM byte (32-bit float)
      dsize = 4;
      memonly = 1;
      if (inmemory)
        da->memtype = DEC_FLOAT4;
      break;
    case MF8:        // Memory in ModRM byte (64-bit float)
      dsize = 8;
      memonly = 1;
      if (inmemory)
        da->memtype = DEC_FLOAT8;
      break;
    case MFA:        // Memory in ModRM byte (80-bit float)
      dsize = 10;
      memonly = 1;
      if (inmemory)
        da->memtype = DEC_FLOAT10;
      break;
    case MFE:        // Memory in ModRM byte (FPU environment)
      dsize = 28;
      memonly = 1;
      break;
    case MFS:        // Memory in ModRM byte (FPU state)
      dsize = 108;
      memonly = 1;
      break;
    case MFX:        // Memory in ModRM byte (ext. FPU state)
      dsize = 512;
      memonly = 1;
      break;
    default:        // Operand is not in ModM!
      da->error = DAE_INTERN;
      break;
    };
  };

  addr = 0;
  // There are many possibilities to decode ModM/SIB address. The first
  // possibility is register in ModM - general-purpose, MMX or 3DNow!

  // 对ModM/SIB地址反汇编有很多种可能。第一种可能就是通用寄存器,MMX或3DNow

  // 为0xC0时则是寄存器
  if ((c & 0xC0) == 0xC0)    // Decode register operand
  {
    if (type == MR8 || type == RR8)
      DecodeMX(c);      // MMX register
    else if (type == MRD || type == RRD)
      DecodeNR(c);      // 3DNow! register
    // 通用寄存器
    else
    {
      DecodeRG(c, regsize, type);    // General-purpose register
    }
    if (memonly != 0)
      softerror = DAE_MEMORY;      // Register where only memory allowed
    return;
  }
  // Next possibility: 16-bit addressing mode, very seldom in 32-bit flat model
  // but still supported by processor. SIB byte is never used here.
  // 下一个可能: 16位地址模式.在32位平坦模式下很少见但是处理器仍然支持
  // 这种情况下,SIB字节是不需要用到的
  if (addrsize == 2)
  {
    // 0x06: 110b
    // Special case of immediate address
    if (c == 0x06)
    {
      dispsize = 2;
      if (size < 4)
        da->error = DAE_CROSS;    // Disp16 outside the memory block
      else if (mode >= DISASM_DATA)
      {
        // displement值
        da->adrconst = addr = *(ushort *)(cmd + 2);
        // 该值为0
        if (addr == 0)
          da->zeroconst = 1;
        seg = SEG_DS;
        Memadr(seg, "", addr, dsize);
      } 
    }
    else
    {
      da->indexed = 1;
      // 0x40: 1000000b
      // 8-bit signed displacement
      if ((c & 0xC0) == 0x40)
      {
        if (size < 3)
          da->error = DAE_CROSS;
        else
          addr = (signed char)cmd[2] & 0xFFFF;
        dispsize = 1;
      }
      // 0x80: 10000000b
      // 16-bit unsigned displacement
      else if ((c & 0xC0) == 0x80)
      {
        if (size < 4)
          da->error = DAE_CROSS;
        else
          addr = *(ushort *)(cmd + 2);
        dispsize = 2; 
      }
      if (mode >= DISASM_DATA && da->error == DAE_NOERR)
      {
        // 保存地址的displactement部分
        da->adrconst = addr;
        if (addr == 0)
          da->zeroconst = 1;
        seg = addr16[c & 0x07].defseg;
        Memadr(seg, addr16[c & 0x07].descr, addr, dsize);
      }
    }
  }
  // Next possibility: immediate 32-bit address.
  // 下一个可能 : 32位立即数的内存地址
  // Special case of immediate address
  else if (c == 0x05)
  {
    dispsize = 4;
    if (size < 6)
      da->error = DAE_CROSS;      // Disp32 outside the memory block
    else if (mode >= DISASM_DATA)
    {
      // 设置地址
      da->adrconst = addr = *(ulong *)(cmd + 2);
      if (pfixup == NULL)
        pfixup = cmd + 2;
      da->fixupsize += 4;
      // 地址为0
      if (addr == 0)
        da->zeroconst = 1;
      // 默认段
      seg = SEG_DS;
      Memadr(seg, "", addr, dsize);
    }
  }
  // Next possibility: 32-bit address with SIB byte.
  // SIB addresation
  // 下一个可能:32位地址并且带有SIB字节
  else if ((c & 0x07) == 0x04)
  {
    // 获取SIB字节
    sib = cmd[2];
    hassib = 1;
    *s = '\0';
    // [SIB]寻址
    if (c == 0x04 && (sib & 0x07) == 0x05)
    {
      dispsize = 4;      // Immediate address without base
      if (size < 7)
        da->error = DAE_CROSS;  // Disp32 outside the memory block
      else
      {
        da->adrconst = addr = *(ulong *)(cmd + 3);
        if (pfixup == NULL) 
          pfixup = cmd + 3;
        da->fixupsize += 4;
        if (addr == 0)
          da->zeroconst = 1;

        // Index register present
        if ((sib & 0x38) != 0x20) 
        {
          da->indexed = 1;
          if (type == MRJ)
            da->jmptable = addr;
        };
        // 默认段寄存器
        seg = SEG_DS;
      }
    }
    // Base and, eventually终于,最后, displacement
    else
    {
      // 8-bit displacement
      if ((c & 0xC0) == 0x40)
      {
        dispsize = 1;
        if (size < 4) 
          da->error = DAE_CROSS;
        else
        {
          da->adrconst = addr = (signed char)cmd[3];
          if (addr == 0)
            da->zeroconst = 1;
        }
      }
      // 32-bit displacement
      else if ((c & 0xC0) == 0x80)
      {
        dispsize = 4;
        if (size < 7)
          da->error = DAE_CROSS;  // Disp32 outside the memory block
        else
        {
          da->adrconst = addr = *(ulong *)(cmd + 3);
          if (pfixup == NULL)
            pfixup = cmd + 3;
          da->fixupsize += 4;
          if (addr == 0)
            da->zeroconst = 1;
          // Most compilers use address of type [index*4+displacement] to
          // address jump table (switch). But, for completeness, I allow all
          // cases which include index with scale 1 or 4, base or both.
          if (type == MRJ)
            da->jmptable = addr;
        }
      }
      da->indexed = 1;
      j = sib & 0x07;
      if (mode >= DISASM_FILE)
      {
        strcpy(s, regname[2][j]);
        seg = addr32[j].defseg;

        da->addrreg1 = j;
      }
    }
    // Scaled index present
    if ((sib & 0x38) != 0x20) 
    {
      if ((sib & 0xC0) == 0x40) 
        da->indexed = 2;
      else if ((sib & 0xC0) == 0x80)
        da->indexed = 4;
      else if ((sib & 0xC0) == 0xC0)
        da->indexed = 8;
      else
        da->indexed = 1;
    };
    if (mode >= DISASM_FILE && da->error == DAE_NOERR) 
    {
      // Scaled index present
      if ((sib & 0x38) != 0x20)
      {
        if (*s != '\0')
          strcat(s, "+");
        strcat(s, addr32[(sib >> 3) & 0x07].descr);
        da->regsscale = 1;
        da->addrreg2 = (sib >> 3) & 0x07;  // 第2个寄存器偏移
        if ((sib & 0xC0) == 0x40) 
        {
          da->jmptable = 0;        // Hardly a switch!
          strcat(s, "*2");
          da->regsscale = 2;
        }
        else if ((sib & 0xC0) == 0x80)
        {
          strcat(s, "*4");
          da->regsscale = 4;
        }
        else if ((sib & 0xC0) == 0xC0) 
        {
          da->jmptable = 0;        // Hardly a switch!
          strcat(s, "*8");
          da->regsscale = 8;
        }
      }
      Memadr(seg, s, addr, dsize);
    };
  }
  // Last possibility: 32-bit address without SIB byte.
  // 最后一个可能: 32位地址
  else    // No SIB
  {
    if ((c & 0xC0) == 0x40)
    {
      dispsize = 1;
      if (size < 3)
        da->error = DAE_CROSS;    // Disp8 outside the memory block
      else 
      {
        da->adrconst = addr = (signed char)cmd[2];
        if (addr == 0)
          da->zeroconst = 1;
      }
    }
    else if ((c & 0xC0) == 0x80)
    {
      dispsize = 4;
      if (size < 6)
        da->error = DAE_CROSS;    // Disp32 outside the memory block
      else 
      {
        da->adrconst = addr = *(ulong *)(cmd + 2);
        if (pfixup == NULL) 
          pfixup = cmd + 2;
        da->fixupsize += 4;
        if (addr == 0)
          da->zeroconst = 1;
        if (type == MRJ)
          da->jmptable = addr;
      }
    }
    da->indexed = 1;
    if (mode >= DISASM_FILE && da->error == DAE_NOERR) 
    {
      seg = addr32[c & 0x07].defseg;
      da->addrreg1 = (c & 0x07);
      Memadr(seg, addr32[c & 0x07].descr, addr, dsize);
    }
  }
}

// Disassemble implicit source of string operations and, if available, dump
// address and contents.
// static void DecodeSO(void)
static int DecodeSO(void)
{
  if (mode < DISASM_FILE)
    return -1;    // No need to decode
  if (datasize == 1)
    da->memtype = DEC_BYTE;
  else if (datasize == 2)
    da->memtype = DEC_WORD;
  else if (datasize == 4)
    da->memtype = DEC_DWORD;
  da->indexed = 1;
  Memadr(SEG_DS, regname[addrsize == 2 ? 1 : 2][REG_ESI], 0L, datasize);

  da->reg[stoperand] = REG_ESI;
  return REG_ESI;
}

// Disassemble implicit destination of string operations and, if available,
// dump address and contents. Destination always uses segment ES, and this
// setting cannot be overridden.
//static void DecodeDE(void) 
static int DecodeDE(void) 
{
  int seg;
  if (mode<DISASM_FILE) 
    return -1;        // No need to decode
  if (datasize==1)
    da->memtype=DEC_BYTE;
  else if (datasize==2)
    da->memtype=DEC_WORD;
  else if (datasize==4)
    da->memtype=DEC_DWORD;
  da->indexed=1;
  seg = segprefix; 
  segprefix=SEG_ES;     // Fake Memadr by changing segment prefix
  Memadr(SEG_DS,regname[addrsize==2?1:2][REG_EDI],0L,datasize);
  segprefix=seg;                       // Restore segment prefix

  da->reg[stoperand] = REG_EDI;
  return REG_EDI;
};

// Decode XLAT operand and, if available, dump address and contents.
static void DecodeXL(void)
{
  if (mode<DISASM_FILE)
    return;        // No need to decode
  da->memtype=DEC_BYTE;
  da->indexed=1;
  Memadr(SEG_DS,(addrsize==2?"BX+AL":"EBX+AL"),0L,1);
};

// Decode immediate operand of size constsize. If sxt is non-zero, byte operand
// should be sign-extended to sxt bytes. If type of immediate constant assumes
// this, small negative operands may be displayed as signed negative numbers.
// Note that in most cases immediate operands are not shown in comment window.
static void DecodeIM(int constsize,int sxt,int type) 
{
  int i;
  signed long data;
  ulong l;
  char name[TEXTLEN],comment[TEXTLEN] = {0};
  immsize+=constsize;                    // Allows several immediate operands
  if (mode<DISASM_DATA)
    return;

  //sprintf_s(da->vm_name,"%s_IMM%02d",da->vm_name,constsize*8);
  sprintf_s(da->vm_name,"%s_IMM32",da->vm_name);//IMM只有32位
  da->optype[stoperand] = Imm;

  l=1+hasrm+hassib+dispsize+(immsize-constsize);
  data=0;
  if (size<l+constsize)
    da->error=DAE_CROSS;
  else if (constsize==1) 
  {
    if (sxt==0) 
    data=(uchar)cmd[l];
    else 
    data=(signed char)cmd[l];
    if (type==IMS && ((data & 0xE0)!=0 || data==0)) 
  {
      da->warnings|=DAW_SHIFT;
      da->cmdtype|=C_RARE;
    }
  }
  else if (constsize==2) 
  {
    if (sxt==0) 
    data=*(ushort *)(cmd+l);
    else 
    data=*(short *)(cmd+l);
  }
  else 
  {
    data=*(long *)(cmd+l);
    if (pfixup==NULL)
    pfixup=cmd+l;
    da->fixupsize+=4;
  }
  if (sxt==2)
    data&=0x0000FFFF;
  if (data==0 && da->error==0)
    da->zeroconst=1;
  // Command ENTER, as an exception from Intel's rules, has two immediate
  // constants. As the second constant is rarely used, I exclude it from
  // search if the first constant is non-zero (which is usually the case).
  if (da->immconst==0)
    da->immconst=data;
  if (mode>=DISASM_FILE && da->error==DAE_NOERR) 
  {
    if (mode>=DISASM_CODE && type!=IMU)
      i=Decodeaddress(data,name,TEXTLEN-nresult-24,comment);
    else 
  {
      i=0; comment[0]='\0'; 
  }
    if (i!=0 && symbolic!=0)
  {
      strcpy(da->result+nresult,name); nresult+=i;
  }
    else if (type==IMU || type==IMS || type==IM2 || data>=0 || data<NEGLIMIT)
      nresult+=sprintf(da->result+nresult,"%lX",data);
    else
      nresult+=sprintf(da->result+nresult,"-%lX",-data);
    if (addcomment && comment[0]!='\0')
    strcpy(da->comment,comment);
  }
}

// Decode VxD service name (always 4-byte).
static void DecodeVX(void) 
{
  ulong l,data;
  immsize+=4;                          // Allows several immediate operands
  if (mode<DISASM_DATA) 
    return;
  l=1+hasrm+hassib+dispsize+(immsize-4);
  if (size<l+4) 
  {
    da->error=DAE_CROSS;
    return;
  }
  data=*(long *)(cmd+l);
  if (data==0 && da->error==0)
    da->zeroconst=1;
  if (da->immconst==0)
    da->immconst=data;
  if (mode>=DISASM_FILE && da->error==DAE_NOERR)
  {
    if ((data & 0x00008000)!=0 && memicmp("VxDCall",da->result,7)==0)
      memcpy(da->result,lowercase?"vxdjump":"VxDJump",7);
    nresult+=sprintf(da->result+nresult,"%lX",data);
  }
}

// Decode implicit constant 1 (used in shift commands). This operand is so
// insignificant that it is never shown in comment window.
static void DecodeC1(void)
{
  if (mode<DISASM_DATA)
    return;
  da->immconst=1;
  if (mode>=DISASM_FILE) 
    nresult+=sprintf(da->result+nresult,"1");
}

// Decode immediate absolute data address. This operand is used in 8080-
// compatible commands which allow to move data from memory to accumulator and
// back. Note that bytes ModRM and SIB never appear in commands with IA operand.
static void DecodeIA(void) 
{
  ulong addr;
  if (size<1+addrsize) 
  {
    da->error=DAE_CROSS; 
  return; 
  };
  dispsize=addrsize;
  if (mode<DISASM_DATA) 
    return;
  if (datasize==1) 
    da->memtype=DEC_BYTE;
  else if (datasize==2)
    da->memtype=DEC_WORD;
  else if (datasize==4)
    da->memtype=DEC_DWORD;
  if (addrsize==2)
    addr=*(ushort *)(cmd+1);
  else
  {
    addr=*(ulong *)(cmd+1);
    if (pfixup==NULL) 
    pfixup=cmd+1;
    da->fixupsize+=4; 
  }
  da->adrconst=addr;
  if (addr==0)
    da->zeroconst=1;
  if (mode>=DISASM_FILE)
  {
    Memadr(SEG_DS,"",addr,datasize);
  }
}

// Decodes jump relative to nextip of size offsize.
static void DecodeRJ(ulong offsize,ulong nextip)
{
  sprintf_s(da->vm_name,"%s_IMM%02d",da->vm_name,4*8);//不管长跳短跳都认为是32位地址
  da->optype[stoperand] = Imm;
  int i;
  ulong addr;
  char s[TEXTLEN];
  if (size<offsize+1) 
  {
    da->error=DAE_CROSS; 
    return; 
  }
  dispsize=offsize;                    // Interpret offset as displacement
  if (mode<DISASM_DATA)
    return;
  if (offsize==1)//短跳转
  {
    addr=(signed char)cmd[1]+nextip;
  }
  else if (offsize==2)
    addr=*(signed short *)(cmd+1)+nextip;
  else
    addr=*(ulong *)(cmd+1)+nextip;
  if (datasize==2)
    addr&=0xFFFF;
  da->jmpconst=addr;
  if (addr==0) 
    da->zeroconst=1;
  if (mode>=DISASM_FILE)
  {
    if (offsize==1) 
      nresult+=sprintf(da->result+nresult, "%s ",(lowercase==0?"SHORT":"short"));
    if (mode>=DISASM_CODE)
      i=Decodeaddress(addr,s,TEXTLEN,da->comment);
    else
      i=0;
    if (symbolic==0 || i==0)
      nresult+=sprintf(da->result+nresult,"%08lX",addr);
    else
      nresult+=sprintf(da->result+nresult,"%.*s",TEXTLEN-nresult-25,s);
    if (symbolic==0 && i!=0 && da->comment[0]=='\0')
      strcpy(da->comment,s);
  }
}

// Decode immediate absolute far jump address. In flat model, such addresses
// are not used (mostly because selector is specified directly in the command),
// so I neither decode as symbol nor comment it. To allow search for selector
// by value, I interprete it as an immediate constant.
static void DecodeJF(void)
{
  ulong addr,seg;
  if (size<1+addrsize+2)
  {
    da->error=DAE_CROSS;
    return;
  }
  dispsize=addrsize; immsize=2;        // Non-trivial but allowed interpretation
  if (mode<DISASM_DATA)
    return;
  if (addrsize==2) 
  {
    addr=*(ushort *)(cmd+1);
    seg=*(ushort *)(cmd+3);
  }
  else
  {
    addr=*(ulong *)(cmd+1);
    seg=*(ushort *)(cmd+5);
  }
  da->jmpconst=addr;
  da->immconst=seg;
  if (addr==0 || seg==0)
    da->zeroconst=1;
  if (mode>=DISASM_FILE)
  {
    nresult+=sprintf(da->result+nresult,"%s %04X:%08X",(lowercase==0?"FAR":"far"),seg,addr);
  }
}

// Decode segment register. In flat model, operands of this type are seldom.
static void DecodeSG(int index) 
{
  int i;
  if (mode<DISASM_DATA)
    return;

  index&=0x07;
  if (index>=6) 
    softerror=DAE_BADSEG;  // Undefined segment register

  sprintf_s(da->vm_name,"%s_%s",da->vm_name,segname[index]);
  da->optype[stoperand] = Seg;

  if (mode>=DISASM_FILE) 
  {
    da->segreg = index;
    i=sprintf(da->result+nresult,"%s",segname[index]);
    if (lowercase)
      strlwr(da->result+nresult);
    nresult+=i;
  }
}

// Decode control register addressed in R part of ModRM byte. Operands of
// this type are extremely rare. Contents of control registers are accessible
// only from privilege level 0, so I cannot dump them here.
static void DecodeCR(int index)
{
  hasrm=1;
  if (mode>=DISASM_FILE)
  {
    index=(index>>3) & 0x07;
    nresult+=sprintf(da->result+nresult,"%s",crname[index]);
    if (lowercase)
    strlwr(da->result+nresult);
  }
}

// Decode debug register addressed in R part of ModRM byte. Operands of
// this type are extremely rare. I can dump only those debug registers
// available in CONTEXT structure.
static void DecodeDR(int index) 
{
  int i;
  hasrm=1;
  if (mode>=DISASM_FILE) 
  {
    index=(index>>3) & 0x07;
    i=sprintf(da->result+nresult,"%s",drname[index]);
    if (lowercase)
    strlwr(da->result+nresult);
    nresult+=i;
  }
}

// Skips 3DNow! operands and extracts command suffix. Returns suffix or -1 if
// suffix lies outside the memory block. This subroutine assumes that cmd still
// points to the beginning of 3DNow! command (i.e. to the sequence of two bytes
// 0F, 0F).
/**
 * 获取3dNow指令的后缀,对于什么是其后缀,可上网搜索
 *
 * 3dNow指令格式:0Fh 0Fh modR/M [sib] [displacement] 3DNow!_suffix
 *
 * 返回值:成功返回其后缀,否则返回-1
 */
static int Get3dnowsuffix(void)
{
  int c, sib;
  ulong offset;
  // 如果3dNow后缀超出内存块
  if (size < 3)
    return -1;      // Suffix outside the memory block

  // 3dNow指令的后缀偏移
  offset = 3;

  // ModRM 字节的组成部分为:mod-reg-r/m

  // 0xC7: 11000111b
  c = cmd[2] & 0xC7;    // Leave only Mod and M fields

  // Register in ModM - general-purpose多方面的,多用途的, MMX or 3DNow!
  // 0xC0: 11000000b
  // 高两位为1,表示寄存器寻址
  if ((c & 0xC0) == 0xC0)
    ;
  // 16-bit addressing mode, SIB byte is never used here.
  // 否则,如果是16位地址模式,则SIB不会被使用
  else if (addrsize == 2)
  {
      if (c == 0x06)      // Special case of immediate address
      offset += 2;
    // 0x40: 1000000b
    else if ((c & 0xC0) == 0x40)  // 8-bit signed displacement
      offset++;
    // 0x80: 10000000b
    else if ((c & 0xC0) == 0x80)  // 16-bit unsigned displacement
      offset += 2;
  }
  // Immediate 32-bit address.
  // 0x05: 101b
  else if (c == 0x05)      // Special case of immediate address
    offset += 4;
  // 32-bit address with SIB byte.
  // 如果指令使用32位地址模式并带有SIB字节
  else if ((c & 0x07) == 0x04)  // SIB addresation
  {
    if (size < 4)
      return -1;      // Suffix outside the memory block
    // 获取SIB字节
    sib = cmd[3]; 
    offset++;
    if (c == 0x04 && (sib & 0x07) == 0x05)
      offset += 4;    // Immediate address without base
    else if ((c & 0xC0) == 0x40)  // 8-bit displacement
      offset += 1;
    else if ((c & 0xC0) == 0x80)  // 32-bit dislacement
      offset += 4;
  }
  // 32-bit address without SIB byte
  else if ((c & 0xC0) == 0x40)
    offset += 1;
  else if ((c & 0xC0) == 0x80)
    offset += 4;
  if (offset >= size) 
    return -1;    // Suffix outside the memory block
  return cmd[offset];
}

// Function checks whether 80x86 flags meet condition set in the command.
// Returns 1 if condition is met, 0 if not and -1 in case of error (which is
// not possible).
int Checkcondition(int code,ulong flags)
{
  ulong cond,temp;
  switch (code & 0x0E) 
  {
    case 0:                            // If overflow
      cond=flags & 0x0800;
    break;
    case 2:                            // If below
      cond=flags & 0x0001;
    break;
    case 4:                            // If equal
      cond=flags & 0x0040;
    break;
    case 6:                            // If below or equal
      cond=flags & 0x0041; 
    break;
    case 8:                            // If sign
      cond=flags & 0x0080; 
    break;
    case 10:                           // If parity
      cond=flags & 0x0004;
    break;
    case 12:                           // If less
      temp=flags & 0x0880;
      cond=(temp==0x0800 || temp==0x0080); 
    break;
    case 14:                           // If less or equal
      temp=flags & 0x0880;
      cond=(temp==0x0800 || temp==0x0080 || (flags & 0x0040)!=0);
    break;
    default:
    return -1;                // Internal error, not possible!
  }
  if ((code & 0x01)==0)
    return (cond!=0);
  else
    return (cond==0);               // Invert condition
}


// bughoho
int stoperand = 0;

// Disasm(base_addr, MAXCMDSIZE, VirtualAddress, &da, DISASM_CODE)

ulong Disasm(char * src, ulong srcsize, ulong srcip, t_disasm * disasm, int disasmmode) 
{
  // isprefix: 表示指令是否有段前缀,加锁总线前缀等前缀
  // is3dnow: 表示指令是否是3dNow指令
  int i, j, isprefix, is3dnow, repeated, operand, mnemosize, arg = 0;
  ulong u, code;
  // 是否存在LOCK前缀
  int lockprefix;        // Non-zero if lock prefix present
  // 是否存在REP前缀
  int repprefix;        // REPxxx prefix or 0
  int cxsize;
  // pname: 保存前缀名称,如REPN、REPE
  char name[TEXTLEN], * pname;
  const t_cmddata * pd, * pdan;

  // Prepare disassembler variables and initialize structure disasm.
  // 初始化结构和变量
  datasize = addrsize = 4;        // 32-bit code and data segments only!
  // 指令的段前缀
  segprefix = SEG_UNDEF;
  hasrm = hassib=0;
  dispsize = immsize = 0;
  // 加锁总线前缀
  lockprefix = 0;
  // REP前缀
  repprefix = 0;
  ndump = 0;
  nresult = 0;

  // 指令
  cmd = src;
  size = srcsize;
  pfixup = NULL;
  softerror = 0;
  is3dnow = 0;
  da = disasm;
  da->ip = srcip;
  da->comment[0] = '\0';
  da->cmdtype = C_BAD;
  da->nprefix = 0;
  da->memtype = DEC_UNKNOWN;
  da->indexed = 0;
  da->jmpconst = 0;
  da->jmptable = 0;
  da->adrconst = 0;
  da->immconst = 0;
  da->zeroconst = 0;
  da->fixupoffset = 0;
  da->fixupsize = 0;
  da->warnings = 0;
  da->error = DAE_NOERR;
  mode = disasmmode;      // No need to use register contents

  // 操作数类型
  da->optype[0] = da->optype[1] = da->optype[2] = -1;
  // 3个寄存器,当操作数为寄存器时
  da->reg[0] = da->reg[1] = da->reg[2] = -1;
  // 段寄存器
  da->segreg = -1;

  da->addrreg1 = da->addrreg2 = -1;
  da->regsscale = 1;
  // 清二进制代码
  memset(da->hexcode, 0, TEXTLEN);
  da->codelen = 0;
  da->highbit[0] = da->highbit[1] = da->highbit[2] = false;

  // Correct 80x86 command may theoretically在理论上 contain up to多达 4 prefixes belonging
  // to different prefix groups. This limits maximal possible size of the
  // command to MAXCMDSIZE=16 bytes. In order to maintain this limit, if
  // Disasm() detects second prefix from the same group, it flushes first
  // prefix in the sequence顺序 as a pseudocommand伪指令.
  u = 0;
  repeated = 0;

  // 统计指令cmd中所有的前缀数
  while (size > 0)
  {
    // 假设存在一些前缀
    isprefix = 1;      // Assume that there is some prefix
    // 根据指令处理其前缀部分
    switch (* cmd)
    {
      // 段前缀
    case 0x26:
      if (segprefix == SEG_UNDEF)
        segprefix = SEG_ES;
      else
        // 重复出现0x26
        repeated = 1;
      break;
    case 0x2E:
      if (segprefix == SEG_UNDEF)
        segprefix = SEG_CS;
      else
        repeated = 1; 
      break;
    case 0x36:
      if (segprefix == SEG_UNDEF)
        segprefix = SEG_SS;
      else
        repeated = 1;
      break;
    case 0x3E:
      if (segprefix == SEG_UNDEF) 
        segprefix = SEG_DS;
      else
        repeated = 1;
      break;
    case 0x64:
      if (segprefix == SEG_UNDEF) 
        segprefix = SEG_FS;
      else
        repeated = 1;
      break;
    case 0x65:
      if (segprefix == SEG_UNDEF)
        segprefix = SEG_GS;
      else
        repeated = 1;
      break;
      // 66H前缀用来更改operands size,当然只能在指令所
      // 支持的 effective operand-size 范围内进行调整
    case 0x66:
      if (datasize == 4) 
        datasize = 2;
      else
        // 前缀重复
        repeated = 1; 
      break;
      // 指令中可以 67H 进行 address size override
    case 0x67:
      if (addrsize == 4) 
        addrsize = 2;
      else
        repeated = 1;
      break;
      // 加锁总线前缀
    case 0xF0:
      if (lockprefix == 0)
        lockprefix = 0xF0;
      else
        repeated = 1;
      break;
      // REP前缀
    case 0xF2:
      if (repprefix == 0) 
        repprefix = 0xF2;
      else
        repeated = 1;
      break;
    case 0xF3: 
      if (repprefix == 0) 
        repprefix = 0xF3;
      else
        repeated = 1;
      break;
    default: 
      isprefix = 0;
      break;
    }

    // 如果没有前缀,或前缀重复,则跳出循环
    if (isprefix == 0 || repeated != 0)
      break;        // No more prefixes or duplicated prefix
    if (mode >= DISASM_FILE)
    {
      ndump += sprintf(da->dump + ndump, "%02X:", * cmd);
      da->hexcode[da->codelen++] = *cmd;
    }
    // 累计指令前缀数
    da->nprefix++;
    cmd++;
    srcip++;
    size--;
    u++;
  }

  // bughoho
  da->segment = segprefix;  // 得到段寄存器
  // We do have repeated prefix. Flush first prefix from the sequence.
  // 如果指令拥有重复的前缀
  if (repeated)
  {
    // 如果是对代码进行反汇编 [??]
    if (mode >= DISASM_FILE)
    {
      da->dump[3] = '\0';      // Leave only first dumped prefix
      // 为何只保留一个前缀
      da->nprefix = 1;

      // 获取前缀的名称
      switch (cmd[-(long)u])    // cmd++ 且 u++;这里是得到cmd[0]
      {
      case 0x26: pname = (char *)(segname[SEG_ES]); 
        break;
      case 0x2E: pname = (char *)(segname[SEG_CS]);
        break;
      case 0x36: pname = (char *)(segname[SEG_SS]);
        break;
      case 0x3E: pname = (char *)(segname[SEG_DS]);
        break;
      case 0x64: pname = (char *)(segname[SEG_FS]);
        break;
      case 0x65: pname = (char *)(segname[SEG_GS]); 
        break;
      case 0x66: pname = "DATASIZE";
        break;
      case 0x67: pname = "ADDRSIZE";
        break;
      case 0xF0: pname = "LOCK";
        break;
      case 0xF2: pname = "REPNE";
        break;
      case 0xF3: pname = "REPE";
        break;
      default: pname = "?";
        break; 
      }

      // 累计已反汇编的指令长度
      nresult += sprintf(da->result + nresult, "PREFIX %s:",pname);
      // 是否强制要求以小写显示
      if (lowercase)
        strlwr(da->result);
      // 不支持扩展的前缀(多余的前缀)
      if (extraprefix == 0) 
        strcpy(da->comment, "Superfluous prefix");
    }
    // 警告:指令有过多的前缀
    da->warnings |= DAW_PREFIX;
    // 指令存在加锁总线的前缀
    if (lockprefix)
      da->warnings |= DAW_LOCK;

    // 该指令罕见,少用
    da->cmdtype = C_RARE;
    return 1;                          // Any prefix is 1 byte long
  }

  // If lock prefix available, display it and forget, because it has no
  // influence on对...有影响 decoding of rest of the command.
  // 指令存在加锁总线的前缀
  if (lockprefix != 0)
  {
    if (mode >= DISASM_FILE) 
      nresult += sprintf(da->result + nresult, "LOCK ");
    da->warnings |= DAW_LOCK;
  }

  // Fetch (if available) first 3 bytes of the command, add repeat prefix and
  // find command in the command table.
  // 取得命令的前三个字节
  code = 0;
  // 如果指令剩余空间存在
  if (size > 0)
    *(((char *)&code) + 0) = cmd[0];
  if (size > 1)
    *(((char *)&code) + 1) = cmd[1];
  if (size > 2) 
    *(((char *)&code) + 2) = cmd[2];
  // 如果REP前缀存在
  if (repprefix != 0)        // RER/REPE/REPNE is considered to be
    // 则将其加入到code指令中
    code = (code << 8) | repprefix;      // part of command.
  if (decodevxd && (code & 0xFFFF) == 0x20CD)
    pd = &vxdcmd;        // Decode VxD call (Win95/98)
  else
  {
    // 遍历指令表
    for (pd = cmddata; pd->mask != 0; pd++)
    {
      // 检测是否pd指令
      if (((code ^ pd->code) & pd->mask) != 0)
        continue;

      // 如果是反汇编模式,并且Use short form of string commands
      if (mode >= DISASM_FILE && shortstringcmds &&
        // 如果寄存器是ESI或EDI
        (pd->arg1 == MSO || pd->arg1 == MDE || pd->arg2 == MSO || pd->arg2 == MDE))
        continue;    // Search short form of string command
      break;
    }
  }

  if ( (pd->type & C_TYPEMASK) == C_NOW)  // 如果是3DNow指令
  {
    // 3DNow! commands require additional search.
    is3dnow = 1;
    // 获取3DNow指令的后缀
    j = Get3dnowsuffix();

    // 当存放指令的内存空间不足时,会返回-1
    // 这表示指令与内存交叉,出错
    if (j < 0)
      da->error = DAE_CROSS;
    else
    {
      // 遍历指令表,获取后缀与欲反汇编指令相匹配的指令
      for ( ; pd->mask != 0; pd++)
      {
        if (((code ^ pd->code) & pd->mask) != 0)
          continue;
        if (((uchar *)&(pd->code))[2] == j)
          break;
      }
    }
  }

  // bughoho
  da->is3dnow = is3dnow;  // 是否是3DNow!指令

  // Command not found
  if (pd->mask == 0)    // 命令未找到
  {
    // 指令错误
    da->cmdtype = C_BAD;
    if (size < 2)
      da->error = DAE_CROSS;
    else 
      da->error = DAE_BADCMD; 
  }
  // 指令OK,是一注册指令,则反汇编之
  else
  {
    // Command recognized, decode it
    // 指令类型信息
    da->cmdtype = pd->type;
    // ECX作为记数寄存器的默认长度,与数据长度等同
    cxsize = datasize;    // Default size of ECX used as counter

    // 如果段前缀是FS 或 GS 或存在 LOCK 前缀
    // 则表明此指令并不常用
    if (segprefix == SEG_FS || segprefix == SEG_GS || lockprefix != 0)
      da->cmdtype |= C_RARE;  // These prefixes are rare
    // 如果该指令是一个特权指令
    if (pd->bits == PR)
      da->warnings |= DAW_PRIV;  // Privileged command (ring 0)
    // IO命令
    else if (pd->bits == WP)
      da->warnings |= DAW_IO;    // I/O command

    // Win32 programs usually try to keep stack dword-aligned, so INC ESP
    // (44) and DEC ESP (4C) usually don't appear in real code. Also check for
    // ADD ESP,imm and SUB ESP,imm (81,C4,imm32; 83,C4,imm8; 81,EC,imm32;
    // 83,EC,imm8).
    // WIN32程序通常会保持栈的双字对齐, 所以inc esp,dec esp一般不会出现在
    // 平常的代码中, 除此同样检测add esp,imm 和 sub esp,imm.
    if (cmd[0] == 0x44 || cmd[0] == 0x4C ||
      (size >= 3 && (cmd[0] == 0x81 || cmd[0] == 0x83) &&
      (cmd[1] == 0xC4 || cmd[1] == 0xEC) && (cmd[2] & 0x03) != 0))
    {
      da->warnings |= DAW_STACK;  // 栈对齐警告
      da->cmdtype |= C_RARE;    // 不常出现的指令
    }

    // Warn also on MOV SEG,... (8E...). Win32 works in flat mode.
    // 警告mov seg,xxx 操作, 因为Win32工作在平坦模式
    if (cmd[0] == 0x8E)
      da->warnings |= DAW_SEGMENT;

    // If opcode is 2-byte, adjust command.
    // 如果操作码是2字节的则调整命令
    if (pd->len == 2)
    {
      if (size == 0)
        da->error = DAE_CROSS;
      else 
      {
        // 工作于反汇编模式
        if (mode >= DISASM_FILE)
        {
          // 将原二进制代码dump至da->dump中
          ndump += sprintf(da->dump + ndump, "%02X", *cmd);
          // 存储二进制代码
          da->hexcode[da->codelen++] = *cmd;
        }
        cmd++;
        srcip++;
        size--;
      }
    }

    if (size == 0)
      da->error = DAE_CROSS;

    // Some commands either feature non-standard data size or have bit which
    // allowes to select data size.
    // 一些命令的数据长度不是标准的,其长度是可变的
    if ((pd->bits & WW) != 0 && (*cmd & WW) == 0)
      datasize = 1;      // Bit W in command set to 0
    else if ((pd->bits & W3) != 0 && (*cmd & W3) == 0)
      datasize = 1;      // Another position of bit W
    else if ((pd->bits & FF) != 0)
      datasize = 2;      // Forced word (2-byte) size

    // Some commands either have mnemonics which depend on data size (8/16 bits
    // or 32 bits, like CWD/CDQ), or have several different mnemonics (like
    // JNZ/JNE). First case is marked by either '&' (mnemonic depends on
    // operand size) or '$' (depends on address size). In the second case,
    // there is no special marker and disassembler selects main mnemonic.
    // 有些指令的助记符依赖于数据长度(例如CWD/CDQ),或拥有多个不同的助记符
    // (例如JNZ/JNE)。第一种情况是使用"&"或"$"标记。第二种情况没有特定标记
    // 并被反汇编为主要的助记符
    if (mode >= DISASM_FILE)
    {
      // 指令的助记符依赖于操作数大小
      if (pd->name[0] == '&')
        mnemosize = datasize;
      // 指令的助记符依赖于地址大小
      else if (pd->name[0] == '$')
        mnemosize = addrsize;
      else
        mnemosize = 0;
      // 助记符大小不为0,依赖数据或地址大小
      if (mnemosize != 0)
      {
        // 遍历pd指令的符号名中每个字符
        for (i = 0,j = 1; pd->name[j] != '\0'; j++) 
        {
          // 如果不是":"分割符
          if (pd->name[j] == ':')
          {
            // Separator between 16/32 mnemonics
            // 分割符用于分割16位/32位的助记符
            if (mnemosize == 4) 
              i = 0;
            else
              break;
          }
          // 指令中如出现"*",则用"W"或"D"代替,作为其长度标记
          else if (pd->name[j] == '*') 
          {
            // Substitute替换,代替 by 'W', 'D' or none
            if (mnemosize == 4 && sizesens != 2)
              name[i++] = 'D';
            else if (mnemosize != 4 && sizesens != 0)
              name[i++] = 'W';
          }
          else
            name[i++] = pd->name[j];
        }
        name[i] = '\0'; 
      }
      // 否则,说明指令不依赖数据或地址大小
      else
      {
        strcpy(name, pd->name);
        // 当指令中有","号分割时,说明该指令有多个
        // 助记符,使用主要的一个就行
        for (i = 0; name[i] != '\0'; i++)
        {
          if (name[i] == ',') 
          {
            // Use main mnemonic
            name[i] = '\0';
            break;
          }
        }
      }
      // 将name反汇编后的指令复制至da中
      // 存在REP前缀,并且...
      if (repprefix != 0 && tabarguments) 
      {
        for (i = 0; name[i] != '\0' && name[i] != ' '; i++)
          da->result[nresult++] = name[i];
        if (name[i] == ' ') 
        {
          da->result[nresult++] = ' ';
          i++; 
        }
        while(nresult < 8) 
          da->result[nresult++] = ' ';
        for ( ; name[i] != '\0'; i++)
          da->result[nresult++] = name[i];
      }
      else
        nresult += sprintf(da->result + nresult, "%s", name);

      // 要求指令转小写
      if (lowercase) 
        strlwr(da->result);
      sprintf_s(da->vm_name, "V%s", name);
    }

    // Decode operands (explicit - encoded in command, implicit - present in
    // mmemonic or assumed - used or modified by command). Assumed operands
    // must stay after all explicit and implicit operands. Up to 3 operands
    // are allowed.
    // 处理操作数,最多可以有3个
    for (operand = 0; operand < 3; operand++)
    {
      stoperand = operand;

      // 如果已出错,则直接跳出,不需继续了
      if (da->error)
        break;      // Error - no sense to continue
      // If command contains both source and destination, one usually must not
      // decode destination to comment because it will be overwritten on the
      // next step. Global addcomment takes care of this. Decoding routines,
      // however, may ignore this flag.
      if (operand == 0 && pd->arg2 != NNN && pd->arg2 < PSEUDOOP)
        addcomment = 0;
      else
        addcomment = 1;
      // Get type of next argument.
      if (operand == 0)
        arg = pd->arg1;
      else if (operand == 1)
        arg = pd->arg2;
      else
        arg = pd->arg3;

      // 没有更多的操作数了
      if (arg == NNN) 
        break;    // No more operands

      // Arguments with arg>=PSEUDOOP are assumed operands and are not
      // displayed in disassembled result, so they require no delimiter.
      // 参数>=PSEUDOOP是假的操作数,并且不显示在反汇编的结果中,所以
      // 他们不需要分割符
      if ((mode >= DISASM_FILE) && arg < PSEUDOOP)  // 添加间隔
      {
        // 指令与第1个操作数之前有 ' ' 间隔
        if (operand == 0)
        {
          da->result[nresult++] = ' ';
          if (tabarguments)
          {
            while (nresult < 8)
              da->result[nresult++] = ' ';
          }
        }
        // 操作数之间则以 ',' 间隔
        else
        {
          da->result[nresult++] = ',';
          if (extraspace)
            da->result[nresult++] = ' ';
        }
      }

      // Decode, analyse and comment next operand of the command.
      // 解码,分析和注释命令的下一个操作数
      // 现在正式开始
      switch (arg)
      {
        // 参数是整型寄存器
      case REG:      // Integer register in Reg field
        if (size < 2)
          da->error = DAE_CROSS;
        else
        {
          DecodeRG(cmd[1] >> 3, datasize, REG);
        }
        // 表示指令含有ModRm字节
        hasrm = 1;
        break;
        // 参数是整型寄存器,并嵌入指令中
      case RCM:        // Integer register in command byte
        // 注意:此处下标为0
        DecodeRG(cmd[0], datasize, RCM);
        break;
        // 4字节长度整型寄存器
      case RG4:        // Integer 4-byte register in Reg field
        if (size < 2)
          da->error = DAE_CROSS;
        else
        {
          DecodeRG(cmd[1] >> 3, 4, RG4);
        }
        hasrm = 1;
        break;
        // 累加器寄存器
      case RAC:        // Accumulator (AL/AX/EAX, implicit)
        DecodeRG(REG_EAX, datasize, RAC);
        break;
        // AX寄存器
      case RAX:        // AX (2-byte, implicit)
        DecodeRG(REG_EAX, 2, RAX); 
        break;
        // DX寄存器,16位端口地址
      case RDX:        // DX (16-bit implicit port address)
        DecodeRG(REG_EDX, 2, RDX); 
        break;
        // CL寄存器,用于移位
      case RCL:        // Implicit CL register (for shifts)
        DecodeRG(REG_ECX, 1, RCL);
        break;
        // FPU堆顶寄存器
      case RS0:        // Top of FPU stack (ST(0))
        DecodeST(0, 0);
        break;
        // FPU寄存器
      case RST:        // FPU register (ST(i)) in command byte
        DecodeST(cmd[0], 0);
        break;
        // MMX寄存器
      case RMX:        // MMX register MMx
        if (size < 2)
          da->error = DAE_CROSS;
        else 
          DecodeMX(cmd[1] >> 3);
        hasrm = 1;
        break;
        // 3DNow寄存器
      case R3D:        // 3DNow! register MMx
        if (size < 2) 
          da->error = DAE_CROSS;
        else 
          DecodeNR(cmd[1] >> 3);
        hasrm = 1; 
        break;
      case MRG:             // Memory/register in ModRM byte
      case MRJ:             // Memory/reg in ModRM as JUMP target
      case MR1:             // 1-byte memory/register in ModRM byte
      case MR2:                      // 2-byte memory/register in ModRM byte
      case MR4:                      // 4-byte memory/register in ModRM byte
      case MR8:                      // 8-byte memory/MMX register in ModRM
      case MRD:                      // 8-byte memory/3DNow! register in ModRM
      case MMA:                      // Memory address in ModRM byte for LEA
      case MML:                      // Memory in ModRM byte (for LES)
      case MM6:                      // Memory in ModRm (6-byte descriptor)
      case MMB:                      // Two adjacent memory locations (BOUND)
      case MD2:                      // Memory in ModRM byte (16-bit integer)
      case MB2:                      // Memory in ModRM byte (16-bit binary)
      case MD4:                      // Memory in ModRM byte (32-bit integer)
      case MD8:                      // Memory in ModRM byte (64-bit integer)
      case MDA:                      // Memory in ModRM byte (80-bit BCD)
      case MF4:                      // Memory in ModRM byte (32-bit float)
      case MF8:                      // Memory in ModRM byte (64-bit float)
      case MFA:                      // Memory in ModRM byte (80-bit float)
      case MFE:                      // Memory in ModRM byte (FPU environment)
      case MFS:                      // Memory in ModRM byte (FPU state)
      case MFX:                      // Memory in ModRM byte (ext. FPU state)
        DecodeMR(arg);
        break;
      case MMS:                      // Memory in ModRM byte (as SEG:OFFS)
        DecodeMR(arg);
        da->warnings |= DAW_FARADDR; 
        break;
      case RR4:                      // 4-byte memory/register (register only)
      case RR8:                      // 8-byte MMX register only in ModRM
      case RRD:                      // 8-byte memory/3DNow! (register only)
        if ((cmd[1] & 0xC0)!=0xC0) 
          softerror=DAE_REGISTER;
        DecodeMR(arg);
        break;
      case MSO:                      // Source in string op's ([ESI])
        DecodeSO();
        break;
      case MDE:                      // Destination in string op's ([EDI])
        DecodeDE();
        break;
      case MXL:                      // XLAT operand ([EBX+AL])
        DecodeXL();
        break;
      case IMM:                      // Immediate data (8 or 16/32)
      case IMU:                      // Immediate unsigned data (8 or 16/32)
        if ((pd->bits & SS)!=0 && (*cmd & 0x02)!=0)
          DecodeIM(1,datasize,arg);
        else
          DecodeIM(datasize,0,arg);
        break;
      case VXD:                      // VxD service (32-bit only)
        DecodeVX();//未解决
        break;
      case IMX:                      // Immediate sign-extendable byte
        DecodeIM(1,datasize,arg);
        break;
      case C01:                      // Implicit constant 1 (for shifts)
        DecodeC1();//未解决
        break;
      case IMS:                      // Immediate byte (for shifts)
      case IM1:                      // Immediate byte
        DecodeIM(1,0,arg);
        break;
      case IM2:                      // Immediate word (ENTER/RET)
        DecodeIM(2,0,arg);
        if ((da->immconst & 0x03)!=0)
          da->warnings|=DAW_STACK;
        break;
      case IMA:                      // Immediate absolute near data address
        DecodeIA();           //[imm] ok
        break;
      case JOB:                      // Immediate byte offset (for jumps)
        DecodeRJ(1,srcip+2);//无须解决jmpconst则是
        break;
      case JOW:                      // Immediate full offset (for jumps)
        DecodeRJ(datasize,srcip+datasize+1);//无须解决jmpconst则是 
        break;
      case JMF:                      // Immediate absolute far jump/call addr
        DecodeJF();//未解决
        da->warnings|=DAW_FARADDR;
        break;
      case SGM:                      // Segment register in ModRM byte
        if (size<2) 
          da->error=DAE_CROSS;
        DecodeSG(cmd[1]>>3);
        hasrm=1;
        break;
      case SCM:                      // Segment register in command byte
        DecodeSG(cmd[0]>>3);
        if ((da->cmdtype & C_TYPEMASK)==C_POP)
          da->warnings|=DAW_SEGMENT;
        break;
      case CRX:                      // Control register CRx
        if ((cmd[1] & 0xC0)!=0xC0) 
          da->error=DAE_REGISTER;
        DecodeCR(cmd[1]);//未解决
        break;
      case DRX:                      // Debug register DRx
        if ((cmd[1] & 0xC0)!=0xC0)
          da->error=DAE_REGISTER;
        DecodeDR(cmd[1]);//未解决
        break;
      case PRN:                      // Near return address (pseudooperand)
        break;
      case PRF:                      // Far return address (pseudooperand)
        da->warnings|=DAW_FARADDR;
        break;
      case PAC:                      // Accumulator (AL/AX/EAX, pseudooperand)
        DecodeRG(REG_EAX,datasize,PAC);
        break;
      case PAH:                      // AH (in LAHF/SAHF, pseudooperand)
      case PFL:                      // Lower byte of flags (pseudooperand)
        break;
      case PS0:                      // Top of FPU stack (pseudooperand)
        DecodeST(0,1);//未解决
        break;
      case PS1:                      // ST(1) (pseudooperand)
        DecodeST(1,1);//未解决
        break;
      case PCX:                      // CX/ECX (pseudooperand)
        DecodeRG(REG_ECX,cxsize,PCX); 
        break;
      case PDI:                      // EDI (pseudooperand in MMX extentions)
        DecodeRG(REG_EDI,4,PDI);
        break;
      default:
        da->error=DAE_INTERN;        // Unknown argument type
        break;
      }
      da->memsize[operand] = datasize;
    }
    // Check whether command may possibly contain fixups.
    if (pfixup != NULL && da->fixupsize > 0)
      da->fixupoffset = (int)(pfixup - src);
    // Segment prefix and address size prefix are superfluous for command which
    // does not access memory. If this the case, mark command as rare to help
    // in analysis.
    // 段前缀与地址大小前缀过多
    if (da->memtype == DEC_UNKNOWN &&
      (segprefix != SEG_UNDEF || (addrsize != 4 && pd->name[0] != '$'))) 
    {
      da->warnings |= DAW_PREFIX;
      da->cmdtype |= C_RARE; 
    }
    // 16-bit addressing is rare in 32-bit programs. If this is the case,
    // mark command as rare to help in analysis.
    // 16位的寻址在32位程序中是少见的,如果是这种情况,在命令中加上标志
    if (addrsize != 4)
      da->cmdtype |= C_RARE;
  }
  // Suffix of 3DNow! command is accounted best by assuming it immediate byte
  // constant.
  // 如果是一个3dNow指令
  if (is3dnow) 
  {
      if (immsize != 0)
      da->error = DAE_BADCMD;
    else
      immsize = 1;
  }
  // Right or wrong, command decoded. Now dump it.
  if (da->error != 0)    // 如果命令有错误
  {
    // Hard error in command detected
      if (mode >= DISASM_FILE)
      nresult = sprintf(da->result, "???");
    if (da->error == DAE_BADCMD && (*cmd == 0x0F || *cmd == 0xFF) && size > 0)
    {
      if (mode >= DISASM_FILE)
      {
        ndump += sprintf(da->dump + ndump, "%02X", *cmd);
        da->hexcode[da->codelen++] = *cmd;
      }
      cmd++;
      size--;
    }
    if (size>0)
    {
      if (mode >= DISASM_FILE)
      {
        ndump += sprintf(da->dump + ndump, "%02X", *cmd);
        da->hexcode[da->codelen++] = *cmd;
      }
      cmd++; 
      size--;
    }
  }
  else
  {
    //如果没有错误,dump命令
    // No hard error, dump command
    if (mode >= DISASM_FILE) 
    {
      ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
      da->hexcode[da->codelen++] = *(cmd - 1);

      if (hasrm)
      {
        ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
        da->hexcode[da->codelen++] = *(cmd - 1);
      }
      if (hassib)
      {
        ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
        da->hexcode[da->codelen++] = *(cmd - 1);
      }
      if (dispsize != 0) 
      {
        da->dump[ndump++] = ' ';
        for (i = 0; i < dispsize; i++)
        {
          ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
          da->hexcode[da->codelen++] = *(cmd - 1);
        }
      }
      if (immsize != 0)
      {
        da->dump[ndump++] = ' ';
        for (i = 0; i < immsize; i++)
        {
          ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
          da->hexcode[da->codelen++] = *(cmd - 1);
        }
      }
    }
    else
      cmd += 1 + hasrm + hassib + dispsize + immsize;
    size -= 1 + hasrm + hassib + dispsize + immsize;
  }
  // Check that command is not a dangerous one.
  // 检查命令是否是危险的
  if (mode >= DISASM_DATA) 
  {
    for (pdan = dangerous; pdan->mask != 0; pdan++)
    {
      if (((code ^ pdan->code) & pdan->mask) != 0)
        continue;
      if (pdan->type == C_DANGERLOCK && lockprefix == 0)
        break;    // Command harmless without LOCK prefix
      if (iswindowsnt && pdan->type == C_DANGER95)
        break;    // Command harmless under Windows NT
      // Dangerous command!
      if (pdan->type == C_DANGER95)
        da->warnings |= DAW_DANGER95;
      else
        da->warnings |= DAW_DANGEROUS;
      break;
    }
  }
  if (da->error == 0 && softerror != 0)
    da->error = softerror;  // Error, but still display command
  if (mode >= DISASM_FILE)
  {
    // 根据错误处理
    if (da->error != DAE_NOERR)
    {
      switch (da->error)
      {
      case DAE_CROSS:
        strcpy(da->comment, "Command crosses end of memory block");
        break;
      case DAE_BADCMD:
        strcpy(da->comment, "Unknown command");
        break;
      case DAE_BADSEG:
        strcpy(da->comment, "Undefined segment register");
        break;
      case DAE_MEMORY:
        strcpy(da->comment, "Illegal use of register");
        break;
      case DAE_REGISTER:
        strcpy(da->comment, "Memory address not allowed");
        break;
      case DAE_INTERN:
        strcpy(da->comment, "Internal OLLYDBG error");
        break;
      default:
        strcpy(da->comment, "Unknown error");
        break;
      }
    }
    // 特权指令,发出警告
    else if ((da->warnings & DAW_PRIV) != 0 && privileged == 0)
      strcpy(da->comment, "Privileged command");
    // IO指令
    else if ((da->warnings & DAW_IO) != 0 && iocommand == 0)
      strcpy(da->comment, "I/O command");
    else if ((da->warnings & DAW_FARADDR) != 0 && farcalls == 0)
    {
      if ((da->cmdtype & C_TYPEMASK) == C_JMP)
        strcpy(da->comment, "Far jump");
      else if ((da->cmdtype & C_TYPEMASK) == C_CAL)
        strcpy(da->comment, "Far call");
      else if ((da->cmdtype & C_TYPEMASK) == C_RET)
        strcpy(da->comment, "Far return");
    }
    else if ((da->warnings & DAW_SEGMENT) != 0 && farcalls == 0)
      strcpy(da->comment, "Modification of segment register");
    else if ((da->warnings & DAW_SHIFT) != 0 && badshift == 0)
      strcpy(da->comment, "Shift constant out of range 1..31");
    else if ((da->warnings & DAW_PREFIX) != 0 && extraprefix == 0)
      strcpy(da->comment, "Superfluous prefix");
    else if ((da->warnings & DAW_LOCK) != 0 && lockedbus == 0)
      strcpy(da->comment, "LOCK prefix");
    else if ((da->warnings & DAW_STACK) != 0 && stackalign == 0)
      strcpy(da->comment, "Unaligned stack operation");
  }
  return (srcsize - size);  // Returns number of recognized bytes
}


// --------------------------- disasm.h -------------
// Free Disassembler and Assembler -- Header file
//
// Copyright (C) 2001 Oleh Yuschuk
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#pragma once

#ifndef MAINPROG
#define odunique extern
#else
#define odunique
#endif

#pragma   warning(disable:4309)//关闭截断常数值警告

// If you prefere Borland, this will force necessary setting (but, as a side
// effect, may cause plenty of warnings if other include files will be compiled
// with different options):

#ifdef __BORLANDC__
#pragma option -K                      // Unsigned char
#endif
//
//#if (char)0xFF != 255
//#error Please set default char type to unsigned
//#endif


#define NEGLIMIT       (-16384)        // Limit to display constans as signed

// 假操作数,伪操作数
#define PSEUDOOP       128             // Base for pseudooperands
#define TEXTLEN        256             // Maximal length of text string

// Special command features.
#define WW             0x01            // Bit W (size of operand)
#define SS             0x02            // Bit S (sign extention of immediate)
#define WS             0x03            // Bits W and S
#define W3             0x08            // Bit W at position 3
#define CC             0x10            // Conditional jump
// 强制要求16位长度的操作数
#define FF             0x20            // Forced 16-bit size
#define LL             0x40            // Conditional loop

// 特权指令
#define PR             0x80            // Protected command

// IO指令
#define WP             0x81            // I/O command with bit W

// All possible types of operands in 80x86. A bit more than you expected, he?
// 操作数类型
#define NNN            0               // No operand \\ 无操作

// 寄存器操作数
// 对于汇编指令,有些指令的操作数是寄存器,并且在指令中需指定操作数是哪个寄存器
//         而有些指令的操作数是寄存器,但不需要指定用哪个寄存器,而是指令
//         一旦确定下来,则使用的寄存器也就确定了 [??]
// 下面两个类型是否分别指上面的两种情况
#define REG            1               // Integer register in Reg field             \\积存器结构中的寄存器
#define RCM            2               // Integer register in command byte          \\命令字节中的寄存器
#define RG4            3               // Integer 4-byte register in Reg field      \\用不到PMOVMSKB指令
#define RAC            4               // Accumulator (AL/AX/EAX, implicit)         \\暗示累加器?
#define RAX            5               // AX (2-byte, implicit)            \\暗示AX
#define RDX            6               // DX (16-bit implicit port address)
#define RCL            7               // Implicit CL register (for shifts)         \\暗示CL寄存器
#define RS0            8               // Top of FPU stack (ST(0), implicit)

// FPU寄存器操作数
#define RST            9               // FPU register (ST(i)) in command byte

// MMX寄存器操作数
#define RMX            10              // MMX register MMx
#define R3D            11              // 3DNow! register MMx
#define MRG            12              // Memory/register in ModRM byte
#define MR1            13              // 1-byte memory/register in ModRM byte
#define MR2            14              // 2-byte memory/register in ModRM byte
#define MR4            15              // 4-byte memory/register in ModRM byte
#define RR4            16              // 4-byte memory/register (register only)
#define MR8            17              // 8-byte memory/MMX register in ModRM
#define RR8            18              // 8-byte MMX register only in ModRM
#define MRD            19              // 8-byte memory/3DNow! register in ModRM
#define RRD            20              // 8-byte memory/3DNow! (register only)
#define MRJ            21              // Memory/reg in ModRM as JUMP target
#define MMA            22              // Memory address in ModRM byte for LEA
#define MML            23              // Memory in ModRM byte (for LES)
#define MMS            24              // Memory in ModRM byte (as SEG:OFFS)
#define MM6            25              // Memory in ModRm (6-byte descriptor)
#define MMB            26              // Two adjacent memory locations (BOUND)
#define MD2            27              // Memory in ModRM (16-bit integer)
#define MB2            28              // Memory in ModRM (16-bit binary)
#define MD4            29              // Memory in ModRM byte (32-bit integer)
#define MD8            30              // Memory in ModRM byte (64-bit integer)
#define MDA            31              // Memory in ModRM byte (80-bit BCD)
#define MF4            32              // Memory in ModRM byte (32-bit float)
#define MF8            33              // Memory in ModRM byte (64-bit float)
#define MFA            34              // Memory in ModRM byte (80-bit float)
#define MFE            35              // Memory in ModRM byte (FPU environment)
#define MFS            36              // Memory in ModRM byte (FPU state)
#define MFX            37              // Memory in ModRM byte (ext. FPU state)
#define MSO            38              // Source in string op's ([ESI])
#define MDE            39              // Destination in string op's ([EDI])

// XLAT操作数
#define MXL            40              // XLAT operand ([EBX+AL])

// 直接数
#define IMM            41              // Immediate data (8 or 16/32)
#define IMU            42              // Immediate unsigned data (8 or 16/32)
#define VXD            43              // VxD service
#define IMX            44              // Immediate sign-extendable byte
#define C01            45              // Implicit constant 1 (for shifts)
#define IMS            46              // Immediate byte (for shifts)
#define IM1            47              // Immediate byte
#define IM2            48              // Immediate word (ENTER/RET)
#define IMA            49              // Immediate absolute near data address
#define JOB            50              // Immediate byte offset (for jumps)
#define JOW            51              // Immediate full offset (for jumps)

// 操作数是JMP跳转或call的地址
#define JMF            52              // Immediate absolute far jump/call addr

// 段寄存器操作
#define SGM            53              // Segment register in ModRM byte
#define SCM            54              // 段寄存器      Segment register in command byte

// 控制寄存器操作数
#define CRX            55              // CRx控制寄存器 Control register CRx

// 调试寄存器操作数
#define DRX            56              // DRx调试寄存器 Debug register DRx
// Pseudooperands (implicit operands, never appear in assembler commands). Must
// have index equal to or exceeding PSEUDOOP.
#define PRN            (PSEUDOOP+0)    // Near return address
#define PRF            (PSEUDOOP+1)    // Far return address
#define PAC            (PSEUDOOP+2)    // Accumulator (AL/AX/EAX)
#define PAH            (PSEUDOOP+3)    // AH (in LAHF/SAHF commands)
#define PFL            (PSEUDOOP+4)    // Lower byte of flags (in LAHF/SAHF)
#define PS0            (PSEUDOOP+5)    // Top of FPU stack (ST(0))
#define PS1            (PSEUDOOP+6)    // ST(1)
#define PCX            (PSEUDOOP+7)    // CX/ECX
#define PDI            (PSEUDOOP+8)    // EDI (in MMX extentions)

// Errors detected during command disassembling.
#define DAE_NOERR      0               // No error

// 错误的指令
#define DAE_BADCMD     1               // Unrecognized command
#define DAE_CROSS      2               // Command crosses end of memory block
#define DAE_BADSEG     3               // Undefined segment register

// 当操作数必须是内存操作数,而发现操作数是寄存器时出的错误
#define DAE_MEMORY     4               // Register where only memory allowed
#define DAE_REGISTER   5               // Memory where only register allowed
// 内部错误
#define DAE_INTERN     6               // Internal error

typedef unsigned char  uchar;          // Unsigned character (byte)
typedef unsigned short ushort;         // Unsigned short
typedef unsigned int   uint;           // Unsigned integer
typedef unsigned long  ulong;          // Unsigned long

typedef struct t_addrdec
{
  int            defseg;
  char           *descr;
} t_addrdec;

// 命令数据
typedef struct t_cmddata
{
  ulong          mask;                 // Mask for first 4 bytes of the command
  ulong          code;                 // Compare masked bytes with this
  char           len;                  // Length of the main command code

  // 例如PR,则表明该指令是一特权指令(ring 0指令)
  // FF:强制操作数为16位操作数
  char           bits;                 // Special bits within the command
  char           arg1,arg2,arg3;       // Types of possible arguments
  char           type;                 // C_xxx + additional information
  char           *name;                // Symbolic name for this command
} t_cmddata;

// Initialized constant data structures used by all programs from assembler
// package. Contain names of register, register combinations or commands and
// their properties.
extern const char      *regname[3][9];
extern const char      *segname[8];
extern const char      *sizename[11];
extern const t_addrdec addr16[8];
extern const t_addrdec addr32[8];
extern const char      *fpuname[9];
extern const char      *mmxname[9];
extern const char      *crname[9];
extern const char      *drname[9];
extern const char      *condition[16];
extern const t_cmddata cmddata[];//可用结构数组
extern const t_cmddata vxdcmd;
extern const t_cmddata dangerous[];

////////////////////////////////////////////////////////////////////////////////
//////////////////// ASSEMBLER, DISASSEMBLER AND EXPRESSIONS ///////////////////

#define MAXCMDSIZE     16              // Maximal length of 80x86 command
#define MAXCALSIZE     8               // Max length of CALL without prefixes
#define NMODELS        8               // Number of assembler search models

#define INT3           0xCC            // Code of 1-byte breakpoint
#define NOP            0x90            // Code of 1-byte NOP command
#define TRAPFLAG       0x00000100      // Trap flag in CPU flag register

#define REG_EAX        0               // Indexes of general-purpose registers
#define REG_ECX        1               // in t_reg.
#define REG_EDX        2
#define REG_EBX        3
#define REG_ESP        4
#define REG_EBP        5
#define REG_ESI        6
#define REG_EDI        7

#define SEG_UNDEF     -1         //没有段寄存器
#define SEG_ES         0               // Indexes of segment/selector registers
#define SEG_CS         1
#define SEG_SS         2
#define SEG_DS         3
#define SEG_FS         4
#define SEG_GS         5

// 指令类型
#define C_TYPEMASK     0xF0            // Mask for command type
#define   C_CMD        0x00            // Ordinary instruction
#define   C_PSH        0x10            // 1-word PUSH instruction
#define   C_POP        0x20            // 1-word POP instruction
#define   C_MMX        0x30            // MMX instruction
#define   C_FLT        0x40            // FPU instruction
// jmp跳转指令
#define   C_JMP        0x50            // JUMP instruction
// jmp条件跳转指令
#define   C_JMC        0x60            // Conditional JUMP instruction
// call调用指令
#define   C_CAL        0x70            // CALL instruction
#define   C_RET        0x80            // RET instruction
#define   C_***        0x90            // Changes system flags
#define   C_RTF        0xA0            // C_JMP and C_*** simultaneously
#define   C_REP        0xB0            // Instruction with REPxx prefix
#define   C_PRI        0xC0            // Privileged instruction
#define   C_DAT        0xD0            // Data (address) doubleword

// 3DNow 指令
#define   C_NOW        0xE0            // 3DNow! instruction
// 错误指令,即未注册的指令
#define   C_BAD        0xF0            // Unrecognized command
#define C_RARE         0x08            // Rare command, seldom used in programs
#define C_SIZEMASK     0x07            // MMX data size or special flag
#define   C_EXPL       0x01            // (non-MMX) Specify explicit memory size

#define C_DANGER95     0x01            // Command is dangerous under Win95/98
#define C_DANGER       0x03            // Command is dangerous everywhere
#define C_DANGERLOCK   0x07            // Dangerous with LOCK prefix

#define DEC_TYPEMASK   0x1F            // Type of memory byte
#define   DEC_UNKNOWN  0x00            // Unknown type
#define   DEC_BYTE     0x01            // Accessed as byte
#define   DEC_WORD     0x02            // Accessed as short
#define   DEC_NEXTDATA 0x03            // Subsequent byte of code or data
#define   DEC_DWORD    0x04            // Accessed as long
#define   DEC_FLOAT4   0x05            // Accessed as float
#define   DEC_FWORD    0x06            // Accessed as descriptor/long pointer
#define   DEC_FLOAT8   0x07            // Accessed as double
#define   DEC_QWORD    0x08            // Accessed as 8-byte integer
#define   DEC_FLOAT10  0x09            // Accessed as long double
#define   DEC_TBYTE    0x0A            // Accessed as 10-byte integer
#define   DEC_STRING   0x0B            // Zero-terminated ASCII string
#define   DEC_UNICODE  0x0C            // Zero-terminated UNICODE string
// 3DNow指令操作数
#define   DEC_3DNOW    0x0D            // Accessed as 3Dnow operand
#define   DEC_BYTESW   0x11            // Accessed as byte index to switch
#define   DEC_NEXTCODE 0x13            // Subsequent byte of command
#define   DEC_COMMAND  0x1D            // First byte of command
#define   DEC_JMPDEST  0x1E            // Jump destination
#define   DEC_CALLDEST 0x1F            // Call (and maybe jump) destination
#define DEC_PROCMASK   0x60            // Procedure analysis
#define   DEC_PROC     0x20            // Start of procedure
#define   DEC_PBODY    0x40            // Body of procedure
#define   DEC_PEND     0x60            // End of procedure
#define DEC_CHECKED    0x80            // Byte was analysed

#define DECR_TYPEMASK  0x3F            // Type of register or memory
#define   DECR_BYTE    0x21            // Byte register
#define   DECR_WORD    0x22            // Short integer register
#define   DECR_DWORD   0x24            // Long integer register
#define   DECR_QWORD   0x28            // MMX register
#define   DECR_FLOAT10 0x29            // Floating-point register
#define   DECR_SEG     0x2A            // Segment register
#define   DECR_3DNOW   0x2D            // 3Dnow! register
#define DECR_ISREG     0x20            // Mask to check that operand is register

/**
 * 反汇编的选项
 */
#define DISASM_SIZE    0               // Determine command size only
#define DISASM_DATA    1               // Determine size and analysis data
#define DISASM_FILE    3               // Disassembly, no symbols
#define DISASM_CODE    4               // Full disassembly

// Warnings issued by Disasm():
#define DAW_FARADDR    0x0001          // Command is a far jump, call or return

// 当执行如MOV SEG, XX的指令时警告
#define DAW_SEGMENT    0x0002          // Command loads segment register

// 特权指令
#define DAW_PRIV       0x0004          // Privileged command
// IO指令
#define DAW_IO         0x0008          // I/O command
#define DAW_SHIFT      0x0010          // Shift constant out of range 1..31

// 表示指令有过多的前缀
#define DAW_PREFIX     0x0020          // Superfluous prefix

// 指令有加锁总线前缀
#define DAW_LOCK       0x0040          // Command has LOCK prefix

// 栈没有对齐(一般栈操作需要双字对齐)
#define DAW_STACK      0x0080          // Unaligned stack operation
#define DAW_DANGER95   0x1000          // May mess up Win95 if executed
#define DAW_DANGEROUS  0x3000          // May mess up any OS if executed

//bughoho 操作数类型
enum Optype
{
  // 立即数
  Imm,
  // 寄存器
  Reg,
  // 内存数
  Mem,
  // 段寄存器
  Seg,
};

// Results of disassembling
/**
 * 用于保存反汇编的结果
 */
typedef struct t_disasm
{
  // 应该是指令本身所在的地址
  ulong          ip;                   // Instrucion pointer
  char           dump[TEXTLEN];        // Hexadecimal dump of the command

  // 用于保存反汇编指令
  char           result[TEXTLEN];      // Disassembled command

  // 对指令的一些说明信息,比如可包含信息"Superfluous prefix"以表示指令的前缀过多
  char           comment[TEXTLEN];     // Brief comment

  // 表示指令的类型
  // C_RARE: 表示指令比较罕见,在程序中不常用
  // C_CAL:表示是一个CALL指令
  //
  int            cmdtype;              // One of C_xxx

  // DEC_BYTE:表示是一个字节内存操作数
  // DEC_3DNOW:表示是一个3dNow指令操作数
  int            memtype;              // Type of addressed variable in memory

  // 统计指令的前缀数
  int            nprefix;              // Number of prefixes
  // 地址是否存在寄存器(index)
  int            indexed;              // Address contains register(s)

  // 常量跳转地址 [??]
  ulong          jmpconst;             // Constant jump address

  // 跳转表,可能赋值为:jmptable = addr;
  ulong          jmptable;             // Possible address of switch table
  ulong          adrconst;             // [edx+100] '100' 地址部分的常量 Constant part of address

  // 保存操作数(立即数)的值
  ulong          immconst;             // Immediate constant

  // 1:表示adrconst为0
  int            zeroconst;            // Whether contains zero constant
  int            fixupoffset;          // Possible offset of 32-bit fixups
  int            fixupsize;            // Possible total size of fixups or 0
  int            error;                // Error while disassembling command

  // 反汇编过程中需要进行警告的信息
  // 如DAW_PRIV,则表明指令是一个特权指令(ring 0)指令
  int            warnings;             // Combination of DAW_xxx

  //bughoho new
  BYTE       hexcode[TEXTLEN];   // 用于存储2进制码
  int         codelen;         // 长度

  int         optype[3];         // 操作数类型
  char       vm_name[TEXTLEN];   // 声称VM对应的Handler名称

  // 3dNow指令
  int         is3dnow;         // 3dnow函数
  int         segment;         // 保存段前缀

  // 当操作数为寄存器时,存储寄存器编号
  int         reg[3];         // 3寄存器(假设操作数为寄存器)
  int         segreg;         // 段寄存器(假设操作数为段寄存器)
  int         addrreg1;       // 内存地址部分的第1个不带比例的寄存器
  int         addrreg2;       // 内存地址部分的第2个带比例的寄存器
  int         regsscale;       // 比例:1,2,4,8
  //还有一个adrconst已经定义,根据正负号来定义加减
  int         memsize[3];       // 操作数长度

  // 当某个操作数是(ah,bh,ch,dh)时,即寄存器的高8位时,则相应元素置true
  bool       highbit[3];       // 当是8位指令时并且为高位时(ah bh ch dh)为1
} t_disasm;

/**
 * 用于保存反汇编的每个指令语句
 */
struct CodeNode
{
  t_disasm  disasm;

  // 表示此指令是跳到外部的指令
  BOOL    IsJmcBeSideType;

  // 指令跳转地址不明确
  BOOL    IsJmcUndefineType;

  // 此指令是一动态跳转指令
  // 注:动态跳转指令指没有指明跳转地址的跳转指令
  BOOL    IsJmcDynamicType;

  // TRUE表示指令是一跳转指令
  BOOL    IsJmcType;

  // 表示此指令是否是从其他指令处跳转过来
  BOOL    IsJmcFromType;

  // 表示此指令是JMC或CALL指令后的指令
  BOOL    IsJmcNextType;

  // 表示该指令是一CALL指令
  BOOL    IsCallType;
  CodeNode()
  {
    memset(this,0,sizeof(CodeNode));
  }
};

/**
 * 虚拟机指令相应的汇编代码必须编译成二进制机器代码,此结构就用于
 * 表示某段编译之后的二进制机器代码及代码的一些相关信息
 */
typedef struct t_asmmodel
{            // Model to search for assembler command
  // 存储解码后的机器指令
  char           code[MAXCMDSIZE];     // Binary code
  char           mask[MAXCMDSIZE];     // Mask for binary code (0: bit ignored)
  // 指令长度
  int            length;               // Length of code, bytes (0: empty)
  int            jmpsize;              // Offset size if relative jump
  int            jmpoffset;            // Offset relative to IP
  int            jmppos;               // Position of jump offset in command
} t_asmmodel;

odunique int       ideal;                // Force IDEAL decoding mode
odunique int       lowercase;            // Force lowercase display
odunique int       tabarguments;         // Tab between mnemonic and arguments
odunique int       extraspace;           // Extra space between arguments
odunique int       putdefseg;            // Display default segments in listing
odunique int       showmemsize;          // Always show memory size
odunique int       shownear;             // Show NEAR modifiers
odunique int       shortstringcmds;      // Use short form of string commands
odunique int       sizesens;             // How to decode size-sensitive mnemonics
odunique int       symbolic;             // Show symbolic addresses in disasm
odunique int       farcalls;             // Accept far calls, returns & addresses
odunique int       decodevxd;            // Decode VxD calls (Win95/98)
odunique int       privileged;           // Accept privileged commands
odunique int       iocommand;            // Accept I/O commands
odunique int       badshift;             // Accept shift out of range 1..31
odunique int       extraprefix;          // Accept superfluous prefixes
odunique int       lockedbus;            // Accept LOCK prefixes
odunique int       stackalign;           // Accept unaligned stack operations
odunique int       iswindowsnt;          // When checking for dangers, assume NT
//bughoho
odunique int     stoperand;       //当前的循环

int    Assemble(char *cmd,ulong ip,t_asmmodel *model,int attempt,
        int constsize,char *errtext);
int    Checkcondition(int code,ulong flags);
int    Decodeaddress(ulong addr,char *symb,int nsymb,char *comment);
ulong  Disasm(char *src,ulong srcsize,ulong srcip,
        t_disasm *disasm,int disasmmode);
ulong  Disassembleback(char *block,ulong base,ulong size,ulong ip,int n);
ulong  Disassembleforward(char *block,ulong base,ulong size,ulong ip,int n);
int    Isfilling(ulong addr,char *data,ulong size,ulong align);
int    Print3dnow(char *s,char *f);
int    Printfloat10(char *s,long double ext);
int    Printfloat4(char *s,float f);
int    Printfloat8(char *s,double d);