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

Parsing 用于为专有VM格式编写反汇编程序的解析器生成器,parsing,bytecode,disassembly,parser-generator,Parsing,Bytecode,Disassembly,Parser Generator,我正试图为Mindstorms EV3 VM二进制文件编写一个反汇编程序,最好是用Python编写,因为我对它非常熟悉。我有详细说明指令集、操作码、参数、数据类型等的文档,但我很难从中构建实际的反汇编程序 据我所知,编写反汇编程序与编写编译器没有太大区别,因为它只是一个美化的确定性有限状态机。然而,除此之外,我找不到任何关于写作的书籍或指南。我试图使用诸如Lex和Yacc(或pythonply中的)之类的编译器编写工具,但是所有这些工具都在某种程度上使我失败 Lex和Yacc组合似乎不是我可以使

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

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

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

我遇到的另一个问题是,我不知道如何处理以null结尾的字符串和参数编码(一些参数的前缀是一个字节,设置了某些位,告诉VM期望的长度和类型,我认为这意味着我无法在字节级别用简单的DFA解析整个字节码)


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

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

传统的解析器在这方面做得不太好(尽管您当然可以为一系列指令编写语法,假定单个位或字节为标记;您仍然需要处理指令序列之间的间隙)

我见过的最聪明的方法是诺曼·拉姆齐(Norman Ramsey)和他的团队开发的一个名为SLED的工具,它可以让你为二进制数据编写简洁的模式,并自动将其组装成二进制编码器和解码器。这与许多类似的系统有关。一个关键点:它们不仅仅是一个“解析器生成器”,但概念是相似的:许多模式用于描述数据,一个代码生成器用于将模式组装成一个有效的引擎以匹配所有模式

为了让您了解这些工具的外观,我根据我在这方面所做的一些工作,提供了一个部分x86-64编码的片段。其思想是定义组成的命名模式(带有约束),以便最终编写指令定义。下面是一个小部分的简单示例(整个示例大约有1200行):


如果你真的只是用一个简单的指令集对一个简单的虚拟机进行解码,也许你不需要所有这些功能,因为“简单虚拟机”不会跨字节边界打包位或分割数据,和/或你可以破解一些违反这些假设的情况。随着人们的虚拟机变得越来越复杂(它们会随着时间的推移而演变),它们必然会变得越来越复杂。YMMV.

你是对的,你需要做相反的事情。然而,不能用lexer+Yacc组合实现这一点并不完全正确。您必须对输入进行预处理,以便能够向lexer提供有意义的输入,或者编写自己的lexer来接受字节而不是字符(即使字符只是基于字符集转换的字节)。您读取的每个字节决定了什么是可接受的输入以及输入的长度,因此,如果设置了一个位,则表示您读取的字节值不同,并且处于不同的状态。请记住,根据用途,这可能违反购买产品的规则(最终用户协议)。不确定这是否是您正在寻找的,但看看这里的想法:我用Python(Krakatau)编写了一个Java类文件反汇编程序,这可能会对您有所帮助。我真的不明白最难的部分是什么。您不需要任何花哨的框架,只需编写代码即可。解析器生成器是用于输入复杂语法的工具。由于编译后的代码是为了高效执行而设计的,因此它自然与之截然相反。因此,合并解析器生成器等是过分的,它会增加您的工作量,而不是减少工作量。对于大多数格式,反汇编程序可以实现为直接的单程翻译。@Holger好的,如果Ira的答案不起作用,看起来我会同意的
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=*)