汇编你还想及格?
你始终得以怀疑的态度去看待事情,做好最坏的打算。
1. 注释下面代码
Title Hello Word
.386 ;使用80386指令集
.model flat, stdcall;内存模型为平坦模型,子程序采用stdcall约定
.stack 4096;为运行时堆栈分配4096空间
ExitProcess proto , dwExitCode:DWORD;为过程创建过程原型
;过程原型声明了过程的名称和参数列表。
DumpRegs proto
.data
gga label word ;不占空间
ggb dd 12345678h,87654321h;占4*2字节
;低78 56 34 12 21 43 65 87高
ggc dword "abcd" ; 占4*1字节
ggd=1234h;不占空间,或者说所占空间不连着ggc
ggf equ 12h;不占空间,或者说所占空间不连着ggc
ggg byte 10 dup("abc");占用1*10*3字节
ggh dd 10h,3 dup(50),20;占用4*5字节
ggi db 2 dup(?);占用1*2字节
listsize = $-gga ;64=0x40
.code
Main proc
mov ax,gga;ax=5678h
mov ebx,[ggb+2];ebx=43211234h,内存中的高位放在ebx高位
xor edx,edx;清零
mov edx,listsize;40h
call dumpregs;打印寄存器
invoke ExitProcess,0;push 0 ;call ExitProcess
Main endp
end main
2. 算术运算
var1 word -16
原码1000 0000 0001 0000
补码1111 1111 1111 0000
十六进制:FFF0
对应的二进制数是1111 1111 1111 0000。
3.根据以下伪代码画出执行完push ebp后的堆栈图。
Push 10h
Push 20h
Push 30h
Call func1
func1 proc
push ebp
mov ebp,esp
sub esp,8;为局部变量保留空间
mov DWORD PTR [ebp-4],10 ;存放局部变量x
mov dword ptr [ebp-8],20 ;y
movzx eax,[ebp+8];30h
mov esp,ebp
pop ebp
ret
func1 endp
4. 要求编写汇编代码实现以下要求。(难)
(1)接受用户输入四位字符串,如"4513"
(2)对字符串进行冒泡排序
(3)以二进制形式输出到控制台。比如1345输出0001001101000101。
(4)在下面代码空余部分书写。
.386
.model flat, stdcall
.stack 4096
ExitProcess proto , dwExitCode:DWORD
.data
.code
Main proc
invoke ExitProcess,0
Main endp
End main
提示:可以使用Writechar(al传递参数),ReadChar(al返回)。
解:本题有一定难度,Readchar容易读到缓冲区的东西,我们得用ReadString。本题最好不要用高级过程,不然堆栈平衡容易出错。
.386
.model flat, stdcall
.stack 4096
ExitProcess proto , dwExitCode:DWORD
ReadString proto;ecx存读取的个数(类似C语言,空格作为结束符),edx存偏移地址
WriteChar proto;将al的值打印
Crlf proto;换行
.data
str1 byte 5 dup(0)
count dword 5;ReadString最后一位是空格
str2 word 8;二进制1000
.code
Main proc
mov edx,offset str1
mov ecx,count
call ReadString
mov ecx,4
dec ecx
L1:push ecx;冒泡排序,建议直接背模板
lea esi,str1;运行时取str1地址
L2:mov eax,[esi]
mov ebx,[esi+1]
and ebx,0FFh;保留最后一个字节
and eax,0FFh
cmp ebx,eax
jl L3
xchg eax,ebx;交换寄存器的值
mov [esi],al
mov [esi+1],bl
L3:add esi,1
loop L2
pop ecx
loop L1
mov ebx,dword ptr str1
mov ecx,4
L4:mov ax,str2
push ax
push ecx
mov ecx,4
rol ebx,8;循环左移8位
mov dx,bx
and dx,0FFh
sub dx,30h;数字的ascii码-30h就是数字
L5:push dx;push至少2个字节(有点奇怪)
mov al,30h
and dl,byte ptr str2;强制类型转换
cmp dl,1
jb L6
mov al,31h
L6:call WriteChar
shr str2,1;逻辑右移1位
pop dx
loop L5
pop ecx;注意pop顺序
pop ax
mov str2,ax
loop L4
call Crlf
invoke ExitProcess,0
Main endp
End main
5. 名词解析
数据传送,寻址
inc,dec不影响进位标志。
mov指令:两操作数尺寸一致,不能同时为内存操作数,目的操作数不能是CS,EIP,IP,立即数不能直接送给段寄存器。
xchg:交换两个操作数的内容
neg:取反
movsx:带符号扩展的传送
movzx:零扩展并传送,只用于无符号整数
align:将变量对齐到边界,如align 4
对齐到双字边界
label:可以插入一个标号,并定义大小,但不为标号分配空间 (或者说占用的空间在其他地方)
ptr:覆盖一个已经被声明的操作数大小
type操作符:返回变量单个元素大小,如type var1
lengthof:计算数组中元素个数,如lengthof array1
sizeof:相当于lengthof返回值*type返回值
lahf:将标志寄存器的低字节复制到ah中。
sahf:将ah复制到标志寄存器的低字节中。
dup:表示变量占用的字节空间
equ:M1 EQU 10*10 把符号名称与一个整数表达式或文本连接起来。(占用的空间在其他地方)
条件处理
not指令不影响标志。
test:两操作数and运算,但不影响目的操作数
loopz:loop if zf=1
ja:基于无符号数比较,大于则跳转
jb:基于无符号数比较,小于则跳转
jg:基于有符号数比较,大于则跳转
jl:基于有符号数比较,小于则跳转
高级过程
addr:在汇编阶段获取偏移地址、只能配合invoke使用,取到传入全局变量参数的偏移地址;
offset:在汇编阶段获取偏移地址、只能取到全局变量的偏移地址;
lea:在执行阶段获取偏移地址,可以获取全局变量、局部变量等采用直接寻址、间接寻址和变址寻址的操作的地址;
stdcall:传参的方式是堆栈传参、传参的顺序是从右到左、堆栈平衡(清理)工作由子函数(被调用者)来完成;
cdecl:传参的方式是堆栈传参、传参的顺序是从右到左、堆栈平衡(清理)工作由父函数(调用者)来完成;
proto:声明过程原型。
proc:定义函数(过程)。
uses:保存子函数用到的寄存器的值,相当于 push和pop两条指令的功能
local:声明一个或多个变量,并赋予大小属性。必须紧跟在proc后。func1 proc LOCAL var1:byte,arr1[10]:dword,arr2:ptr word
enter:可以简化堆栈帧的建立过程,带有两个参数,相当于 push ebp、mov ebp、esp和sub esp、imm等3条指令的功能;
leave:可以简化堆栈帧的销毁过程,相当于mov esp、ebp和pop ebp等2条指令的功能;
invoke:调用函数的另外一种方式,通过该指令调用的函数在调用之前必须进行声明,其本质相当于 push 和 call 的组合。
call:调用过程
ret:指令从堆栈把返回地址弹回到指令寄存器。
push:先减esp再压栈。
pop:先出栈再加esp。
pushfd:将EFL寄存器内容压入堆栈
pushad:按EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI顺序,将所有32位通用寄存器的值压入堆栈
pusha:按AX,CX,DX,BX,SP,BP,SI,DI顺序,将16位通用寄存器的值压入堆栈
整数算术运算
imul:有符号整数乘法
idiv:有符号整数除法
CBW:符号扩展指令,字节转字
CWD:字转双字,dx每位按ax符号位置位
CDQ:双字转四字
ADC:带进位加法,将源操作数和进位标志的值都加到目的操作数中。 adc d,s ;d=d+s+cf
SBB:带借位减法:从目的操作数中减去源操作数和进位标志的值。
stc:cf置1
clc:cf清0
shl:逻辑左移
sal:算数左移
rol:循环左移
rcl:带进位循环左移
shld:双精度左移
串处理
cmps和scas指令会对标志位有影响。
movs,lods,stos不会影响标志位。
std:df置1
cld:df置0
rep:重复前缀,以ecx作循环控制知道ecx减到0为止.
repz/repe:ecx>0且zf=1
movsb/movsw/movsd:将数据从esi指向的内存位置复制到edi指向的内存位置。这两个寄存器自动地递增或递减(根据方向标志的值,df=0则递增)。
cmpsb/cmpsw/cmpsd:将ESI指向的内存操作数与EDI指向的内存操作数进行比较。根据方向标志的值决定ESI和EDI递增或递减。
scasb/scasw/scasd:分别将al/ax/eax中的值与edi寻址的一个byte/word/dword进行比较。
stosb/stosw/stosd:分别将al/ax/eax中的值存入edi指向的内存偏移量处。根据方向标志的值决定EDI递增或递减。
lodsb/lodsw/lodsd:分别从edi指向的内存地址向al/ax/eax装入byte,word,dword。根据方向标志的值决定esi递增或递减。
mov esi,offset source
mov edi,offset target
cld
mov ecx,lengthof source
repe cmpsd