C函数和汇编

C代码

(编译工具gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609平台ubuntu i386 32位)

  1. int bar(int c , int d){
  2. int e = c +d;
  3. return e;
  4. }
  5. int foo (int a , int b){
  6. return bar(a,b);
  7. }
  8. int main (void){
  9. foo(2,3);
  10. return 0;
  11. }

gcc -S -masm=intel function_asm.c 生成一个intel风格的汇编代码文件

  1. .file "function_asm.c"
  2. .intel_syntax noprefix
  3. .text
  4. .globl bar
  5. .type bar, @function
  6. bar:
  7. .LFB0:
  8. .cfi_startproc
  9. push ebp
  10. .cfi_def_cfa_offset 8
  11. .cfi_offset 5, -8
  12. mov ebp, esp
  13. .cfi_def_cfa_register 5
  14. sub esp, 16
  15. mov edx, DWORD PTR [ebp+8]
  16. mov eax, DWORD PTR [ebp+12]
  17. add eax, edx
  18. mov DWORD PTR [ebp-4], eax
  19. mov eax, DWORD PTR [ebp-4]
  20. leave
  21. .cfi_restore 5
  22. .cfi_def_cfa 4, 4
  23. ret
  24. .cfi_endproc
  25. .LFE0:
  26. .size bar, .-bar
  27. .globl foo
  28. .type foo, @function
  29. foo:
  30. .LFB1:
  31. .cfi_startproc
  32. push ebp
  33. .cfi_def_cfa_offset 8
  34. .cfi_offset 5, -8
  35. mov ebp, esp
  36. .cfi_def_cfa_register 5
  37. push DWORD PTR [ebp+12]
  38. push DWORD PTR [ebp+8]
  39. call bar
  40. add esp, 8
  41. leave
  42. .cfi_restore 5
  43. .cfi_def_cfa 4, 4
  44. ret
  45. .cfi_endproc
  46. .LFE1:
  47. .size foo, .-foo
  48. .globl main
  49. .type main, @function
  50. main:
  51. .LFB2:
  52. .cfi_startproc
  53. push ebp
  54. .cfi_def_cfa_offset 8
  55. .cfi_offset 5, -8
  56. mov ebp, esp
  57. .cfi_def_cfa_register 5
  58. push 3
  59. push 2
  60. call foo
  61. add esp, 8
  62. mov eax, 0
  63. leave
  64. .cfi_restore 5
  65. .cfi_def_cfa 4, 4
  66. ret
  67. .cfi_endproc
  68. .LFE2:
  69. .size main, .-main
  70. .ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609"
  71. .section .note.GNU-stack,"",@progbits

objdump -M intel -S -d a.out 将生成的二进制文件用intel格式反汇编,我们关心的如下:

  1. 080483db <bar>:
  2. 80483db: 55 push ebp
  3. 80483dc: 89 e5 mov ebp,esp
  4. 80483de: 83 ec 10 sub esp,0x10
  5. 80483e1: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
  6. 80483e4: 8b 45 0c mov eax,DWORD PTR [ebp+0xc]
  7. 80483e7: 01 d0 add eax,edx
  8. 80483e9: 89 45 fc mov DWORD PTR [ebp-0x4],eax
  9. 80483ec: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
  10. 80483ef: c9 leave
  11. 80483f0: c3 ret
  12. 080483f1 <foo>:
  13. 80483f1: 55 push ebp
  14. 80483f2: 89 e5 mov ebp,esp
  15. 80483f4: ff 75 0c push DWORD PTR [ebp+0xc]
  16. 80483f7: ff 75 08 push DWORD PTR [ebp+0x8]
  17. 80483fa: e8 dc ff ff ff call 80483db <bar>
  18. 80483ff: 83 c4 08 add esp,0x8
  19. 8048402: c9 leave
  20. 8048403: c3 ret
  21. 08048404 <main>:
  22. 8048404: 55 push ebp
  23. 8048405: 89 e5 mov ebp,esp
  24. 8048407: 6a 03 push 0x3
  25. 8048409: 6a 02 push 0x2
  26. 804840b: e8 e1 ff ff ff call 80483f1 <foo>
  27. 8048410: 83 c4 08 add esp,0x8
  28. 8048413: b8 00 00 00 00 mov eax,0x0
  29. 8048418: c9 leave
  30. 8048419: c3 ret
  31. 804841a: 66 90 xchg ax,ax
  32. 804841c: 66 90 xchg ax,ax
  33. 804841e: 66 90 xchg ax,ax

