c语言·其二
1.代码区:可读可执行
2.堆栈:(参数,局部变量,临时数据)
3.堆:(动态申请,大小可变)可读可写
5.常量区:只读
变量
变量的声明
全局变量
int a,b,c; //全局变量的声明
void Fun()
{
a = 10; //全局变量的赋值
b = 20;
c = a;
}
局部变量
void Fun()
{
int a,b,c; //局部变量的声明
a = 10; //局部变量的赋值
b = 20;
c = a;
}
全局变量
特点:
1、在程序编译完以后就已经预留的空间,预留的大小由类型决定,且位置不会发生变化.
2、全局变量如果没有给的初始值,默认为0.
3、全局变量可以在任何其他的函数里面进行读写
如图:
等于:MOV 寄存器,byte/word/dword ptr ds:[0x12345678]
4、多个函数使用同一个全局变量,只要exe程序不结束,里面将一直存储最后一次修改的值.
注:全局变量就是所谓的基址
局部变量
局部变量的特点:
1、局部变量在程序编译完成后并没有分配固定的地址.
2、在所属的方法没有被调用时,局部变量并不会分配内存地址,只有当所属的程序被调用了,才会在
堆栈中分配内存.
3、当局部变量所属的方法执行完毕后,局部变量所占用的内存将变成垃圾数据.局部变量消失.
4、局部变量只能在方法内部使用,函数A无法使用函数B的局部变量.
5、局部变量的反汇编识别: [ebp-4].....
6.使用前要赋初值
参数
传进来给函数里用
确定参数
一般情况
步骤一:观察调用处的代码
push 3
push 2
push 1
call 0040100f //3个参数
步骤二:找到平衡堆栈的代码继续论证
call 0040100f
add esp,0Ch
或者函数内部
ret 4/8/0xC/0x10 //1/2/3/4个参数
寄存器传参
举例
1.
push ebx
push eax
mov ecx,dword ptr ds:[esi]
mov edx,dword ptr ds:[edi]
push 45
push 33
call 函数地址 函数地址未知
2、函数调用处的代码无法查看.
00401050 push ebp
00401051 mov ebp,esp
00401053 sub esp,48h
00401056 push ebx
00401057 push esi
00401058 push edi
00401059 push ecx
0040105A lea edi,[ebp-48h]
0040105D mov ecx,12h 将12h赋给ecx(不属于参数
00401062 mov eax,0CCCCCCCCh
00401067 rep stos dword ptr [edi]
00401069 pop ecx
0040106A mov dword ptr [ebp-8],edx 将edx里的值赋给[ebp-8]中(发现前面没有给edx赋值,所以edx存了一个参数
0040106D mov dword ptr [ebp-4],ecx
00401070 mov eax,dword ptr [ebp-4]
00401073 add eax,dword ptr [ebp-8]
00401076 add eax,dword ptr [ebp+8]
00401079 mov [g_x (00427958)],eax
0040107E pop edi
0040107F pop esi
00401080 pop ebx
00401081 mov esp,ebp
00401083 pop ebp
00401084 ret 4 有一个压栈的参数
2.if汇编
00401030 push ebp
00401031 mov ebp,esp
00401033 sub esp,40h
00401036 push ebx
00401037 push esi
00401038 push edi
00401039 lea edi,[ebp-40h]
0040103C mov ecx,10h
00401041 mov eax,0CCCCCCCCh
00401046 rep stos dword ptr [edi]
——————————————————————————————————————堆栈图准备部分
00401048 mov eax,dword ptr [ebp+8] 参数x
0040104B cmp eax,dword ptr [ebp+0Ch] 参数y
0040104E jle 00401059 ≤跳转不执行
00401050 mov ecx,dword ptr [ebp+8] x的值赋值给ecx
00401053 mov dword ptr [004225c4],ecx 全局变量(宽度4个字节)ecx赋值给全局变量
——————————————————————————————————————————平衡堆栈
00401059 pop edi
0040105A pop esi
0040105B pop ebx
0040105C mov esp,ebp
0040105E pop ebp
0040105F ret
公式:
1、不考虑ebp、esp
2、只找给别人赋值的寄存器
eax/ecx/edx/ebx/esi/edi
3、找到以后追查其来源,如果,该寄存器中的值
不是在函数内存赋值的,那一定是传进来的参数.
公式一:寄存器 + ret 4 = 参数个数
公式二:寄存器 + [ebp+8] +[ebp+0x] = 参数个数
循环
if与else
1.if....else
int x=1;
int y=2
void one()
{
if(x>y) //如果
{
printf("%d\n",x);
}
else //否则
{
printf("%d\n",y);
}
}
2.else if
3.嵌套
汇编(if
1.
cmp:eax里的值减去[ebp+0Ch],根据结果后只修改标志寄存器
jle:当eax里的值<或者=[ebp+0Ch]的时候跳转(不执行if里的代码
2.
jl:小于才跳转
jge:≥
( else
004010B0 push ebp
004010B1 mov ebp,esp
004010B3 sub esp,44h
004010B6 push ebx
004010B7 push esi
004010B8 push edi
004010B9 lea edi,[ebp-44h]
004010BC mov ecx,11h
004010C1 mov eax,0CCCCCCCCh
004010C6 rep stos dword ptr [edi]
———————————————————————————————————————————————————————
004010C8 mov eax,[004225c4] 全局变量
004010CD mov dword ptr [ebp-4],eax 局部变量(将全局变量赋值给局部变量)
004010D0 mov ecx,dword ptr [ebp+8] 参数x
004010D3 cmp ecx,dword ptr [ebp+0Ch] 参数y
004010D6 jle 004010e6 ≤跳转,不执行
—————————————————————————————————————————————
004010D8 mov edx,dword ptr [ebp+8]
004010DB add edx,dword ptr [ebp-4] x+全局变量
004010DE mov dword ptr [004225c4],edx
004010E4 jmp 004010f1
int a=r;
if(x>y)
{
r=x+a;
}—————————————————————————————————————————
004010E6 mov eax,dword ptr [ebp+0Ch]
004010E9 add eax,dword ptr [ebp-4]
004010EC mov [004225c4],eax
————————————————————————————————————————————————
004010F1 pop edi
004010F2 pop esi
004010F3 pop ebx
004010F4 mov esp,ebp
004010F6 pop ebp
004010F7 ret
do....while
格式
do..while语句的语法
do
{
//执行代码
}while(表达式)
例子
void Fun(int x,int y)
{
do
{
printf("%d\n",x);
x++;
}while(x>y);
}
汇编
0040B7F8 mov eax,dword ptr [ebp+8]
0040B7FB push eax
0040B7FC push offset string "%d\n" (0042001c)
0040B801 call printf (004010f0)
0040B806 add esp,8
0040B809 mov ecx,dword ptr [ebp+8] //x++
0040B80C add ecx,1 //
0040B80F mov dword ptr [ebp+8],ecx //while(x>y)
0040B812 mov edx,dword ptr [ebp+8] //
0040B815 cmp edx,dword ptr [ebp+0Ch] //
0040B818 jg 0040b7f8
总结
1、根据条件跳转指令所跳转到的地址,可以得到循环语句块的起始地址。
2、根据条件跳转指令所在的地址,可以得到循环语句块的结束地址。
3、条件跳转的逻辑与源码相同。
while
语法
while(表达式)
{
//执行代码
}
例子
void Fun(int x,int y)
{
while(x<y)
{
printf("%d\n",x);
x++;
}
}
汇编
0040B7F8 mov eax,dword ptr [ebp+8]
0040B7FB cmp eax,dword ptr [ebp+0Ch]
0040B7FE jge 0040b81c //
0040B800 mov ecx,dword ptr [ebp+8] //
0040B803 push ecx //
0040B804 push offset string "%d\n" (0042001c) //
0040B809 call printf (004010f0) //
0040B80E add esp,8 //
0040B811 mov edx,dword ptr [ebp+8] //
0040B814 add edx,1 //
0040B817 mov dword ptr [ebp+8],edx //
0040B81A jmp 0040b7f8 //
0040B81C pop edi //
0040B81D pop esi //
0040B81E pop ebx //
总结:
1、根据条件跳转指令所跳转到的地址,可以得到循环语句块的结束地址;
2、根据jmp 指令所跳转到的地址,可以得到循环语句块的起始地址;
for(表达式1;表达式2;表达式3)
for循环的执行次序:
表达式1
表达式2
执行的代码(大括号里面的内容)
表达式3
————————————————
表达式2 //如果表达式2成立
执行的代码(大括号里面的内容)
表达式3
————————————————
表达式2 //如果表达式2成立
执行的代码(大括号里面的内容)
表达式3
———————————————
表达式2 //如果不成立
跳出循环
汇编
0040B7F8 mov eax,dword ptr [ebp+8] //i=x
0040B7FB mov dword ptr [ebp-4],eax //
0040B7FE jmp 0040b809 //
0040B800 mov ecx,dword ptr [ebp-4] //
0040B803 add ecx,1 //
0040B806 mov dword ptr [ebp-4],ecx //
0040B809 mov edx,dword ptr [ebp-4] //
0040B80C cmp edx,dword ptr [ebp+0Ch] //
0040B80F jge 0040b824 //
0040B811 mov eax,dword ptr [ebp-4] //
0040B814 push eax //
0040B815 push offset string "%d\n" (0042001c) //
0040B81A call 004010f0 //
0040B81F add esp,8 //
0040B822 jmp 0040b800 //
0040B824 pop edi //
0040B825 pop esi //
0040B826 pop ebx //
总结:
1、第一个jmp 指令之前为赋初值部分.
2、第一个jmp 指令所跳转的地址为循环条件判定部分起始.
3、判断条件后面的跳转指令条件成立时跳转的循环体外面
4、条件判断跳转指令所指向的地址上面有一个jmp jmp地址为表达式3的起始位置