当前位置:   article > 正文

汇编入门_076a汇编

076a汇编


1. 笔记

1.1 常用寄存器

8086有14个寄存器,物理地址 = 段地址*16+偏移地址,

  • 注意:一般不支持将输入直接送入段寄存器,例如 mov ds:1000H 是非法的
  • 入栈时,栈顶从高地址向低地址方向增长,出栈则相反;使用时应注意栈顶超界
  • 栈空时SP = 0 ,栈满时也是 SP = 0 ,超界时,栈顶将环绕
寄存器名称说明示例
AX通用寄存器16位寄存器,分高8位AH,低8位AL
BX通用寄存器mov ax,[bx] : 从 ds:bx 位置开始读两个字节写到ax中;mov al,[bx] : 从 ds:bx 位置开始读一个字节写到al中
CX通用寄存器
DX通用寄存器
CS段寄存器,CS:IP指向当前要读取指令的地址,读取一条指令后IP值自动增加段地址*16+偏移地址 构成20位物理地址
DS段寄存器,存放要访问的数据段地址mov al,[0] : 将 ds:0 中的低8位读入al,[0]是ds的偏移地址,写入数据寄存器:mov [0],al 将ax寄存器的低8位写入ds:0;[bx] 也是偏移地址,具体的偏移值存放在bx中
SS段寄存器,栈段,SS:SP指向栈顶
ES段寄存器
IP代码偏移地址
SP栈偏移地址
SI与BX功能相近的寄存器,不能分为两个8位寄存器不能分成两个8位寄存器mov ax,[si] : 将起始位 ds:si 的后两个字节的值写到ax中
DI与SI相似不能分成两个8位寄存器mov ax,[di] : 将起始位 ds:di 的后两个字节的值写到ax中
BP
PSW

1.2 常用指令

  • 在汇编中包含两种指令:汇编指令、伪指令
指令名称说明相关指令指令性质指令执行者对应机器码
mov寄存器写值,mov ax,10H : ax = 10H
add寄存器累加,add ax, 10H : ax += 10H
sub寄存器减法,sub ax, 10H : ax -= 10H
pop出栈pop ax : 将栈顶数据读入ax,修改SP = SP + 2
push入栈push ax : SP = SP - 2, 将ax从栈顶写入
int
inc自增1inc bx : bx中的值自增1
looploop s : 执行过程:cx = cx - 1 , 判断cx值,大于 0 跳转至 s 位置 (修改cs:ip),否则 往下执行;通常用于循环,cx存放循环次数
and与运算,and ax,0 : ax&=0
or或运算,or ax,1 : ax|=1

1.3 其他

  • 字节byte 8bit,字word 16bit = 2byte

2. 工具使用

我使用的是AsmTools,这个工具下的debug、masm、link都是在16位windows dos下运行的,在当前的32位或64位系统中不能运行,需安装DOSBox,在DOSBox中运行汇编的工具。

2.1 debug跟踪调试工具

2.1.1 指令

指令说明示例
r查看当前寄存器
a写入一条汇编指令
u查看所有待运行指令,将机器指令翻译成汇编指令
t执行一条指令
d查看内存单元-d 076A:0000 : 查看076A:0000开始的内存数据
e写入数据-e ds:0 11 22 33 44 55 66 : 写入数据到从ds:0开始的内存
p
q退出debug

2.1.2 使用

> debug 1.exe
  • 1

2.2 masm编译工具

masm将.asm源文件编译生成.obj目标文件
例如:编译1.asm

> masm.exe 1;
  • 1

2.3 link链接工具

link将.obj文件链接生成.exe目标文件
例如:链接1.obj

> link.exe 1;
  • 1

3. 汇编程序结构

assume cs:sg1
sg1 segment
		mov ax,2000H
		mov ss,ax
		mov sp,0
		add sp,10
		pop ax
		pop bx
		push ax
		push bx
		pop ax
		pop bx
		
		mov ax,4c00H
		int 21H
sg1 ends
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

解释:

  • assume cs:sg1 :假设sg1段与cs段寄存器关联,是一个代码段
  • sg1 segment … sg1 ends :定义名为sg1的段,ends 段结束
  • end : 汇编程序的结束标记,碰到end就结束对源程序的编译,程序结束
  • 程序返回,以下两行代码在程序最后执行程序返回:
    mov ax,4c00H
    int 21H

