一、关于裸函数
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传送前两个 自身清理堆栈
剩下:从右至左入栈
1、int __cdecl Plus(int a, int b) { return a+b; } push 2 push 1 call @ILT+15(Plus) (00401014) add esp,8
2、int __stdcall Plus(int a, int b) { return a+b; //函数里边平衡堆栈,内平栈 } push 2 push 1 call @ILT+10(Plus) (0040100f)
函数内部:
ret 8
3、int __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的方式传入参数,没有意义
4、int __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编码
这种编码存储在的问题