信息来源:红狼安全小组(www.wolfexp.net)
文章作者:Asm
注:择写过程参考和借鉴一些书籍资料.....在此感谢各大牛淫 :)
汇编指令-------------(推荐程度:必须)
计算机能直接识别的只有机器语言,就是只有0和1组成的二进制的指令,而汇编语言,其实就是利用字符来表示的一种机器语言,本质上,汇编也算是一种机器语言。机器语言和汇编语言统属“低级语言”,相反的,还有一些高级语言,例如 C/C++,VB,VC++,Perl等等,这些语言和我们的自然语法都类型,但是不能被计算机直接识别,只有通过编译,才能够转换成0和1。
在DOS时代,积存器是16位的,那个时候的汇编,是DOS16位,积存器是16位的,即:ax,bx,cx,dx。现在我们所使用的windows,是32位的处理器,所以,在windows下用汇编语言编程,也叫win32位汇编,积存器分别是eax,ebx,ecx,edx.当然还有LINUX/UNIX等汇编.
现在我们所学的,就是win32位汇编,首先我们要知道汇编基础的指令。例如
mov,它的用法是:mov eax,ebx,执行了这条指令,就是把ebx的值传送到eax里边,如果eax=0001H,ebx=0002H,指令执行后,它们的值分别是eax=0002H,
ebx=0002H。下面我把常用的指令列举出来:
1)MOV AX,0001H ;把0001H送到寄存器AX,执行后(AX)=0001H
2)PUSH AX ;压栈指令 其中对SP的影响是:由低向高,SP-2
3)POP AX ;出栈指令 其中对SP的影响是:由高向低,SP+2
4)XCHG AX,BX;交换指令,当(AX)=1234H,(BX)=5678H,执行的结果是
(AX)=5678H,(BX)=1234H
5)IN ;输入指令,一般对于CPU与I/O设备之间的联系
6)OUT ;输出指令,一般对于CPU与I/O设备之间的联系
7)XLAT ;换码指令,
8)LEA SPO SPO1 ;执行的结果是将SPO1送SPO,即SPO1=SPO 有效地址送寄存器
9)LDS ;指针送寄存器DS
10)LES ;指针送寄存器ES
=======================================================================
标志寄存器传送指令
11)LAHF ;标志送AH
12)SAHF ;AH送标志寄存器
13)PUSHF ;标准出栈
14)POPF ;标志压栈
=====================================================================
算术指令
15)ADD ;加法指令
16)ADC ;带进位加法指令
17)INC ;加1
======================================================================
减法指令
18)SUB ;减法指令
19)SBB ;带借位减法指令
20)DEC ;减1
21)NEG ;求补
22)CMP ;比较
=======================================================================
乘法指令
23)MUL ;无符号数乘法
24)IMUL ;带符号数乘法
=======================================================================
除法指令
25)DIV ;无符号数除法
26)IDIV ;带符号数除法
由于使用除法指令,务必有如下两个
27)CBW ;字节转换为字
28)CWD ;字转换为双字
=======================================================================
十进制调整指令(压缩BCD码调整指令)
29)DAA ;加法的十进制调整指令
30)DAS ;减法的十进制调整指令
非压缩BCD码调整指令
31)AAA ;加法的ASCII调整指令
32)AAS ;减法的ASCII调整指令
33)AAM ;乘法的ASCII调整指令
34)AAD ;出发的ASCII调整指令
=======================================================================
逻辑运算指令
35)AND ;逻辑与
36)OR ;逻辑或
37)NOT ;逻辑非
38)XOR ;异或
39)TEST ;测试
DOS汇编寻址方式-------------(推荐程度:一般了解)
计算机指令是由操作码和操作数组成,操作码表明操作性质的代码,操作数则说明操作的数或数的地址.一般计算机指令是由二进制0或1组成,也就是可以直接被计算机识别的二进制代码.
操作数直接存放在指令中,紧跟操作码之后,它作为指令的一部分存放在代码段里,这种操作数称为立即数.立即数可以是8位或者16位,如果是16位,则高字节存放在高地址中,低字节存放在低地址中.
(1)立即寻址方式用来表示常数,它经常用于给寄存器赋初值,并且只能用于源操作数字段,不能用于目的操作数字段..例如:
MOV AL,5
指令执行后结果为(AL)=05H
MOV AX,3064H
指令执行后结果为(AX)=3064H
(2)寄存器寻址方式:操作数在寄存器中,指令指定寄存器号,对于16位操作数,寄存器可以是AX,BX,CX,DX,SI,DI,SP,BP等;对于8位数的操作数,寄存器可以是AL,AH,BL,BH,CL,CH,DL,DH.这种寻址方式由于操作数就在寄存器中,不需要访问存储器来取得操作数,因而可以取得较高的运算速度.
MOV AX BX
指令执行前,(AX)=3064H,(BX)=1234H,指令执行后,(AX)=1234H,(BX)保持不变.
(3)直接寻址方式:在IBM PC中把操作数的偏移地址称为有效地址EA.在直接寻址方式中,有效地址EA就在指令中,它存放在代码段中指令操作码之后,但操作数一般存放在数据段中,所以必须先求出操作数的物理地址,然后再访问存储器才能取得操作数.操作数在数据段中,那么 物理地址=(DS)*16D+有效地址EA
MOV AX,[2000H]
如(DS)=3000H,执行后,(AX)=16D*3000H+2000H=3050H
在汇编语言指令中,可以用符号地址代替数值地址,如:
MOV AX,VALUE
此时VALUE为存档操作数单元的符号地址,也可以写成:
MOV AX,[VALUE]
两者是等效的.在VALUE附加段中,则应指定段跨越前缀如下:
MOV AX,ES:VALUE
MOV AX,ES:[VALUE]
(4)寄存器间接寻址方式:操作书的有效地址在基址寄存起BX,SI,DI,则操作数据段中,所以用DS段寄存器的内容作为段地址,即操作数的物理地址为:
物理地址=16D*(DS)+(BX)
物理地址=16D*(DS)+(SI)
物理地址=16D*(DS)+(DI)
如指令中指定BP寄存器,则操作数在堆栈中,段地址在SS中,所以操作数的物理地址为物理地址=16D*(SS)+(BP)
例: MOV AX,[BX]
如果段寄存器(DS)=2000H,(BX)=1000H,
则 物理地址=20000H+1000H=21000H
然后送AX,(AX)=50A0H
指令中也可以指定段跨越来取得其他段中的数据.如 MOV AX,ES:[BX] 这种寻址方式可以用于表格处理,执行完一条指令后,只需要修改寄存器内容就可以取出表格中的下一项.
EAX,AX,AL和AH的关系:
AX,AH,AL是EAX的一部分,EAX是一个32位的寄存器(仅在386以上存在),AX包含了EAX的低16位(2字节),AH包含了AX的高字节,而AL包含了AX的低字节。因而AX是16位的,AL和AX是8位的。
(5)段内直接寻址转移指令的汇编格式表示:
JMP NEAR PTR PROGIA
JMP SHORT QUEST
其中,PROGIA和QUEST均为转向的符号地址,在机器指令中,用位移来表示。在汇编指令中,如果位移量为16位,则在符号地址前加操作符NEAR PTR,如果位移量为8位,则在符号地址前加操作符SHORT
(6)段内间接寻址转移指令的汇编格式表示:
JMP BX
JMP WORD RTP[BP+TABLE]等,其中WORD PTR为操作符,用以指出其后的寻址方式所取得的转向地址是一个字的有效地址,也就是说它是一种段内转移!
以上的两种寻址方式都为短内转移,所以直接把求得的转移的有效地址送到IP寄存器就可以了。如果需要计算转移的物理地址,则计算公式应该是:
物理地址=16D*(CS)+EA
win32汇编开始-------------(推荐程度:必须)
一般常用的指令有:mov,add,inc,jmp,push,pop,cmp等等
在win32位汇编中,已经没有了dos汇编的中断int,编写一些程序,还要熟悉API函数。例如一个简单的win32汇编的例子:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib
.data
szTitle db '简单的例子',0
szCaption db '恭喜,你看到了一个win32汇编程序,0
.code
start:
invoke MessageBox,NULL,addr szCaption,addr szTitle,MB_OK
invoke ExitPorcess,NULL
end start
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
利用IDC集成环境,推荐编译工具是 MASMPlus,下载地址:http://www.aogosoft.com/masmplus/官方站已经对它的编译方法详细说明
我们注意分析这个例子
.386
.model flat,stdcall
option casemap:none
386,就是采用386指令集,另外的还有486,586,686等指令集,特权指令486P,586P。如果要编写驱动程序,防火墙,就要用到特权指令。我们编写一般的win32程序,只要386就行了。
.model flat,stdcall,这个表示内存平坦模式编程,option casemap:none要求程序区分大小写。接着用include包含调用函数用到的库,例如MessageBox是user32.dll里面,所以要include user32.inc,includelib user32.lib
.data,下面定义了一些数据,例如我定义的是szTitle和szCaption,等到下面要调用MessageBox函数的时候需要用到的参数。
start 这里是程序开始的标号,也可以是Mian。接着利用伪指令invoke调用MessageBox,将定义的szTitle和szCaption的数值显示出来,也就是弹出来.ExitPorcess用来结束进程退出。如果没有ExitPorcess,程序在运行完毕后会提示说:“程序遇到麻烦需要结束..........”
一般的win32汇编的结构如下
.386
.model flat,stdcall
option casemap:none
include .....
.data
.data?
.code
start: ;开始标号
....................;这里程序开始
end start ;结束标号
注:以上代码用MASM格式编写
上面的例子使用了伪指令invoke,为什么要使用这个?原因就是使用伪执行,windows可以帮我们自动对齐堆栈.
还可以写成如下:
.386
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib
.data
szTitle db '简单的例子',0
szCaption db '恭喜,你看到了一个win32汇编程序,0
.code
start:
push MB_OK
push szTitle
push szCaption
push NULL
call MessageBox
push NULL
call ExitPorcess
end start
有堆栈先进后出的原则,将MessageBox的参数逐步压栈,然后MessageBox
换成一个执行地址指向导入表,导入表指向MessageBox函数的实际地址,接着windows会根据User32.dll的内存地址动态填入.