1 2 3
|
|
环境准备:ubuntu20.04安装 | xiaoxiaoxy (xiaoxiaoxy1.github.io)
零散知识
1 2 3 4 5 6 7
| 1、汇编语言的组成 1):汇编指令 机器码助记符,有对应的机器码 2):伪指令 没有对应的机器码,由编译器识别,计算机并不执行 3):其他符号 如+、-、*、/等,由编译器识别,没有对应的机器码
|
运行一个汇编程序代码
1 2 3 4 5
| 四步骤: 编程:要用到文本编辑器去编写汇编程序代码 编译:要下载编译器及会用编译指令 链接:要会用链接指令 跟踪:即Debug,要会利用工具Debug
|
汇编指令风格
两种主流风格:AT&T格式 与 Intel格式
据了解:Intel风格适用得更加广泛,所以本章内容以Intel风格为主导。
两种风格写出来的 hello world :
AT&T格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #hello.s .data # 数据段声明 msg : .string "Hello, world!\\n" # 要输出的字符串 len = . - msg # 字串长度 .text # 代码段声明 .global _start # 指定入口函数
_start: # 在屏幕上显示一个字符串 movl $len, %edx # 参数三:字符串长度 movl $msg, %ecx # 参数二:要显示的字符串 movl $1, %ebx # 参数一:文件描述符(stdout) movl $4, %eax # 系统调用号(sys_write) int $0x80 # 调用内核功能
# 退出程序 movl $0,%ebx # 参数一:退出代码 movl $1,%eax # 系统调用号(sys_exit) int $0x80 # 调用内核功能
|
Intel格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ; hello.asm section .data ; 数据段声明 msg db "Hello, world!", 0xA ; 要输出的字符串 len equ $ - msg ; 字串长度 section .text ; 代码段声明 global _start ; 指定入口函数 _start: ; 在屏幕上显示一个字符串 mov edx, len ; 参数三:字符串长度 mov ecx, msg ; 参数二:要显示的字符串 mov ebx, 1 ; 参数一:文件描述符(stdout) mov eax, 4 ; 系统调用号(sys_write) int 0x80 ; 调用内核功能 ; 退出程序 mov ebx, 0 ; 参数一:退出代码 mov eax, 1 ; 系统调用号(sys_exit) int 0x80 ; 调用内核功能
|
个人观点:Intel风格更加简洁
基于Intel格式的文件汇编步骤
1、汇编器的安装
1 2 3 4 5
| sudo apt-get install nasm 不成功则执行: sudo rm <提示中给的锁文件路径>
参考链接:https://blog.csdn.net/weixin_44121966/article/details/118143296
|
2、汇编器及链接器的使用
1 2 3 4 5 6 7 8 9 10 11 12
| 编译指令: nasm -f elf64 <编辑器编辑的.asm文件> 链接指令: ld -s -o <生成文件的文件名> <编译出来的.o文件> or gcc -fPIC -no-pie -o <生成文件的文件名> <编译出来的.o文件> 为什么gcc的链接指令不是 gcc -o <生成文件的文件名> <编译出来的.o文件> ? 从Ubuntu16.10开始默认启用PIE,而makefile的库不支持PIE。 详请查看以下链接 链接:https://blog.csdn.net/weixin_43360707/article/details/124272319
|
3、跟踪(Debug)
Debug的话,基本步骤就是下断点(break)、跑程序(run)以及单步调试(ni/si)
使用的工具:gdb
ubuntu20.04安装 | xiaoxiaoxy (xiaoxiaoxy1.github.io)(6、7点讲的是pwndbg的安装,也可以自行找别的安装教程)
gdb插件安装与配置:pwndbg,peda,gef_byerose的博客-CSDN博客_gdb插件安装
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
|
pwndbg> b main pwndbg> r pwndbg> ni pwndbg> si pwndbg> p pwndbg> i b pwndbg> vmmap pwndbg> cyclic 200 pwndbg> cyclic -l <数据> pwndbg> b *<地址> pwndbg> x/130wx <地址> pwndbg> i all-registers
pwndbg> c pwndbg> d pwndbg> disass
pwndbg> l pwndbg> fi pwndbg> j pwndbg> q pwndbg> u
pwndbg> set $eip = 0x8048300 pwndbg> p $eip pwndbg> x/i $eip
|
感觉讲的话有点多,不太想讲,但还是讲一下吧,因为我刚接触的时候简单的指令我也老是记不住
首先 ,$ gdb <二进制文件名> ,进入debug界面
其次,pwndbg> b main ,在main函数出下断点
然后就可以,pwndbg> r ,运行程序
最后单步,pwndbg> ni ,逐步调试即可
这就是一个简单的debug流程,复杂的debug过程中你可以看程序的走向,寄存器的值、栈信息、堆信息等
编程(写代码)、编译、链接、跟踪
先对这个hello world程序浅浅地做下分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ; hello.asm section .data ; 数据段声明 msg db "Hello, world!", 0xA ; 要输出的字符串 len equ $ - msg ; 字串长度 section .text ; 代码段声明 global _start ; 指定入口函数 _start: ; 在屏幕上显示一个字符串 mov edx, len ; 参数三:字符串长度 mov ecx, msg ; 参数二:要显示的字符串 mov ebx, 1 ; 参数一:文件描述符(stdout) mov eax, 4 ; 系统调用号(sys_write) int 0x80 ; 调用内核功能 ; 退出程序 mov ebx, 0 ; 参数一:退出代码 mov eax, 1 ; 系统调用号(sys_exit) int 0x80 ; 调用内核功能
|
1 2 3 4 5 6 7
| section .data ; 数据段声明 msg db "Hello, world!", 0xA ; 要输出的字符串 len equ $ - msg ; 字串长度
#数据段的定义,什么是数据段?存储字符串、常量等信息,对应五大分区的常量区 #链接:https://www.jianshu.com/p/a3e80cb5198b #这里不能这么讲,换个方式吧,这里也不删了,暂时留在这,也不分析这个hello world了
|
1、ida反编译一个二进制文件,我这是以ctfwiki中的ret2text为例,文件下载地址:ret2text
这是main函数
我们查看main函数的汇编:
.text段对应内存五大分区中的代码区
拉动滚动条,在最左端我们会查看到LOAD、.fini、.eh_frame_hdr、got、got.plt、.plt、.rodata、.bss……等很多字段
然后我主要要讲的就是.data、.rodata、.bss、.text,.text前面讲了,接下来讲一下前三个
.data是代码段.rodata是只读代码段,详见下面的链接
链接:bss、data和rodata区别与联系_brlee的博客-CSDN博客_rodata
.bss段是指静态代码段,程序中未赋值的变量就会存储在这,例如C代码中定义了一个 int a;但为给a赋值,a就会存到.bss段
这张图还是贴一下吧 来源:内存五大分区 - 简书 (jianshu.com)
现在回头看一下这个hello world
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ; hello.asm section .data ; 数据段声明 msg db "Hello, world!", 0xA ; 要输出的字符串 len equ $ - msg ; 字串长度 section .text ; 代码段声明 global _start ; 指定入口函数 _start: ; 在屏幕上显示一个字符串 mov edx, len ; 参数三:字符串长度 mov ecx, msg ; 参数二:要显示的字符串 mov ebx, 1 ; 参数一:文件描述符(stdout) mov eax, 4 ; 系统调用号(sys_write) int 0x80 ; 调用内核功能 ; 退出程序 mov ebx, 0 ; 参数一:退出代码 mov eax, 1 ; 系统调用号(sys_exit) int 0x80 ; 调用内核功能 #global 全局变量 #_start 对比main函数去理解 #int 0x80 系统调用(进入内核模式),在这条指令前,先往指定寄存器中压入值,进入内核模式后, #系统根据指定寄存器中的值判断做什么样的操作(操作的例子:退出程序、文件读写打开关闭等一系列操作)
|
对于汇编代码中的指令符,以下是我个人记录的一些
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
| 数据传送与访问:mov
算术运算与逻辑运算: inc/dec #操作数±1 add/sub #长度相同的操作数相加减 and/or #按位逻辑与/或 mul #乘法操作 xor #异或操作,常用做寄存器值置零 cmp #比较两个值 neg #将操作数转换为二进制补码,并将操作数的符号位取反 跳转指令与循环指令: jmp #无条件跳转指令,一般需要使用一个标号来标识,可以实现循环 LOOP #循环指令,每循环一次循环计数寄存器减1 je #条件跳转,链接:https://blog.csdn.net/ssihc0/article/details/5215044 test #与AND命令有相同效果,只是Test指令不改变AX和BX的内容,而AND指令会把结果保存到AX中 栈与函数调用: push #入栈,详细操作P41 pop #从栈中pop一个值,给ebp 使用栈保存函数返回地址: call #call调用子函数时,下一条指令的地址作为返回地址存入栈中 #相当于 push IP # jmp near ptr 标号 这两条汇编 #https://blog.csdn.net/u013018721/article/details/51264199 ret #往栈上高地址一个单位取地址当做跳转地址 ******* #链接:https://blog.csdn.net/qq_37340753/article/details/81585083 其他: lea #官方解释Load Effective Address,即装入有效地址的意思,它的操作数就是地址 nop #空操作,链接:https://www.cnblogs.com/shangzhijian/p/4994028.html leave #mov esp,ebp和pop ebp #链接:https://blog.csdn.net/striver1205/article/details/25216699 #链接:https://blog.csdn.net/zhangxinrun/article/details/5888425 int 0x80 #系统调用
|
emm…暂时打住,这篇暂时就到这吧
QAQ