@perkyoung
2015-07-12T14:36:22.000000Z
字数 5107
阅读 2562
汇编语言
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++符号的过滤器gprofldnm 列出目标文件的符号objcopy 复制和翻译目标文件objdumpranlib 生成存档文件内容的索引readelf 按照elf格式显示来自目标文件的索引size 列出目标文件或存档文件的段长度strings 显示目标文件中的可打印字符串strip 丢弃符号
cpuinfo.s
.section .dataoutput:.ascii "the processor Vendor ID is 'xxxxxxxxxxxx'\n".section .text.globl _start_start:movl $0, %eaxcpuid //该程序通过寄存器传参movl $output, %edimovl %ebx, 28(%edi)movl %edx, 32(%edi)movl %ecx, 36(%edi)movl $4, %eaxmovl $1, %ebxmovl $output, %ecxmovl $42, %edxint $0x80movl $1, %eaxmovl $0, %ebxint $0x80
as -o cpuid.o cpuid.s -gstabsld -o cpuid cpuid.o
break * _start+offset //停在某一个标签,但是貌似不支持+offsetinfo registers 显示所有寄存器的值print 查看变量,或寄存器的值 print/d十进制, print/x十六进制,print/t二进制,print/d $eaxx 查看特定内存位置的值,x/n[cdx]输出格式[bhw]每个字段的长度,x/
gdb
next nsetp nni,si单步执行机器码,可疑通过x/i $pc 查看机器码finish //运行完当前函数until //将循环运行完毕break 19 if $eax==11 //当eax为11,在19行下断点break 20condition 1 $eax==10 //编号为1的断点,只有在eax==10的情况下才有效condition 1 //取消编号为1的断点的条件ignore 1 2 //忽略编号为1的断点2次watch expression 该变量或表达式发生变化时停止rwatch expression 。。。。。被读时候停止awatch expression。。。。。被读或被写时候停止clear 清理在某行或者某函数上的停止点,操作对象是行号或这函数filename:function, filename:linenumdelete 删除停止点,操作对象是断点编号disable 置为不可用,但是并未删除,操作对象是编号enable 置为可用,操作对象是编号针对断点2运行命令conditions 2print $eaxend
.section .dataoutput:.asciz "The processor Vendor ID is '%s'\n".section .bss.lcomm buffer, 12 //不需要定义缓冲区的值,所以bss段。类似C中全局为初始化的变量.section .text.globl _start_start:movl $0, %eaxcpuidmovl $buffer, %edi //标签前加$,表示地址。movl %ebx, (%edi)movl %edx, 4(%edi)movl %ecx, 8(%edi)pushl $bufferpushl $outputcall printfaddl $8, %esppushl $0call exit
执行以上程序需要gcc 4.5.1版本,高版本的4.8.1不支持变量到寄存器的传递,反过来亦是如此
.octa十六字节整数,.quad 八字节整数,.short 十六位整数,.single单精度浮点数
.section .datamsg:.ascii "dfdsfas"factors:.double 34.3 , 343.3, 343.5height:.int 64length:.int 634
bss,无需声明类型,内存中会保留这块长度,原是内容不变。好处是数据不会被包含在可执行文件中,该内存区域被保留在运行时使用,但数据段会包含在可执行文件中,因为会用某一个值去初始化它。
.section .bss.comm buffer, 10 全局.lcomm output, 20 局部
movx source, destinationmovl %eax, %ebxmovw %ax, %bxmovb %al, %bl
.section .datavalue:.int 100movl value, %eax //4.8.1不支持这种赋值,反过来亦是如此movl %eax, value
表达是格式:base_address (offset_address, index, size),
获取的地址是,base_address + offset_address + index * size, offset_address,index必须是寄存器
.section .dataoutput:.asciz "the value is %d\n"values:.int 9,8,7,6,5,4,3,2,1,0.globl mainmain:movl $0, $edi //刚开始,用到了 %ecx 寄存器,发生段错误,经调试,是因为,每次调用完printf,会有值覆盖%ecx寄存器。切记:%eax,%ebx,%ecx,%edx是函数返回值经常覆盖的寄存器loop:movl values(,$edi, 4), %eax //反过来赋值也可以pushl $edipushl $outputcall printfaddl $8, %esp //退栈顶inc %edicmpl $10, %edijne loopmovl $1, %eaxmovl $0, %ebxint $0x80
.section .datavalues:.int 2,4,5,6,7,865,3.section .text.globl mainmain:movl values, %eax //将第一个数字存入寄存器movl $values, %edi //将地址存入寄存器movl $100, 4(%edi) //加上括号后表示寄存器内的地址,这个表示后面的第四个字节,注意是第四个字节地址啊,也可以表示为,values(,4,1)movl $1, %edimovl values(,%edi,4), %ebxmovl $1, %eaxint $0x80
找出数组中的最大值.section .datavalues:.int 4,6,8,45,2,44,678,11output:.asciz "the largest num is %d\n".section .text.globl mainmain:movl values, %eaxmovl $1, %ediloop:movl values(,%edi,4), %ebxcmpl %eax, %ebx //第二个减去第一个,并会设置EFLAGS的值cmova %ebx, %eax //如果上面的cmpl结果为大于,则传输ebx到eax,注意,cmp和cmova后面寄存器的顺序正好相反inc %edicmpl $7, %edijne looppushl %eaxpush $outputcall printfaddl $8, $esppushl $0call 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 mainmain:movl $1, %eax //$eip始终指向下一条指令的地址,也就是马上要执行的指令的地址jmp overhere //jmp指令,修改了eip,使他指向了overhere标签下的movl $20, %ebx,但是该指令还没有执行,jmp仅仅修改了修改了%eip,可以objdump -D 查看一下内存地址movl $0, %ebxint $0x80overhere:movl $20, %ebxint $0x80
当执行call指令时,返回地址会添加到堆栈中
.section .dataoutput:.asciz "this is section %d\n".section .text.globl mainmain:pushl $1pushl $outputcall printfaddl $8, %esp //前面入栈两次,所以要恢复栈顶call overhere //在调用call之后,返回地址(pushl $3的内存地址)会压入栈中pushl $3pushl $outputcall printfadd $8, %esppushl $0call exitoverhere:pushl %ebp //首先要保存%ebp,在这个例子中体现的不明显,如果是多层函数的调用,那上层函数的%ebp是非常有用的,但是这层函数又需要把当前的栈顶赋值给%ebp,所以要保存上层函数需要的%ebp,一会儿会恢复movl %esp, %ebp //为什么要将栈顶赋值给%ebp,因为在该函数内部有可能会有临时变量也会入栈,%esp会改变,所以首先保存esp的值,当函数内部再入栈临时数据的时候,esp会变化,变化之后,是偶那个该esp就可以了。pushl $2push $outputcall printfaddl $8, %espmovl %ebp, %esp //恢复%esppopl %ebp //恢复%ebpret //ret执行之后,当初调用call入栈的地址会出栈
软件中断 ,如int $0x80
硬件中断
不总是执行,取决于eflags的状态。下面的跳转常常把标签转化为偏移地址,所以条件跳转不支持分段模式下的远跳转,如果想跳转,则需判断,再进行无条件跳转
movl $3, %ecxloop1:<instruction ....> //这里的函数容易有坑,比如printf函数,运行过程中会使用ecx。所以在使用call的时候要格外小心loop loop1 //执行这条指令之后,ecx自动减1
在反汇编代码中常常包括
pushl %ebppushl %esp, %ebpandl $-16, %esp //初步认为是内存对齐,esp指向的内存地址是16的倍数,-16二进制表示的后四位是0000subl $32, %esp //为该函数的临时变量开辟斩空间
movl value, %ecxcmpl %ebx, %ecxcmova %ecx, %ebx //避免了分支
略去一大部分,关于优化的,回头再仔细研究