`
gk23
  • 浏览: 177210 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

函数调用的堆栈分析_汇编

阅读更多

函数调用堆栈,一个关键的或深或浅的问题 (转自pediy)

汇编初学者比较头痛的一个问题
////////////////////////////////////////////////////////////////////
比如 我们有这样一个C函数

1#include<stdio.h>
2long test(int a,int b)
3{
4     a = a + 1;
5     b = b + 100;
6    return a + b;
7}

8void main()
9{  
10   printf("%d",test(1000,2000));
11}

12


写成32位汇编就是这样

1;/**///////////////////////////////////////////////////////////////////////////////////////////////////////
2.386
3.module flat,stdcall            ;这里我们用stdcall 就是函数参数 压栈的时候从最后一个开始压,和被调用函数负责清栈
4option casemap:none             ;区分大小写
5
6includelib msvcrt.lib           ;这里是引入类库 相当于 #include<stdio.h>了       
7printf   PROTO C:DWORD,:VARARG   ;这个就是声明一下我们要用的函数头,到时候 汇编程序会自动到msvcrt.lib里面找的了
8                                 ;:VARARG 表后面的参数不确定 因为C就是这样的printf(const char *, );
9                                ;这样的函数要注意 不是被调用函数负责清栈 因为它本身不知道有多少个参数
10                                ;而是有调用者负责清栈   下面会详细说明
11.data
12szTextFmt   BYTE '%d',0         ;这个是用来类型转换的,跟C的一样,字符用字节类型
13a           dword 1000          ;假设
14b           dword 2000          ;处理数值都用双字 没有int 跟long 的区别
15
16;/**//////////////////////////////////////////////////////////////////////////////////////////
17.code
18
19_test proc A:DWORD,B:DWORD
20       push ebp
21       mov   ebp,esp
22       mov   eax,dword ptr ss:[ebp+8]
23       add   eax,1
24       mov   edx,dword ptr ss:[ebp+0Ch]
25       add   edx,100
26       add   eax,edx
27       pop   ebp      
28       retn 8
29_test endp
30
31_main proc
32       push dword ptr ds:b        ;反汇编我们看到的b就不是b了而是一个[*****]数字 dword ptr 就是我们在ds(数据段)把[*****]
33                                 ;开始的一个双字长数值取出来
34       push dword ptr ds:a        ;跟她对应的还有 byte ptr ****就是取一个字节出来 比如这样 mov   al,byte ptr ds:szTextFmt
35                                 ;就把 % 取出来 而不包括 d
36       call _test                  
37       push eax                   ;假设push eax的地址是×××××
38       push offset szTextFmt
39       call printf
40       add   esp,8
41       ret             
42_main endp
43end   _main
44
45;/**/////////////////////////////////////////////////////////////// 下面介绍堆栈的变化
46

首先要明白的是 操作堆栈段 ss 只能用 esp或ebp寄存器 其他的寄存器eax ebx edx等都不能够用 而 esp永远指向堆栈栈顶 ebp用来 在堆栈段

里面寻址
push 指令是压栈 ESP=ESP-4
pop   指令是出栈 ESP=ESP+4
我们假设main函数一开始堆栈定是 ESP=400
push dword ptr ds:b                  ;ESP-4=396 ->里面的值就是 2000 就是b的数值
push dword ptr ds:a                  ;ESP-4=392 ->里面的值就是 1000 就是a的数值
call test                            ;ESP-4=388->里面的数值是什么?这个太重要了 就是我们用来找游戏函数的原理所在。
                                                  里面的数值就是call test 指令下一条指令的地址->即push eax的地址×××××

到了test函数里面

push ebp                            ;ESP-4=384->里面保存了当前ebp的值 而不是把ebp清零
mov   ebp,esp                        ;这里ESP=384就没变化了,但是 ebp=esp=384,为什么要这样做呢 因为我们要用ebp到堆栈里面找参数
mov   eax,dword ptr ss:[ebp+8]       ;反汇编是这样的 想想为什么a就是[ebp+8]呢
                                    ;我们往上看看堆栈里地址392处就保存着a的值 这里ebp=384 加上8正好就是392了
                                    ;这样就把传递过来的1000拿了出来eax=1000
add   eax,1                          ;相当于 a+1了 eax=1001
mov   edx,dword ptr ss:[ebp+0Ch]     ;0Ch=12 一样道理这里指向堆栈的地址是384+12=396 就是2000了 edx=2000
add   edx,100                        ;相当于 b+100 edx=2100
add   eax,edx                        ;eax=eax+edx=1001+2100=3101 这里eax已经保存了最终的结果了
                                    ;因为win32汇编一般用eax返回结果 所以如果最终结果不是在eax里面的话 还要把它放到eax
                                    ;比如假设我的结果保存在变量nRet里面 最后还是要这样 mov eax,dword ptr nRet
pop   ebp                            ;ESP=384+4=388 而保存在栈顶384的值 保存到 ebp中 即恢复ebp原来的值                       
                                    ;因为一开始我们就把ebp的值压栈了,mov ebp,esp已经改变了ebp的值,这里恢复就是保证了堆栈平衡
retn   8                             ;ESP+8->396 这里retn是由系统调用的 我们不用管 系统会自动把EIP指针指向 原来的call的下一条指令
                                    ;由于是系统自动恢复了call那里的压栈所以 真正返回到的时候ESP+4就是恢复了call压栈的堆栈
                                    ;到了这个时候 ESP=400 就是函数调用开始的堆栈,就是说函数调用前跟函数调用后的堆栈是一样的
                                    ;这就是堆栈平衡
由于我们用stdcall上面retn 8就是被调用者负责恢复堆栈的意思了,函数test是被调用者,所以负责把堆栈加8,call 那里是系统自动恢复的

push eax                 ;ESP-4=396->里面保存了eax的值3101
                         ;上面已经看到了eax保存着返回值,我们要把它传给printf也是通过堆栈传递       
push offset szTextFmt    ;ESP-4=392->里面保存了szTextFmt的地址 也就是C里面的指针 实际上没有什么把字符串传递的,我们传的都是地址
                         ;无论是在汇编或C 所以在汇编里没有什么字符串类型 用最多的就是DWORD。嘿嘿游戏里面传递参数 简单多了
call printf              ;ESP-4=388->里面保存了下一条指令的地址
add   esp,8               ;ESP+8=400 恢复了调用printf前的堆栈状态
                         ;上面说了由于printf后面参数是:VARARG 这样的类型是有调用者恢复堆栈的 所以printf里面没有retn 8之类的指令
                         ;这是由调用者负责清栈 main是调用者 所以下面一句就是 add esp,8 把堆栈恢复到调用printf之前
                         ;而call printf那里的压栈 是由系统做的 恢复的工作也是系统完成 我们不用理 只是知道里面保存是返回地址就够了


ret                      ;main 函数返回 其他的事情是系统自动搞定 我们不用理 任务完成

分享到:
评论

相关推荐

    调用汇编代码_me.rar_C 调用 汇编_VC 中嵌入汇编_VC 汇编_汇编语言

    例如,可以直接操作内存和寄存器,避免不必要的函数调用开销,或者利用向量指令进行并行计算。 6. **调试汇编代码** 使用Visual Studio的调试器,可以单步执行汇编代码,查看和修改寄存器和内存状态,这对于理解和...

    函数调用堆栈变化分析[参考].pdf

    本文将深入探讨函数调用堆栈的变化分析,以帮助理解程序执行过程。 首先,我们需要了解函数调用的基本原理。在C语言中,函数调用通过栈来传递参数和存储局部变量。当一个函数被调用时,控制权转移给被调用函数,...

    通过EBP EIP来找函数调用堆栈

    总的来说,理解和利用EBP和EIP找寻函数调用堆栈是深入分析程序行为的关键技能,它可以帮助开发者和逆向工程师追踪代码执行路径,诊断问题,甚至对恶意软件进行分析。在实际应用中,结合其他调试工具和知识,如内存...

    反汇编深入分析函数调用

    ### 反汇编深入分析函数调用 #### 函数调用的基本概念 在计算机程序设计中,函数调用是程序中最基本的操作之一。一个函数可以完成特定的功能,并且可以通过调用该函数的方式重复使用这些功能,提高代码的复用性和可...

    Win32环境下函数调用的堆栈之研究

    在Win32环境下,函数调用涉及到堆栈的操作,这是理解程序执行和内存管理的关键部分。本文将深入探讨这个主题,特别是通过一个简单的C语言示例程序来解析其背后的汇编代码。 首先,我们要明白堆栈是计算机内存中的一...

    main函数调用子函数堆栈解析

    Main 函数调用子函数堆栈解析 在计算机程序设计中,函数调用是最基本的编程单元。函数调用会在内存中创建一个新的堆栈帧,该堆栈帧用于存放函数的实参、局部变量和返回地址等信息。在这个过程中,main 函数如何调用...

    函数的调用规则(__cdecl,__stdcall,__fastcall,__pascal)

    这意味着,在函数调用完成后,调用者需要手动清理压入堆栈的参数。 #### 关键特性: - **参数压栈顺序**:从右至左。 - **堆栈清理**:由调用者负责。 - **返回值**:对于返回类型为整型的情况,通常存储在 EAX ...

    易语言汇编动态堆栈调用源码.7z

    动态堆栈调用是编程中的一个重要概念,它涉及到函数调用过程中的参数传递、内存管理和控制流程恢复等核心知识。 首先,我们来理解一下动态堆栈。在计算机程序中,堆栈是一种特殊的数据结构,它遵循“后进先出”...

    汇编语言(ASM)从入门到精通.rar_学精通汇编_汇编_汇编 学习_汇编 语言 ASM从 入门 精通_汇编语言

    9. **链接和加载**:理解程序的链接和加载过程,以及如何处理外部函数调用和全局变量。 10. **调试技巧**:掌握使用汇编语言调试工具,如使用调试器跟踪代码执行,查找和修复错误。 11. **优化技巧**:学习如何...

    浅谈在linux kernel中打印函数调用的堆栈的方法

    在Linux内核开发和调试过程中,理解函数调用堆栈是非常关键的,它能帮助开发者追踪问题的根源,尤其是在处理内核崩溃或者异常时。本文将深入探讨如何在Linux内核中打印函数调用的堆栈。 首先,Linux内核提供了一个...

    易语言汇编动态堆栈调用源码

    动态堆栈调用涉及到如何在运行时管理和操作堆栈,以执行函数调用和返回。 在易语言汇编动态堆栈调用源码中,可能包括以下几个核心知识点: 1. **堆栈管理**:理解堆栈的工作原理,包括PUSH(压栈)和POP(出栈)...

    利用 RtlWalkFrameChain 回溯调用堆栈

    在Windows系统中,调试和分析程序行为时,回溯调用堆栈是一项非常重要的技能。本文将深入探讨如何利用RtlWalkFrameChain函数来实现这一功能,并结合NtQueryVirtualMemory查询模块信息,以帮助我们更好地理解代码执行...

    naoling.rar_clock_汇编 闹钟_汇编语言 闹钟_闹钟

    数据段用于存储常量和变量,代码段存放程序指令,而堆栈段则用于函数调用和临时数据的存储。 此外,与用户的交互可能需要利用到BIOS或操作系统提供的功能。例如,BIOS中断可以用来显示文本、读取键盘输入,而操作...

    编译原理课程设计之函数调用分析

    ### 编译原理课程设计之函数调用分析 在计算机科学领域中,了解不同平台上的函数调用约定是非常重要的,这不仅有助于理解程序的行为,也是深入学习编译原理的基础之一。本文将详细介绍Windows系统下的三种主要函数...

    huibianyuyan.rar_huibianyuyan_汇编语言_汇编语言学习

    4. **调用约定**:了解如何通过汇编语言调用函数,包括参数传递、返回值处理、堆栈操作等,这对于理解和编写系统级程序至关重要。 5. **链接与加载**:学习如何将汇编源代码编译成可执行文件,以及链接器如何将多个...

    易语言汇编动态堆栈调用

    2. **函数调用约定**:了解不同的函数调用约定,比如stdcall、cdecl等,它们决定了参数如何压入堆栈,以及由谁清理堆栈。 3. **汇编指令的使用**:熟练运用PUSH、POP以及其他汇编指令,如MOV、ADD、SUB等,来处理...

    8051C语言调用汇编详细注释源代码

    1. **函数调用约定**:C语言与汇编语言的交互主要通过函数调用来完成。在8051上,函数的参数传递和返回值的处理需要遵循一定的约定。例如,参数通常通过寄存器或堆栈传递,而返回值可能存储在ACC(累加器)或其他...

    gdb中有调用关系的函数在堆栈中的关系

    当程序运行时,函数调用会在堆栈中留下轨迹。堆栈由栈底(通常由`%ebp`寄存器标识)和栈顶(`%esp`寄存器标识)组成。每次函数调用,栈顶会向下移动,新的局部变量和函数调用信息被压入栈中。而函数返回时,栈顶会...

    Linux_Assembly_Language_Programming.rar_x86_操作系统_汇编 基于linux_汇编 操

    5. 子程序和堆栈:通过PUS和POP操作,以及栈指针ESP和EBP,可以实现函数调用和局部变量管理。 6. 内存管理:理解虚拟内存、页表和段的概念,以及如何通过指令访问和修改内存。 7. 输入/输出操作:通过IN和OUT指令...

Global site tag (gtag.js) - Google Analytics