0x01.函数

这节就先讲函数吧,函数大致分为四种类型

1、无参数、无返回值的函数格式

void 函数名()
{
//代码段
}
void Hello()
{
printf("Hello World!");
}

2、有参数,无返回值的函数格式

void 函数名(参数类型 参数名,参数类型 参数名)
{
//代码段
}
void add(int a,int b)
{
int c = a + b;
printf("当前的值:%d",%c);
}

3、无参数,有返回值的函数格式

返回值类型 函数名()
{
//代码段
return 具体的值,需要和返回值类型 匹配
}
int sub()
{
int c = a - b;
return c;
}

4、有参数、有返回值的函数格式

返回值类型 函数名(参数类型 参数名,参数类型,参数名)
{
//代码段
return 具体的值,必须与返回值类型匹配
}
int ret(int b,int c)
{
int c = b * c;
return c;
}

0x02. 函数的反汇编

a.空函数

#include <stdio.h>

void add()
{ } int main()
{
add();
return 0;
}

运行窗口之后,进入反汇编窗口

可以看到这边call了 add函数,这个函数的地址是0x0DF135Ch,这时候我们再f11进去

这边下一步又跳到了jmp(jmp指令是无条件跳转)这边,可以看到它是在调用add的参数了,进行执行了

跳到了 0x0DF3840

终于到了函数内部,这时候我们脑里想象出一个图,

这时候可以发现call的时候会跳到 jmp那边,一跳完后才是真正的函数内部

1)开辟空间

我们先看前三条

00DF3840  push        ebp
00DF3841 mov ebp,esp
00DF3843 sub esp,0C0h

看不懂没关系,我们可以把随便一个东西拉到od中,然后调一下

栈窗口在这边,我们看一下怎么回事

(补充:push是压栈,把数据压进去、pop是出栈,把东西拿出来。拿出来的话是按照先进后出,后进先出的顺序,之后汇编部分会仔细讲。esp寄存器是栈顶,ebp是栈低)

可以看出现在esp是 00D3FAA8,ebp是00D3FAB4

这时候我们把刚刚在visual studio的那段反汇编写进去试试

这边是把ebp栈低压入栈中

压入之后栈顶和栈低就一样了,这时候再把栈顶(esp)的地址,给栈低(ebp),由于一样,就没啥变化

然后在esp减掉 c0

我们计算一下,栈低减栈顶等于c0,说明正确。其实这边就是在栈中开辟一块空间,方便存储数据

2)保护数据恢复数据

00DF3840  push        ebp        开辟空间
00DF3841 mov ebp,esp
00DF3843 sub esp,0C0h 00DF3849 push ebx 保护数据
00DF384A push esi
00DF384B push edi
...
...
00DF385E pop edi 恢复数据
00DF385F pop esi
00DF3860 pop ebx

保护和恢复其实是一个对应的,我就写出来

就类似我们这种web狗,一般打了站要试头像能不能拿shell,就会先把这个管理员的头像保存一下,然后随便搞。之后恢复一下现场

可以看出先压入的数据在下面,最后压入的数据在上面

恢复数据的时候用pop,然后先把edi弹出去,就是后进先出的意思

3)缓冲区填充数据

00DF384C  lea         edi,[ebp-0C0h]
00DF3852 mov ecx,30h
00DF3857 mov eax,0CCCCCCCCh
00DF385C rep stos dword ptr es:[edi]

我们单步走一下,发现ebp-c0的地址给edi了,发现这个地址其实就是栈低的值

(补充:lea是传送地址,mov是传送数据)

ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。

然后把30h给了ecx,这边的30是十六进制的,不是10进制的,最后把CCCCCCCC给eax

rep指令的目的是重复其上面的指令.ECX的值是重复的次数.

STOS指令的作用是将eax中的值拷贝到ES:EDI指向的地址.

最后发现其实就是把其他地址的值变为8个C而已

最后再恢复数据,恢复数据和保存数据差不多,遵守后进先出即可

4)恢复堆栈

00DF3861     mov         esp,ebp
00DF3863 pop ebp

瞬间回到了之前,最后再把原本的栈低恢复

回到了之前的样子了,执行完函数后就用ret返回

b.简单的函数功能

我们再看一下函数内有数据又会是怎么样?

#include <stdio.h>

void add()
{
int c = 10 + 10;
} int main()
{
add();
return 0;
}

发现就多了一行,因为我们代码中就已经把值写死了,我们传个有参数看看怎么样

