环境

VC6.0环境

空函数反汇编

#include "stdafx.h"
void function(){ }
int main(int argc, char* argv[])
{
function();
printf("Hello World!\n");
return 0;
}

我们通过反汇编来分析这段空函数

###函数外部
10: function();
00401068 call @ILT+5(function) (0040100a)
11: printf("Hello World!\n");
0040106D push offset string "Hello World!\n" (0042201c)
00401072 call printf (004010a0)
00401077 add esp,4
12: return 0;
0040107A xor eax,eax

函数内部

4:    #include "stdafx.h"
5: void function(){
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
6:
7: }
00401038 pop edi
00401039 pop esi
0040103A pop ebx
0040103B mov esp,ebp
0040103D pop ebp
0040103E ret

分析函数

函数调用

00401048   call        @ILT+5(function) (0040100a)

函数内部

接着进到函数的内部

有了之前画堆栈的经验,我们不难看出,尽管我们的函数是个空函数,但其汇编代码依然完成了以下流程:

1.提升堆栈

2.保护现场

3.初始化提升的堆栈

4.恢复现场

5.返回

提升堆栈

00401010   push        ebp
00401011 mov ebp,esp
00401013 sub esp,40h

保护现场

00401026   push        ebx
00401027 push esi
00401028 push edi

初始化提升的堆栈

00401029   lea         edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]

恢复现场

00401028   pop         edi
00401029 pop esi
0040102A pop ebx
0040102B mov esp,ebp
0040102D pop ebp

PS:这里的mov esp,ebp就是降低堆栈,与前面的提升堆栈相对应,所以也属于恢复现场的一部分

返回

0040103E   ret

函数返回后



函数返回后不出意料地返回调用CALL地下一行语句,我们接着看

0040104D   xor         eax,eax

这里是将eax清零,注意到我们的语句为return 0 这里就是将eax作为返回值来传递

一般来说eax都是作为函数的返回值,但不绝对,有的函数返回值是存在内存里或其他情况,要具体情况具体分析

0040104F   pop         edi
00401050 pop esi
00401051 pop ebx

很明显,这里是在还原现场,别忘了我们的主程序main本身也是个函数,这是在还原main前保护的现场

接着往下走

0040107F   add         esp,40h
00401082 cmp ebp,esp
00401084 call __chkesp (00401120)

这里首先是将esp减少了40h,然后比较ebp和esp,最后再调用一个chesp函数从名称就不难看出 chkesp = check esp,检查esp,这个函数就是用来检查堆栈是否平衡

那么接下来

00401089   mov         esp,ebp
0040108B pop ebp

依旧是恢复现场

最后是

0040108C   ret
##总结空函数分析
我们可以看到,即便是一个空函数什么都没有做,但调用一个空函数所产生的汇编代码却不少
保护现场、恢复现场以及堆栈平衡的检查等等都没少,可谓麻雀虽小五脏俱全。

简单加法函数反汇编

#include "stdafx.h"
int Plus(int x ,int y)
{
retuen x+y;
}
int main(int argc, char* argv[])
{
//调用加法函数
Plus(1,2);
return 0;
}

加法函数的反汇编

11:       //调用加法函数
12: Plus(1,2);
00401068 push 2
0040106A push 1
0040106C call @ILT+0(Plus) (00401005)
00401071 add esp,8
13: return 0;
00401074 xor eax,eax
14: }
00401076 pop edi
00401077 pop esi
00401078 pop ebx
00401079 add esp,40h
0040107C cmp ebp,esp
0040107E call __chkesp (004010a0)
00401083 mov esp,ebp
00401085 pop ebp
00401086 ret

分析函数

00401068   push        2
0040106A push 1
0040106C call @ILT+0(Plus) (00401005)

结合前面的空函数分析,我们可以明显发现这里的函数调用环节,多了两个push就是将函数所需要的参数压入堆栈,这里的参数为2和1,注意压入的顺序是反着(由调用约定决定)

函数内部



提升堆栈保护现场初始化

提升堆栈、保护现场、初始化部分和空函数如出一辙,这里就不在赘述

00401020   push        ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]

实际执行主要的部分

7:      return x+y;
00401038 mov eax,dword ptr [ebp+8]
0040103B add eax,dword ptr [ebp+0Ch]

这里的[ebp+8] 就是我们前面压入的参数1,[ebp+c]就是前面压入的参数2

于是这两条语句其实就是

00401038   mov         eax,1
0040103B add eax,2

将1+2的结果保存到eax中,(此时eax又作为函数返回值的载体)

恢复现场和返回

接下来的内容和空函数一样了,恢复现场和返回,也不再赘述

0040103E   pop         edi
0040103F pop esi
00401040 pop ebx
00401041 mov esp,ebp
00401043 pop ebp
00401044 ret

函数返回后

函数返回后我们会发现与先前的空函数相比多了这一行代码

00401071   add         esp,8

这里是对应我们前面压入的两个参数1和2,压入参数后esp减少了8,这里我们函数调用结束后,就不再需要之前压入的两个参数了,于是将esp恢复到压入参数前,这其实也算是恢复现场,用来平衡堆栈,我们可以发现,这条语句是在我们call调用完毕后执行的平衡堆栈操作,所以这种操作也被称为堆栈外平衡

与之相对应的就是堆栈内平衡:即在call里面就把堆栈平衡好了

