0x01:环境配置

直接参考文章:https://blog.csdn.net/gaofengyakun/article/details/81516497
然后可以用以下代码测试一下:

1
2
3
4
5
6
7
8
9
Include irvine32.inc
.code
main proc
mov eax,1000h
add eax,4000h
call dumpregs
exit
main endp
End main

如果上述代码编译成功,出现黑窗口,则配置成功
vs下面环境的配置可以

0x02:调试

首先打开监视窗口:
调试–>窗口–>监视
然后任选一个窗口,vs下方就会出现如下的框框

以上面的程序为例,我们先在mov eax,5 处打一个断点,然后F5调试,F11继续执行

当执行完mov sum,eax后我们在下方输入sum,查看sum的值

0x03:一些基础知识

3.1:变量和常量

在讨论变量和常量之前,首先要清楚汇编里面的数据类型,以及寄存器的位数

数据类型:

1
2
3
4
5
6
7
8
9
10
11
8位整数:S代表有符号
BYTE
SBYTE

16位整数:
WORD
SWORD

32位整数:
DWORD
SDWORD

寄存器:

1
2
3
4
5
6
7
8
8位:
al,bl,cl,dl,dil,sil等

16位:
ax,bx,cx,dx,di,si等

32位:
eax,ebx,ecx,edx,edi,esi等

变量和常量都是定义在数据段的(.data)
如下例子定义了一个变量sum,数据类型为DWORD,初始值为0

1
2
.data
sum DWORD 0

如果不想给数据赋初值,可以用?来代替初值,表示不赋初值

1
2
.data
sum DWORD ?

3.2:等号伪指令

感觉有点像c语言中的宏定义

1
name = expression

在程序运行时,会自动将所有的name替换为expression
比如

1
COUNT= 500

在运行时

1
mov eax,COUNT

会被自动替换为

1
mov eax,500

3.3:计算相关指令

1
2
3
4
5
6
7
8
9
10
加法:
add
inc(一个操作数)

减法:
sub
dec(一个操作数)

取反:
neg

3.4:逻辑运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
与运算:
AND

或运算:
OR

异或运算:
XOR

按位取反:(反码)
NOT

比较:(当)
CMP des,sou

3.5:标志位

进位标志位:CF(Carry Flag)
运算结果的最高位产生了一个进位或者结尾,那么其值为1,否则为0

零标志位:ZF(Zero Flag)
如果运算结果为0,则其值为1,否则为0

3.6:条件判断指令

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
jz:为0跳转
jnz:不为0跳转

jc:最高位进位或借位跳转
jnc:最高位进位或借位不跳转

je:相等则跳转
jne:不相等则跳转

无符号:
==========================
ja:大于则跳转
jna
jae:大于或等于则跳转
jnae:不大于或等于则跳转

jb:小于则跳转
jnb
jbe:小于或等于则跳转
jnbe:不小于或等于则跳转
==========================

有符号:
==========================
jg:大于则跳转
jng
jge:大于或等于则跳转
jnge:不大于或等于则跳转

jl:小于则跳转
jnl
jle:小于或等于则跳转
jnle:不小于或等于则跳转
==========================

3.7:SIZEOF计算字符长度

计算结果是占的字节数

1
2
3
4
.data
intArray WORD 32 DUP(0) ;32个WORD类型大小的数组,初始化为0
.code
mov eax,SIZEOF intArray ;结果为32*2=64

3.8:代码标号

代码标号用于表示当前指令的地址

1
2
3
target:
mov ax,bx
jmp target

3.9:mov和movzx

mov用于和源操作数位数相同的寄存器赋值
如果寄存器的位数大于源操作数,那么需要用movzx把源操作数高位补零
movsx的效果和movzx差不多,只是是用于有符号数的

程序实例

第一个简单的汇编程序(两数相加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.386	;表示是一个32位程序,能访问32位寄存器和地址
.model flat,stdcall ;flag为内存模式,stdcall是子程序的调用规范
.stack 4096 ;为运行时堆栈保留4096字节的存储空间
ExitProcess PROTO,dwExitCode:DWORD

.data ;数据段
sum DWORD 0 ;定义变量sum,数据类型为DWORD,值为0

.code ;代码段
main PROC
mov eax,5
add eax,6
mov sum,eax

INVOKE ExitProcess,0
main ENDP ;标记一个过程的结束
END main ;标记一个程序的结束

简单的输出一个数组中的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD

.data
arrayW WORD 1000h,2000h,3000h
.code
main PROC
mov esi,OFFSET arrayW ;OFFSET求出arrayW数组的起始偏移量
mov ax,[esi]
mov ax,[esi+2] ;因为一个WORD占两个字节
mov ax,[esi+4]

INVOKE ExitProcess,0
main ENDP
END main

对于BYTE类型的数组,可以用inc来自增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD

.data
arrayB BYTE 10h,20h,30h ;注意每个元素的大小最大只能为8位(一个字节)
.code
main PROC
mov esi,OFFSET arrayB
mov al,[esi]
inc esi
mov al,[esi]
inc esi
mov al,[esi]

INVOKE ExitProcess,0
main ENDP
END main

jmp指令的简单用法

我们在top处打断点,然后debug,可以看到ax的值一直在4096和8192之间变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD

.data
arrayB BYTE 10h,20h,30h
.code
main PROC
top:
mov ax,1000h
mov ax,2000h
jmp top

INVOKE ExitProcess,0
main ENDP
END main

通过栈操作实现字符串反转

将字符一个一个压栈,然后再出栈,由于栈LIFO(last in first out)的特性造成了字符串反转

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
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD

.data
aName BYTE "Abraham Lincoln",0 ;0表示字符串的结尾
nameSize=($-aName)-1 ;$表示当前偏移地址 后面的-1是减去0所占的空间

.code
main PROC
mov ecx,nameSize
mov esi,0 ;从第0号元素开始
L1: movzx eax,aName[esi] ;movzx将无符号整数扩展到16位或32位
push eax
inc esi
loop L1

mov ecx,nameSize
mov esi,0

L2: pop eax
mov aName[esi],al
inc esi
loop L2

INVOKE ExitProcess,0
main ENDP
END 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
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD

.data
array DWORD 10000h,20000h,30000h,40000h,50000h
theSum DWORD ? ;存最终结果

.code
main PROC
mov esi,OFFSET array ;数组的起始偏移
mov ecx,LENGTHOF array ;LENGTHOF返回数组的元素个数
call ArraySum

main ENDP

;定义一个数组求和过程
ArraySum PROC USES esi ecx ;USES后面跟的寄存器在调用过程时压栈,在过程调用结束出栈
mov eax,0 ;eax清零来存放计算结果

L1:
add eax,[esi] ;通过地址来解析元素
add esi,TYPE DWORD ;每次加4,指向下一个DWORD类型数组元素
loop L1

ret ;返回,数组元素之和的结果在eax中
ArraySum ENDP

END main

最后更新: 2018年10月11日 16:28

原始链接: http://drac0nids.top/2018/09/13/汇编课程学习,持续更新/

× 请我吃糖~
打赏二维码