c和汇编---函数
环境:VC++
作用:
函数是完成特定任务的独立程序代码单元
1、创建和使用函数
- 函数原型:声明函数是什么类型,指明函数的返回值和函数接收的参数类型,函数和变量一样,有多种类型,任何程序在使用函数之前都要声明该函数的类型
- 函数调用:表明在此处执行函数,执行到函数调用的语句时,程序会找到该函数的定义并执行其中的内容,执行完返回调用函数继续执行下一行
- 函数定义:详细说明函数要干啥
我们看看反汇编:
函数原型:
我们可以看出,函数原型这里没有生成机器码,这个是给编译器看得,告诉编译器这个函数的返回值和函数接收的参数类型,并在别处查看该函数类型,机器码是给CPU执行的,所以CPU执行到这里,不会干任何事情
函数调用:
函数调用之前,我们可以看到会先把参数存放到栈里面,也就是a,b的值,然后到00401005地址执行,这个地址有个jmp语句,会跳转到函数的定义出执行
函数定义:
从上面的程序我们可以看出,函数定义会先把esp存放到栈里面,然后把esp的值给ebp,接着开辟一个40h的栈,然后把ebx、 esi、 edi存放到栈里面,接着在一些连续地址存放0CCCCCCCCh,把这些做好后,再执行函数定义里的语句。
16: return a+b; 004010B8 8B 45 08 mov eax,dword ptr [ebp+8] 004010BB 03 45 0C add eax,dword ptr [ebp+0Ch]我们看看函数的最后
004010BE 5F pop edi 004010BF 5E pop esi 004010C0 5B pop ebx 004010C1 8B E5 mov esp,ebp 004010C3 5D pop ebp 004010C4 C3 ret函数的最后,把函数开始存放这些寄存器的内容又给了他们,ebp的值给esp,ebp恢复函数之前的ebp,接着返回。与函数调用的作用是一样的
函数的作用只完成特定任务,其他什么都没变,从函数调用到函数定义,最后返回,看起来是只对了a和b的值进行了操作,其他啥都没变
总结:
函数原型没有生成机器码,告诉编译器我的参数是那些和返回值是那些,函数调用会把参数先压入栈,接着执行call到一个地址执行,这个地址有一个jmp命令,会到函数定义出执行,函数定义会先把一些寄存器先压入栈,然后给一些内存赋值,在最后又会把这些寄存器给弹出,恢复成原值,执行ret命令,返回调用函数继续执行下一行。
2、传值和传址的区别
首先我们要认识几个小知识
- &运算符:取变量的存储地址
- *间接运算符:取存储在指针指向地址上的值,也可以用来声明指针
- 声明指针变量:类型 * 变量名,声明指针变量必须指定指针所指向变量的类型,因为不同变量类型占用不同的存储空间
传值:
8: sum1=add1(a,b); //函数调用 0040D786 8B 45 F8 mov eax,dword ptr [ebp-8] 0040D789 50 push eax 0040D78A 8B 4D FC mov ecx,dword ptr [ebp-4] 0040D78D 51 push ecx 0040D78E E8 7C 38 FF FF call @ILT+10(add) (0040100f) 0040D793 83 C4 08 add esp,8 0040D796 89 45 F4 mov dword ptr [ebp-0Ch],eax值传给eax寄存器,然后入栈
传址:
10: sum2=add2(&a,&b); //函数调用 0040D7AA 8D 45 F8 lea eax,[ebp-8] 0040D7AD 50 push eax 0040D7AE 8D 4D FC lea ecx,[ebp-4] 0040D7B1 51 push ecx 0040D7B2 E8 5D 38 FF FF call @ILT+15(add2) (00401014) 0040D7B7 83 C4 08 add esp,8 0040D7BA 89 45 F0 mov dword ptr [ebp-10h],eax把地址传给eax,然后入栈
我们知道传值不可以修改变量的值,而传址却可以,从汇编角度看,我们可以更加的清晰明白,传值只是将值传过去了,函数调用是去函数定义处执行,不知道变量在哪里,所以没办法修改,传地址到函数定义时就知道变量的地址在哪里了,所以能修改变量得内容
总结
- 上一篇: 摩尔庄园怎么在头顶聊天
- 下一篇: cobaltstrike生成一个原生c,