编写二进制反汇编器实际上意味着您想要指定二进制数据的模式以及解码实体之间的关系(如果您识别出两个指令,则合理地假设它们不重叠)。
传统的解析器并不擅长这样做(尽管您可以为一系列指令编写语法,假设单个位或字节作为标记;您仍然需要处理指令序列之间的间隙)。
我见过的最聪明的方法是由Norman Ramsey和他的团队开发的一个名为SLED的工具,它允许您为二进制数据编写简洁的模式,并将其自动组装成二进制编码器和解码器。这篇
研究论文讨论了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。