3.1 汇编程序分析

以上面的代码为例进行分析,将上面的代码保存为文件1.asm,存放在AsmTools文件夹下。

  • 编译 masm 1;
  • 链接 link 1;
  • debug分析 : debug 1.exe
    在这里插入图片描述
  1. 用r命令查看各个寄存器的设置情况
    cx 中存放的是程序的长度
    在这里插入图片描述
  • DS = 075A ,这是程序内存的起始位置。程序的内存分为PSP区和程序区,PSP区占前256个字节,之后是程序区。DS是内存的起始位置,也是PSP区的起始位置;程序区位置 = DS + 256 = DS +10H
  • CS = 076A ,这是程序区起始位置,CS = DS + 10H , CS:IP指向程序的第一条指令
  • CX = 0016H 为 22 ,如何知道程序的长度呢?继续接着看
  • SS:SP = 0769:0000 ,初始栈顶位置
  1. 用u查看程序指令
    在这里插入图片描述
  • 中间红框中的即是机器代码,到 INT 21程序返回,计算代码长度 : 3 + 2 + 3 + 3 + 1 + 1 + 1 + 1 + 1 + 1 + 3 + 2 = 22 ,刚好是CX的大小。从地址也能看出来 076A:0000 到 076A:0016。
  • 起始指令地址为 076A:0000,即是 CS:IP
  1. 用t单步执行指令,并查看各个寄存器的变化
    在这里插入图片描述
    ax = 2000H
    IP = 0003H

执行了一句指令,为ax赋值了,代码偏移地址IP自动加3

  1. 剩下不说了

4. 包含多个段的程序

assume cs:seg_test
a segment
	db 1,2,3,4,5,6,7,8
a ends
b segment
	db 1,2,3,4,5,6,7,8
b ends
c segment
	db 0,0,0,0,0,0,0,0
c ends

seg_test segment
start :	mov bx,a
	mov ds,bx
	mov bx,0H
	mov si,10H
	mov di,20H
	mov cx,8H
	and ax,0H
s : mov al,[bx]
	add al,[bx+si]
	mov [bx+di],al
	inc bx
	loop s
	
	mov ax,4c00H
	int 21h
seg_test ends
end start
  • 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
  • 程序中定义了4个段,分别是a、b、c、seg_test,前3个为数据段,seg_test为代码段。
  • 默认的数据段寄存器DS的值为程序内存起始位置,但是a、b、c并不在起始代码段,所以首先需要为DS赋值为a的数据段位置。其实还可以在assume中定义:
assume cs:seg_test,ds:a
  • 1
  • 根据DS为a的数据段值,a 的数据偏移地址为0,b的数据偏移地址为16(10H),c的数据偏移地址为32(20H);
  • 设置cx的值为loop循环次数,每循环一次cx减1;因为有8个数据,需要循环8次
  • add ax,0H 是为了将ax赋值为0,其实可以使用 mov ax,0H
  • 只有bx可以根据al或ax分别使用8位或16位寄存器,si和di都只能一次使用16位寄存器
  • inc bx : 使 bx自增1

查看程序中的内存地址及数据:
在这里插入图片描述
已知默认的DS为0x075A,则程序段的位置为0x075A+256 = 0x76A,可以看到程序段的前3行为a、b、c的值。

5. 查看C程序汇编

写一个C程序,代码如下:

// code.c

int add(int i, int j)
{
    return i + j;
}

