汇编的角度看C语言数据类型与调用约定

一、关于裸函数

1、什么是裸函数?

  void __declspec(naked) Function()
  {...}

  上面的函数调用时,为什么会出错?

  void __declspec(naked) Function()
  {

    __asm ret
  }

2、无参数无返回值的函数框架


  void __declspec(naked) Function()
  {
    __asm
  {
    push ebp
    mov ebp,esp
    sub esp,0x40
    push ebx
    push esi
    push edi
    lea edi,dword ptr ds:[ebp-0x40]
    mov eax,0xCCCCCCCC
    mov ecx,0x10
    rep stosd

    pop edi
    pop esi
    pop ebx
    mov esp,ebp
    pop ebp

    ret
  }
  }


3、有参数有返回值的函数框架

  int __declspec(naked) Function(int x,int y)
  {
    __asm
    {
      push ebp
      mov ebp,esp
      sub esp,0x40
      push ebx
      push esi
      push edi
      lea edi,dword ptr ds:[ebp-0x40]
      mov eax,0xCCCCCCCC
      mov ecx,0x10
      rep stosd

      mov eax,dword ptr ds:[ebp+8]
      add eax,dword ptr ds:[ebp+0xC]

      pop edi
      pop esi
      pop ebx
      mov esp,ebp
      pop ebp

      ret
    }
  }


4、带局部变量的函数框架


  int __declspec(naked) Function(int x,int y)
  {
    __asm
    {
      push ebp
      mov ebp,esp
      sub esp,0x40
      push ebx
      push esi
      push edi
      lea edi,dword ptr ds:[ebp-0x40]
      mov eax,0xCCCCCCCC
      mov ecx,0x10
      rep stosd
      mov dword ptr ds:[ebp-4],2
      mov dword ptr ds:[ebp-8],3

      mov eax,dword ptr ds:[ebp+8]
      add eax,dword ptr ds:[ebp+0xC]

      pop edi
      pop esi
      pop ebx
      mov esp,ebp
      pop ebp

      ret
    }
  }

 


5、常见的几种调用约定:

  调用约定           参数压栈顺序                平衡堆栈

  __cdecl               从右至左入栈                 调用者清理栈 (外平栈)
  
    __stdcall                         从右至左入栈                 自身清理堆栈

    __fastcall           EDX/ECX传送前两个                       自身清理堆栈
                    剩下:从右至左入栈       


1int __cdecl Plus(int a, int b)    
{    
    return a+b;    
}    

    push 2    
    push 1    
    call @ILT+15(Plus) (00401014)    
    add esp,8    

 


2int __stdcall Plus(int a, int b)    
    {    
        return a+b;   //函数里边平衡堆栈,内平栈   
    }        

    push 2    
    push 1    
    call @ILT+10(Plus) (0040100f)   
  函数内部: 
  ret 8 

 

3int __fastcall Plus(int a, int b)    
 {    
    return a+b;    
 }    

mov edx,2    
mov ecx,1    
call @ILT+0(Plus) (00401005)    

函数内部

ret (放到寄存器中的,不需要平衡堆栈)

 

fastcall:如果是传入参数大于两个,没有意义,因为前两个寄存器EDX,ECX来传参数,
      后边的依旧用push的方式传入参数,没有意义
4int __fastcall Plus4(int a, int b,int c,int d) {   return a+b+c+d; } push 4 push 3 mov edx,2 mov ecx,1 call @ILT+5(Plus) (0040100a)
函数内部:
ret 8

如何判断函数传入几个参数:

  push 了几个不准确

  call完了之后

  add esp,XX也不准确

  最好的方法就是,跟进去看看,用了哪个寄存器,用了哪个参数,然后结合堆栈平衡代码,结合参数调用处的代码。

 

 

二、C语言的数据类型


1、C语言中的数据类型:


  a.基本类型:整数类型,浮点类型

  b.构造类型:数组类型、结构体类型、共用体(联合)类型

  c.指针类型

  d.空类型(void)
  
2、学习数据类型的三个要素:

  1、存储数据的宽度

  2、存储数据的格式

  3、作用范围(作用域)


3、整数类型:char short int long

  

 

 

 

 

 



 

 



4、浮点类型:float double

  float和double在存储方式上都是遵从IEEE的规范的

  float的存储方式如下图所示:

  

       符号位                                指数部分 (指数-1)                                                                                    尾数部分 (补0)

      

  double的存储方式如下图所示:
  

  符号位            指数部分                     尾数部分

  将一个float型转化为内存存储格式的步骤为:

  1、先将这个实数的绝对值化为二进制格式

  2、将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。

  3、从小数点右边第一位开始数出二十三位数字放入第22到第0位。

  4、如果实数是正的,则在第31位放入“0”,否则放入“1”。

  5、如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。

  6、如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。
  如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。

 

  举例说明:

  8.25转成浮点存储

  整数部分8转成2进制

  8/2 = 4 0
  4/2 = 2 0
  2/2 = 1 0
  1/2 = 0 1
  3 - 1 = 2 10
  小数部分0.25转成二进制:
  Float = 6
  0.25*2 = 0.5 0
  0.5*2 = 1.0 1 1 8 23
  0 10000010 00001000000000000000000
  8.25用二进制表示可表示为1000.01

  1000.01 = 1.00001 *2的3次方 小数点向左移动3位 指数为3


  1 10000010 00001000000000000000000 0100 0001 0000 0100 0000 0000 0000 0000 = 41040000


  -8.25转成浮点数是多少呢? 1100 0001 0000 0100 0000 0000 0000 0000 = C1040000
  C1040000

  0.25转成浮点存储

  整数部分0 0

  0.25 * 2 = 0.5 0
  0.5*2 = 1.0 1

  0.01 = 1.0 * 2的2次方 右移动 注意:向右移动2个位 指数为-2


  0 01111101 00000000000000000000000



5、英文字符存储

  char x = 'A';

  char y = 65;
  A 10101
  参见ASCII编码 B 11111
  C 11110
  , 101011

6、中文字符存储


char* x = "啊";

char* y = "北";

参见GB2312-80编码

这种编码存储在的问题

 

posted @ 2020-12-26 18:35  heyhx  阅读(220)  评论(0编辑  收藏  举报