OllyMachine由一个汇编器(Assembler)和一个32位虚拟机(Virtual Machine)所组成。其核心为寄存器虚拟机(Register-Machine),拥有堆栈、寄存器结构以及相关的寻址方式,与x86 CPU比较相似。
OllyMachine是32位虚拟机,因此,它的有效寻址空间为:
有符号数 : -2GB ~ +2GB
无符号数 : 0 ~ 4GB
但是,在实际中可能达不到这么大的寻址空间(例如,现在大部分的机器的物理内存只有512MB),因此这只是一个理论值。
在OllyMachine虚拟机中,有83个通用寄存器以及3个隐含的寄存器。其中:
reg00、reg01、reg02 ... reg64、FreeBufferReg、FreeBufferSizeReg
以上这67个寄存器可以供用户自由使用。FreeBufferReg和FreeBufferSizeReg有其特殊用途,留待后面再说。
以及9个寄存器为:
eax、ecx、edx、ebx、esp、ebp、esi、edi、eip
这9个寄存器比较特殊,它们是我在设计OllyMachine时,为了方便程序员与OllyDbg进行交互而定义的特殊寄存器,对它们的操作将直接反映到OllyDbg的当前被调试进程上。
还有7个标志位寄存器:
CF、PF、AF、ZF、SF、DF、OF
这7个标志位寄存器对应着OllyDbg里面的相应的标志位,举例如下:
not cf mov zf, 0 mov pf, 1
另外,OllyMachine中还有2个隐含的寄存器,它们是:eip和esp。eip为指令指针寄存器,用来存放代码段中的偏移地址;esp为堆栈指针寄存器,用来指示栈顶的偏移地址。
注意!这2个隐含的寄存器必须区分于上面提到的那9个寄存器中的同名寄存器。主要的区别在于:上面那9个寄存器其实是引用自OllyDbg的当前被调试进程,程序员可以直接操纵它们,其结果相当于在OllyDbg中直接操纵对应的寄存器;而这2个隐含的寄存器则是属于OllyMachine CPU内部的,不能被程序员直接访问,我给它们取名为eip和esp只是由于我对x86 CPU体系的个人喜好。
OllyMachine CPU内部,还有一个隐含的EFlags寄存器,它是标志位寄存器。标志位往往用来作为后续条件转移指令的转移控制条件,所以称为条件码。在OllyMachine CPU内部,有以下2位标志位:
CF (Carry Flag) : 进位标志,记录运算时从最高有效位产生的进位值。
ZF (Zero Flag) : 零标志,运算结果为0时ZF位置1,否则置0。
OllyMachine拥有自己的一套指令集(opcode),它与用户的接口是一套被称为OllyMachine Script的汇编语言,用户通过编写汇编语言与OllyMachine打交道。
在运行前,OllyMachine会首先通过内置的Assembler把汇编语言源代码编译成字节码(byte codes),其工作流程如下图所示:
|
通过Assembler生成字节码后,字节码即可送进虚拟机内部进行执行。
因此,实际上OllyMachine的工作流程是分成两个独立的步骤:
汇编(Assemble)
执行(Run in Virtual Machine)
我们也可以事先执行好汇编的步骤,即把源代码编译成一个字节码文件并保存起来,以后就可以直接用OllyMachine载入这个字节码文件而无需再次汇编,达到节省时间的目的。
OllyMachine有一个内置的汇编器,它是一个两遍扫描的LL(1)语法驱动汇编器。如果源程序中有词法、语法错误,会在编译期间被检测出来,并提示出错文件、行、单词以及出错类型给程序员,方便程序员进行debug。
请看下面这个例子程序:
mov reg00, reg73 // Error: "reg73" is invalid inc reg00 inc reg00, 1 // Error: "inc" doesn't need second operand exit: // Warning: unreferenced label
OllyMachine的汇编器会报错:
|
OllyMachine在运行期如产生异常,会被捕捉(例如除0异常等),并且会给出导致异常的指令名称,以及dump出所有的字节码,供程序员进行debug。
请看下面这个例子程序:
mov reg00, 9 mov reg01, 3 sub reg01, 3 // now reg01 is 0 div reg00, reg01 // oh shit!
由于程序员的粗心产生了除0错误,于是就会导致OllyMachine捕获这个异常(依次显示下面的两个报错对话框):
|
|