int main()
{
    int a = 1;
    int b = 7;
    a = add(a, b);
    return a;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

很简单的一段代码,先直接生成汇编看看:

5.1 在linux上使用gcc生成汇编文件

> gcc -O0 -S code.c
  • 1

-O是编译器优化参数,-O0不优化。执行上面的代码后,会生成一个code.s文件:

	.file	"code.c"
	.text
	.globl	add
	.type	add, @function
add:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	%edi, -4(%rbp)
	movl	%esi, -8(%rbp)
	movl	-4(%rbp), %edx
	movl	-8(%rbp), %eax
	addl	%edx, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	add, .-add
	.globl	main
	.type	main, @function
main:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	$1, -8(%rbp)
	movl	$7, -4(%rbp)
	movl	-4(%rbp), %edx
	movl	-8(%rbp), %eax
	movl	%edx, %esi
	movl	%eax, %edi
	call	add
	movl	%eax, -8(%rbp)
	movl	-8(%rbp), %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0"
	.section	.note.GNU-stack,"",@progbits

  • 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
  • 51
  • 52

我们继续,生成可执行文件,然后再查看可执行文件的汇编:

> gcc -O0 -c code.c
  • 1

将会生成一个code.o文件,使用命令查看汇编:

> objdump -d code.o
  • 1

汇编如下:

code.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <add>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   89 75 f8                mov    %esi,-0x8(%rbp)
   a:   8b 55 fc                mov    -0x4(%rbp),%edx
   d:   8b 45 f8                mov    -0x8(%rbp),%eax
  10:   01 d0                   add    %edx,%eax
  12:   5d                      pop    %rbp
  13:   c3                      retq   

0000000000000014 <main>:
  14:   55                      push   %rbp
  15:   48 89 e5                mov    %rsp,%rbp
  18:   48 83 ec 10             sub    $0x10,%rsp
  1c:   c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%rbp)
  23:   c7 45 fc 07 00 00 00    movl   $0x7,-0x4(%rbp)
  2a:   8b 55 fc                mov    -0x4(%rbp),%edx
  2d:   8b 45 f8                mov    -0x8(%rbp),%eax
  30:   89 d6                   mov    %edx,%esi
  32:   89 c7                   mov    %eax,%edi
  34:   e8 00 00 00 00          callq  39 <main+0x25>
  39:   89 45 f8                mov    %eax,-0x8(%rbp)
  3c:   8b 45 f8                mov    -0x8(%rbp),%eax
  3f:   c9                      leaveq 
  40:   c3                      retq  
  • 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

5.2 在windows上使用cl生成汇编文件

> cl /FA code.c
  • 1

生成的汇编code.asm:

; Listing generated by Microsoft (R) Optimizing Compiler Version 18.00.40629.0 

	TITLE	D:\vscode\cpp\socket_model\linux\code.c
	.686P
	.XMM
	include listing.inc
	.model	flat

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC	_add
PUBLIC	_main
; Function compile flags: /Odtp
_TEXT	SEGMENT
_b$ = -8						; size = 4
_a$ = -4						; size = 4
_main	PROC
; File d:\vscode\cpp\socket_model\linux\code.c
; Line 8
	push	ebp
	mov	ebp, esp
	sub	esp, 8
; Line 9
	mov	DWORD PTR _a$[ebp], 1
; Line 10
	mov	DWORD PTR _b$[ebp], 7
; Line 11
	mov	eax, DWORD PTR _b$[ebp]
	push	eax
	mov	ecx, DWORD PTR _a$[ebp]
	push	ecx
	call	_add
	add	esp, 8
	mov	DWORD PTR _a$[ebp], eax
; Line 12
	mov	eax, DWORD PTR _a$[ebp]
; Line 13
	mov	esp, ebp
	pop	ebp
	ret	0
_main	ENDP
_TEXT	ENDS
; Function compile flags: /Odtp
_TEXT	SEGMENT
_i$ = 8							; size = 4
_j$ = 12						; size = 4
_add	PROC
; File d:\vscode\cpp\socket_model\linux\code.c
; Line 3
	push	ebp
	mov	ebp, esp
; Line 4
	mov	eax, DWORD PTR _i$[ebp]
	add	eax, DWORD PTR _j$[ebp]
; Line 5
	pop	ebp
	ret	0
_add	ENDP
_TEXT	ENDS
END

  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

6. 其他

6.1 通过缓冲区溢出实现函数调用

问题:不实用汇编或函数调用,实现函数调用。

函数调用在汇编中是使用jmp指令实现的,我们只需要想办法将jmp指令插入到程序要执行的地方即可。

通过strcpy或memcpy溢出缓冲区,将jmp指令写入正确位置即可实现。

参考:缓冲区溢出之Strcpy和Memcpy

7. 如何看懂汇编程序

同一段c语言代码,由于编译器、平台、指令集等的不同,编译后的汇编是有区别的。如何去看懂一段汇编程序,需要先了解以下内容:

    1. 指令集中的寄存器,通用寄存器、专用寄存器等
    1. 函数传参方式,例如 ARM-V7-A 中,fastcall函数调用前4个参数由R0-R3四个寄存器保存,之后的参数从右往左入栈,由栈传递
    1. 熟悉常用指令
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/461035
推荐阅读
相关标签
  

闽ICP备14008679号