用于为专有VM格式编写反汇编程序的分析器生成器

时间:2016-09-25 12:03:45

标签: parsing bytecode disassembly parser-generator

我正在尝试为Mindstorms EV3 VM二进制文件编写一个反汇编程序,最好用Python编写,因为我对它非常熟悉。我有详细说明指令集,操作码,参数,数据类型等的文档available here但是我无法从中构建一个实际的反汇编程序。

据我了解,编写反汇编程序与编写编译器并没有太大区别,因为编译器只是一个美化的确定性有限状态机。然而,除此之外,我找不到任何有关写作的书籍或指南。我试图使用编译器编写工具,如Lex和Yacc(或在python PLY中),但所有这些工具都以某种方式使我失望。

Lex和Yacc组合似乎不是我可以使用的东西,因为它与我想做的相反。它从文本生成标记,然后您将其转换为字节码,而我有字节码并希望将其转换为标记,然后我将其转换为文本。正如我所看到的,我唯一的选择就是编写自己的解析器生成器,我希望避免使用它,因为它看似很多工作。

我遇到的另一个问题是我不知道如何处理诸如以null结尾的字符串和参数编码之类的东西(一些参数以一个字节为前缀,其中某些位设置告诉VM预期的长度和类型,我认为我无法用字节级的简单DFA解析整个字节码。

我如何为这样的格式编写反汇编程序?

1 个答案:

答案 0 :(得分:1)

编写二进制反汇编程序实际上意味着您要指定二进制数据的模式以及解码实体之间的关系(如果您识别两条指令,则可以合理地假设它们不重叠)。

常规解析器不能很好地执行此操作(尽管您可以为一系列指令编写语法,假设单个位或字节为标记;您仍然必须处理执行序列之间的间隙)。

我见过的最聪明的方法是Norman Ramsey及其团队的一个名为SLED的工具,它允许您为二进制数据编写简洁的模式,并自动将其组装成二进制编码器和解码器。这个research paper discusses SLED和许多类似的系统。关键点:这些不仅仅是“解析器生成器”,而是概念类似:描述数据的许多模式,将模式组合成高效引擎以匹配所有模式的代码生成器。

为了让您了解这些工具的外观,我根据我在该领域所做的一些工作提供了部分x86-64编码的片段。我们的想法是定义组成的命名模式(带有约束),以便最终编写指令定义。这是一个小部分的简短样本(整个事情是大约1200行):

datatype SIB
 { ScaleSize:     unsigned encoding { Times1=0 Times2=1 Times4=2 Times8=3} 2 bits
   ScaledIndex:   unsigned encoding { EAX=0 ECX=1 EDX=2 EBX=3 none=4 EBP=5         ESI=6 EDI=7 } 3 bits
   IndexRegister: unsigned encoding { EAX=0 ECX=1 EDX=2 EBX=3 ESP=4  EBP,disp32=5  ESI=6 EDI=7 } 3 bits
 }

encoding Grp1     { ADD=0 OR ADC SBB AND SUB XOR CMP }
encoding Grp1A    { POP=0 * * * * * * * }
encoding Grp2     { ROL=0 ROR RCL RCR SHL,SAL SHR  * SAR }
encoding Grp3     { TESTIbIz=0 * NOT NEG MULAX,MULrAX IMULAL,IMULrAX DIVAL,DIVrAX IDIVAL,IDIVrAX }
encoding Grp4     { INCEb=0  DECEb * * * * * * }
encoding Grp5     { INCEv=0  DECEv CALLNEv CALLFEp  JMPNEv JMPFEp  PUSHEv * }
encoding Grp6     { SLDTRvMW=0 STRRvMw LLDTEw LTREw VERREw VERWEw * * }
encoding Grp7mem  { SGDTMs=0                          SIDTMs        LGDTMs LIDTMs SMSWMwRv * LMSWEw INVLPGMb }
encoding Grp7reg  { VMCALL,VMLAUNCH,VMRESUME,VMXOFF=0 MONITOR,MWAIT *      *      SMSWMwRv * LMSWEw SWAPGS }
encoding Grp8     { *=0 * * * BT BTS BTR BTC }
encoding Grp9mem  { * CMPXCH8BMq,CMPXCH16BMdq * * * * VMPTRLDMq,VMCLEARMq,VMXONMq VMPTRSTMq }
encoding Grp9reg  { *=0 * * * * * * * }
encoding Grp10    { * * * * * * * }
encoding Grp11Ib  { MOVEbIb * * * * * * * }
encoding Grp11Iz  { MOVEvIz * * * * * * * }
encoding Grp12mem { * * * * * * * * }
encoding Grp12reg { *=0 * * PSRLWNqIb,PSRLWUdqIb *             PSRAWNqIb,PSRAWUdqIb * PSLLWNqIb,PSLLWUdqIb * }
encoding Grp13mem { * * * * * * * * }
encoding Grp13reg { *=0 * * PSRLDNqIb,PSLRDUdqIb *             PSRADNqIb,PSRADUdqIb * PSLLDNqIb,PSLLDUdqIb * }
encoding Grp14mem { * * * * * * * * }
encoding Grp14reg { *=0 * * PSRLQNqIb,PSRLQUdqIb  PSRLDQUdqIb  *                    * PSLLQNqIb,PSLLQUdqIb PSLLDQUDqIb }
encoding Grp15mem { FXSAVE=0 FXRSTOR LDMXCSR STMXCSR * * * CFLUSH }
encoding Grp15reg { *=0 * * * LFENCE MFENCE SFENCE }
encoding Grp16mem { PREFETCHNTA=0 PREFETCHT0 PREFETCHT1 PREFETCHT2 * * * } 
encoding Grp16reg { * * * * * * * * }

...

instruction { ADCr64Immediate => Grp1.ADC
      ADDr64Immediate => Grp1.ADD
      ANDr64Immediate => Grp1.AND
      CMPr64Immediate => Grp1.CMP
      ORr64Immediate  => Grp1.OR
      SBBr64Immediate => Grp1.SBB
      SUBr64Immediate => Grp1.SUB
      XORr64Immediate => Grp1.XOR
    }
   (Target: Register32, Immediate: signed 32 bits)
   BasicInstruction
   & prefix_length0
   & if Intel64 => prefix_REX(W=On R=Target:3)
   & OneByteOpcode & Subcode=ImmGrp1EvIz
   & ModRM(Mod=reg RSlashM=Target:2-0 reg=*)

如果您真的正在使用简单的指令集解码只是一个简单的虚拟机,那么您可能不需要所有这些功能,因为“简单虚拟机”不会打包位或拆分数据跨越字节边界,和/或您可以通过少数违反这些假设的案例进行破解。随着人们的虚拟机越来越复杂(它们经过多年发展),它们必然变得更加复杂。 YMMV。