函数有几个关键点调用函数、参数传递、返回值、调用后会返回计息执行下面的指令

其中参数和返回地址都是通过栈来实现的。平衡栈的动作根据不同的调用约定来实现的。C编译器符合

参数从右到左入栈

  1. int main (void){
  2. foo(2,3);
  3. return 0;
  4. }
  5. main函数调用foo函数(23)作为参数被传递
  6. 8048407: 6a 03 push 0x3
  7. 8048409: 6a 02 push 0x2
  8. 804840b: e8 e1 ff ff ff call 80483f1 <foo>
  9. 8048410: 83 c4 08 add esp,0x8
  10. 先将3入栈,再将2入栈
  11. call foo 函数(跳转到80483f1 <foo>执行)
  12. call指令
  13. call首先将它的下一条指令入栈(也就是8048410地址)将它调用的函数地址(foo函数地址复制到EIP寄存器当中)
  14. 下条指令CPU会执行EIP中地址,现在进入foo函数当中
  15. 080483f1 <foo>:
  16. 80483f1: 55 push ebp
  17. 80483f2: 89 e5 mov ebp,esp
  18. 80483f4: ff 75 0c push DWORD PTR [ebp+0xc]
  19. 80483f7: ff 75 08 push DWORD PTR [ebp+0x8]
  20. 80483fa: e8 dc ff ff ff call 80483db <bar>
  21. 80483ff: 83 c4 08 add esp,0x8
  22. 8048402: c9 leave
  23. 8048403: c3 ret
  24. foo调用了bar进入bar
  25. int bar(int c , int d){
  26. int e = c +d;
  27. return e;
  28. }
  29. int foo (int a , int b){
  30. return bar(a,b);
  31. }
  32. ================================================================================
  33. 080483db <bar>:
  34. 80483db: 55 push ebp
  35. 80483dc: 89 e5 mov ebp,esp
  36. 80483de: 83 ec 10 sub esp,0x10
  37. 80483e1: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
  38. 80483e4: 8b 45 0c mov eax,DWORD PTR [ebp+0xc]
  39. 80483e7: 01 d0 add eax,edx
  40. 80483e9: 89 45 fc mov DWORD PTR [ebp-0x4],eax
  41. 80483ec: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
  42. 80483ef: c9 leave
  43. 80483f0: c3 ret
  44. 其中
  45. 80483f1: 55 push ebp;保存ebp现场
  46. 80483f2: 89 e5 mov ebp,esp;将esp的值赋给ebp现在是参数、参数、返回地址、ebp现场。
  47. ebp是用来取调用者传过来的参数的也可以用来引用局部变量(在栈上分配)。为啥呢?因为esp总是指向栈顶,刚进入被调用函数的时候将esp赋值给ebp这个时候好找前面压过栈的参数。编译器好实现。
  48. EBP:高级语言通过 EBP 来引用堆栈中的函数参数和局部变量。除了高级编程,它不用于一般算术运算和数据传输。
  49. 取参数
  50. 80483e1: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
  51. 80483e4: 8b 45 0c mov eax,DWORD PTR [ebp+0xc]
  52. 所有函数都是通过eax来返回值的
  53. 80483ec: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
  54. leave是下面函数开始的时候的逆操作重新将ebp赋值给esp,然后pop ebp;ebp又恢复到维护foo函数的了,前面的数据还在栈上但是我已经不维护了,没有意义了。
  55. 80483db: 55 push ebp
  56. 80483dc: 89 e5 mov ebp,esp
  57. ret指令是call指令的逆操作,pop现在的esp的保存的数据给EIP(也就是foo函数调用bar函数前call保存的地址),然后esp值-4
  58. 其中EBP总是指向当前栈帧的栈底、返回值通过EAX传递。

在逆向中将重点放在函数的识别和参数的传递上是节省体力的方法,函数是一个程序模块,程序就是由这样一个模块一个模块组成的。