反汇编分析C语言的更多相关文章

  1. Linux下简单C语言小程序的反汇编分析

    韩洋原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 写在开始,本文为因为参加MOO ...

  2. 《C专家编程》第三章——分析C语言的声明

    前面一章我们已经说过C语言存在的一些问题和它晦涩的地方,让我们对这门神奇的语言有了更深的了解.现在这一章则集中精力来讨论C语言的声明,分为三块,首先是说明C语言声明晦涩难懂的原因和声明是如何形成的,其 ...

  3. C++写一个简单的解析器(分析C语言)

    该方案实现了一个分析C语言的词法分析+解析. 注意: 1.简单语法,部分秕.它可以在本文法的基础上进行扩展,此过程使用自上而下LL(1)语法. 2.自己主动能达到求First 集和 Follow 集. ...

  4. 基于Spark和SparkSQL的NetFlow流量的初步分析——scala语言

    基于Spark和SparkSQL的NetFlow流量的初步分析--scala语言 标签: NetFlow Spark SparkSQL 本文主要是介绍如何使用Spark做一些简单的NetFlow数据的 ...

  5. 以杨辉三角为例,从内存角度简单分析C语言中的动态二维数组

    学C语言,一定绕不过指针这一大难关,而指针最让人头疼的就是各种指向关系,一阶的指针还比较容易掌握,但一旦阶数一高,就很容易理不清楚其中的指向关系,现在我将通过杨辉三角为例,我会用四种方法从内存的角度简 ...

  6. 32 Profiling Go Programs 分析go语言项目

    Profiling Go Programs  分析go语言项目 24 June 2011 At Scala Days 2011, Robert Hundt presented a paper titl ...

  7. 浅析VS2010反汇编 VS 反汇编方法及常用汇编指令介绍 VS2015使用技巧 调试-反汇编 查看C语言代码对应的汇编代码

    浅析VS2010反汇编 2015年07月25日 21:53:11 阅读数:4374 第一篇 1. 如何进行反汇编 在调试的环境下,我们可以很方便地通过反汇编窗口查看程序生成的反汇编信息.如下图所示. ...

  8. 反汇编分析objc函数枢纽objc_msgSend

    在分析objc_msgSend之前,先来搞清楚另一个问题. 函数是什么?可能会答 void foo(void) {} 像这样就是一个函数.或者函数包括函数原型和函数定义,是一段执行某样功能的机器代码. ...

  9. objc反汇编分析,block函数块为何物?

    上一篇向大家介绍了__block变量的反汇编和它的伪代码,本篇函数块block,通常定义成原型(^){},它在反汇编中是什么东西. 我们先定义将要反汇编的例子,为减少篇幅例子采用non-arc环境. ...

  10. 通过分析反汇编还原 C 语言 if…else 结构

    让我们从反汇编的角度去分析并还原 C 语言的 if - else 结构,首先我们不看源代码,我们用 OllyDBG 载入 PE 文件,定位到 main 函数领空,如下图所示. 在图示中,我已经做好了关 ...

随机推荐

  1. linux发行版中的i386/i686/x86-64/的区别

    在yum上找32位的i386找不到,看到i686以为是64位呢,原来它也是32位啊 i686 只是i386的一个子集,支持的cpu从Pentium 2 (686)开始,之前的型号不支持. 备注: 1. ...

  2. Reshaper 代码清理工具

    reshaper是个好工具,能帮助我们提升开发效率,比如本文要介绍的全局代码清理功能. 如果你的VS安装了reshaper,可以通过Ctrl+E+C快捷键打开代码清理窗口. 代码清理,可以格式化多种文 ...

  3. .NET Core 离线生成 Tron 波场私钥和地址笔记

    NuGet 引入依赖库 PM> Install-Package Tron.Wallet.Net 随机生成私钥和对应的地址 using Tron.Wallet.Net; namespace Con ...

  4. python库Munch的使用记录

    开头 日常操作字典发现发现并不是很便利,特别是需要用很多 get('xxx','-') 的使用,就觉得很烦,偶然看到Kuls大佬公众号发布的一篇技术文有对 python munch库的使用, 使得字典 ...

  5. 2022-10-15:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。 你可以按 任意顺序 返回答案。 要求时间复杂度O(N)。 输入: nums = [1,1,1

    2022-10-15:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素. 你可以按 任意顺序 返回答案. 要求时间复杂度O(N). 输入: nums = [1,1,1 ...

  6. 2020-10-22:谈谈java中的LongAdder和LongAccumulator的相同点和不同点。

    福哥答案2020-10-22: 简单回答:相同点:都是多个单元操作.不同点:LongAdder相加,LongAccumulator自定义计算规则. 中级回答:相同点: LongAddr与LongAcc ...

  7. npm init vite@latest; 项目名字是abcde,选了vue-ts; cd abcde; npm install; npm run dev;浏览器访问,结果是空白的,这是怎么回事?

    npm init vite@latest 项目名字是abcde,选了vue-ts cd abcde npm install npm run dev 浏览器访问,结果是空白的,这是怎么回事? 后来发现是 ...

  8. Django认证流程源码及自定义 Backend

    Django自己的认证方法只能认证用户名和密码 user = authenticate(**credentials) # authenticate会自动校验用户名和密码 authenticate 源码 ...

  9. K8s Pod状态与容器探针

    1.pod的调度流程及常见状态 1.1.pod的调度流程 Pod创建过程如上图所示,首先用户向apiserver发送创建pod的请求,apiserver收到用于创建pod请求后,对应会对该用户身份信息 ...

  10. 生信服务器 | 更改 CentOS/RHEL 6/7 中的时区

    这几天在学习折腾 docker 的时候遇到一个很常见的问题,就是 run container 的时候发现大部分 image 默认使用的时间都是 UTC  (Universal Time Coordin ...