@perkyoung
2015-07-12T14:36:22.000000Z
字数 5107
阅读 2507
汇编语言
EAX,操作数和结果数据的累加器
EBX,指向数据内存段中的数据的指针
ECX,字符串和循环操作的计数器
EDX,字的乘除运算,间接的输入输出
EDI,存储器指针,字符串操作的目标数据的指针
ESI,存储器指针,字符串操作的源数据的指针
ESP,栈顶指针
EBP,存取堆栈指针,存储器指针
CS,代码段,结合EIP获取内存中的指令码,程序不能改变CS寄存器
DS,数据段
SS,堆栈段
ES,FS,GS为附加段指针
EIP,程序不能直接修改指令指针本身。可以通过跳转等指令去修改
CR0,...CR4,不能直接取控制寄存器中的值,可以将控制寄存器中的值传给通用寄存器。如果想给控制寄存器赋值,可以先将值赋给通用寄存器,在赋值。
状态标志
略
控制标志
略
汇编器
将汇编代码翻译成机器代码的程序,不同硬件平台的汇编器不同
连接器
如果有调用外部函数,则需要链接器找到该函数的位置,并且生成目标代码
调试器
gdb等工具
反汇编器
binutils工具
addr2line 把地址转换成文件名和行号
ar 创建、修改和展开文件存档
as 把汇编语言代码汇编成目标代码
c++filt 还原C++符号的过滤器
gprof
ld
nm 列出目标文件的符号
objcopy 复制和翻译目标文件
objdump
ranlib 生成存档文件内容的索引
readelf 按照elf格式显示来自目标文件的索引
size 列出目标文件或存档文件的段长度
strings 显示目标文件中的可打印字符串
strip 丢弃符号
cpuinfo.s
.section .data
output:
.ascii "the processor Vendor ID is 'xxxxxxxxxxxx'\n"
.section .text
.globl _start
_start:
movl $0, %eax
cpuid //该程序通过寄存器传参
movl $output, %edi
movl %ebx, 28(%edi)
movl %edx, 32(%edi)
movl %ecx, 36(%edi)
movl $4, %eax
movl $1, %ebx
movl $output, %ecx
movl $42, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
as -o cpuid.o cpuid.s -gstabs
ld -o cpuid cpuid.o
break * _start+offset //停在某一个标签,但是貌似不支持+offset
info registers 显示所有寄存器的值
print 查看变量,或寄存器的值 print/d十进制, print/x十六进制,print/t二进制,print/d $eax
x 查看特定内存位置的值,x/n[cdx]输出格式[bhw]每个字段的长度,x/
gdb
next n
setp n
ni,si单步执行机器码,可疑通过x/i $pc 查看机器码
finish //运行完当前函数
until //将循环运行完毕
break 19 if $eax==11 //当eax为11,在19行下断点
break 20
condition 1 $eax==10 //编号为1的断点,只有在eax==10的情况下才有效
condition 1 //取消编号为1的断点的条件
ignore 1 2 //忽略编号为1的断点2次
watch expression 该变量或表达式发生变化时停止
rwatch expression 。。。。。被读时候停止
awatch expression。。。。。被读或被写时候停止
clear 清理在某行或者某函数上的停止点,操作对象是行号或这函数filename:function, filename:linenum
delete 删除停止点,操作对象是断点编号
disable 置为不可用,但是并未删除,操作对象是编号
enable 置为可用,操作对象是编号
针对断点2运行命令
conditions 2
print $eax
end
.section .data
output:
.asciz "The processor Vendor ID is '%s'\n"
.section .bss
.lcomm buffer, 12 //不需要定义缓冲区的值,所以bss段。类似C中全局为初始化的变量
.section .text
.globl _start
_start:
movl $0, %eax
cpuid
movl $buffer, %edi //标签前加$,表示地址。
movl %ebx, (%edi)
movl %edx, 4(%edi)
movl %ecx, 8(%edi)
pushl $buffer
pushl $output
call printf
addl $8, %esp
pushl $0
call exit
执行以上程序需要gcc 4.5.1
版本,高版本的4.8.1
不支持变量到寄存器的传递,反过来亦是如此
.octa十六字节整数,.quad 八字节整数,.short 十六位整数,.single单精度浮点数
.section .data
msg:
.ascii "dfdsfas"
factors:
.double 34.3 , 343.3, 343.5
height:
.int 64
length:
.int 634
bss,无需声明类型,内存中会保留这块长度,原是内容不变。好处是数据不会被包含在可执行文件中,该内存区域被保留在运行时使用,但数据段会包含在可执行文件中,因为会用某一个值去初始化它。
.section .bss
.comm buffer, 10 全局
.lcomm output, 20 局部
movx source, destination
movl %eax, %ebx
movw %ax, %bx
movb %al, %bl
.section .data
value:
.int 100
movl value, %eax //4.8.1不支持这种赋值,反过来亦是如此
movl %eax, value
表达是格式:base_address (offset_address, index, size),
获取的地址是,base_address + offset_address + index * size, offset_address,index必须是寄存器
.section .data
output:
.asciz "the value is %d\n"
values:
.int 9,8,7,6,5,4,3,2,1,0
.globl main
main:
movl $0, $edi //刚开始,用到了 %ecx 寄存器,发生段错误,经调试,是因为,每次调用完printf,会有值覆盖%ecx寄存器。切记:%eax,%ebx,%ecx,%edx是函数返回值经常覆盖的寄存器
loop:
movl values(,$edi, 4), %eax //反过来赋值也可以
pushl $edi
pushl $output
call printf
addl $8, %esp //退栈顶
inc %edi
cmpl $10, %edi
jne loop
movl $1, %eax
movl $0, %ebx
int $0x80
.section .data
values:
.int 2,4,5,6,7,865,3
.section .text
.globl main
main:
movl values, %eax //将第一个数字存入寄存器
movl $values, %edi //将地址存入寄存器
movl $100, 4(%edi) //加上括号后表示寄存器内的地址,这个表示后面的第四个字节,注意是第四个字节地址啊,也可以表示为,values(,4,1)
movl $1, %edi
movl values(,%edi,4), %ebx
movl $1, %eax
int $0x80
找出数组中的最大值
.section .data
values:
.int 4,6,8,45,2,44,678,11
output:
.asciz "the largest num is %d\n"
.section .text
.globl main
main:
movl values, %eax
movl $1, %edi
loop:
movl values(,%edi,4), %ebx
cmpl %eax, %ebx //第二个减去第一个,并会设置EFLAGS的值
cmova %ebx, %eax //如果上面的cmpl结果为大于,则传输ebx到eax,注意,cmp和cmova后面寄存器的顺序正好相反
inc %edi
cmpl $7, %edi
jne loop
pushl %eax
push $output
call printf
addl $8, $esp
pushl $0
call exit
XCHG 交换寄存器之间,或寄存器和内存之间的数据
BSWAP 反转数据,不是32位反转,是4个字节进行反转,也就是大端小端之间的转换
XADD 交换两个值(寄存器之间或寄存器与内存),并且将和加载到目的寄存器或内存位置
栈的增长是由高地址向低地址增长,如图有新数据0X00000012入栈,小端法高位在高地址,低位在低地址,0X12在低地址
PUSHA/POPA //压入弹出16位寄存器
PUSHAD/POPAD //压入弹出32位寄存器
PUSHF/POPF //压入弹出低16位eflags寄存器
PUSHFD/POPFD //压入弹出全部32位寄存器
jmp location //location表示要跳转到内存地址,被声明为标签
短跳转:128字节范围内
远跳转:分段模式下的跳转
近跳转:其他所有的跳转
.section .text
.globl main
main:
movl $1, %eax //$eip始终指向下一条指令的地址,也就是马上要执行的指令的地址
jmp overhere //jmp指令,修改了eip,使他指向了overhere标签下的movl $20, %ebx,但是该指令还没有执行,jmp仅仅修改了修改了%eip,可以objdump -D 查看一下内存地址
movl $0, %ebx
int $0x80
overhere:
movl $20, %ebx
int $0x80
当执行call指令时,返回地址会添加到堆栈中
.section .data
output:
.asciz "this is section %d\n"
.section .text
.globl main
main:
pushl $1
pushl $output
call printf
addl $8, %esp //前面入栈两次,所以要恢复栈顶
call overhere //在调用call之后,返回地址(pushl $3的内存地址)会压入栈中
pushl $3
pushl $output
call printf
add $8, %esp
pushl $0
call exit
overhere:
pushl %ebp //首先要保存%ebp,在这个例子中体现的不明显,如果是多层函数的调用,那上层函数的%ebp是非常有用的,但是这层函数又需要把当前的栈顶赋值给%ebp,所以要保存上层函数需要的%ebp,一会儿会恢复
movl %esp, %ebp //为什么要将栈顶赋值给%ebp,因为在该函数内部有可能会有临时变量也会入栈,%esp会改变,所以首先保存esp的值,当函数内部再入栈临时数据的时候,esp会变化,变化之后,是偶那个该esp就可以了。
pushl $2
push $output
call printf
addl $8, %esp
movl %ebp, %esp //恢复%esp
popl %ebp //恢复%ebp
ret //ret执行之后,当初调用call入栈的地址会出栈
软件中断 ,如int $0x80
硬件中断
不总是执行,取决于eflags的状态。下面的跳转常常把标签转化为偏移地址,所以条件跳转不支持分段模式下的远跳转,如果想跳转,则需判断,再进行无条件跳转
movl $3, %ecx
loop1:
<instruction ....> //这里的函数容易有坑,比如printf函数,运行过程中会使用ecx。所以在使用call的时候要格外小心
loop loop1 //执行这条指令之后,ecx自动减1
在反汇编代码中常常包括
pushl %ebp
pushl %esp, %ebp
andl $-16, %esp //初步认为是内存对齐,esp指向的内存地址是16的倍数,-16二进制表示的后四位是0000
subl $32, %esp //为该函数的临时变量开辟斩空间
movl value, %ecx
cmpl %ebx, %ecx
cmova %ecx, %ebx //避免了分支
略去一大部分,关于优化的,回头再仔细研究