写于2014年7月18日
" vim:fen:fdm=marker:fmr={{{,}}}:fdl=0:fdc=1:ts=2:sw=2:sts=2
">>>>>>>>>>:指令{{{
test 指令 模拟执行AND 但不改变 两寄存器的值 只影响寄存器
"<<<<<<<<<<:指令}}}
"1.无符号整数 {{{
8 位整数范围 0至255
16 位整数范围 0至65535(等于64K)
32 位整数范围 0至4294967295(等于4G)
"1.无符号整数 }}}
"2.带符号整数 {{{
正数高位为0,负数高位为1
8 位整数范围 负 128 至 127
16 位整数范围 负 3 2768 至 3 2767
32 位整数范围 负 21 4748 3648 至 21 4748 3647
"2.带符号整数 }}}
"3.快速求负数补码 {{{
1.把其正数从左至右按位求反,直到遇到最右边1和后边0保持不变
2.或者按位取反再加一
"3.快速求负数补码 }}}
"4.各进制数后缀 {{{
二进制数 b Binary
八进制数 o Octal
十进制数 d Decimal
十六进制 h Hexadecimal
"4.各进制数后缀 }}}
"5.逻辑运算规则 {{{
与运算 全1则1 , 否则0
或运算 有1则1 , 否则0
异或运算 不同1 , 否则0
非运算 取反
"5.逻辑运算规则 }}}
"6.存储单位 {{{
一字节等于 8bit位
一个字等于 两字节 等于 16bit位
一个双字等于 四字节 等于 32bit位
一小段等于 十六字节 等于 128bit位
一KB等于 1024字节 等于 65536bit位 等于 2的10次幂
一MB等于 1048576字节 等于 2的20次幂
"6.存储单位 }}}
"7.标志寄存器名称的意义 {{{
------------------------------------------------------------------------
NV 无溢出 比特值为0
OV 有溢出 比特值为1
PL 结果为正值 比特值为0
NG 结果为负值 比特值为1
NZ 结果非0 比特值为0
ZR 结果为0 比特值为1
NC 运算最高位无进位 比特值为0
CY 运算最高位有进位 比特值为1
NA 加法低字节无进位 比特值为0
AC 加法低字节有进位 比特值为1
PO 1的个数为奇数 比特值为0
PE 1的个数为偶数 比特值为1
UP 从低到高地址增量 比特值为0
DN 从高到低地址减量 比特值为1
DI 屏蔽中断 比特值为0
EI 允许中断 比特值为1
屏蔽陷阱 比特值为0
允许陷阱 比特值为1
------------------------------------------------------------------------
OF overflow flag 溢出标志
SF sign Flag 符号标志
ZF zero flag 零标志
CF carry flag 进位标志
AF auxiliary carry flag 辅助进位标志
PF parity flag 奇偶标志
DF direcion flag 方向标志
IF interrupt flag 中断标志
TF trap flag 陷阱标志
------------------------------------------------------------------------
NV Not oVerflow 无溢出 0 容
OV OVerflow 有溢出 1 溢
NG NeGative 结果为正 0 正
PL PLus 结果为负 1 负
NZ Not Zero 结果非0 0 非
ZR Zero 结果为0 1 零
NC Not Carry 运算最高位无借位 0 无进借
CY CarrY 运算最高位有借位 1 有进阶
NA Not Auxiliary 加法低字节无进位 0 无进借
AC Auxiliary Carry 加法低字节有进位 1 有进借
PO Parity Odd 1的个数为奇数 0 奇单
PE Parity Even 1的个数为偶数 1 偶双
UP UP 从低到高地址增量 0 低高
DN Down 从高到低地址增量 1 高低
DI Disable Interrupt 屏蔽中断 0 屏蔽
EI Enable Interrupt 允许中断 1 允许
屏蔽陷阱 0 屏蔽
允许陷阱 1 允许
标志寄存器的状态与比特位的0或1状态对应关系很难记,其实我们只需要记住,
全是零的状态,而相反时为1的状态自然就记住了
容,正,非,
无,无,奇,
低,屏,屏
容:能容纳自然不溢出
正:正数呗
非:非零
无:无进借位 二进制运算
无:无进借位 十进制运算
奇:奇数,单数
低:低到高,
我们爬山的话,肯定是先从低处爬上去,然后在爬下来,
这和正常排列自然数的循序,0,1相同.
屏:屏蔽中断
屏:屏蔽陷阱 幸好Intel把这俩标志位屏蔽都状态都和零对应
正负标志位,0是按整数算的,在规定位数的表示范围时候,
就可以看出来比如:2的8次幂的数值范围是:
-128 - 127,
0在整数范围,正负数的数量才平衡.
------------------------------------------------------------------------
OF溢出标志
NV Not oVerflow 无溢出 比特值为0
OV OVerflow 有溢出 比特值为1
SF符号标志
PL PLus 结果为正值 比特值为0
NG NeGative 结果为负值 比特值为1
ZF零标志
NZ Not Zero 结果非0 比特值为0
ZR Zero 结果为0 比特值为1
CF进位标志
NC Not Carry 运算最高位无进位 比特值为0
CY CarrY 运算最高位有进位 比特值为1
AF辅助进位标志
NA Not Auxiliary 加法低字节无进位 比特值为0
AC Auxiliary Carry 加法低字节有进位 比特值为1
PF奇偶标志
PO Parity Odd 1的个数为奇数 比特值为0
PE Parity Even 1的个数为偶数 比特值为1
DF方向标志位
UP UP 从低到高地址增量 比特值为0
DN Down 从高到低地址减量 比特值为1
IF中断标志
DI Disable Interrupt 屏蔽中断 比特值为0
EI Enable Interrupt 允许中断 比特值为1
TF陷阱标志
屏蔽陷阱 比特值为0
允许陷阱 比特值为1
------------------------------------------------------------------------
"7.标志寄存器标志状态符词义 }}}
"11.所有指令名 {{{
------------------------------------------------------------------------
数据传送指令
mov
movsz
movxz
push
pop
pusha
popa
pushad
popad
xchg
in
out
xlat
lea
lds
les
lfs
lgs
lss
pushf
popf
pushfd
popfd
lahf
sahf
------------------------------------------------------------------------
算术指令
类型转换指令
cbw
cwd
cdq
cwde
二进制加法指令
add
adc
inc
xadd
二进制减法指令
sub
sbb
dec
cmp
neg
二进制乘法指令
mul
imul
二进制除法指令
div
idiv
十进制算术指令
aaa
aas
aam
aad
daa
das
逻辑运算指令
not
and
test
or
xor
位测试指令
bt
bts
btr
btc
------------------------------------------------------------------------
位扫描指令
bsf
bsr
------------------------------------------------------------------------
基本移位指令
shl
sal
shr
ar
------------------------------------------------------------------------
循环移位指令
rol
ror
rcl
rcr
------------------------------------------------------------------------
双精度移位指令
shld
shrd
------------------------------------------------------------------------
程序控制指令
jmp
条件转移指令
jc
jnc
jo
jno
jp / jpe
jnp / jpo
js
jns
jz / je
jnz / jne
带符号数比较转移指令 less 小的,greater大的
jg / jnle
jng / jle
jl / jnge
jnl / jge
无符号数比较转移 above在上方,below在下方
ja / jnbe
jna / jbe
jb / jnae / jc
jnb / jae / jnc
------------------------------------------------------------------------
循环指令
loop
相等循环指令
loope / loopz
不等循环指令
loopne / loopnz
------------------------------------------------------------------------
子程序调用与返回指令
call
子程序返回指令
ret
ret n
------------------------------------------------------------------------
中断调用/返回指令
int n
iret
------------------------------------------------------------------------
处理机控制指令
标志操作指令
clc
stc
cmc
cld
std
cli
sti
------------------------------------------------------------------------
其它处理机控制指令
nop
hlt
wait
lock
------------------------------------------------------------------------
重复前缀指令
rep
相等重复前缀指令
repe / repz
不相等重复前缀
repne / repnz
------------------------------------------------------------------------
串传送指令
movs
movsb
movsw
mosd
取串指令
lods
lodsb
lodsw
lodsd
------------------------------------------------------------------------
存串指令
stos
stosb
stosw
stosd
------------------------------------------------------------------------
串输入指令
ins
insb
insw
insd
------------------------------------------------------------------------
串输出指令
outs
outsb
outsw
outsd
串比较指令
cmps
cmpsb
cmpsw
cmpsd
串扫描指令
scas
scasb
scasw
scasd
------------------------------------------------------------------------
条件字节设置指令
setz / sete zf为零指令执行结果为1
setnz / setne zf非零指令执行结果为1
sets
setns
seto
setno
setp / setpe
setnp / setpo
setg / setnle
setng / setle
setnl / setge
seta / setnbe
setna / setbe
setb / setnge / setc
setnb / setae / setnc
------------------------------------------------------------------------
32位cpu增加堆栈指令
enter
leave
------------------------------------------------------------------------
" }}}
"12.与非或异或运算 {{{
与运算
1 and 1=1
1 and 0=0
0 and 1=0
0 and 0=0
或运算
1 or 1 = 1
1 or 0 = 1
0 or 1 = 1
0 or 0 = 0
异或运算
1 xor 1 = 0
1 xor 0 = 1
0 xor 1 = 1
0 xor 0 = 0
非运算
not 1 =0
not 0 =1
与运算: 1与1为1,其它为0
或运算: 0或0为0,其它为1
异或运算:相同为0,不同为1
非运算:全部取反
与运算:0与目标数,全为0,1与目标数,全不变
或运算:0或目标数,全不变,1或目标数,全为1
异或运算:0异或目标数,全不变,1异或目标数,全取反
非运算:非运算全取反
与运算:
操作数"与"目标数,一起为真,才为真,否则为假
或运算:
或操作数为真,或目标数为真,就为真,否则为假
异或运算:
符合异的条件就为真,也就是不同喽,不符合异,也就是相同就为假
反运算:
就是1变0,0变1呗
"12.与非或异或运算 }}}
"13.addr伪指令{{{
而add伪指令:
addr 局部变量名/全局变量名
如果是全局变量,编译器自动按照offset的用法使用,
如果是局部变量,编译器会自动用lea指令先把地址取到
eax中,然后用eax代替变量地址使用
注意:
1. addr 伪只能用在invoke调用的函数的参数中
2. 以addr 调用的参数,对于采用从右向左压栈调用约定的函数,
其左边的参数不能以有eax
在刚刚跳转到入子程序入口时候,栈顶指针,总是指向子程序的返回地址,
因为在所有压栈指令完成以后,才执行call指令压入子程序的返回地址
1. esp -> 子程序返回地址
2. esp+4 -> 第一个参数 (对于从右向左压栈调用约定的子程序)
3.对于类成员函数
esp+4 -> this指针
esp+8 -> 函数从左向右第一个参数 (对于从右向左压栈调用约定的子程序)
"13.addr伪指令}}}
"14.取地址指令{{{
取全局变量地址:
mov 寄存器,offset 变量名
lea 寄存器,变量名
取局部变量地址:
lea eax,[ebp-4]
lea指令可以再运行时计算地址
"14.取地址指令}}}
"15.asm裸体函数{{{
asm裸体函数
option prologue:none
option epilogue:none
"15.裸体函数}}}
"16.VC裸体函数{{{
__declspec(naked)
"16.VC裸体函数}}}
"17.寄存器{{{
1.9寄存器
1.9.1 段寄存器
CS寄存器:程序代码段的起始地址 CS:IP
DS寄存器:程序数据段的启始地址 DS:BP DI:BP SI:BP
SS寄存器:程序堆栈段的起始地址
ES寄存器:在字符串,数据串操作中,用于存储器寻址,DS:DI
FS,GS:备用的附加段寄存器
1.9.2 指针寄存器
32位指针寄存器EIP,ESP,EBP最右边16位部分是IP,SP,BP
1.9.3 通用寄存器
EAX,EBX,ECX,EDX和ax,bx,cx,dx和ah,al
AX寄存器:主累加器.用于输入/输出和大多数算术运算操作
BX寄存器:基址寄存器,用于:扩展寻址的变址
CX计数寄存器:用于:控制循环重复次数,左,右移位的次数
DX数据寄存器:用于:1.输入,输出操作 2.大值乘,除法中AX,DX成对配合使用
1.9.4 变址寄存器
ESI,EDI可和SI,DI
DS:SI 联合使用 ES:DI联合使用
通用寄存器
AH&AL=AX(accumulator):累加寄存器,常用于运算;在乘除等指令中指定用来存放操作数,另外,所有的I/O指令都使用这一寄存器与外界传送数据.
BH&BL=BX(base):基址寄存器,常用于地址索引.
CH&CL=CX:计数寄存器,常用于计数;常用于保存计算值,如在移位指令,循环(loop)和串处理指令中用作隐含的计数器.
DH&DL=DX:数据寄存器,常用于数据传递.
AX:Accumulator
BX:Base
CX:Cout
DX:Date
SI:Source Index
DI:Destination Index
BP:Base Pointer
SP:Stack Pointer
CF:Carry Flag
ZF:Zero Flag
SF:Sign Flag
OF:Overflow Flag
PF:Parity Flag
AF:Auxiliary Flag
TF:Trap Flag
IF:Interrupt Flag
AF:Auxiliary Flag
CS:Code Segment
DS:Data Segment
ES:Extra Segment
SS:Stack Segment
段寄存器 :CS DS SS ES
AX BX CX
DX是CPU内部的通用寄存器中的数据寄存器,数据寄存器一般用于存放参与运算的数据或运算的结果,
每一个数据寄存器都是16位的(即16个二进制位),但又可以将高,低8位分别作为两个独立的8位寄存器使用.
它们的高8位记作AH,BH,CH,DH,低8位记作AL,BL,CL,DL.这种灵活的使用方法给编程带来极大的方便,既可以处理16位数据,也能处理8位数据.
数据寄存器除了作为通用寄存器使用外,它们还有各自的习惯用法
AX
称为累加器,常用于存放算术逻辑运算中的操作数,另外所有的I/O指令都使用累加器与外设接口传送信息
BX 称为基址寄存器,常用来存放访问内在时的基地址,
CX 称为计数寄存器,在循环和串操作指令中用作计数器
DX 称为数据寄存器,在寄存器间接寻址中的I/O指令中存放I/O端口的地址
另外,在做双字长乘除法运算时,DX
与AX合起来存放一个双字长数(32位),其中DX存放高16位,AX存放低16位.
AH&AL=AX(accumulator):累加寄存器
BH&BL=BX(base):基址寄存器
CH&CL=CX(count):计数寄存器
DH&DL=DX(data):数据寄存器
SP(Stack Pointer):堆栈指针寄存器
BP(Base Pointer):基址指针寄存器
SI(Source Index):源变址寄存器
DI(Destination Index):目的变址寄存器
IP(Instruction Pointer):指令指针寄存器
CS(Code Segment)代码段寄存器
DS(Data Segment):数据段寄存器
SS(Stack Segment):堆栈段寄存器
ES(Extra Segment):附加段寄存器
OF overflow flag 溢出标志
操作数超出机器能表示的范围表示溢出,溢出时为1.
SF sign Flag 符号标志
记录运算结果的符号,结果负时为1.
ZF zero flag 零标志
运算结果等于0时为1,否则为0.
CF carry flag 进位标志
最高有效位产生进位时为1,否则为0.
AF auxiliary carry flag 辅助进位标志
运算时,第3位向第4位产生进位时为1,否则为0.
PF parity flag 奇偶标志
运算结果操作数位为1的个数为偶数个时为1,否则为0.
DF direcion flag 方向标志
用于串处理.DF=1时,每次操作后使SI和DI减小.DF=0时则增大.
IF interrupt flag 中断标志
IF=1时,允许CPU响应可屏蔽中断,否则关闭中断.
TF trap flag 陷阱标志 用于调试单步操作.
段寄存器
CS(Code Segment):代码段寄存器
DS(Data Segment):数据段寄存器
SS(Stack Segment):堆栈段寄存器
ES(Extra Segment):附加段寄存器
指针寄存器和变址寄存器
SP(Stack
Pointer):堆栈指针,与SS配合使用,可指向目前的堆栈位置.
BP(Base
Pointer):基址指针寄存器,可用作SS的一个相对基址位置.
SI(Source
Index):源变址寄存器,可用来存放相对于DS段之源变址指针.
DI(Destination
Index):目的变址寄存器,可用来存放相对于ES段之目的变址指针.
IP(Instruction Pointer):指令指针
"17.寄存器}}}
"18.1.标志位一{{{
一,运算结果标志位
1,进位标志CF(Carry Flag)
进位标志CF主要用来反映运算是否产生进位或借位.如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则其值为0.
使用该标志位的情况有:多字(字节)数的加减运算,无符号数的大小比较运算,移位操作,字(字节)之间移位,专门改变CF值的指令等.
2,奇偶标志PF(Parity Flag)
奇偶标志PF用于反映运算结果中"1"的个数的奇偶性.如果"1"的个数为偶数,则PF的值为1,否则其值为0.
利用PF可进行奇偶校验检查,或产生奇偶校验位.在数据传送过程中,为了提供传送的可靠性,如果采用奇偶校验的方法,就可使用该标志位.
3,辅助进位标志AF(Auxiliary Carry Flag)
在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:
(1),在字操作时,发生低字节向高字节进位或借位时;
(2),在字节操作时,发生低4位向高4位进位或借位时.
对以上6个运算结果标志位,在一般编程情况下,标志位CF,ZF,SF和OF的使用频率较高,而标志位PF和AF的使用频率较低.
4,零标志ZF(Zero Flag)
零标志ZF用来反映运算结果是否为0.如果运算结果为0,则其值为1,否则其值为0.在判断运算结果是否为0时,可使用此标志位.
5,符号标志SF(Sign Flag)
符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同.在微机系统中,有符号数采用补码表示法,所以,SF也就反映运算结果的正负号.
运算结果为正数时,SF的值为0,否则其值为1.
6,溢出标志OF(Overflow Flag)
溢出标志OF用于反映有符号数加减运算所得结果是否溢出.如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,
否则,OF的值被清为0.
plus 正的
negative 负数
parity 报错 平价;同等;相等
odd 奇数
even 偶数
carry 进位
assistant 辅助
interrupt 中断
disable 使失去能力
enable 使能够
overflow 溢出
immediate 立即数
procedures 程序,过程
repetition 重复
PSW(Program Status Word):程序状态寄存器
ZF(Zero Flag):零标志位,用来反映运算结果是否为0.如果运算结果为0,则其值为1,否则其值为0.
PF(Parity Flag):奇偶标志位,用来反映运算结果中"1"的个数的奇偶性,如果"1"的个数为偶数,则PF的值为1,否则其值为0.
SF(Sign Flag):符号标志位,用来反映运算结果的符号位,它与运算结果的最高位相同.
CF(Carry Flag):进位标志位,用来反映运算结果是否产生进位或借位,如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则为0.
OF(Overflow Flag):溢出标志位,用来反映有符号数加减运算所得结果是否溢出.如果运算结果超出当前运算位数所能表示的范围,则称为溢出,
OF的值被置为1,否则,OF的值被清为0.
DF(Direction Flag):方向标志位,用来决定在串操作指令执行时有关指针寄存器发生调整的方向.
IF(Interrupt Flag):中断标志位,IF=1处理可屏蔽中断,IF=0不出来可屏蔽中断.
TF(Trace Flag):跟踪标志位,该标志可用于程序调试,没有专门的指令来设置或清除.
AF(Auxiliary carry Flag):辅助进位标志位,在字操作时,发生低字节向高字节进位或借位时;在字节操作时,发生低4位向高4位进位或借位时.
相关指令
abc(c-carry):带进位加法指令.它利用了CF位上记录的进位值.
sbb(b-borrow):带借位减法指令.它利用了CF位上记录的借位值.
cmp(compare):比较指令.它的功能相当于减法指令,只是不保存结果.
je(e-equal):ZF=1则转移
jne(ne-not equal):ZF=0则转移
jb(b-below):CF=0则转移
jnb(nb-not below):CF=0则转移
ja(a-above):CF=0且ZF=0则转移
jna(na-not above):CF=1或ZF=1则转移
movesb(b-byte):传送一个字节
movesw(w-word):传送一个字
rep movesb(rep-repeat):循环执行字节串传送指令
rep movesw(rep-repeat):循环执行字串传送指令
cld(clear DF):将DF置0
std(set DF):将DF置1
pushf(push flag):将标志寄存器的值压栈
popf(pop flag):从栈中弹出数据送入标志寄存器
1.9.5 标志位寄存器
OF溢出 算术运算最高位的溢出
DF方向 传送或比较字符串,数据传的方向
IF中断 是否响应外部中断
TF陷阱 是否允许单步方式工作
SF符号 算数运算的结果是正还是负 0=正,1=负
ZF零 算数运算或比较操作的结果是否为0,0则ZF=1,1则ZF=0
AF辅助进位 算数运算操作中第3位到第4位的进位输出
PF奇偶 运算结果中值为1的位的数量,是奇数PF=1,偶数,PF=0
CF进位
1.算数运算,最高位是否有进位和借位,
2.移位和循环操作位最后位的内容
ZF 零标志位 相关指令结果为0,ZF=1,结果不为0,ZF=0 zero 零
PF 奇偶标示位 所有bit位1的位数量为:奇数,PF=1 偶数,PF=0,
SF 负正标志位 相关指令执行后,结果:负为1,正为0 positive ['p z tiv, -zi-] 正数
CF 借,进位标志位 向高位借1或者进1 CF=1 没有向高位借1,或者进1 CF=0 carry ['k ri] 进位,射程
OF 溢出标识位 溢出OF=1 没有溢出OF=0 out输出
SF 符号 0 正数 PL PLus 1 负数 NG NeGative 正是零负是一
ZF 零 0 非零 NZ Not Zero 1 零 ZR Zero 非零零零是一
PF 奇偶 0 奇数 PO Parity Odd 1 偶数 PE Parity Even 奇为零偶为一
CF 进位 0 无进位 NC Not Carry 1 有进位 CY CarrY 无进零进为一
AF 辅助进位 0 无辅助进位 NA Not Auxiliary 1 有辅助进位 AC Auxiliary Carry 无辅零辅为一
OF 溢出 0 无溢出 NV Not oVerflow 1 有溢出 OV OVerflow 不溢零溢为一
DF 方向 0 增量 UP Up 1 减量 DN Down 左增零右减一
IF 中断 0 禁止 DI Disable Interrupt 1 允许 EI Enable Interrupt 禁止零允许一
TF 陷阱
为1的项 负零偶进辅溢右减允
"18.1.标志位一}}}
"18.2.标志位二{{{
一,运算结果标志位
1,进位标志CF(Carry Flag)
进位标志CF主要用来反映运算是否产生进位或借位.如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则其值为0.
使用该标志位的情况有:多字(字节)数的加减运算,无符号数的大小比较运算,移位操作,字(字节)之间移位,专门改变CF值的指令等.
2,奇偶标志PF(Parity Flag)
奇偶标志PF用于反映运算结果中"1"的个数的奇偶性.如果"1"的个数为偶数,则PF的值为1,否则其值为0.
利用PF可进行奇偶校验检查,或产生奇偶校验位.在数据传送过程中,为了提供传送的可靠性,如果采用奇偶校验的方法,就可使用该标志位.
3,辅助进位标志AF(Auxiliary Carry Flag)
在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:
(1),在字操作时,发生低字节向高字节进位或借位时;
(2),在字节操作时,发生低4位向高4位进位或借位时.
对以上6个运算结果标志位,在一般编程情况下,标志位CF,ZF,SF和OF的使用频率较高,而标志位PF和AF的使用频率较低.
4,零标志ZF(Zero Flag)
零标志ZF用来反映运算结果是否为0.如果运算结果为0,则其值为1,否则其值为0.在判断运算结果是否为0时,可使用此标志位.
5,符号标志SF(Sign Flag)
符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同.在微机系统中,有符
号数采用补码表示法,所以,SF也就反映运算结果的正负号.运算结果为正数时,SF的值为0,否则其值为1.
6,溢出标志OF(Overflow Flag)
溢出标志OF用于反映有符号数加减运算所得结果是否溢出.如果运算结果超过当前运算位数
所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0.
这段C语言的程序里的第2排的"flag"是什么意思?
这段C语言的程序里的第2排的"flag"是什么意思?有什么作用呢?
int findStr(char *str,char *substr)
{ int i,j,len1,len2,cnt=0,flag;
len1=strlen(str);
len2=strlen(substr);
for(i=0;i
{ for(j=0;j if(str[i+j]==substr[j]) flag=1;
else {flag=0;break;}
if(flag==1) cnt++;
}
return cnt;
}
问题补充:
什么是"标志位"?
提问者: bobsuker - 四级
最佳答案
它就是一个变量,是用来判断符合哪个条件的.当等于1是是说明str[i+j]==substr[j];0时是说明不满足上面的要求的...
其实在这个程序里这个是多余的,程序可以改为:
int findStr(char *str,char *substr)
{ int i,j,len1,len2,cnt=0;
len1=strlen(str);
len2=strlen(substr);
for(i=0;i { for(j=0;j if(str[i+j]==substr[j])cnt++;
}
return cnt;
}
carry 进位 flag标志 cf
OF
NV: no overflow OV: overflow
UP: up DN:down
DI: disable interrupt EI: enable interrupt
PL: plus NG: negative
NZ: no zero ZR: zero
NA: no assistant carry AC: assistant carry
PO: parity odd PE: parity even 奇偶校验
NC: no carry CY: carry
汇编标志位:
标志名 标志 标志 0
OF (溢出标志) OV NV
DF (方向标志) UP DN
IF (中断标志) DI EI
SF (符号标志位) PL NG
ZF (零标志) NZ ZR
AF (辅助进位标志位) NA AC
PF (奇偶标志) PO PE
CF (进位标志 ) NC CY
OF 溢出(是/否) OV OVerflow NV Not oVerflow
DF 方向(减量/增量) DN DowN UP UP
IF 中断(允许/关闭) EI Enable Interrupt DI Disable Interrupt
SF 符号(负/正) NG NeGative PL PLus
ZF 零(是/否) ZR ZeRo NZ Not Zero
AF 辅助进位(是/否) AC Auxiliary Carry NA Not Auxiliary
PF 奇偶(是/否) PE Parity Even PO Parity Odd
CF 进位(是/否) CY CarrY NC Not Carry
英文解释:
NV: no overflow OV: overflow
UP: up DN:down
DI: disable interrupt EI: enable interrupt
PL: plus NG: negative
NZ: no zero ZR: zero
NA: no assistant carry AC: assistant carry
PO: parity odd PE: parity even
NC: no carry CY: carry
plus [pl?s] 正的 negative ['neɡ?tiv] 负数
parity ['p?riti] 报错 n. 平价;同等;相等 odd [?d] 奇数even ['i:v?n]偶数
carry ['k?ri] 进位
assistant [?'sist?nt] 辅助
interrupt [,int?'r?pt] 中断 disable [dis'eib] 使失去能力 enable [i'neibl]使能够
overflow [,?uv?'fl?u, '?uv?fl?u] 溢出
先熟悉一下FLAGS标志位:
OV,NV即溢出标志位OF=1或0,表示运算结果有无溢出.当算术运算的结果超出了带符号数的范围(8位带符号数的范围是-128——+127,16位的是-32728——+32767).
DN,UP即方向标志位DF=1或0,表示串操作时按地址减或加的方式进行.
EI,DI即中断标志位IF=1或0,表示CPU可否响应可屏蔽中断请求.IF的状态对不可屏蔽中断及内部中断没有影响.
NG,PL即符号标志位SF=1或0,表示运算结果是负(最高位为1)或正(最高位为0).
ZR,NZ即零标志位ZF=1或0,表示运算结果是0或非0.
AC,NA即辅助进位标志位AF=1或0,在8位加减法操作中表示低4位向高4位有无进借位.
PE,PO即奇偶标志位PF=1或0,表示逻辑运算结果中1的个数是否为偶数.
CY,NC即进位标志位CF=1或0,表示加减法操作中最高位有无进借位.
8086寄存器
通用寄存器
AH&AL=AX(accumulator):累加寄存器,常用于运算;在乘除等指令中指定用来存放操作数,另外,所有的I/O指令都使用这一寄存器与外界传送数据.
BH&BL=BX(base):基址寄存器,常用于地址索引.
CH&CL=CX:计数寄存器,常用于计数;常用于保存计算值,如在移位指令,循环(loop)和串处理指令中用作隐含的计数器.
DH&DL=DX:数据寄存器,常用于数据传递.
段寄存器
CS(Code Segment):代码段寄存器
DS(Data Segment):数据段寄存器
SS(Stack Segment):堆栈段寄存器
ES(Extra Segment):附加段寄存器
指针寄存器和变址寄存器
SP(Stack Pointer):
堆栈指针,与SS配合使用,可指向目前的堆栈位置.
BP(Base Pointer):
基址指针寄存器,可用作SS的一个相对基址位置.
SI(Source Index):
源变址寄存器,可用来存放相对于DS段之源变址指针.
DI(Destination Index):
目的变址寄存器,可用来存放相对于ES段之目的变址指针.
IP(Instruction Pointer):指令指针
PSW(Program Status Word):程序状态寄存器
ZF(Zero Flag):
零标志位,用来反映运算结果是否为0.如果运算结果为0,则其值为1,否则其值为0.
PF(Parity Flag):
奇偶标志位,用来反映运算结果中"1"的个数的奇偶性,如果"1"的个数为偶数,则
PF的值为1,否则其值为0.
SF(Sign Flag):
符号标志位,用来反映运算结果的符号位,它与运算结果的最高位相同.
CF(Carry Flag):
进位标志位,用来反映运算结果是否产生进位或借位,如果运算结果的最高位产
生了一个进位或借位,那么,其值为1,否则为0.
OF(Overflow Flag):
溢出标志位,用来反映有符号数加减运算所得结果是否溢出.如果运算结果超出
当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0.
DF(Direction Flag):
方向标志位,用来决定在串操作指令执行时有关指针寄存器发生调整的方向.
IF(Interrupt Flag):
中断标志位,IF=1处理可屏蔽中断,IF=0不出来可屏蔽中断.
TF(Trace Flag):
跟踪标志位,该标志可用于程序调试,没有专门的指令来设置或清除.
AF(Auxiliary carry Flag):
辅助进位标志位,在字操作时,发生低字节向高字节进位或借位时;在字节操作时
,发生低4位向高4位进位或借位时.
相关指令
abc(c-carry):带进位加法指令.它利用了CF位上记录的进位值.
sbb(b-borrow):带借位减法指令.它利用了CF位上记录的借位值.
cmp(compare):比较指令.它的功能相当于减法指令,只是不保存结果.
je(e-equal):ZF=1则转移
jne(ne-not equal):ZF=0则转移
jb(b-below):CF=0则转移
jnb(nb-not below):CF=0则转移
ja(a-above):CF=0且ZF=0则转移
jna(na-not above):CF=1或ZF=1则转移
movesb(b-byte):传送一个字节
movesw(w-word):传送一个字
rep movesb(rep-repeat):循环执行字节串传送指令
rep movesw(rep-repeat):循环执行字串传送指令
cld(clear DF):将DF置0
std(set DF):将DF置1
pushf(push flag):将标志寄存器的值压栈
popf(pop flag):从栈中弹出数据送入标志寄存器
Debug中标志位的表示:
标志位 值为1 值为0
OF(Overflow Flag) OV(overflow) NV(no overflow)
SF(Sign Flag) NG(negative) PL(plus)
ZF(Zero Flag) ZR(zero) NZ(no zero)
PF(Parity Flag) PE(parity even) PO(parity odd)
CF(Carry Flag) CY(carry) NC(no carry)
DF(Direction Flag) DN(down) UP(up)
AF(Auxiliary carry Flag) AC(assistant carry) NA(no assistant carry)
IF(Interrupt Flag) EI(enable interrupt) DI(disable interrupt)
TF(Trace Flag)
AX BX CX DX是CPU内部的通用寄存器中的数据寄存器,数据寄存器一般用于存放
参与运算的数据或运算的结果,每一个数据寄存器都是16位的(即16个二进制位),
但又可以将高,低8位分别作为两个独立的8位寄存器使用.它们的高8位记作AH,BH,
CH,DH,低8位记作AL,BL,CL,DL.这种灵活的使用方法给编程带来极大的方便,既可
以处理16位数据,也能处理8位数据.
数据寄存器除了作为通用寄存器使用外,它们还有各自的习惯用法
AX 称为累加器,常用于存放算术逻辑运算中的操作数,另外所有的I/O指令都
使用累加器与外设接口传送信息
BX 称为基址寄存器,常用来存放访问内在时的基地址,
CX 称为计数寄存器,在循环和串操作指令中用作计数器
DX 称为数据寄存器,在寄存器间接寻址中的I/O指令中存放I/O端口的地址
另外,在做双字长乘除法运算时,DX 与AX合起来存放一个双字长数(32位),其中
DX存放高16位,AX存放低16位.
AH&AL=AX(accumulator):累加寄存器
BH&BL=BX(base):基址寄存器
CH&CL=CX(count):计数寄存器
DH&DL=DX(data):数据寄存器
SP(Stack Pointer):堆栈指针寄存器
BP(Base Pointer):基址指针寄存器
SI(Source Index):源变址寄存器
DI(Destination Index):目的变址寄存器
IP(Instruction Pointer):指令指针寄存器
CS(Code Segment)代码段寄存器
DS(Data Segment):数据段寄存器
SS(Stack Segment):堆栈段寄存器
ES(Extra Segment):附加段寄存器
OF overflow flag
溢出标志 操作数超出机器能表示的范围表示溢出,溢出时为1.
SF sign Flag
符号标志 记录运算结果的符号,结果负时为1.
ZF zero flag
零标志 运算结果等于0时为1,否则为0.
CF carry flag
进位标志 最高有效位产生进位时为1,否则为0.
AF auxiliary carry flag
辅助进位标志 运算时,第3位向第4位产生进位时为1,否则为0.
PF parity flag
奇偶标志 运算结果操作数位为1的个数为偶数个时为1,否则为0.
DF direcion flag
方向标志 用于串处理.DF=1时,每次操作后使SI和DI减小.DF=0时则增大.
IF interrupt flag
中断标志 IF=1时,允许CPU响应可屏蔽中断,否则关闭中断.
TF trap flag
陷阱标志 用于调试单步操作.
寻址方式:
立即寻址 MOV AX , 常数
直接寻址 MOV AX , [常数] ;加上中括号 或 MOV AX , 变量名或标号
寄存器寻址 MOV AX , 通用寄存器
寄存器间接寻址 MOV AX , [(BX,BP,SI,DI)中的一个] ;以下四行均需加上中括号
寄存器相对寻址 MOV AX , [(BX,BP,SI,DI)中的一个 + 偏移量]
基址变址寻址 MOV AX , [基址寄存器 + 变址寄存器]
相对基址变址寻址 MOV AX , [基址寄存器 + 变址寄存器 + 偏移量]
XCHG OPRD1, OPRD2 交换指令
地址传送指令
LEA REG, OPRD 地址传送指令
LDS REG, OPRD (load pointer into DS)
LES REG, OPRD (Load pointer into ES )
堆栈指令
进栈指令 SP-2
PUSH SRC
出栈指令 SP+2
POP DST
标志操作指令
(1) 标志传送指令
LAHF (Load AH with Flags) 把标志位寄存器的低8位传送到寄存器AH的指定位
SAHF (Store AH into Flags) 与LAHF相反 (SF ZF AF PF CF)
CLC (Clear Carry flag) 清进位标志
CLD 清方向标志
STD 置方向标志
CLI 清中断允许
STI 置中断
ADC 带进位的加法指令 (Add and carry)
inc oprd 加一指令 (IN Crement)
SUB 减法指令 (SUBtraction)
SBB 带借位的减法 (SuBtract with Borrow)
DEC 减一指令 (DECrement)
Offset 偏移值
test 指令 模拟执行AND 但不改变 两寄存器的值 只影响寄存器
AX:Accumulator
BX:Base
CX:Cout
DX:Date
SI:Source Index
DI:Destination Index
BP:Base Pointer
SP:Stack Pointer
CF:Carry Flag
ZF:Zero Flag
SF:Sign Flag
OF:Overflow Flag
PF:Parity Flag
AF:Auxiliary Flag
TF:Trap Flag
IF:Interrupt Flag
AF:Auxiliary Flag
CS:Code Segment
DS:Data Segment
ES:Extra Segment
SS:Stack Segment
段寄存器 :CS DS SS ES
"18.2.标志位二}}}
"19.跳转指令{{{
无符号转移条件 相等equal 小于less 大于greater
带符号转移条件 相等equal 低于below 高于above
JA 高于
JAE 高于等于
JB 低于
JBE 低于等于
JC 进位为1
JCXZ CX为零
JE 相等转移
JGE 大于等于
JL 小于
JLE 小于等于
JNA 不高于
JNEA 不高于等于
JNB 不低于
JNBE 不低于等于
JNC 进位为零
JNE 不相等
JNG 不大于
JNGE 不大于等于
JNL 不小于
JNLE 不小于等于或等于
JNO 不溢出
JNP 奇偶为0
JNS 结果为正
JNZ 结果不为零
JO 溢出
JP 奇偶为1
JPE 奇偶性为偶
JPO 奇偶性为奇
JS 结果为负
JZ 结果为零
"19.跳转指令}}}
"20.指令{{{
寻址方式:
立即寻址 MOV AX , 常数
直接寻址 MOV AX , [常数] ;加上中括号 或 MOV AX , 变量名或标号
寄存器寻址 MOV AX , 通用寄存器
寄存器间接寻址 MOV AX , [(BX,BP,SI,DI)中的一个] ;以下四行均需加上中括号
寄存器相对寻址 MOV AX , [(BX,BP,SI,DI)中的一个 + 偏移量]
基址变址寻址 MOV AX , [基址寄存器 + 变址寄存器]
相对基址变址寻址 MOV AX , [基址寄存器 + 变址寄存器 + 偏移量]
XCHG OPRD1, OPRD2 交换指令
地址传送指令
LEA REG, OPRD 地址传送指令
LDS REG, OPRD (load pointer into DS)
LES REG, OPRD (Load pointer into ES )
堆栈指令
进栈指令 SP-2
PUSH SRC
出栈指令 SP+2
POP DST
标志操作指令
(1) 标志传送指令
LAHF (Load AH with Flags) 把标志位寄存器的低8位传送到寄存器AH的指定位
SAHF (Store AH into Flags) 与LAHF相反 (SF ZF AF PF CF)
CLC (Clear Carry flag) 清进位标志
CLD 清方向标志
STD 置方向标志
CLI 清中断允许
STI 置中断
ADC 带进位的加法指令 (Add and carry)
inc oprd 加一指令 (IN Crement)
SUB 减法指令 (SUBtraction)
SBB 带借位的减法 (SuBtract with Borrow)
DEC 减一指令 (DECrement)
Offset 偏移值
test 指令 模拟执行AND 但不改变 两寄存器的值 只影响寄存器
PSW(Program Status Word):程序状态寄存器
ZF(Zero Flag):零标志位,用来反映运算结果是否为0.如果运算结果为0,则其值为1,否则其值为0.
PF(Parity Flag):奇偶标志位,用来反映运算结果中"1"的个数的奇偶性,如果"1"的个数为偶数,则PF的值为1,否则其值为0.
SF(Sign Flag):符号标志位,用来反映运算结果的符号位,它与运算结果的最高位相同.
CF(Carry Flag):进位标志位,用来反映运算结果是否产生进位或借位,如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则为0.
OF(Overflow Flag):溢出标志位,用来反映有符号数加减运算所得结果是否溢出.如果运算结果超出当前运算位数所能表示的范围,则称为溢出,
OF的值被置为1,否则,OF的值被清为0.
DF(Direction Flag):方向标志位,用来决定在串操作指令执行时有关指针寄存器发生调整的方向.
IF(Interrupt Flag):中断标志位,IF=1处理可屏蔽中断,IF=0不出来可屏蔽中断.
TF(Trace Flag):跟踪标志位,该标志可用于程序调试,没有专门的指令来设置或清除.
AF(Auxiliary carry Flag):辅助进位标志位,在字操作时,发生低字节向高字节进位或借位时;在字节操作时,发生低4位向高4位进位或借位时.
相关指令
abc(c-carry):带进位加法指令.它利用了CF位上记录的进位值.
sbb(b-borrow):带借位减法指令.它利用了CF位上记录的借位值.
cmp(compare):比较指令.它的功能相当于减法指令,只是不保存结果.
je(e-equal):ZF=1则转移
jne(ne-not equal):ZF=0则转移
jb(b-below):CF=0则转移
jnb(nb-not below):CF=0则转移
ja(a-above):CF=0且ZF=0则转移
jna(na-not above):CF=1或ZF=1则转移
movesb(b-byte):传送一个字节
movesw(w-word):传送一个字
rep movesb(rep-repeat):循环执行字节串传送指令
rep movesw(rep-repeat):循环执行字串传送指令
cld(clear DF):将DF置0
std(set DF):将DF置1
pushf(push flag):将标志寄存器的值压栈
popf(pop flag):从栈中弹出数据送入标志寄存器
"20.指令}}}
"21.movsx指令{{{
汇编语言数据传送指令MOV的变体.带符号扩展,并传送.
例如:
MOV BL,80H
MOVSX AX,BL
运行完以上汇编语句之后,AX的值为FF80H.由于BL为80H,最高位也即符号位为1,
在进行带符号扩展时,其扩展的高8位均为1,故赋值AX为FF80H.
"21.movsx指令}}}
"22.王爽汇编{{{
第二章 寄存器
1.开始>运行>command
alt+Enter
debug+回车
2.R+回车
R+寄存器+回车
R+空格+寄存器+回车
3.D+段地址+ :+偏移地址
D+空格+段地址+ : +偏移地址
D+段地址+ : +偏移地址1+空格+偏移地址2
4.E+段地址+回车 数据或空格...回车
E+空格+段地址+回车 数据或空格...回车
E+空格+段地址+ : + 偏移地址+回车 数据或空格...回车
E+空格+段地址+ : +偏移地址+ 数据+回车
E+空格+段地址+ : +偏移地址+ '字符'+回车
E+空格+段地址+ : +偏移地址+ "字符串"+回车
5.U+段地址+ :+偏移地址
U+空格+段地址+ : +偏移地址
U+段地址+ : +偏移地址1+空格+偏移地址2
6.T+回车
7.A+段地址+回车 (指令+回车)(指令+回车)...(回车)
A+空格+段地址+回车 (指令+回车)(指令+回车)...(回车)
A+空格+段地址+ : + 偏移地址+回车 (指令+回车)(指令+回车)...(回车)
8.d+段寄存器+ :+偏移地址1 偏移地址2
E+段寄存器+ :+偏移地址1 偏移地址2
U+段寄存器+ :+偏移地址1 偏移地址2
A+段寄存器+ :+偏移地址1 偏移地址2
9.P+回车 执行当前的int n中断指令(Debug中如果当前指令是int n,P是响应中断,T是忽略中断向下执行)
10. q+回车 //Q命令推出debug
11.G命令 运行存储器中的可执行程序
12.H命令 完成16进制算数运算
13.N 给程序命名
/****************************************************************************************************************************************/
第四章 第一个程序
SA段地址 EA偏移地址
9.伪指令
段名 segment //段从此开始
.
.
段名 ends //段到此结束
end //汇编程序结束
assume
assume cs:段名
段名 segment
.
.
段名 ends //告诉编译器segment到ends是一个代码段
标号代表一个地址,最终处理为可执行文件中一个段的地址.
程序返回,在程序的末尾添加返回程序段
mov ax,4c00H
int 21H //实现程序返回
语法错误
逻辑错误
DOS>运行exit>编辑源程序>程序文件保存为>c:\1.asm
4.4编译
编译器 masm5.0>masm.exe>假设c:\masm
DOS>进入c:\masm>masm+回车>(文件名1(格式asm))或(文件名2.扩展名(其它格式))+回车
>路径+(文件名1(格式asm))或(文件名2.扩展名(其它格式))+回车
(生成)文件名.job+回车
Overlay Linker3.60连接器,link.exe
DOS>进入c:\masm>link+回车>文件名.job+回车>提示要生成的文件名称+直接回车>提示输入映像文件名称+直接回车>
输入库文件名称+直接回车>提示没有栈段>生成目标文件>文件名.exe
简化编译,连接
masm+空格+路径+文件名+回车 //不需要加扩展名
link+空格+路径+文件名+回车 //不需要加扩展名
每种操作系统都有一个shell程序加载器,dos的shell是command.com
编译到执行汇编程序的过程
Edit>masm>link>command>CPU
程序执行过程的个跟踪 //发现程序隐藏较深的错误
c:\masm\debug 1.exe +回车 //需要加扩展名
程序前缀数据区PSP,DOS系统利用PSP和应用程序进行通讯
空闲内存区 SA:0
PSP 区 SA:0
程序区 SA+10H:0
Progrma terminated normally //程序正常结束,返回到debug中
/****************************************************************************************************************************************/
第五章 [bx]和loop
汇编语言中,数据不要能以字母开头
dw 数据1,数据2, //定义字型数据
mov cx,循环次数
s:
循环执行程序段
loop s
/****************************************************************************************************************************************/
第六章 包含多个段的程序
在代码段中定义数据,用end start指明程序入口地址
assume cs:code
code segment
..
数据
..
start:
..
代码
..
code ends
end start
"22.王爽汇编}}}
"23.PC硬件的基本特征{{{
第一章 PC硬件的基本特征
1.2 位与字节
计算机存储器,最基本构造单元bit,off关闭态值为0,open开通态值为1
字节:8个数据位,1个奇偶校验位
奇偶效验规则,1的个数,总是奇数,如果,偶数,系统认为,硬件故障,电器干扰,系统认定丢失一位,并提示错误信息
字节 8位
字 2字节 16位
两字 4字节 32位
四字 8字节 64位
小段 16字节 128位
KB 2的10次幂 1024位 640K=640*1024=655360字节
MB 2的20次幂 1048576位
1.3二进制数系统
bit 二进制数 Binary digIT
1.用于算术运算定义和使用的数据
2.用于描述目的定义和使用的数据
用于算术运算的二进制数
最左边位为0,表示正数
最左边位为1,表示负数
负二进制数,在计算计存储器中按补码的规则存储:每位取反并加1
补码中值为0的位取反相加的和,为该数的绝对值
1.4 十六进制数
hex 十六进制数,汇编语言和debug默认采用十六进制数
十六进制数表示格式: hex 数据 ,数据H
1.5 ASCII码
1.6 PC的组成
处理器单位 MHz(秒/百万) 1MHz 一百万分之1秒
处理器分两个逻辑单位:
执行部件EU:执行指令,算数运算,逻辑操作 包括:算数逻辑部件ALU,控制部件CU和寄存器
总线接口部件BIU:
1.提供指令和数据
2.管理:
数据总线:在EU和存储器,I/O设备之间传输数据的总线
段寄存器:控制存储器寻址
指令队列:当EU执行当前指令时,BIU从存储器中预取EU要执行的下一条指令,放到指令队列中
1.7 存储器
PC只有RAM可读写存储器,ROM只读存储器
数据在寄存器中是正顺存储的,存储器中是反序存储的
存储器的寻址方式
1.绝对地址寻址
2.段:偏移地址寻址
1.8 段与寻址
段:程序中定义的区域,
段的起始地址必须是小段边界(paragraph boundary)
一个能被16或(hex)10 整除的数,所以段地起始地址一定是0NNN0H(最右边一位为0的hex数,才能被16整除)
段寄存器中存储的段地址总是省略掉最右边的0的,如0NNN,
但书中用0NNN[0],用[0]表示哪个被省略的0
段分三种:
代码段:包含要执行的机器指令
数据段:程序定义的数据
堆栈段:需要暂存的数据和地址,或子程序的数据于地址
附加段寄存器 ES,FS,GS
段偏移值
段地址+偏移值=实际地址
"23.PC硬件的基本特征}}}
"Win32汇编语言学习笔记{{{
第二章 编译环境
微软的32位Link只支持将32位的COFF格式的obj文件链接成PE文件
NASM官网:http:nasm.octium.net
NASM:
不具备MASM和TASM的高级语法,比如.if/.endif等高级语法,调用语句自动转化成多个push和一个call指令
可以支持Windows,Linux和OS/2等不同的平台,熟悉它的语法后可以再Linux汇编中上手.
MASM32官网:http://www.movsd.com
MASM编译器命令:
Ml [/选项] 汇编源文件列表 [/link 链接选项]
Ml /c /coff /Cp Test.asm
Ml /c /coff Test.asm /Cp
/c 仅编译,不自动链接
/coff 产生的obj文件格式为COFF格式
/Cp 源代码区分大小写
/Fo filename 指定输出的obj文件名
/Fe filename 指定链接后输出的文件名
/Fl [filename] 产生.lst列表文件
/Gc 函数调用类型用FORTRAN或PASCAL形式
/Gd 函数调用类型用C语言形式
/Gz 函数调用类型用StdCall形式
/l pathname 指定include文件的路径
/link 选项 指定链接时候使用的选项
/Sc 在列表文件中列出指令的时钟周期
/Zi 增加符号调试信息
/Zd 包含调试信息的行号
# ml [options] filename.asm {[options] filename.asm}... [/link options]
# /AT 指明程序使用Tiny存储类型,该命令把程序转换成.com格式
# /c 只汇编,不链接
# /Fl 产生列表(.LST)文件
# /Fm 产生连接映像(.MAP)文件
# /Fr 产生.SBR(交叉引用)文件
# /Sn 禁止符号表的列表
# /Zd 包含调试信息的行号
# /Zi 包含符号的调试信息(对于Code View)
# 例子#
# ml /Sn /Fr Program23.asm
# ml /AT /Zd Program34.asm
Win32 PE文件的链接器为Incremental Linker,命令:
Link [选项] [文件列表]
文件列表:列出需要链接到可执行文件中的模块,obj文件,res资源文件,导入库文件.
/BASE:地址 指定入口地址
/COMMENT: 注释 PE文件的文件头后面加上文本注释,比如加入版权字符串,如有空格头尾加双引号
/DEBUG 在PE文件中加入调试信息
/DEBUGTYPE:类型 加入调试信息类型,可以是CV或COFF
/DLL 生成动态链接库
/DEF:文件名 编写DLL文件时,使用的def文件名,指定导出函数列表
/ENTRY:标号 指定入口标号
/IMPLIB:文件名 当链接有导出函数的文件时(如DLL)要建立的导入库名
/INCREMENTAL:ON|OFF 是否增量链接,增量链接只重写可执行文件自上次链接后改动的部分,所以可以增加链接速度,但会增加文件长度
/LIBPATH:路径 指定库文件的目录
/MACHINE:平台名称 指定输出的可执行程序运行平台,可以是 ALPHA,ARM,IX86,MIPS,MIPS16,MIPSR41XX,
PPC,SH3和SH4等
/MAP:文件名 生成MAP文件
/OUT:文件名 指定输出文件名,默认 .exe,生成其他文件名,如 *.scr 等
/RELEASE 填写文件头中的校验字段
/SECTION:节区,属性 改变节区的属性,属性可以是:E,R,W,S,D,K,L,P和X等
/STACK:尺寸 设定堆栈尺寸
/STUB:文件名 指定DOS头执行的文件,默认执行"必须在Windows下执行",可以替换此文件,则DOS和Windows下都可以执行自己指定的文件,比如用DOS的
FDISK.EXE
/SUBSYSTEM:系统名 指定程序运行的操作系统,可以是NATIVE,WINDOWS,CONSOLE,WINDOWSCE和POSIX等
/VXD 编写Windows 95 VxD驱动程序时指定
//=====================================================================================================================================//
用MASM编译和链接一个Win32汇编源程序常用的命令是:
Ml /c /coff xx.asm
Link /subsystem:windows xx.obj yy.lib zz.res (普通PE文件)
Link /subsystem:console xx.obj yy.lib zz.res (控制台文件)
Link /subsystem:windows /dll /def:aa.def xx.obj yy.lib zz.res (DLL文件)
/Cp和/Gz虽然也是必须得,但可以再asm源文件中伪定义设置,一般不在命令行中设置,以免遗漏.
//=====================================================================================================================================//
MASM32目录说明
\masm32 IDE环境,内带的文本编译程序和模板生成程序等
\masm32\include 所有的问头件,Windows.inc为数据结构和预定义值的定义文件,Resource.h为资源文件的头文件,其他.inc文件为对应同名dll文件中的
API函数声明文件
\masm32\lib 所有的导入库文件,每个.lib文件对应DLL文件的导入库
\masm32\bin 可执行文件目录,包含Ml.ex,link.exe和Rc.exe等
\masm32\help 帮助文件目录
\masm32\m32lib 常用C子程序的汇编实现源代码
其他目录 例子和可用可不用的小工具
有了bin,include和lib三个目录,就可以进行Win32编译编程了.
//=====================================================================================================================================//
2.3 资源编译器
把资源脚本文件 *.rc 编译成资源文件 *.res
VC++的资源编译器Rc.exe ,TASM的资源编译器BRC32.exe或BRCC32.exe
命令行语法:
Rc [选项] 资源脚本文件名
资源脚本文件的语法是C格式的
C格式 汇编
等值定义 #define equ
注释 // ;
头文件 .h .inc
参数或操作 | or
BRC32.exe可以解释Windows的一些预定义值,不需要附带头文件
Rc.exe需要附带Resource.h头文件
//=====================================================================================================================================//
Borland公司的Resource Workshop资源编辑器
同时支持*.rc和*res文件,并可以转换保存
需要Resource.h头文件,并且要和*.rc文件在同一目录
不支持新的特征,如窗口的扩展风格.
//=====================================================================================================================================//
微软VS自带的资源编辑器Resource Workshop
Visual C++生成的.rc文件包含很多VC自己的头文件,在调试完成,准备发布的时候,保存一份.rc 文件并把文件中使用VC的多余内容去掉.
编译成 Rc.exe可以编译的格式
//=====================================================================================================================================//
2.4 make工具的用法
//================================================================
dos编写程序,编译器和链接器不用参数:
Masm xxx.asm;
Link xxx.obj;
做个批处理把xxx换成%1
在命令行中键入
asm.bat xxx
//================================================================
Win32汇编编程
用make工具维护代码
make工具:
1.是一个智能的批处理工具
2.调用makefile文件中用户定义的命令,进行编译和链接
3.批处理执行全部命令编译全部源文件
4.make工具默认使用makefile文件,获取源文件之间的依赖关系
5.make工具根据文件上次编译的时间和所以来的源文件的更新时间,已经从makefile文件获取的源文件依赖关系,自动判断应当编译那些源文件
6.1.Microsoft的make工具文件名为nmake.exe,可以再Visual C++的Bin目录下找到
6.2.Borland的make工具文件名是make.exe
6.3.两者默认的描述文件都是makefile
//================================================================
2.4.2 nmake的用法
命令行中键入 nmake /?
Usage: NMAKE @commandfile
NMAKE [options] [/f makefile] [/x stderrfile] [macrodefs] [targets]
nmake [选项] [/f描述文件名] [/x 输出信息文件名] [宏定义] [目标]
/f 描述文件不使用默认的makefile,用次参数指定
/x 把屏幕输出信息保存到文件中,用次参数 /x 文件名 (dos下管道操作符 nmake > 文件名 的方法无效)
宏定义 用心的定义覆盖描述文件中的宏定义
目标
Options:
/A Build all evaluated targets 不检测文件时间,强制更新所有文件
/B Build if time stamps are equal 文件时间相等时也要更新文件
/C Suppress output messages
/D Display build information make时显示文件新旧信息
/E Override env-var macros
/HELP Display brief usage message
/I Ignore exit codes from commands
/K Build unrelated targets on error
/N Display commands but do not execute 显示make时要执行的命令,但不真正执行
/NOLOGO Suppress copyright message
/P Display NMAKE information make时显示详细的信息
/Q Check time stamps but do not build
/R Ignore predefined rules/macros
/S Suppress executed-commands display
/T Change time stamps but do not build
/U Dump inline files
/Y Disable batch-mode
/? Display brief usage message
计算机时间不准,或源文件计算机间拷贝时,用/A 命令全部重新编译一次
以makefile作描述文件名,nmake命令不加参数,就可以完成所有工作了
//================================================================
2.4.3 描述文件语法
注释 宏定义 显示规则 隐含规则
test.exe描述文件:
//======================================================//
# nmake 工具的描述文件例子
EXE = Test.exe #指定输出文件
OBJS = x.obj \
y.obj #需要的目标文件
RES = x.res #需要的资源文件
LINK_FLAG = /subsystem:windows
#链接选项
ML_FLAG = /c /coff #编译选项
#定义依赖关系和执行命令
$(EXE):$(OBJS) $(RES)
Link $(LINK_FLAG) /out:$(EXE) $(OBJS) $(RES)
$(OBJS):Commom.inc
y.obj:y.inc
#定义汇编编译和资源编译的默认规则
.asm.obj:
ml $(ML_FLAG) $<
.rc .res:
rc $<
#清除临时文件
clean:
del *.obj
del *.res
//==========================================================//
语法:
注释和换行:
# 注释以#号开头知道行尾字符,忽略到#号及其后面的全部字符
\ 用\换行符来换行, \ 号后同一行中如果有字符,\ 号不当做字符
//==========================================================//
宏定义: 指代源文件和相关编译信息
变量名=变量内容
符合这个语法的行,就是宏定义
$ $号引用宏定义,变量名超过一个字符,引用是加圆括号 ()
比如:
$(LINK_FLAG)
$(EXE)
$A
$(A)
1.这样可以在命令行中,用新的宏定义符号可以替换makefile文件中的宏定义.
2.用不懂参数编译文件时,不必修改makefile文件
3.不必阅读整个makefile文件,只修改文件头部的宏就可以改变编译参数
4.多个文件用一个makefile文件时,文件名定义为宏名可以减少错误,增加可
读性.
比如:
makefile文件中:
ML_FLAG = /c /coff #编译选项
在命令行中:
nmake ML_FLAG="/c /coff /F1"
1.宏名区分大小写
2.宏名定义值中有空格时,用双引号引起来
//==========================================================//
*****************************************************************************************************************************************
第三章 使用ASM
*****************************************************************************************************************************************
1.指定使用的指令集
.386
.386p //告诉编译器本程序程序中将要使用特权指令
.586
.mmx //告诉编译器本程序将使用奔腾处理器的MMX指令集
*****************************************************************************************************************************************
2. .model语句
.model 内存模式[,语言模式][,其它模式]
1.
windows每一个程序都有相互独立的4GB地址空间,且所有4GB地址空间可以用32位寄存器直接访问
如果程序定义了.model flat,MASM自动做如下定义
ASSUME cs:FLAT, ds:FLAT,ss:FLAT,es:FLAT,fs:ERROR,gs:ERROR
如果要使用FS和GS寄存器需做如下声明
assume fs:nothing,gs:nothing或者assume fs:flat,gs:flat
2.
windows在API调用的时候使用的是stdcall,所以WIN32汇编只能选择stdcall
*****************************************************************************************************************************************
option casemap:none
声明程序中的变量,子程序名是否区分大小写
*****************************************************************************************************************************************
段的定义
包含全部断的源程序结构
系统自动分配足够大的堆栈空间,所以.stack段常常忽略
win32汇编源程序使用80386处理器的内存分页机制,把不同属性的数据或代码归类放到不同属性的内存页中
内存中:页的大小4KB,可自由指定属性
*****************************************************************************************************************************************
数据段 不可执行
.data 数据段 可读可写的变量,程序加载时候已经在内存中实际存在了,在可执行文件_DATA节区
.data? 未初始化数据段 可读写,在可执行文件中不占硬盘空间,在可执行文件_BSS节区
.const 常量段 可读不可写,如果对常量段进行写操作,会引发winodws保护错误的提示框,并结束程序
*****************************************************************************************************************************************
.code 代码段 _TEXT节区
所以指令必须写在代码段中,唯一具有可执行属性的段
特权级0运行的程序,对所有段都有读写权利
特权级3运行的程序,需要把可执行文件PE文件头的属性位设置为可写,才可以修改自己的代码段
UPX和PECompact等加壳软件,就是修改PE文件的代码段,来达到加壳的目的
*****************************************************************************************************************************************
.stack 堆栈段 可读可写可执行,不必定义,系统自动分配,
病毒和黑客工具就是找到系统或软件的堆栈的缓冲区溢出漏洞,动态的修改跟踪模块中的代码并拷贝到堆栈中去,边修改边执行,来实现的
*****************************************************************************************************************************************
3.1.3程序的入口和结束地址
汇编程序可在代码段中任何位置开始执行
end [开始地址] 指定程序的入口地址,并且以此语句为代码段的结束标志,开始地址必须在代码段中有所定义
编写多模块程序,每个分支程序都可以有相应的各种段,但只有主程序指定入口地址,分程序不指定
*****************************************************************************************************************************************
3.1.4 注释和换行
注释以: ; (分号)开始
换行: 在一行的最后加 \ (反斜杠)
*****************************************************************************************************************************************
3.2.1 API
DLL动态链接库是windows集成提供给应用程序的系统功能,PE格式的windows可执行文件,以字符串形式指出可以供应用程序调用的函数列表
KERNEL32.DLL 系统服务 内存管理,进程管理,动态链接
GDI32.DLL 图形服务 显示文本,显示图形
USER32.DLL 用户服务 创建窗口,消息循环
Wsock32.DLL 用TCP/IP协议进行网络通讯 Socket API函数
Tapi32.DLL 电话服务 TAPI(Telephone API)函数
*****************************************************************************************************************************************
3.2.2 调用API
WIN32环境中参数都是32位整数,定义为不同类型是为了说明它的用途
*****************************************************************************************************************************************
1.
inovke伪指令格式:
invoke 函数名 [,参数1][,参数2]... ...
error A2137:too few arguments to INVOKE 参数数量和函数声明时数量不符
*****************************************************************************************************************************************
2.api返回参数的方法
win32汇编返回值的类型都是DWORD,永远放在EAX中,如果EAX容纳不下,函数返回数据的指针到EAX,或者函数返回一个缓冲区地址指针,把数据放到缓冲区中
*****************************************************************************************************************************************
3.函数的声明的格式
距离,定义时一般忽略
语言,也忽略,使用.MODEL定义时候的默认类型
绝大多数数据类型都是DWOD类型,参数名称,是为了增加可读性,可有可无的
WIN32环境中,
ANSI字符集相关的函数,在尾部带字符A 欧洲语言体系,一字符一字节,以NULL结尾的一串字符数组
Unicode字符集相关函数,在尾部带字符W 东方语言体系,一字符量字节,
*****************************************************************************************************************************************
4.include语句格式
include 文件名
include <文件名> ;关键字可能引起同名混淆时候加尖括号
*****************************************************************************************************************************************
5.includelib语句格式
includelib 文件名
includelib <文件名> ;关键字可能引起同名混淆时候加尖括号
*****************************************************************************************************************************************
3.2.3 API函数中的等值定义
程序开头定义:
include windows.inc
就可以安全按照API手册来使用WIN32函数
*****************************************************************************************************************************************
3.3 标号,变量和数据结构
标号和变量的命名规范:
*****************************************************************************************************************************************
标号定义的格式
标号: 目的指令 ;子程序内跳转, 作用域:当前子程序
标号:: 目的指令 ;整个程序跳转,从一个子程序跳转到另一个子程序 作用域:正个程序
MASM中@@
*****************************************************************************************************************************************
3.3.2 全局变量
全局变量定义的格式:
只有定义全局变量时,变量类型才可以缩写
用?号定义全局变量,程序在运行的时候,变量的值为0,所以不必在进行初始化
全局变量定义实例:
*****************************************************************************************************************************************
局部变量:
local伪指令格式:
1.local伪指令紧跟在proc指令后,其它指令开始前,因为局部变量数目,必须在子程序开始前确定
2.数据类型不能缩写,定义数据结构,用数据结构名称当做数据类型,定义数组用[]括起来,不能使用DUP伪指令
3.local定义局部变量只是简单的把局部变量的内存空间留出来,它的值是前一个函数运行后,留在堆栈中的值,所以局部变量的值一定要初始化
4.最好的办法是:在赋值前,首先将整个数据结构填零,然后初始化要用的字段,RtlZeroMemory可以实现填0的功能
局部变量使用的典型例子,以下是一个TESTPROC的子程序:
TestProc proc
local @loc1:dword,@loc2:word
local @loc3:byte
mov eax,@loc1
mov ax,@loc2
mov al,@loc3
ret
TestProc endp
编译成可执行文件,并反汇编得到以下指令:
push ebp ;EBP压栈用来在程序返回时恢复ESP的值
mov ebp,esp ;WIN32汇编中EBP寄存器也是以堆栈段位默认数据段,所以把ESP的值放到EBP中,用
;EBP当做读取局部变量的指针
add esp,FFFFFFF8 ;FFFFFFF8是-8的补码,这段代码是:ESP的值减8,栈顶的指针就指向[ESP-8],也就是在堆栈段中定义了8字节的数据项
;因为CPU始终把ESP指向的数据段当做堆栈段,这是由@loc1,@loc2,@loc3分别定义,产生的一个8字节的堆栈,
;386处理器,以DOWRD为界内存存取速度最快,所以MASM编译时将@loc3定义为DWORD类型的数据
mov eax,dword ptr [ebp-04] ;现在[ebp-04]现在的EBP中的值是原来ESP中的值,所以[ebp-04]指向的是第一个数据
mov ax,word ptr [ebp-06]
mov al,byte ptr [ebp-07]
leave ;leave指令实现mov esp,ebp和pop ebp的功能,所以无论ESP的值如何被修改,只要EBP的值不变,都不会影响子程序的返回
ret
*****************************************************************************************************************************************
汇编语言定义数据结构的格式:
*****************************************************************************************************************************************
以不同的类型访问变量
*****************************************************************************************************************************************
3.6变量和函数的命名
1.匈牙利标识法命名约定
用连在一起类型前缀(都是小写)加变量说明(首字母大写的英文单词),来命名变量,让人很容易看得出变量的类型和用途,发现变量的使用错误
常用前缀:
b byte
w word
dw dword
h 句柄
lp 指针
sz 以0结尾的字符串
lpsz 以0结尾的字符串指针
f 浮点数
st 数据结构
2.匈牙利标识法的补充
针对WIN32汇编的特殊情况
1.局部变量命名的时候头部加 @ ,以边调用参数时明了其作用域
2.命名程序内服子程序时候头部加 _ ,API 函数区别
*****************************************************************************************************************************************
代码的书写格式
1.指令和寄存器用小写
2.EQU伪操作定义的常量用大写
3.变量和标号采用匈牙利标识法,则大小写混合
4.用TAB键隔开之指令和操作数(取代空格键)可以使源文件格式对齐
5.缩进:变量和标号定义不缩进,指令两个TAB缩进,分支和循环语句再缩进一格
*****************************************************************************************************************************************
注释和空行
1.不写无意义注释
2.修改代码,就要修改注释,保持代码和注释一致性
3.以描写一组指令实现的功能为主
4.子程序,在头部加注:功能,参数,返回值,调用注意事项
5.利用空格隔开,不同的代码功能块,以高级语言一句指令完成的一段汇编指令为界
*****************************************************************************************************************************************
MASM的宏功能
除了用在条件汇编构建不同的版本,其它地方尽量不适用宏汇编,因为很容易忽略了里面适用了哪些寄存器,而高级语言则不用考虑这个问题
*****************************************************************************************************************************************
代码的组织:定义子程序
1.使用频繁的代码,尽量封装成子程序
2.规模不要太大,完成单个功能,参数要精简,并且检查危险参数和合法性
3.申请资源,退出前要释放,打开的文件,要关闭
4.尽量把子程序写成黑匣子,可以不经修改就可以拿来用
5.每次编程只是编写新增加的部分,随着自写的子程序的增加,开发任何程序都将是很快的事情
学习编程,已经学会代码的组织是重中之重
为避免在长期在每一个工程中修改的子程序库,造成子程序库混乱的问题
1.自定义一个子程序文件夹,每当工程需要定义或修改子程序库时候,现在子程序文件夹定义或修改子程序库,然后copy到工程目录中调用
2.如果工程中的子程序库,需要添加或修改,先移除工程中的子程序库,然后修改子程序文件夹中的子程序库,然后COPY回来调用,不可以再工程中直接修改,就调用
3.需要联合使用的函数,封装到类中
4.尽量避免全局变量或者函数,把它们也封装到类中,就是尽量封装成黑匣子,以后工程中可以不经修改直接调用
*****************************************************************************************************************************************
section分段,切片,划分
reference引用
proto原型;样机;典型
invoke调用;祈求;引起;恳求
cancel取消;删去vi. 取消;相互抵销n. 取消,撤销
abort流产;堕胎;夭折;发育不全vt. 使流产;使中止 n. 中止计划
retry 重试;重审n. 重操作
ignore驳回诉讼;忽视;不理睬
MASM微软宏汇编语言
local局部;当地居民;
proc程序,步骤
ptr 指针
FORTRAN 公式翻译程式语言
PASCAL电脑语言
param参数
Parametric参数
frame框架;结构
*****************************************************************************************************************************************
"Win32汇编语言学习笔记}}}
"25.指令寻址与执行{{{
第二章 指令寻址与执行
2.2 操作系统的作用
1.管理系统磁盘上的目录与文件
2.减轻用户在硬件设备层的编码负担
3.管理应用程序
4.实现自动的内存管理
5.可以访问外部设备(网络,资源共享)
2.3 BIOS引导过程
接通电源>存储器清0>奇偶校验>初始化为CS=FFFF[0]H,IP=0>ROM中BIOS的入口点
ROM中BIOS的功能:
1.检查每个端口,识别与初始化设备
2.将设备连接到计算机,建立在设备上进行读写的服务
3.BIOS判断磁盘是否有系统文件,
4.如果有,就访问引导程序,把系统文件从磁盘装入内存,移交控制权给操作系统
BIOS要建立两个数据区:
1.中断向量表 起始地址0单元,包含256个4字节地址
2.BIOS数据区 起始40[0]单元,与所连接设备状态有关
2.4 系统程序的加载程序
执行程序分两类:
COM 由一个包含代码,数据和堆栈的段组成
EXE 由各自独立的代码段,数据段,堆栈段组成
操作系统加载程序的过程
1.从磁盘读取E2.在可用内存某一小段边界上,构造256字节程序段前缀(PSP)
3.在PSP的下一地址,把程序装入存储器
4.PSP地址装入DS与ES寄存器
5.CS和IP指向代码段第一条指令
6.根据程序的堆栈段,初始化SS和SP寄存器
7.根据程序数据段,初始化,DS寄存器
8,传送控制权给代码段的第一条指令
2.5 堆栈
堆栈的最小单位是字(2字节)
堆栈的作用:
1.调用子程序时候,用堆栈保留返回的地址
2.调用子程序时候,利用堆栈存取,主程序存入堆栈中的数据
3.调用子程序前,包存寄存器状态,程序返回时候恢复
PUSH 压栈指令 SP=SP-2
POP 出栈指令 SP=SP+2
PUSHF 保存标志位寄存器的值 SP=SP-2
POPF 恢复标志位寄存器的值 SP=SP+2
PUSHA 保存所有通用寄存器的值 SP=SP+16
POPA 恢复所有通用寄存器的值 SP=SP-16
PUSHAD 保存所有扩充寄存器 SP=SP+32
POPAD 恢复所有扩充寄存器 SP=SP-32
2.6 指令的执行与寻址
处理器运算基本步骤
1.指令读取:取出要执行下一条指令,放在指令队列中
2.指令译码:访问存储器地址,传送数据到算术逻辑单元,指令指针寄存器IP的值增加
3.指令完成:完成运算,结果存进寄存器和存储器,设置标志位寄存器
2.7 指令的操作数
指令可以没有操作数,也可以有1~3个操作数
2.8 保护模式
1.windows保护模式下,处理器可以从一个任务切换到另一个任务
2.每个程序有自己的存储区,处理器保护每个区域,并保存每个程序的状态
3.保护模式用描述符表增加寻址能力,
1.段的实际地址存储在描述符表中,
2.段寄存器存储的是指向当前描述符表的指针
3.中断向量表放在0000:0000单元,每个任务都可以有属于自己的该表的副本
LDT局部描述符表 (local descriptor table)每个任务都有一个局部描述符表,16位的LDT寄存器保存当前执行任务的局部描述符表的地址
IDT中断描述符表 (interrupt descriptor table)IDT寄存器包含这个表的地址
GDT全局描述符表(global descriptor table)包含每个局部描述符表的地址,32位GDT寄存器包含这个表的地址
"25.指令寻址与执行}}}
"26.IBMPC汇编语言程序设计{{{
汇编语言编码要求
4.2 汇编语言特性
4.2.1 程序注释
1. 由 ; 号开头
2.可以放在程序任何地方
4.2.2 保留字
保留字包括
1.指令 MOV,ADD 计算机执行的指令
2.伪指令 END,SEGMENT 编译器执行的指令
3.操作符 FAR,SIZE 在表达式中使用
4.预定义符 @Data,@Model 向程序返回信息
4.2.3 标识符
标示符分名称和标号
名称:数据项的地址 比如以下语句中的COUNTER
COUNTER DB 0
标号:指令,过程,段的地址 比如以下语句中的MAIN和B30
MAIN PROC FAR
B30:ADD BL,25
标识符命名规则:
1.第一字符必须是字母或专用字符(不包括.)
2.允许的字符
字母:a~z,A~Z,
数字:0~9,
专用符号: _ $ @ , . 破折号
4.2.4 语句
1.指令语句
2.伪指令语句
基本格式
[identifier] operation [operand(s)] [;comment]
标识符 操作 操作数 注释
4.2.5伪指令
PAGE指定每页行数,每行字符数
1.PAGE [length] [,width] ,省略PAGE语句,默认 PAGE 50,80
2.无参数 PAGE 自动换页
TITLE 为程序产生标题 TITLE text [comment]
SEGMENT和ENDS 定义段 //实模式下段的大小是64KB
格式
名称 操作 操作数
segment_name SEGMENT [align] [combine] ['class']
segment_name ENDS
align(定位)指明段开始边界,PARA定位在小段边界,省略则默认PARA
combine(组合)是否与其它段组合,
1.组合类型:stack,common,public,ATexpression(AT表达式)
2.不和其它程序组合,则省略,或选择NONE
class(类)连接时把段组成一组,代码段('code')数据段('data')堆栈段('stack')
PROC和ENDP 定义过程的开始和结束 //程序的可执行代码由一或多个过程组成
附加的PROC 默认使用参数 NEAR
END 结束一个完成程序
操作 操作数
END [procedure -name]
操作数:
1.空白,程序并不执行,和另一模块向连结
2.大多数情况下,包含第一个名字,或者PROC指定为FAR的过程名,程序从这里开始执行
ASSUME 将堆栈段,数据段,代码段的名称与相应的寄存器联系在一起,帮助汇编程序把符号码转换成机器码
还可以包含ES寄存器,比如:ES:datasegname
处理器伪指令 使用了8086以后的处理器提供的指令或特性时,
1.要用.286 .386 .486 .586通知汇编程序
2.出现在指令前,代码前,保护模式下,可以出现在源程序的开始
"26.IBMPC汇编语言程序设计}}}
"27.masm编译和链接参数{{{
set path=H:\soft\IDE\汇编\MASM6.15\MASM6.15\%path%
ML_FLAG=/Fl /Fm /Fr /Zd /Zi
LINK_FLAG=
ml $ML_FLAG
# ml [options] filename.asm {[options] filename.asm}... [/link options]
# /AT 指明程序使用Tiny存储类型,该命令把程序转换成.com格式
# /c 只汇编,不链接
# /Fl 产生列表(.LST)文件
# /Fm 产生连接映像(.MAP)文件
# /Fr 产生.SBR(交叉引用)文件
# /Sn 禁止符号表的列表
# /Zd 包含调试信息的行号
# /Zi 包含符号的调试信息(对于Code View)
# 例子#
# ml /Sn /Fr Program23.asm
# ml /AT /Zd Program34.asm
Win32 PE文件的链接器为Incremental Linker,命令:
Link [选项] [文件列表]
文件列表:列出需要链接到可执行文件中的模块,obj文件,res资源文件,导入库文件.
/BASE:地址 指定入口地址
/COMMENT: 注释 PE文件的文件头后面加上文本注释,比如加入版权字符串,如有空格头尾加双引号
/DEBUG 在PE文件中加入调试信息
/DEBUGTYPE:类型 加入调试信息类型,可以是CV或COFF
/DLL 生成动态链接库
/DEF:文件名 编写DLL文件时,使用的def文件名,指定导出函数列表
/ENTRY:标号 指定入口标号
/IMPLIB:文件名 当链接有导出函数的文件时(如DLL)要建立的导入库名
/INCREMENTAL:ON|OFF 是否增量链接,增量链接只重写可执行文件自上次链接后改动的部分,所以可以增加链接速度,但会增加文件长度
/LIBPATH:路径 指定库文件的目录
/MACHINE:平台名称 指定输出的可执行程序运行平台,可以是 ALPHA,ARM,IX86,MIPS,MIPS16,MIPSR41XX,
PPC,SH3和SH4等
/MAP:文件名 生成MAP文件
/OUT:文件名 指定输出文件名,默认 .exe,生成其他文件名,如 *.scr 等
/RELEASE 填写文件头中的校验字段
/SECTION:节区,属性 改变节区的属性,属性可以是:E,R,W,S,D,K,L,P和X等
/STACK:尺寸 设定堆栈尺寸
/STUB:文件名 指定DOS头执行的文件,默认执行"必须在Windows下执行",可以替换此文件,则DOS和Windows下都可以执行自己指定的文件,比如用DOS的
FDISK.EXE
/SUBSYSTEM:系统名 指定程序运行的操作系统,可以是NATIVE,WINDOWS,CONSOLE,WINDOWSCE和POSIX等
/VXD 编写Windows 95 VxD驱动程序时指定
//====================================================================================================
用MASM编译和链接一个Win32汇编源程序常用的命令是:
Ml /c /coff xx.asm
Link /subsystem:windows xx.obj yy.lib zz.res (普通PE文件)
Link /subsystem:console xx.obj yy.lib zz.res (控制台文件)
Link /subsystem:windows /dll /def:aa.def xx.obj yy.lib zz.res (DLL文件)
/Cp和/Gz虽然也是必须得,但可以再asm源文件中伪定义设置,一般不在命令行中设置,以免遗漏.
//====================================================================================================
MASM32目录说明
\masm32 IDE环境,内带的文本编译程序和模板生成程序等
\masm32\include 所有的问头件,Windows.inc为数据结构和预定义值的定义文件,Resource.h为资源文件的头文件,其他.inc文件为对应同名dll文件中的
API函数声明文件
\masm32\lib 所有的导入库文件,每个.lib文件对应DLL文件的导入库
\masm32\bin 可执行文件目录,包含Ml.ex,link.exe和Rc.exe等
\masm32\help 帮助文件目录
\masm32\m32lib 常用C子程序的汇编实现源代码
其他目录 例子和可用可不用的小工具
有了bin,include和lib三个目录,就可以进行Win32编译编程了.
//=====================================================================================================================================//
2.3 资源编译器
把资源脚本文件 *.rc 编译成资源文件 *.res
VC++的资源编译器Rc.exe ,TASM的资源编译器BRC32.exe或BRCC32.exe
命令行语法:
Rc [选项] 资源脚本文件名
资源脚本文件的语法是C格式的
C格式 汇编
等值定义 #define equ
注释 // ;
头文件 .h .inc
参数或操作 | or
BRC32.exe可以解释Windows的一些预定义值,不需要附带头文件
Rc.exe需要附带Resource.h头文件
//=====================================================================================================================================//
Borland公司的Resource Workshop资源编辑器
同时支持*.rc和*res文件,并可以转换保存
需要Resource.h头文件,并且要和*.rc文件在同一目录
不支持新的特征,如窗口的扩展风格.
//=====================================================================================================================================//
微软VS自带的资源编辑器Resource Workshop
Visual C++生成的.rc文件包含很多VC自己的头文件,在调试完成,准备发布的时候,保存一份.rc 文件并把文件中使用VC的多余内容去掉.
编译成 Rc.exe可以编译的格式
//=====================================================================================================================================//
2.4 make工具的用法
//================================================================
dos编写程序,编译器和链接器不用参数:
Masm xxx.asm;
Link xxx.obj;
做个批处理把xxx换成%1
在命令行中键入
asm.bat xxx
//================================================================
Win32汇编编程
用make工具维护代码
make工具:
1.是一个智能的批处理工具
2.调用makefile文件中用户定义的命令,进行编译和链接
3.批处理执行全部命令编译全部源文件
4.make工具默认使用makefile文件,获取源文件之间的依赖关系
5.make工具根据文件上次编译的时间和所以来的源文件的更新时间,已经从makefile文件获取的源文件依赖关系,自动判断应当编译那些源文件
6.1.Microsoft的make工具文件名为nmake.exe,可以再Visual C++的Bin目录下找到
6.2.Borland的make工具文件名是make.exe
6.3.两者默认的描述文件都是makefile
//================================================================
2.4.2 nmake的用法
命令行中键入 nmake /?
Usage: NMAKE @commandfile
NMAKE [options] [/f makefile] [/x stderrfile] [macrodefs] [targets]
nmake [选项] [/f描述文件名] [/x 输出信息文件名] [宏定义] [目标]
/f 描述文件不使用默认的makefile,用次参数指定
/x 把屏幕输出信息保存到文件中,用次参数 /x 文件名 (dos下管道操作符 nmake > 文件名 的方法无效)
宏定义 用心的定义覆盖描述文件中的宏定义
目标
Options:
/A Build all evaluated targets 不检测文件时间,强制更新所有文件
/B Build if time stamps are equal 文件时间相等时也要更新文件
/C Suppress output messages
/D Display build information make时显示文件新旧信息
/E Override env-var macros
/HELP Display brief usage message
/I Ignore exit codes from commands
/K Build unrelated targets on error
/N Display commands but do not execute 显示make时要执行的命令,但不真正执行
/NOLOGO Suppress copyright message
/P Display NMAKE information make时显示详细的信息
/Q Check time stamps but do not build
/R Ignore predefined rules/macros
/S Suppress executed-commands display
/T Change time stamps but do not build
/U Dump inline files
/Y Disable batch-mode
/? Display brief usage message
计算机时间不准,或源文件计算机间拷贝时,用/A 命令全部重新编译一次
以makefile作描述文件名,nmake命令不加参数,就可以完成所有工作了
//================================================================
2.4.3 描述文件语法
注释 宏定义 显示规则 隐含规则
test.exe描述文件:
//======================================================//
# nmake 工具的描述文件例子
EXE = Test.exe #指定输出文件
OBJS = x.obj \
y.obj #需要的目标文件
RES = x.res #需要的资源文件
LINK_FLAG = /subsystem:windows
#链接选项
ML_FLAG = /c /coff #编译选项
#定义依赖关系和执行命令
$(EXE):$(OBJS) $(RES)
Link $(LINK_FLAG) /out:$(EXE) $(OBJS) $(RES)
$(OBJS):Commom.inc
y.obj:y.inc
#定义汇编编译和资源编译的默认规则
.asm.obj:
ml $(ML_FLAG) $<
.rc .res:
rc $<
#清除临时文件
clean:
del *.obj
del *.res
//==========================================================//
语法:
注释和换行:
# 注释以#号开头知道行尾字符,忽略到#号及其后面的全部字符
\ 用\换行符来换行, \ 号后同一行中如果有字符,\ 号不当做字符
//==========================================================//
宏定义: 指代源文件和相关编译信息
变量名=变量内容
符合这个语法的行,就是宏定义
$ $号引用宏定义,变量名超过一个字符,引用是加圆括号 ()
比如:
$(LINK_FLAG)
$(EXE)
$A
$(A)
1.这样可以在命令行中,用新的宏定义符号可以替换makefile文件中的宏定义.
2.用不懂参数编译文件时,不必修改makefile文件
3.不必阅读整个makefile文件,只修改文件头部的宏就可以改变编译参数
4.多个文件用一个makefile文件时,文件名定义为宏名可以减少错误,增加可
读性.
比如:
makefile文件中:
ML_FLAG = /c /coff #编译选项
在命令行中:
nmake ML_FLAG="/c /coff /F1"
1.宏名区分大小写
2.宏名定义值中有空格时,用双引号引起来
//==========================================================//
*****************************************************************************************************************************************
第三章 使用ASM
*****************************************************************************************************************************************
1.指定使用的指令集
.386
.386p //告诉编译器本程序程序中将要使用特权指令
.586
.mmx //告诉编译器本程序将使用奔腾处理器的MMX指令集
*****************************************************************************************************************************************
2. .model语句
.model 内存模式[,语言模式][,其它模式]
1.
windows每一个程序都有相互独立的4GB地址空间,且所有4GB地址空间可以用32位寄存器直接访问
如果程序定义了.model flat,MASM自动做如下定义
ASSUME cs:FLAT, ds:FLAT,ss:FLAT,es:FLAT,fs:ERROR,gs:ERROR
如果要使用FS和GS寄存器需做如下声明
assume fs:nothing,gs:nothing或者assume fs:flat,gs:flat
2.
windows在API调用的时候使用的是stdcall,所以WIN32汇编只能选择stdcall
*****************************************************************************************************************************************
option casemap:none
声明程序中的变量,子程序名是否区分大小写
*****************************************************************************************************************************************
段的定义
包含全部断的源程序结构
系统自动分配足够大的堆栈空间,所以.stack段常常忽略
win32汇编源程序使用80386处理器的内存分页机制,把不同属性的数据或代码归类放到不同属性的内存页中
内存中:页的大小4KB,可自由指定属性
*****************************************************************************************************************************************
数据段 不可执行
.data 数据段 可读可写的变量,程序加载时候已经在内存中实际存在了,在可执行文件_DATA节区
.data? 未初始化数据段 可读写,在可执行文件中不占硬盘空间,在可执行文件_BSS节区
.const 常量段 可读不可写,如果对常量段进行写操作,会引发winodws保护错误的提示框,并结束程序
*****************************************************************************************************************************************
.code 代码段 _TEXT节区
所以指令必须写在代码段中,唯一具有可执行属性的段
特权级0运行的程序,对所有段都有读写权利
特权级3运行的程序,需要把可执行文件PE文件头的属性位设置为可写,才可以修改自己的代码段
UPX和PECompact等加壳软件,就是修改PE文件的代码段,来达到加壳的目的
*****************************************************************************************************************************************
.stack 堆栈段 可读可写可执行,不必定义,系统自动分配,
病毒和黑客工具就是找到系统或软件的堆栈的缓冲区溢出漏洞,动态的修改跟踪模块中的代码并拷贝到堆栈中去,边修改边执行,来实现的
*****************************************************************************************************************************************
3.1.3程序的入口和结束地址
汇编程序可在代码段中任何位置开始执行
end [开始地址] 指定程序的入口地址,并且以此语句为代码段的结束标志,开始地址必须在代码段中有所定义
编写多模块程序,每个分支程序都可以有相应的各种段,但只有主程序指定入口地址,分程序不指定
*****************************************************************************************************************************************
3.1.4 注释和换行
注释以: ; (分号)开始
换行: 在一行的最后加 \ (反斜杠)
*****************************************************************************************************************************************
3.2.1 API
DLL动态链接库是windows集成提供给应用程序的系统功能,PE格式的windows可执行文件,以字符串形式指出可以供应用程序调用的函数列表
KERNEL32.DLL 系统服务 内存管理,进程管理,动态链接
GDI32.DLL 图形服务 显示文本,显示图形
USER32.DLL 用户服务 创建窗口,消息循环
Wsock32.DLL 用TCP/IP协议进行网络通讯 Socket API函数
Tapi32.DLL 电话服务 TAPI(Telephone API)函数
*****************************************************************************************************************************************
3.2.2 调用API
WIN32环境中参数都是32位整数,定义为不同类型是为了说明它的用途
*****************************************************************************************************************************************
1.
inovke伪指令格式:
invoke 函数名 [,参数1][,参数2]... ...
error A2137:too few arguments to INVOKE 参数数量和函数声明时数量不符
*****************************************************************************************************************************************
2.api返回参数的方法
win32汇编返回值的类型都是DWORD,永远放在EAX中,如果EAX容纳不下,函数返回数据的指针到EAX,或者函数返回一个缓冲区地址指针,把数据放到缓冲区中
*****************************************************************************************************************************************
3.函数的声明的格式
距离,定义时一般忽略
语言,也忽略,使用.MODEL定义时候的默认类型
绝大多数数据类型都是DWOD类型,参数名称,是为了增加可读性,可有可无的
WIN32环境中,
ANSI字符集相关的函数,在尾部带字符A 欧洲语言体系,一字符一字节,以NULL结尾的一串字符数组
Unicode字符集相关函数,在尾部带字符W 东方语言体系,一字符量字节,
*****************************************************************************************************************************************
4.include语句格式
include 文件名
include <文件名> ;关键字可能引起同名混淆时候加尖括号
*****************************************************************************************************************************************
5.includelib语句格式
includelib 文件名
includelib <文件名> ;关键字可能引起同名混淆时候加尖括号
*****************************************************************************************************************************************
3.2.3 API函数中的等值定义
程序开头定义:
include windows.inc
就可以安全按照API手册来使用WIN32函数
*****************************************************************************************************************************************
3.3 标号,变量和数据结构
标号和变量的命名规范:
*****************************************************************************************************************************************
标号定义的格式
标号: 目的指令 ;子程序内跳转, 作用域:当前子程序
标号:: 目的指令 ;整个程序跳转,从一个子程序跳转到另一个子程序 作用域:正个程序
MASM中@@
*****************************************************************************************************************************************
3.3.2 全局变量
全局变量定义的格式:
只有定义全局变量时,变量类型才可以缩写
用?号定义全局变量,程序在运行的时候,变量的值为0,所以不必在进行初始化
全局变量定义实例:
*****************************************************************************************************************************************
局部变量:
local伪指令格式:
1.local伪指令紧跟在proc指令后,其它指令开始前,因为局部变量数目,必须在子程序开始前确定
2.数据类型不能缩写,定义数据结构,用数据结构名称当做数据类型,定义数组用[]括起来,不能使用DUP伪指令
3.local定义局部变量只是简单的把局部变量的内存空间留出来,它的值是前一个函数运行后,留在堆栈中的值,所以局部变量的值一定要初始化
4.最好的办法是:在赋值前,首先将整个数据结构填零,然后初始化要用的字段,RtlZeroMemory可以实现填0的功能
局部变量使用的典型例子,以下是一个TESTPROC的子程序:
TestProc proc
local @loc1:dword,@loc2:word
local @loc3:byte
mov eax,@loc1
mov ax,@loc2
mov al,@loc3
ret
TestProc endp
编译成可执行文件,并反汇编得到以下指令:
push ebp ;EBP压栈用来在程序返回时恢复ESP的值
mov ebp,esp ;WIN32汇编中EBP寄存器也是以堆栈段位默认数据段,所以把ESP的值放到EBP中,用
;EBP当做读取局部变量的指针
add esp,FFFFFFF8 ;FFFFFFF8是-8的补码,这段代码是:ESP的值减8,栈顶的指针就指向[ESP-8],也就是在堆栈段中定义了8字节的数据项
;因为CPU始终把ESP指向的数据段当做堆栈段,这是由@loc1,@loc2,@loc3分别定义,产生的一个8字节的堆栈,
;386处理器,以DOWRD为界内存存取速度最快,所以MASM编译时将@loc3定义为DWORD类型的数据
mov eax,dword ptr [ebp-04] ;现在[ebp-04]现在的EBP中的值是原来ESP中的值,所以[ebp-04]指向的是第一个数据
mov ax,word ptr [ebp-06]
mov al,byte ptr [ebp-07]
leave ;leave指令实现mov esp,ebp和pop ebp的功能,所以无论ESP的值如何被修改,只要EBP的值不变,都不会影响子程序的返回
ret
*****************************************************************************************************************************************
汇编语言定义数据结构的格式:
*****************************************************************************************************************************************
以不同的类型访问变量
*****************************************************************************************************************************************
3.6变量和函数的命名
1.匈牙利标识法命名约定
用连在一起类型前缀(都是小写)加变量说明(首字母大写的英文单词),来命名变量,让人很容易看得出变量的类型和用途,发现变量的使用错误
常用前缀:
b byte
w word
dw dword
h 句柄
lp 指针
sz 以0结尾的字符串
lpsz 以0结尾的字符串指针
f 浮点数
st 数据结构
2.匈牙利标识法的补充
针对WIN32汇编的特殊情况
1.局部变量命名的时候头部加 @ ,以边调用参数时明了其作用域
2.命名程序内服子程序时候头部加 _ ,API 函数区别
*****************************************************************************************************************************************
代码的书写格式
1.指令和寄存器用小写
2.EQU伪操作定义的常量用大写
3.变量和标号采用匈牙利标识法,则大小写混合
4.用TAB键隔开之指令和操作数(取代空格键)可以使源文件格式对齐
5.缩进:变量和标号定义不缩进,指令两个TAB缩进,分支和循环语句再缩进一格
*****************************************************************************************************************************************
注释和空行
1.不写无意义注释
2.修改代码,就要修改注释,保持代码和注释一致性
3.以描写一组指令实现的功能为主
4.子程序,在头部加注:功能,参数,返回值,调用注意事项
5.利用空格隔开,不同的代码功能块,以高级语言一句指令完成的一段汇编指令为界
*****************************************************************************************************************************************
MASM的宏功能
除了用在条件汇编构建不同的版本,其它地方尽量不适用宏汇编,因为很容易忽略了里面适用了哪些寄存器,而高级语言则不用考虑这个问题
*****************************************************************************************************************************************
代码的组织:定义子程序
1.使用频繁的代码,尽量封装成子程序
2.规模不要太大,完成单个功能,参数要精简,并且检查危险参数和合法性
3.申请资源,退出前要释放,打开的文件,要关闭
4.尽量把子程序写成黑匣子,可以不经修改就可以拿来用
5.每次编程只是编写新增加的部分,随着自写的子程序的增加,开发任何程序都将是很快的事情
学习编程,已经学会代码的组织是重中之重
为避免在长期在每一个工程中修改的子程序库,造成子程序库混乱的问题
1.自定义一个子程序文件夹,每当工程需要定义或修改子程序库时候,现在子程序文件夹定义或修改子程序库,然后copy到工程目录中调用
2.如果工程中的子程序库,需要添加或修改,先移除工程中的子程序库,然后修改子程序文件夹中的子程序库,然后COPY回来调用,不可以再工程中直接修改,就调用
3.需要联合使用的函数,封装到类中
4.尽量避免全局变量或者函数,把它们也封装到类中,就是尽量封装成黑匣子,以后工程中可以不经修改直接调用
"27.masm编译和链接参数}}}2
"28.asm用户层代码框架{{{
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
.data
.const
.code
start:
end start
"28.asm用户层代码框架}}}