赞
踩
8086有14个寄存器,物理地址 = 段地址*16+偏移地址,
寄存器 | 名称 | 说明 | 示例 |
---|---|---|---|
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 |
指令 | 名称 | 说明 | 相关指令 | 指令性质 | 指令执行者 | 对应机器码 |
---|---|---|---|---|---|---|
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 | 自增1 | inc bx : bx中的值自增1 | ||||
loop | loop s : 执行过程:cx = cx - 1 , 判断cx值,大于 0 跳转至 s 位置 (修改cs:ip),否则 往下执行;通常用于循环,cx存放循环次数 | |||||
and | 与运算,and ax,0 : ax&=0 | |||||
or | 或运算,or ax,1 : ax|=1 |
我使用的是AsmTools,这个工具下的debug、masm、link都是在16位windows dos下运行的,在当前的32位或64位系统中不能运行,需安装DOSBox,在DOSBox中运行汇编的工具。
指令 | 说明 | 示例 |
---|---|---|
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 |
> debug 1.exe
masm将.asm源文件编译生成.obj目标文件
例如:编译1.asm
> masm.exe 1;
link将.obj文件链接生成.exe目标文件
例如:链接1.obj
> link.exe 1;
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.asm,存放在AsmTools文件夹下。
执行了一句指令,为ax赋值了,代码偏移地址IP自动加3
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
assume cs:seg_test,ds:a
查看程序中的内存地址及数据:
已知默认的DS为0x075A,则程序段的位置为0x075A+256 = 0x76A,可以看到程序段的前3行为a、b、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;
}
很简单的一段代码,先直接生成汇编看看:
> gcc -O0 -S code.c
-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
我们继续,生成可执行文件,然后再查看可执行文件的汇编:
> gcc -O0 -c code.c
将会生成一个code.o文件,使用命令查看汇编:
> objdump -d code.o
汇编如下:
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
> cl /FA code.c
生成的汇编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
问题:不实用汇编或函数调用,实现函数调用。
函数调用在汇编中是使用jmp指令实现的,我们只需要想办法将jmp指令插入到程序要执行的地方即可。
通过strcpy或memcpy溢出缓冲区,将jmp指令写入正确位置即可实现。
同一段c语言代码,由于编译器、平台、指令集等的不同,编译后的汇编是有区别的。如何去看懂一段汇编程序,需要先了解以下内容:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。