C++基础——调用约定分析

调用约定其实自己 Pwn 打了两年半了也是比较了解的,但是还是开个具体的笔记去记录吧。

CPU架构

常见的 CPU 架构有 x86 架构,ARM 架构和 MIPS 架构。不同的架构能执行的机器指令集是不一样的,比如 ARM 下面编译的程序指定是不能在 x86 架构下面去跑的。

x64 为一个简称,全称是 x86-64 架构,也就是 x86 架构下的 64 位版本,多见于我们的 PC,服务器,笔记本等等。

ARM 架构多见于一系列的水果产品和移动端设备。

MIPS 架构多见于路由器、智能设备等等。

处理器发展史

  • 1971年,Intel推出了世界上第一款微处理器4004,它是一个包含了2300个晶体管的4位cPU。
  • 1978年,Intel公司首次生产出16位的微处理器命名为i8086,同时还生产出与之相配合的数学协处理器i8087。
  • 1978年,Intel还推出了具有16位数据通道、内存寻址能力为1MB、最大运行速度8MHz的8086,并根据外设的需求推出了外部总线为8位的8088,从而有了IBM的XT机。
  • 1979年,Intel公司推出了8088芯片,它是第一块成功用于个人电脑的CPU。它仍旧是属于16位微处理器,内含29000个晶体管,时钟频率为4.77MHz,地址总线为20位,寻址范围仅仅是1MB内存。
  • 1981年8088芯片首次用于 IBM PC机中,开创了全新的微机时代。
  • 1982年,Intel推出80286芯片,它比8086和8088都有了飞跃的发展,虽然它仍旧是16位结构,但在CPU的内部集成了13.4万个晶体管,时钟频率由最初的6MHz逐步提高到20MHz。
  • 1985年Intel推出了80386芯片,它X86系列中的第一种32位微处理器,而且制造工艺也有了很大的进步。80386内部内含27.5万个晶体管,时钟频率从 12.5MHz 发展到 33MHz
  • 1989年,80486横空出世,它第一次使品体管集成数达到了120万个,并且在周期内能执行2条指令。
  • 2004,奔四处理器开始占据市场的主流地位。
  • 2006,AMD速龙64*2处理器占主流地位。
  • 2007,年酷睿四核第一次出现在市场上。
  • 2008年,intel诞生720与820处理器。
  • 2010年,I3与I5处理器诞生。
  • 2010年9月,全世界尚未发布的消息,amd六核已经开始供应。
  • 2011年,I7 980X处理器即将退市。
  • 2013年,Intel在 IvyBridge 发布后仅一年发布了新的Haswell架构。
  • 2015年,Intel发布了下一代产品Skylake架构。

汇编语言

计算机只能保存二进制的数据,因此它能直接理解的操作也仅仅是由二进制编码形成的指令,指令通常只能完成很小范围的一个操作,比如加减乘除,取数存数等等。但是二进制的编码不利于人类阅读,因此汇编语言诞生了,汇编语言与机器语言是硬编码,一一对应的一个关系。

汇编代码执行的过程:

16位

在这个位数的处理器下面我们有 8 个通用寄存器——ax,bx,cx,dx,di,si,bp,sp,一个指令指针寄存器——ip还有标志寄存器——flag,段寄存器——CS,SS,DS,ES

32位

对以上寄存器进行扩展(expand),在所有寄存器前面加上 e 变成了 32 位版本的寄存器,段寄存器无变化多了 FS 和 GS

64位

多了 8 个通用寄存器 r8-r15,然后所有的寄存器标识符由 32 位的 e 变为了 r。

函数调用约定

32位

一共有四种:

  • **__cdecl**:C/C++的基本调用约定,参数从右往左压入栈中,调用者清理栈。
  • **__stdcall**:win32 api 的调用约定,参数从右往左压入栈中,被调用者平衡栈,在返回的时候会使用 ret x 指令,这个 x 会在我们把返回地址弹出之后给 esp 加上 x 用于平衡栈。
  • **__fastcall**:快速传参,前两个参数给 ecx 和 edx 寄存器,若有多余,则剩余参数从右往左压入栈中,被调用者清理栈,与 stdcall 类似。
  • **__thiscall**:对象的传参使用,this 指针给到 ecx 寄存器,其它参数从右往左压入栈中,被调用者清理栈。

如果参数不固定,则调用约定只能是 **__cdecl,因为被调用者不能知道自己参数是多少个,只有调用者知道它参数给了几个,因此只能选择调用者清理栈的 __cdecl**。

64位

只有两种:

  • **__cdecl:前六个参数保存在 rdi,rsi,rdx,rcx,r8,r9** 中,若参数有多余,其余参数从右往左压入栈中,调用者清栈。
  • **__fastcall:前四个参数保存子 rcx,rdx,r8,r9** 中,若参数有多余,其余参数从右往左压入栈中,被调用者清栈。