二进制基础

二进制基础

从C语言到可执行程序

  • 源代码C Code —(编译)— 汇编代码 —(汇编)— 目标文件 —(链接)— 可执行程序

汇编语言是机器指令的助记语言,汇编语句和机器语句可一一对应

目标文件是源代码经过编译后没有被链接的那些中间文件,例如Linux下的.o文件

可执行程序是可被操作系统加载到内存并执行的文件,例如Linux系统下的ELF文件和Windows下的EXE文件

库函数是可复用的代码(包含函数或变量)集合,用于向其他程序提供代码

库分为静态库(.a)和动态库(.so):静态库中的代码和数据会完整的拷贝到可执行程序中,而动态库则需要在程序运行时通过动态链接的方式加载

动态库的目标是为了节约操作系统运行时的内存和文件的内存,如大量程序内都会用到的一些常规函数,不比出现多份,动态链接的机制可以帮助你复用这些代码

无论是何种方式,链接的作用都是修复文件之间的引用关系

机器指令的执行

  • 取指令 → 指令译码 → 指令执行 → 访存取数 → 结果写回
    在这里插入图片描述

CUP从内存中读取指令并对指令进行解码,去执行,并在最后写回。执行过程中会涉及内存中其他数据的读取,执行结果有可能涉及一些结果的写回

寄存器:CPU片上的一些存储单元(存储能力有限,但访问速度很快,一些不需要返回到内存中的中间值可以储存到寄存器中来减少CPU的访存次数提高运行速度)

一种先进后出的数据结构

被用于函数的局部内存管理

  • 保存局部变量
  • 保存函数的调用信息(如返回地址)

栈往低地址方向增长,栈底高地址,栈顶低地址

esp寄存器永远指向栈顶

栈操作

入栈,Push:esp = esp - 4(32位下是4字节)
出栈,Pop: esp = esp + 4
无论是存入数据还是取出数据,这些行为都是在栈顶进行的
  • 栈是从高地址向低地址增长,在每一层栈中会保存参数,返回地址,栈帧指针(ebp),被调用者保存的寄存器,局部变量这些数据。这里引入栈帧的概念

  • 栈帧(Stack Frame):在函数调用过程中产生的局部内存信息。当函数的调用深度越深,栈帧依次排布,一层一层逐渐向下排布。调试时可以根据一层一层栈帧的顺序来查看信息

在这里插入图片描述

常见指令

  • 数据迁移指令
mov:把数据从一个地方移到另一个地方。用于立即数,寄存器,内存三个地方拷贝数据
		立即数寻址:操作数包含在指令中,紧跟在操作码后,作为指令的一部分
        		mov al,5//把5放到al里面
        		mov eax,1000h
		寄存器寻址:操作数在寄存器中,指令指定寄存器
				mov ax,bx//把bx里的数据放到ax中
				mov ebp,esp
		直接内存寻址:操作数在内存中,指令直接指定内存地址
				mov ax,[2000h]//从2000h这个地址的操作数拿到ax中
		寄存器间接寻址:操作数在内存中,操作数的地址在寄存器中
				mov eax,[ebx]//把ebx指向的内存的数据存到eax中
		索引寻址:通过基址寄存器内容加上一个索引值来寻址啊内存中的数据
				mov ax,[di+100h]
		相对基址索引寻址:用基址寄存器+变址内存器的内容+偏移量 完成内容单元的寻址
				mov dh,[bx+si+10h]
		比例寻址变址:基址寄存器+变址寄存器的内容和比例因子的乘积 来完成内容单元的寻址
				mov eax,[ebx+4*ecx]
push:入栈操作,向栈顶存入一个数。
		push<reg32>==sub esp,4;mov [esp],<reg32>
		push<mem>
		push<con32>
pop:push的逆运算,从栈顶取出一个数存到其他地方
		pop<reg32>
		pop<mem>
lea:加载有效地址。
		lea目标,源
		lea<reg32>,<mem>
				lea eax,[var]//将地址var放入寄存器eax中
				lea edi,[ebx+4*esi]//等价于edi=ebx+4*esi
				#一些编译器会使用lea指令进行算数运算,因为速度更快
  • 算数与逻辑指令(用法与数据转移类似)
add/sub
inc/dec
imul/idiv
and/or/xor
not/neg
shl/shr
  • 控制转移指令
jmp:无条件跳转
j[condition]:条件跳转(if/else语句会生成这样的指令)
cmp:比较两个操作数
call/ret:函数调用/函数返回(和jmp区别是保存了返回的地址)

汇编指令的两种语法

IntelAT&T
mov eax, 8movl $8, %eax
mov ebx, 0ffffhmovl $0xfffff, %ebx
int 80hint $80
mov eax, [ecx]movl (%ecx), %eax
  • 操作数顺序不同
  • 寄存器记法不同
  • 立即数记法不同
  • 访存寻址计法不同
  • 操作码助记符不同

由于汇编指令与机器语言联系紧密,具体可在使用时检查手册

不同的CPU有不同的指令集,如果都是x86的话,指令集应该是一样

不同的汇编器有不同的伪指令,编写的汇编语言格式有可能不同

不同的操作系统,对操作系统调用的方法和过程有可能不同

调用约定

  • 什么是调用约定?

    函数调用约定,是指当一个函数被调用时,函数的参数会被传递给被调用的函数和返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和由谁平衡堆栈的,当然还有返回值

    • 实现层面(底层)的规范
    • 约定了函数之间如何传递参数
    • 约定了函数如何传递返回值
  • 常见x86调用约定

    • 调用者负责清理栈上的参数(Caller Clean-up)
      • cdecl
      • optlink
    • 被调用者负责清理栈上的参数(Callee Clean-up)
      • stdcall
      • fastcall

进程内存空间布局

  • 常见x86调用约定

    • 调用者负责清理栈上的参数(Caller Clean-up)
      • cdecl
      • optlink
    • 被调用者负责清理栈上的参数(Callee Clean-up)
      • stdcall
      • fastcall

进程内存空间布局

对于不同进程来说,进程内存空间布局是大致相同的。同一地址其实是虚拟内存,数据被存放到不同的物理内存当中并不会冲突,而虚拟内存与物理内存之间的对应工作由操作系统来完成(例:大部分程序代码从0x08048000开始存放代码)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值