终于来到最有意思的部分了!OllyMachine Script是我定义的一套汇编语言,它是用户与OllyMachine虚拟机打交道的最重要的接口(当然,如果你不嫌麻烦,也可以用16进制编辑器直接编写字节码,不过我想应该没有什么人会这样做吧。^_^)。实际上,编写一个Assembler比编写一个Virtual Machine要难得多,我花费在这个汇编器上的时间足足有半个多月。
每一个OllyMachine汇编语言源程序都可以被分解成一系列的语句,而每条语句将在汇编语言源文件里面占用一个独立的文本行,即一行只能写一条语句,语句行的长度没有最大限制。语句又可以分成两种不同的形式:
指令(instruction)
注释(comment)
请注意:OllyMachine汇编语言中所有的指令都是不区分大小写的。
指令是用来给汇编器转换为字节码的语句。一条指令由一个操作码(opcode)和零个或多个操作数(operand)组成:
指令 = 操作码 [ 操作数 [ 操作数 ... ] ]
操作码是一个符号,它定义了指令将要采取的操作动作和整条指令的格式。我为OllyMachine汇编语言所定义的操作码能够完全决定其操作数的个数和这些操作数的类型。
请看下面这两条指令:
add reg00, 0x1234 add reg01, reg00
操作码add要求自己的后面跟有一个通用寄存器,一个逗号,以及一个立即数或者一个通用寄存器,它同时还规定了要把0x1234或者reg00里面的值放入寄存器reg00或者reg01。也就是说,操作码add既给出了这条指令将要采取的操作动作,又给出了这条指令的完整格式。
操作数给出的是指令将要对其进行操作的数据。OllyMachine汇编语言能够识别3种不同类型的指令操作数。这些操作数的类型如下图所示:
|
OllyMachine虚拟机一共有83个通用寄存器,它们全都是32位的,寻址范围为:无符号类型:0~4GB,有符号类型:-2GB~+2GB。其中:
reg00、reg01、reg02 ... reg64、FreeBufferReg、FreeBufferSizeReg
这67个寄存器可以供用户自由使用。FreeBufferReg和FreeBufferSizeReg有其特殊用途。
FreeBufferReg指向一个提供给脚本解码、临时存放数据使用的长度为4K的缓冲区;FreeBufferSizeReg表示FreeBuffer的当前长度。例如,如果需要获得一段字符串,这两个寄存器就可以派上用场了。
注意!程序员在编程时,应尽量避免对FreeBufferReg和FreeBufferSizeReg直接进行写操作,从语法上来说对它们进行的操作是跟别的寄存器一样的(即允许读、写操作),但由于这两个寄存器有其特殊含义,它们一般是由相关的API在内部进行维护的,所以如果由程序员显式地改写了它们的值,可能会导致无法预期的程序运行结果。
以及9个寄存器为:
eax、ecx、edx、ebx、esp、ebp、esi、edi、eip
对这9个寄存器的使用必须小心谨慎,因为对它们的操作将直接反映到OllyDbg的当前被调试进程上。例如:
mov reg00, eax
将会把当前调试进程的eax寄存器的值赋值给reg00。
mov eax, 0x12345678
将会把0x12345678赋值给当前调试进程的eax寄存器。
还有7个标志位寄存器:
CF、PF、AF、ZF、SF、DF、OF
这7个标志位寄存器对应着OllyDbg里面的相应的标志位,举例如下:
not cf mov zf, 0 mov pf, 1
标识符是由一组字符构成的符号。标识符的作用是命名标号(label)。标识符的第一个字符必须是一个字母(即a-z和A-Z之中的一个字符),或者一个下划线(“_”)。跟在第一个字符后面的其余字符可以是字母、下划线或者数字,即:
第一个字符:a-z、A-Z、_
后 续 字符:a-z、A-Z、_、0-9
下面是一些合法的标识符:
_1continue exit0 loop1
下面是一些非法的标识符:
1continue .exit0 my_#_loop2
整数常数可以分为两种类型:10进制和16进制。
10进制数不需要任何前缀,例如:
100、-1234
16进制数要加上“0x”或者“0X”作为前缀,例如:
0x100、-0x1234
10进制数和16进制数都可以由正数和负数表示,正数无需加“+”号(加了反而会编译不通过),负数需要加上“-”号。注意,整数常数最大不能超过0xFFFFFFFF。
注释的作用就不必多说了。注释的重要特点是汇编器将完全忽略它们,不对它们做任何处理。通常来说,注释是夹杂在源代码中的说明文档。
OllyMachine汇编语言中的注释分为两种:行注释和块注释。
行注释:以“//”或“;”开头,一直到行尾,例如:
// line comment 1. ; line comment 2.
块注释:以“/*”开头,直到遇到“*/”为止,可以占据多行,例如:
/* this is a block comment. */
从程序设计的布局来说,一个汇编语言程序无非是一个由多个过程组成的集合体。那么我们该如何控制过程呢?我定义了标号(label)这种结构。
标号的格式是在标识符的后面加上一个冒号,例如:
Error0:
注意,OllyMachine汇编语言中的标号必须是独一无二的,它们不得与任何其他的名字重复。
定义好标号之后,就可以通过跳转指令(后面再详细介绍)来控制过程了,例如:
jmp Error0 // instructions ... Error0: // other instructions ...
数据传送指令负责把数据、地址或立即数传送到寄存器中。它可以分成:
MOV (Move) 传送
XCHG (Exchange) 交换
LDS (Load String) 载入字符串
PUSH (Push onto the stack) 进栈
POP (Pop from the stack) 出栈
格式为:MOV DST, SRC
执行操作:(DST) <- (SRC)
它有两种格式:
MOV 寄存器, 立即数
MOV 寄存器, 寄存器
例如:
MOV reg00, 0x100 MOV reg00, reg01
格式为:XCHG OPR1, OPR2
执行操作:(OPR1) <-> (OPR2)
它只有一种格式:
XCHG 寄存器, 寄存器
例如:
mov reg01, 1 mov reg02, 2 XCHG reg01, reg02 // now reg01 == 2, reg02 == 1
XCHG指令不会影响虚拟机的标志位。
格式为:LDS 寄存器, "字符串"
例如:
LDS reg00, "Hello World!"
可以把LDS记忆成:LoaD String,即载入字符串到寄存器的意思,这样比较容易记住。^_^
格式为:PUSH SRC
执行操作:(ESP) <- (ESP) - 4
((ESP) + 4, (ESP)) <- (SRC)
它有两种格式:
PUSH 立即数
PUSH 寄存器
例如:
PUSH 0x100 PUSH reg00
格式为:POP DST
执行操作:(DST) <- ((ESP) + 4, (ESP))
(ESP) <- (ESP) + 4
它只有一种格式:
POP 寄存器
例如:
POP reg00
算术运算指令用来执行算术运算,它们当中有双操作数指令,也有单操作数指令。
ADD (add) 加法
INC (increment) 加1
格式:ADD DST, SRC
执行操作:(DST) <- (SRC) + (DST)
它有两种格式:
ADD 寄存器, 立即数
ADD 寄存器, 寄存器
例如:
ADD reg00, 0x100 ADD reg00, reg01
注意:ADD指令会影响虚拟机的CF标志位。
格式:INC DST
执行操作:(DST) <- (DST) + 1
它只有一种格式:
INC 寄存器
例如:
INC reg00
INC指令不影响虚拟机的标志位。
SUB (subtract) 减法
DEC (increment) 减1
CMP (Compare) 比较
格式:SUB DST, SRC
执行操作:(DST) <- (DST) - (SRC)
它有两种格式:
SUB 寄存器, 立即数
SUB 寄存器, 寄存器
例如:
SUB reg00, 0x100 SUB reg00, reg01
注意:SUB指令会影响虚拟机的CF和ZF标志位。
格式:DEC DST
执行操作:(DST) <- (DST) - 1
它只有一种格式:
DEC 寄存器
例如:
DEC reg00
DEC指令不影响虚拟机的CF标志位,但会影响ZF标志位。
格式:CMP OPR1, OPR2
执行操作:(OPR1) - (OPR2)
它有两种格式:
CMP 寄存器, 立即数
CMP 寄存器, 寄存器
例如:
CMP reg00, 0x100 CMP reg00, reg01
注意:该指令与SUB指令一样执行减法操作,但它并不保存结果,只是根据结果设置条件标志位CF和ZF,CMP指令后往往跟着一条条件转移指令,根据比较结果产生不同的程序分支。
MUL (mltiple) 乘法
格式:MUL DST, SRC
执行操作:(DST) <- (DST) * (SRC)
它有两种格式:
MUL 寄存器, 立即数
MUL 寄存器, 寄存器
例如:
MUL reg00, 0x100 MUL reg00, reg01
MUL指令不会影响虚拟机的标志位。
DIV (divide) 除法
格式:DIV DST, SRC
执行操作:(DST) <- (DST) / (SRC)
它有两种格式:
DIV 寄存器, 立即数
DIV 寄存器, 寄存器
例如:
DIV reg00, 0x100 DIV reg00, reg01
DIV指令不会影响虚拟机的标志位。
注意:不能把0当作除数,否则虚拟机在运行时会检测出来并报错。
AND (and) 逻辑与
OR (or) 逻辑或
NOT (not) 逻辑非
XOR (exclusive or) 异或
格式:AND DST, SRC
执行操作:(DST) <- (DST) & (SRC)
它有两种格式:
AND 寄存器, 立即数
AND 寄存器, 寄存器
例如:
AND reg00, 0x100 AND reg00, reg01
注意:AND指令会影响虚拟机的ZF标志位,并把CF标志位置为0。
格式:OR DST, SRC
执行操作:(DST) <- (DST) | (SRC)
它有两种格式:
OR 寄存器, 立即数
OR 寄存器, 寄存器
例如:
OR reg00, 0x100 OR reg00, reg01
注意:OR指令会影响虚拟机的ZF标志位,并把CF标志位置为0。
格式:NOT DST
执行操作:(DST) <- !(DST)
它只有一种格式:
NOT 寄存器
例如:
NOT reg00
NOT指令不会影响虚拟机的标志位。
格式:XOR DST, SRC
执行操作:(DST) <- (DST) ^ (SRC)
它有两种格式:
XOR 寄存器, 立即数
XOR 寄存器, 寄存器
例如:
XOR reg00, 0x100 XOR reg00, reg01
注意:XOR指令会影响虚拟机的ZF标志位,并把CF标志位置为0。
SHL (shift left) 逻辑左移
SHR (shift right) 逻辑右移
格式:SHL DST, SRC
它有两种格式:
SHL 寄存器, 立即数
SHL 寄存器, 寄存器
例如:
MOV reg00, 0x10 SHL reg00, 8 // reg00 is now 0x1000 MOV reg00, 0x10 MOV reg01, 8 SHL reg00, reg01 // reg00 is now 0x1000
注意:SHL指令不会影响虚拟机的标志位。
格式:SHR DST, SRC
它有两种格式:
SHR 寄存器, 立即数
SHR 寄存器, 寄存器
例如:
MOV reg00, 0x1000 SHR reg00, 8 // reg00 is now 0x10 MOV reg00, 0x1000 MOV reg01, 8 SHR reg00, reg01 // reg00 is now 0x10
注意:SHR指令不会影响虚拟机的标志位。
一般情况下指令是顺序地逐条执行的,但实际上程序不可能全部顺序执行而经常需要改变其执行流程,在这里我们要介绍的控制转移指令就是用来控制程序的执行流程的。
格式:JMP label
执行操作:(EIP) <- (EIP) + 32位偏移量
无条件地转移到指令指定的地址去执行从该地址开始的指令,呵呵,好拗口啊。举个例子如下:
jmp Error0 mov reg01, 0x200 Error0: mov reg00, 0x100
当程序运行到 jmp Error0 的时候,程序的执行流程就会跳转到 mov reg00, 0x100 处,因此,mov reg01, 0x200 将不会执行。
结果为零(或相等)则转移。
测试条件:ZF = 1
结果不为零(或不相等)则转移。
测试条件:ZF = 0
低于,或者不高于或者等于,或进位为1则转移。
测试条件:CF = 1
同JB。
不低于,或者高于或等于,或进位位为0则转移。
测试条件:CF = 0
同JNB。
低于或等于,或不高于则转移。
测试条件:CF = 1 or ZF = 1
同JBE。
不低于或等于,或者高于则转移。
测试条件:CF = 0 and ZF = 0
同JNBE。
还有一些其他的指令是无法归类到上面的指令集中的,它们是:
格式:INCLUDE "filename.oms"
INCLUDE指令可以包含另外一个汇编语言源文件一同进行汇编。
格式:NOP
NOP指令什么都不做,仅仅是空耗费一次虚拟机CPU运行周期,不会影响任何标志位,它所造成的影响是使虚拟机CPU的EIP加1。
格式:PAUSE
PAUSE指令会使虚拟机暂时停止运行,如要让虚拟机继续运行,请选择Plugins菜单中的“OllyMachine -> Resume”。
格式:HALT
HALT是停机指令,OllyMachine运行时只要一遇到HALT指令,就会立即停机,并且以正常状态退出。
格式 INVOKE Api_Name, parameter1, parameter2, ...
INVOKE宏用来调用API,类似MASM32中的INVOKE宏。