#include <stdio.h>

void add(int a,int b)
{
int c = a + b;
} int main()
{
add(2,3);
return 0;
}

这时候我们可以发现这边先是把3压入栈中,再把2压入栈中,接下来就和上面空函数一样,jmp完之后就是真正的函数内部地址了

00333840  push        ebp
00333841 mov ebp,esp
00333843 sub esp,0CCh 00333849 push ebx
0033384A push esi
0033384B push edi 0033384C lea edi,[ebp-0CCh]
00333852 mov ecx,33h
00333857 mov eax,0CCCCCCCCh
0033385C rep stos dword ptr es:[edi] int c = a + b;
0033385E mov eax,dword ptr [a]
00333861 add eax,dword ptr [b]
00333864 mov dword ptr [c],eax
} 00333867 pop edi
00333868 pop esi
00333869 pop ebx 0033386A mov esp,ebp
0033386C pop ebp
0033386D ret

每一个部分我都加了个换行,这时候可以发现是在填充数据之后,再进行加减法

先是把a的值给eax,然后使用add指令,给eax寄存器加个b的值

(补充:add exp1,exp2

add指令:把exp2的值给exp1

最后我们是定义了用c接受传参,所以它这边把算完后结果的eax给c

0x03.局部变量与全局变量

#include <stdio.h>

int Request = 10;
void add()
{
int b = 8;
int c = b + Request;
printf("%d", c);
} int main()
{
add();
return 0;
}

这时候我们反汇编看一下request的定义

再看一下把变量放到函数内部是什么样子的



0025385E mov dword ptr [Request],0Ah

把0a放到request地址上面,也就是赋值给request

这时候可以总结全局变量的特点了:

1、全局变量在程序编译完成后地址就已经确定下来了,只要程序启动,全局变量就已经存在了,启动后里面是否有值取决于声明时是否给定了初始值,如果没有,默认为0

2、全局变量的值可以被所有函数所修改,里面存储的是最后一次修改的值.

3、全局变量所占内存会一直存在,直到整个进程结束.

4、全局变量的反汇编识别:

MOV 寄存器,byte/word/dword ptr ds:[0x12345678]

通过寄存器的宽度,或者byte/word/dword 来判断全局变量的宽度

局部变量特点:

1、局部变量在程序编译完成后并没有分配固定的地址.

2、在所属的方法没有被调用时,局部变量并不会分配内存地址,只有当所属的程序被调用了,才会在堆栈中分配内存.

3、当局部变量所属的方法执行完毕后,局部变量所占用的内存将变成垃圾数据.局部变量消失.

4、局部变量只能在方法内部使用,函数A无法使用函数B的局部变量.

5、局部变量的反汇编识别:

[ebp-4]

[ebp-8]

[ebp-0xc]

0x04.函数参数分析

先看一下调用约定

void __cdecl Function1(int x, int y, int z)
{
g_r = x + y + z;
}
void __stdcall Function2(int x, int y, int z)
{
g_r = x + y + z;
}
void __fastcall Function3(int x, int y, int z)
{
g_r = x + y + z;
}

然后这边我们要判断看是否存在几个参数,用Function2来试,因为一般默认都是使用stdcall

断点下到函数这个地方,然后我们直接去反汇编窗口看



可以看到这边是压入3个参数,分别是从右往左压入



这时候就和前面讲的差不多了,也看的懂了。通过push压入的值来判断函数参数几个,我们也可以仔细看一下ret的值,也可以得到参数值

或者看下main函数里面的堆栈平衡也可以判断参数,由于都是int类型,4字节,在汇编中是dword类型。0ch其实就是十进制的12。

12 / 4 = 3。这时候也可以算出参数

所以判断函数参数数量有三种方法(补充,main函数也是函数,也是需要开辟空间,保存数据等...)

1、查看push入栈的参数

00D142BE  push        3
00D142C0 push 2
00D142C2 push 1
00D142C4 call Function2 (0D1139Dh)

2、查看堆栈平衡

3、进入函数查看返回值



查看函数内部返回值的值

C语言函数的学习的更多相关文章

  1. R语言函数化学习笔记6

    R语言函数化学习笔记 1.apply函数 可以让list或者vector的元素依次执行一遍调用的函数,输出的结果是list格式 2.sapply函数 原理和list一样,但是输出的结果是一个向量的形式 ...

  2. R语言函数化学习笔记3

    R语言函数化学习笔记3 R语言常用的一些命令函数 1.getwd()查看当前R的工作目录 2.setwd()修改当前工作目录 3.str()可以输出指定对象的结构(类型,位置等),同理还有class( ...

  3. R语言函数化学习笔记4

    条件语句和循环语句 当你说话时候用到了如果,此时条件出现了 举个条件函数的例子 sign_t<-function(x){ if(x>0){ return(1) }else if(x< ...

  4. R语言函数话学习笔记5

    使用Tidyverse完成函数化编程 (参考了家翔学长的笔记) 1.magrittr包的使用 里面有很多的管道函数,,可以减少代码开发时间,提高代码可读性和维护性 1.1 四种pipeline 1.1 ...

  5. C语言(函数)学习之strstr strcasestr

    C语言(函数)学习之[strstr]&[strcasestr]一.strstr函数使用[1]函数原型char*strstr(constchar*haystack,constchar*needl ...

  6. IOS学习笔记07---C语言函数-printf函数

    IOS学习笔记07---C语言函数-printf函数 0 7.C语言5-printf函数 ------------------------- ----------------------------- ...

  7. IOS学习笔记06---C语言函数

    IOS学习笔记06---C语言函数 --------------------------------------------  qq交流群:创梦技术交流群:251572072              ...

  8. 学习LoadRunner之C语言函数

    学习LoadRunner之C语言函数 Action() { /*strchr和strrchr的区别*/ /* char *strTest1="citms citms"; char ...

  9. 【C语言学习笔记】C语言函数执行成功时,返回1和返回0,究竟哪个好?

    基本上,没有人会将大段的C语言代码全部塞入 main() 函数,更好的做法是按照复用率高,耦合性低的原则,尽可能的将代码拆分不同的功能模块,并封装成函数.C语言代码的组合千变万化,因此函数的功能可能会 ...

随机推荐

  1. dubbo学习(六)dubbo管理控制台

    管理控制台的安装与使用 下载地址:https://github.com/apache/dubbo-admin/tree/master(包含管理控制台和监控中心) PS:  下载前要选择master分支 ...

  2. PyCharm2018.3.5下载和安装及永久破解详解(成功案例)

    靓仔靓女,你是否在网上找了很多的方法都破解不了PyCharm,是有原因的!无论什么编程工具都不要下载近一到/两年内的版本,人家即把网上的一些破解方法修复了,而且还在测试阶段,不稳定就完事了我装的是20 ...

  3. 实现select下拉框的无限加载(懒加载)

    在实际开发中我们有时无法避免select下拉功能数据过大导致页面卡顿(如在我在一次迭代中有一个select项接口返回了5000多条数据).用户体验差!结合实际开发给出了3个解决方案: 方案1.sele ...

  4. RabbitMQ小记(一)

    1.了解消息中间件 消息中间件,Message Queue Middleware,简称MQ,又称消息对列或消息对列中间件,利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式 ...

  5. tensorflow(一):基础

    一.张量 1.张量的概念 在TensorFlow中,所有的数据都通过张量的形式来表示.从功能的角度,张量可以简单理解为多维数组,零阶张量表示标量(scalar),也就是一个数:一阶张量为向量(vect ...

  6. HTML & CSS & JavaScript 从一个表格到一个灰阶颜色表 02

    工具1:HBuilder X 1.9.9.20190522 工具2:火狐浏览器 67.0.4 (64 位) 其实,我想使用表格,做一个这样的颜色表,如下图所示: 例 3:我们参照上图,基于上一个例子, ...

  7. apt-get 安装软件时出现:“文件尺寸不符” 问题

    报错信息 命中:1 http://packages.deepin.com/deepin panda InRelease 命中:2 http://linux.teamviewer.com/deb sta ...

  8. js获取foreach循环选中的值

    一,循环出来的值,通过checked选中,获取到value值 二,定义一个空数组,用push将数据保存在数组里面 以上操作便可以进行虎丘选中的值了

  9. DM9000网卡驱动分析(转)

    s3c6410自带的DM9000网卡驱动也是基于platform设备模型. 其定义的设备资源在arch/arm/mach-s3c64xx/mach-smdk6410中.有网卡的resource res ...

  10. 物联网wifi模块

    物联网wifi模块 物联网wifi模块 是上海卓岚推出的MQTT+JSON转Modbus物联网WiFi核心模块.支持以MQTT的方式连接云端服务器,支持可以界面话配置,自主采集Modbus仪表/645 ...