汇编基础

汇编基础

过程

如何在汇编语言中定义一个过程?编写一个"函数"?

堆栈操作

Q:运行时堆栈与抽象数据类型有什么不同?
A:运行时堆栈是内存数组,CPU用ESP寄存器对其进行直接管理,该寄存器被称为堆栈指针寄存器,在32位模式下,ESP及寄存器存放的是堆栈中某个位置的32位偏移量。ESP基本不会被程序员控制,它用的是CALL,POP,PUSH,RET等指令进行修改。

Q:32位模式中哪个寄存器管理堆栈?如何管理?
K:ESP,顺便一提,EIP寄存器为指令指针寄存器
A:ESP(extended stack pointer),32位入栈操作是:先把栈顶指针减4,再将数值复制到栈顶指针指向的堆栈的位置,所有,该栈是“向下”生长的,从高地址向低地址扩展
压入一个数 再压入一个数

Q:(判断)过程中的局部变量是在堆栈中新建的
A:T
K:
堆栈的应用

  • 当寄存器用于多个目的时,堆栈可以作为寄存器的一个方便的临时保存区域。在寄存器被修改后还能保存其初始值
  • 执行CALL指令时,CPU在堆栈中保存了当前过程的返回值
  • 调用过程时,输入数值(参数),通过其压栈实现参数传递
  • 堆栈也为过程局部变量提供了临时存储区域

PUSH & POP

  • PUSH指令就是压栈操作,首先减少堆栈指针,然后复制源操作数,POP首先把ESP指向的内容复制到寄存器或者变量,然后增加ESP的值
  • PUSHAD把32位通用寄存器都压入栈,POPAD指令把堆栈中的数据弹出到32位通用寄存器
  • PUSHA则是把16位通用寄存器压入栈,POPA指令把堆栈中的数据弹出到16位通用寄存器
  • PUSHFD指令将32位EFLAGS寄存器压入栈,POPFD弹出,PUSHF和POPF对应16位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD

.data
aName BYTE "Abraham Lincoln", 0
nameSize = ($ - aName) - 1

;字符串翻转
.code
main PROC
; 将名字压入栈
mov ecx, nameSize
mov esi, 0
L1:
mov al, aName[esi] ; 获取字符
push eax ; 压入栈
inc esi
loop L1

mov esi, 0
mov ecx, nameSize

L2:
pop eax ; 出栈
mov aName[esi], al ; 保存
inc esi
loop L2

INVOKE ExitProcess, 0

main ENDP
END main

定义并使用过程

Q:(判断) PROC伪指令标识过程的开始,ENDP伪指令标识过程的结束
A:T
K:所有的程序都需要含有一个main过程,当在程序之外创建一个过程时,就用RET指令来结束它。RET强制CPU返回到被调用的位置

Q:(判断)CALL指令把自身指令的偏移量压入栈
A:F
K:
CALL指令调用一个过程,指挥处理器从新的内存地址开始执行,使用RET指令将处理器转回到被调用过程的程序点上。从物理上来说,CALL将其返回地址(运行的下一行)压入堆栈,再把被调用的过程的地址给EIP,RET从堆栈把返回地址弹回EIP
当存在过程嵌套时,每次调用CALL就会把下一行位置压栈,然后遇见一个RET就弹出一个,直到返回main

例子:求和数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD

.data
array DWORD 100h, 200h, 300h, 400h, 500h
theSum DWORD ?

.code
main PROC
; 将名字压入栈
mov esi, OFFSET array
mov ecx, LENGTHOF array
call ArraySum
mov sum, eax

INVOKE ExitProcess, 0

main ENDP

; 良好的过程注释风格如下:

;-------------------
;ArraySum
;计算32位整数数组元素的和
;接受:ESI = 数组偏移量,ECX = 数组元素的个数
;返回: EAX = 数组元素的和
;-----------------

;把寄存器作为参数和返回值的载体

ArraySum PROC
push esi
push exc
mov eax, 0

L1:
add eax, [esi] ;相加
add esi, TYPE DWORD ;指向下一个整数
loop L1 ;按数组大小重复

pop ecx
pop esi ;顺序别反了
ret ;结果放在了eax中

ArraySum ENDP

END main

  • 过程中的push和pop可以省略,在PROC后加上 USES esi, ecx,可以让汇编器自己生成

链接到外部库

Q: (判断)链接库由汇编语言源代码组成
A:F
K:链接库是一种文件,包含了已经汇编为机器代码的过程。链接库开始时是一个或多个源文件,这些文件再被汇编为目标文件。目标文件插入到一个特殊格式文件,该文件由链接器工具识别。

Q:在一个外部链接库中,用PROTO伪指令声明过程MyProc并调用
A:
Myproc proto
call Myproc

  • 当程序进行汇编时,汇编器不指定call的地址,链接器在链接库中寻找Myproc,并把库中适当的机器指令复制到程序可执行文件中,同时给call一个地址
  • .dll文件(dynamic link library)

写在后面

在对应实验的博客中,我们会使用Irvine32链接库,再更往后的内容中,我们会学习如何实现库的功能
我就是鸽子精,咕咕咕

Author

Ctwo

Posted on

2019-07-22

Updated on

2020-10-25

Licensed under

Comments