软件运行 函数调用 堆 栈 寄存器 汇编指令

一、堆、栈、寄存器

  win32下PE文件结构(Portable Execute),EXE,DLL,OCX,SYS文件都是用此结构:
这里写图片描述
主体结构部分常有段:

  1. 执行代码段: .text (Microsoft,已编译程序的机器代码)或 CODE(Borland)
  2. 数据段: .data(已初始化的全局变量和静态变量) 、.rdata 或 .bss(Microsoft,未初始化的全局变量和静态变量)、DATA(Borland)
  3. 资源段: .rsrc
  4. 导出表: .edata
  5. 导入表: .idata
  6. 调试信息段: .debug

进程的虚拟内存空间

这里写图片描述
  其中堆区和栈区程序运行时的内存分配区域。“共享内存映射区”用来给共享库(.so,.dll)分配地址的,它的地址增长方式同堆一样,从低到高。
  调用函数时,主调函数所拥有的局部变量等信息需要存储在特定区域,称为栈内存区。而利用new和malloc进行分配的内存区域称为堆内存。而程序在启动时,栈内存就被同一分配,不可再扩大。由于栈内存的限制,函数的递归深度也有上限。
堆(HEAP):
  地址从低到高。按照顺序分配。
  存储程序员自行分配的内存。malloc分配的内存只能用free来释放,而new分配的地址只能用delete来释放,如果new分配的是数组,则需要delete[ ]来释放。
  堆中内存由程序员分配和释放,若程序员不释放,程序结束时由系统收回。
栈(STACK):
  地址从高到低。(这样堆栈指针分别从最高处和最低处向中间移动。如果是同一方向的话,二者的起始位置就不好定。)由于空闲内存不一定连续,离散分配。
  函数中声明的任何局部变量(非静态)都是在栈中分配的。
  栈中内存由程序自动的分配和释放。
X86寄存器:
  X86有8个32位寄存器,以E开头。其中ESP是指向上图的栈指针,EBP是基址指针(栈底指针)。64位系统寄存器以R开头。
这里写图片描述
8086/8088内部的寄存器组分成8个通用寄存器,4个段寄存器,1个标志寄存器和1个指令指针寄存器(一共14个寄存器,分为专用与通用),它们均为16位。 分别如下:
通用寄存器有:
  数据寄存器(AX,BX,CX,DX);
  指针寄存器:
    堆栈指针寄存器SP;
    基址指针寄存器BP;
  变址寄存器:
    源变址(source index)寄存器SI;
    目标变址(destination index)寄存器DI;

SI通常指向源数组, DI通常指向目的数组. 通常用于成块地移动数据, 比如移动数组或结构体. SI和DI通常和DS和ES一起使用; DS:SI和ES:DI配对时通常用来执行一些字符串操作。

专用寄存器有:
  控制寄存器:
    指令指针IP(指示当前运行程序的当前指针);
    标志寄存器FLAGS;
  段(segment)寄存器:
    代码段(code segment)寄存器CS;
    堆栈段(stack segment)寄存器SS;
    数据段(data segment)寄存器DS;
    附加段寄存器ES;  

寻址方式:
  程序获取数据的方式无非三种:直接赋值、从寄存器取值、从内存中取值。寻址方式总共7种,后四种无非是对前三种进行了一些变化。
  1、立即寻址:第二操作数直接是数值而非地址。 mov eax,80H(80H直接数)
  2、寄存器寻址: add vard EBX(寄存器)
  3、直接寻址: mov AX, DS[12 34H](默认段超越前缀为DS,可指定其他。通过算法计算出地址)

  4、寄存器间接寻址: mov BX,[DI]
  5、寄存器相对寻址: mov BX,[SI+1000H]
  6、基址+变址: mov BX,[BX+SI]
  7、相对基址+变址: mov BX,[BX+100H+SI]

二、汇编指令简介

声明:
  其中DWORD PTR,BYTE PTR,WORD PTR作为一种标记表明直接数的字节数

指令解释实例
dd(DWORD PTR)双字节(4字节)dd 100 DUP(?) 声明双字节未初始化数组[100]
db(BYTE PTR)单字节mov WORD PTR [ebx], 2
dw(WORD PTR)双字节

指令:

指令解释实例
move将第二个操作数复制到第一个操作数。不能用于直接从内存复制到内存mov byte ptr [var], 2
push将操作数压入内存的栈中push eax
pop将ESP指示的地址中的内容出栈,然后将ESP值加4
add/sub将两个操作数相加,且将相加后的结果保存到第一个操作数中/相减add eax, 1
inc/dec自加/自减inc DWORD PTR [var]
imul整数相乘imul eax, [var] — eax→ eax * [var] imul esi, edi, 25 — ESI → EDI * 25
idividiv只有一个操作数,此操作数为除数,而被除数则为EDX:EAX中的内容(一个64位的整数),操作的结果有两部分:商和余数,其中商放在eax寄存器中,而余数则放在edx寄存器中idiv ebx
and/or/xor与/或/异或and eax, 0fH
not将操作数中的每一位取反not BYTE PTR [var]
neg取负neg eax — EAX → - EAX
shl/shr位移,第一个操作数表示被操作数,第二个操作数指示位移的数量shl eax, 1
je/jne/jz/jg/jge/jl/jle=/不等于/=0/>/>=/cmp eax, ebx jle done
cmp比较两个操作数的值,并根据比较结果设置机器状态字中的条件码
call子程序调用
retn子程序返回
lea将第二个操作数表示的地址载入到第一个操作数(寄存器)中lea eax, [var] — var指示的地址载入eax中

关于cmp:
  比较二数后设置flag的CF,ZF,OF,AF,PF位。
执行指令后
  ZF=1 这个简单,则说明两个数相等,因为zero为1说明结果为0
当无符号时:

   CF=1 则说明了有进位或借位,cmp是进行的减操作,故可以看出为借位,所以,此时oprd1<oprd2
   CF=0 则说明了无借位,但此时要注意ZF是否为0,若为0,则说明结果不为0,故此时oprd1>oprd2
当有符号时:

  SF=0,OF=0 则说明了此时的值为正数,没有溢出,可以直观的看出,oprd1>oprd2
  SF=1,OF=0 则说明了此时的值为负数,没有溢出,则为oprd1<oprd2
  SF=0,OF=1 则说明了此时的值为正数,有溢出,可以看出oprd1<oprd2
  SF=1,OF=1则说明了此时的值为负数,有溢出,可以看出oprd1>oprd2

三、函数调用

#include <stdio.h>
int func(int param1 ,int param2)
{
        int var1 = param1;
        int var2 = param2;

        printf("var1=%d,var2=%d",var1,var2);
        return var1;
}
int main(int argc, char* argv[])
{
        int result = func(1,2);
        return 0; 
}

栈过程:
这里写图片描述
1、从右到左压入main函数参数
2、压入返回地址
3、将函数func参数从右到左压入栈
4、压入返回地址
5、压入EBP地址
6、EBP赋值为ESP
7、执行赋值,压入var1,var2
8、调用函数printf
9、将func返回值保存到eax寄存器
10、局部变量出栈
11、EBP恢复原值
12、返回地址出栈,返回原来执行地址
13、参数param1,param2依次出栈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值