逆向--C函数和汇编的更多相关文章

  1. strlen函数的汇编实现分析

    [原创][老刘谈算法001]这位运算玩的真溜—strlen函数的汇编实现分析-『密码算法』-看雪安全论坛 https://bbs.pediy.com/thread-229243.htm

  2. 【黑客免杀攻防】读书笔记14 - 面向对象逆向-虚函数、MFC逆向

    虚函数存在是为了克服类型域解决方案的缺陷,以使程序员可以在基类里声明一些能够在各个派生类里重新定义的函数. 1 识别简单的虚函数 代码示例: #include "stdafx.h" ...

  3. Linux的.a、.so和.o文件 windows下obj,lib,dll,exe的关系 动态库内存管理 动态链接库搜索顺序 符号解析和绑定 strlen函数的汇编实现分析

    Linux的.a..so和.o文件 - chlele0105的专栏 - CSDN博客 https://blog.csdn.net/chlele0105/article/details/23691147 ...

  4. 【安卓逆向】ARM常见汇编指令总结

    跳转指令 B 无条件跳转 BL 带链接的无条件跳转 BX 带状态切换的无条件跳转 BLX 带链接和状态的无条件跳转 存储器与寄存器交互数据指令(核心) 存储器:主存和内存 寄存器中放的数据:可以是字符 ...

  5. iOS 逆向之ARM汇编

    最近对iOS逆向工程很感兴趣. 目前iOS逆向的书籍有: <Hacking and Securing IOS Applications>, <iOS Hacker's Handboo ...

  6. 从linux0.11中起动部分代码看汇编调用c语言函数

    上一篇分析了c语言的函数调用栈情况,知道了c语言的函数调用机制后,我们来看一下,linux0.11中起动部分的代码是如何从汇编跳入c语言函数的.在LINUX 0.11中的head.s文件中会看到如下一 ...

  7. 从汇编看c++成员函数指针(二)

    下面先看一段c++源码: #include <cstdio> using namespace std; class X { public: virtual int get1() { ; } ...

  8. 从汇编看c++中成员函数指针(一)

    下面先来看c++的源码: #include <cstdio> using namespace std; class X { public: int get1() { ; } virtual ...

  9. C语言与汇编的嵌入式编程:main中模拟函数的调用(两数交换)

    编写一个两数交换函数swap,具体代码如下: #include<stdio.h> void swap(int *p1,int *p2) { int temp; temp = *p1; *p ...

随机推荐

  1. cmd如何进入和退出Python编程环境?

    cmd里面进入python编译环境的方式: 安装Python之后需直接运行: python 即可进入Python开发环境 退出Python编译环境主要有三种方式: 1:输入exit(),回车 2:输入 ...

  2. 二 基于java动态数组手写栈

    package dataStucture2.stack; import dataStucture2.array.MyDynamicArray; /** * 基于动态数组手写栈 * 设计时,栈中仅栈顶对 ...

  3. Python 数组

    使用之前要先导入函数库  import numpy as np 数组名=np.zeros(数组大小,数据类型)    初始化为0值,这里的数据类型只能是数值类型,字符类型不能用 一.一维数组 impo ...

  4. 蓝桥杯java 迷宫

    0101010100101100100101011001011010010000100010101000001000100000101010010000100000001001100110100101 ...

  5. Java 8 Stream 的终极技巧——Collectors 操作

    1. 前言 昨天在 Collection移除元素操作 相关的文章中提到了 Collectors .相信很多同学对这个比较感兴趣,那我们今天就来研究一下 Collectors . 2. Collecto ...

  6. 简单模拟IOC容器:返回对象并能抛出异常

    本次要求:已知com.zzj.vo包下分别有Tiger.lion.Elephant三个Java源文件,请据此实现以下功能:①.自定义一个名为Component的注解,要求该注解只能用于类且代码运行时该 ...

  7. JAVA开源爬虫列表及简介

    本文列举了一些较为常用的JAVA开源爬虫框架: 1.Apache Nutch 官方网站:http://nutch.apache.org/ 是否支持分布式:是 可扩展性:中.Apache Nutch并不 ...

  8. (实例)Linux 内核添加exfat驱动

    背景: 由于exfat是常用的文件系统格式,而Linux由于版权的问题,没有在官方中添加有关的驱动. 但是 微软也同意开源了,所以比较新的 Linux 会支持这一块. 为了支持exfat的驱动,我们需 ...

  9. SimpleAliasRegistry

    SimpleAliasRegistry :主要使用map最为alias的缓存,并对接口AliasRegister进行实现. ==============================插曲====== ...

  10. upper_bound()和low_bound函数的基本使用和理解(转载,已获博主授权)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sdz20172133/article/details/80101838 前提:一个非降序列!!!!! ...