Windbg查看函数调用过程中的内存布局

news2024/12/24 11:35:15

     我们在分析问题的时候经常会需要查看进程的栈和帧中的值,下面我们就用一个简单的例子来分析一下这个过程。

源代码:

#include <iostream>
int add(int a, int b)
{
   return a + b;
}


int main()
{
    int a, b;
    a = 3;
    b = 4;
    int ret = add(a, b);
    std::cout << "Result:"<<ret<<"\t\n";
}

生成的汇编代码:

main 函数的汇编代码

 10 004c1000 55              push    ebp
   10 004c1001 8bec            mov     ebp,esp
   10 004c1003 83ec0c          sub     esp,0Ch
   12 004c1006 c745f803000000  mov     dword ptr [ebp-8],3
   13 004c100d c745fc04000000  mov     dword ptr [ebp-4],4
   14 004c1014 8b45fc          mov     eax,dword ptr [ebp-4]
   14 004c1017 50              push    eax
   14 004c1018 8b4df8          mov     ecx,dword ptr [ebp-8]
   14 004c101b 51              push    ecx
   14 004c101c e83f000000      call    statckTest!add (004c1060)
   14 004c1021 83c408          add     esp,8
   14 004c1024 8945f4          mov     dword ptr [ebp-0Ch],eax
   15 004c1027 6840314c00      push    offset statckTest!GS_ExceptionPointers+0x8 (004c3140)
   15 004c102c 8b55f4          mov     edx,dword ptr [ebp-0Ch]
   15 004c102f 52              push    edx
   15 004c1030 6844314c00      push    offset statckTest!std::_Fake_alloc+0x1 (004c3144)
   15 004c1035 a16c304c00      mov     eax,dword ptr [statckTest!_imp_?coutstd (004c306c)]
   15 004c103a 50              push    eax
   15 004c103b e8e0020000      call    statckTest!std::operator<<<std::char_traits<char> > (004c1320)
   15 004c1040 83c408          add     esp,8
   15 004c1043 8bc8            mov     ecx,eax
   15 004c1045 ff1540304c00    call    dword ptr [statckTest!_imp_??6?$basic_ostreamDU?$char_traitsDstdstdQAEAAV01HZ (004c3040)]
   15 004c104b 50              push    eax
   15 004c104c e8cf020000      call    statckTest!std::operator<<<std::char_traits<char> > (004c1320)
   15 004c1051 83c408          add     esp,8
   16 004c1054 33c0            xor     eax,eax
   16 004c1056 8be5            mov     esp,ebp
   16 004c1058 5d              pop     ebp
   16 004c1059 c3              ret

这段汇编代码是一个函数的一部分,它执行了以下操作:

  1. 函数开始

    • push ebp 和 mov ebp,esp:保存当前的栈指针(esp)到基指针(ebp),并设置ebp为当前栈指针的值。这是函数调用的标准开头,用于设置函数的局部变量和参数的基础地址。
  2. 局部变量分配

    • sub esp,0Ch:从栈指针中减去12(0Ch是十六进制的12),为局部变量分配空间。
  3. 初始化局部变量

    • mov dword ptr [ebp-8],3:将整数值3存储到ebp-8指向的局部变量中。
    • mov dword ptr [ebp-4],4:将整数值4存储到ebp-4指向的局部变量中。
  4. 函数调用准备

    • mov eax,dword ptr [ebp-4] 和 mov ecx,dword ptr [ebp-8]:将两个局部变量的值分别加载到eaxecx寄存器中。
    • push eax 和 push ecx:将这两个值压入栈中,作为add函数的参数。
  5. 函数调用

    • call statckTest!add (004c1060):调用名为add的函数,该函数可能位于004c1060地址处。
  6. 处理函数返回值

    • mov dword ptr [ebp-0Ch],eax:将add函数的返回值存储在ebp-0Ch指向的局部变量中。
  7. 异常处理准备

    • push offset statckTest!GS_ExceptionPointers+0x8:将GS_ExceptionPointers+0x8的地址压入栈中,可能是为了异常处理。
    • push dword ptr [ebp-0Ch]:将先前存储的add函数的返回值压入栈中。
    • push offset statckTest!std::_Fake_alloc+0x1:将std::_Fake_alloc+0x1的地址压入栈中,这可能与异常处理相关。
  8. 输出到控制台

    • mov eax,dword ptr [statckTest!_imp_?coutstd (004c306c)]:获取std::cout的地址,并将其存储在eax寄存器中。
    • push eax:将std::cout的地址压入栈中。
    • call statckTest!std::operator<<<std::char_traits<char> > (004c1320):调用std::operator<<来输出一个值到std::cout
    • 同样的操作重复了一次,可能是为了输出两个不同的值。
  9. 清理栈

    • add esp,8:调整栈指针,清理之前压入栈的参数。
  10. 函数结束

    • xor eax,eax:将eax寄存器清零,这通常是函数返回前的一个操作。
    • mov esp,ebp 和 pop ebp:恢复栈指针和基指针。
    • ret:从函数返回。

add 函数的汇编代码 

   18 004c1060 55              push    ebp
   18 004c1061 8bec            mov     ebp,esp
   19 004c1063 8b4508          mov     eax,dword ptr [ebp+8]
   19 004c1066 03450c          add     eax,dword ptr [ebp+0Ch]
   20 004c1069 5d              pop     ebp
   20 004c106a c3              ret

这段汇编代码是一个简单的函数,它执行以下操作:

  1. push ebp 和 mov ebp,esp:保存当前的栈指针(esp)到基指针(ebp),并设置ebp为当前栈指针的值。这是函数调用的标准开头,用于设置函数的局部变量和参数的基础地址。

  2. mov eax,dword ptr [ebp+8]:从ebp+8的地址处取出一个双字(32位)值,并将其存储到eax寄存器中。这通常意味着函数接收一个整数参数,该参数在栈上的位置是ebp+8

  3. add eax,dword ptr [ebp+0Ch]:从ebp+0Ch的地址处取出另一个双字值,并将其加到eax寄存器中的当前值上。这通常意味着函数接收另一个整数参数,该参数在栈上的位置是ebp+0Ch

  4. pop ebp:恢复基指针ebp的原始值。

  5. ret:从函数返回。由于eax寄存器现在包含两个参数的和,这通常意味着函数返回这两个参数的和作为结果。

函数调用过程的栈帧分布:

从main函数的汇编中我们可以看到调用函数add 的过程, 下面两句给局部变量在栈中分配内存

   12 004c1006 c745f803000000  mov     dword ptr [ebp-8],3
   13 004c100d c745fc04000000  mov     dword ptr [ebp-4],4

从中可以看到a 放在ebp-8 和b 放在ebp -4 这个地方。 


eax=765712f0 ebx=00c9d000 ecx=00000000 edx=00000000 esi=011fa0c8 edi=011fea68
eip=004c1006 esp=00f6fd70 ebp=00f6fd7c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

从中可以看到ebp=00f6fd7c,esp=00f6fd70,分配了12个字节的空间。

0:000> t
eax=00000004 ebx=00c9d000 ecx=00000000 edx=00000000 esi=011fa0c8 edi=011fea68
eip=004c1017 esp=00f6fd70 ebp=00f6fd7c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
statckTest!main+0x17:
004c1017 50              push    eax
0:000> t
eax=00000004 ebx=00c9d000 ecx=00000000 edx=00000000 esi=011fa0c8 edi=011fea68
eip=004c1018 esp=00f6fd6c ebp=00f6fd7c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
statckTest!main+0x18:
004c1018 8b4df8          mov     ecx,dword ptr [ebp-8] ss:002b:00f6fd74=00000003
0:000> t
eax=00000004 ebx=00c9d000 ecx=00000003 edx=00000000 esi=011fa0c8 edi=011fea68
eip=004c101b esp=00f6fd6c ebp=00f6fd7c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
statckTest!main+0x1b:
004c101b 51              push    ecx
0:000> t
eax=00000004 ebx=00c9d000 ecx=00000003 edx=00000000 esi=011fa0c8 edi=011fea68
eip=004c101c esp=00f6fd68 ebp=00f6fd7c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
statckTest!main+0x1c:
004c101c e83f000000      call    statckTest!add (004c1060)
0:000> dd 002b:00f6fd40
002b:00f6fd40  00f6fd60 7648abda 00000000 004c310c
002b:00f6fd50  00000000 004c1716 764a5406 00000001
002b:00f6fd60  00f6fd80 004c1726 00000003 00000004
002b:00f6fd70  00c9d000 00000003 00000004 00f6fdc4
002b:00f6fd80  004c1822 00000001 011fa0c8 011fea68
002b:00f6fd90  a7b8e129 004c18aa 004c18aa 00c9d000
002b:00f6fda0  00000000 00000000 00000000 00f6fd90
002b:00f6fdb0  00000000 00f6fe20 004c1fb5 a7022ab5

调用add 函数之前会把参数压入栈中

   14 004c1014 8b45fc          mov     eax,dword ptr [ebp-4]
   14 004c1017 50              push    eax
   14 004c1018 8b4df8          mov     ecx,dword ptr [ebp-8]
   14 004c101b 51              push    ecx
   14 004c101c e83f000000      call    statckTest!add (004c1060)

004c101c e83f000000      call    statckTest!add (004c1060)这条指令调用之前的main 函数堆栈

 进入add 函数之后的内存布局

002b:00f6fd40  00f6fd60 7648abda 00000000 004c310c
002b:00f6fd50  00000000 004c1716 764a5406 00000001
002b:00f6fd60  00f6fd7c 004c1021 00000003 00000004
002b:00f6fd70  00c9d000 00000003 00000004 00f6fdc4
002b:00f6fd80  004c1822 00000001 011fa0c8 011fea68
002b:00f6fd90  a7b8e129 004c18aa 004c18aa 00c9d000
002b:00f6fda0  00000000 00000000 00000000 00f6fd90
002b:00f6fdb0  00000000 00f6fe20 004c1fb5 a7022ab5

 

 函数的返回地址是 14 004c1021 83c408          add     esp,8 正好是add 函数调用完成之后下一条汇编的地址。

   14 004c1021 83c408          add     esp,8
   14 004c1024 8945f4          mov     dword ptr [ebp-0Ch],eax

上面两条是内存回收和将add 函数的返回值存贮在002b:00f6fd70 这个地址上面。

002b:00f6fd40  00f6fd60 7648abda 00000000 004c310c
002b:00f6fd50  00000000 004c1716 764a5406 00000001
002b:00f6fd60  00f6fd7c 004c1021 00000003 00000004
002b:00f6fd70  00000007 00000003 00000004 00f6fdc4
002b:00f6fd80  004c1822 00000001 011fa0c8 011fea68
002b:00f6fd90  a7b8e129 004c18aa 004c18aa 00c9d000
002b:00f6fda0  00000000 00000000 00000000 00f6fd90
002b:00f6fdb0  00000000 00f6fe20 004c1fb5 a7022ab5

完成之后函数的ebp 和esp 恢复到之前的状态。

本文简单介绍了c 语言函数调用过程中的内存布局,在我们分析dump文件或者实时查看内存布局的时候可以使用本文的方法。

备注:如果生成的exe 无法添加函数断点请在工程配置中设置如下(vistual studio)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1574350.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】快速排序法 Leetcode 148. 排序链表

题目 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [4,2,1,3] 输出&#xff1a;[1,2,3,4] 代码 第一次使用朴素快速排序&#xff0c;基准值为头节点值&#xff0c;居然超时了&#xff0c;然后…

pymilvus创建IVF_FLAT向量索引

索引简介 索引的作用是加速大型数据集上的查询。 目前&#xff0c;向量字段仅支持一种索引类型&#xff0c;即只能创建一个索引。 milvus支持的向量索引类型大部分使用近似最近邻搜索算法(ANNS,approximate nearest neighbors search) 。ANNS 的核心思想不再局限于返回最准确…

什么是数据治理?你都了解吗?

在当今数字化时代&#xff0c;数据已成为企业重要的战略资产。有效管理数据对于企业提高运营效率、降低成本、做出更好的决策至关重要。数据治理作为一种重要的管理方法&#xff0c;可以帮助企业确保数据的质量、安全、合规性和有效利用。 一、数据治理的定义与重要性 近日&a…

2024.4.6-day11-CSS 背景和精灵图

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 作业2024.4.6学习笔记1 背景2 背景图片3 CSS 精灵图 作业 <!DOCTYPE html&…

C语言初阶—9函数

函数的声明 &#xff08;main函数前&#xff09;----告诉有一个函数 格式&#xff1a; 类型 函数名&#xff08;参数&#xff09;&#xff1b; 函数的声明 放到头文件add.c 函数的定义 ----创建函数----放到add.c 格式&#xff1a;类型 函数名(参数) { 语句项; } 在文…

【Qt 学习笔记】使用两种方式实现helloworld

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 使用两种方式实现helloworld 文章编号&#xff1a;Qt 学习笔记 / 05 …

Ais client LA8295 camx KMD enqueue dequeue

Ais client 调用qcarcam_s_buffers 设置buffer 进行enqueue buf PUBLIC_API qcarcam_ret_t qcarcam_s_buffers(qcarcam_hndl_t hndl, qcarcam_buffers_t* p_buffers) {return camera_to_qcarcam_result(ais_s_buffers(hndl, p_buffers)); }qcarcam_s_buffers(input_ctxt->q…

卷积神经网络实战

构建卷积神经网络 卷积网络中的输入和层与传统神经网络有些区别&#xff0c;需重新设计&#xff0c;训练模块基本一致 1.首先读取数据 - 分别构建训练集和测试集&#xff08;验证集&#xff09; - DataLoader来迭代取数据 # 定义超参数 input_size 28 #图像的总尺寸28*28…

使用Springfox Swagger实现API自动生成单元测试

目录 第一步&#xff1a;在pom.xml中添加依赖 第二步&#xff1a;加入以下代码&#xff0c;并作出适当修改 第三步&#xff1a;在application.yaml中添加 第四步&#xff1a;添加注解 第五步&#xff1a;运行成功之后&#xff0c;访问相应网址 另外&#xff1a;还可以导出…

JavaScript代码小挑战

题目如下&#xff1a; 朱莉娅和凯特正在做一项关于狗的研究。于是&#xff0c;她们分别询问了 5 位狗主人他们的狗的年龄&#xff0c;并将数据存储到一个数组中&#xff08;每人一个数组&#xff09;。目前&#xff0c;她们只想知道一只狗是成年狗还是小狗。如果狗的年龄至少为…

算力在现实生活中的多方面应用!

算力在现实生活中的应用是多方面的&#xff0c;它已经成为推动现代社会发展的重要力量。 以下是算力在不同领域中的具体应用&#xff1a; 立即免费体验&#xff1a;https://gpumall.com/login?typeregister&sourcecsdn #分布式云服务#算力#GpuMall#GpuMall智算云#训练#…

【AI-3】Transformer

Transformer? Transformer是一个利用注意力机制来提高模型训练速度的模型&#xff0c;因其适用于并行化计算以及本身模型的复杂程度使其在精度和性能上都要高于之前流行的循环神经网络。 标准的Transformer结构如下图所示&#xff08;图来自知乎-慕文&#xff09;&#xff0c…

特征提取算法

特征提取算法 0. 写在前边1. Harris算法1.1 写在前面1.2 Harris算法的本质1.3 Harris算法的简化 2. Harris3D2.1 Harris3D算法问题定义2.2 Harris3D with intensity2.3 Harris3D without intensity 3. ISS特征点的应用 0. 写在前边 本篇将介绍几种特征提取算法&#xff0c;特征…

C++从入门到精通——类对象模型

类对象模型 前言一、如何计算类对象的大小问题 二、类对象的存储方式猜测对象中包含类的各个成员代码只保存一份&#xff0c;在对象中保存存放代码的地址只保存成员变量&#xff0c;成员函数存放在公共的代码段问题总结 三、结构体内存对齐规则四、例题结构体怎么对齐&#xff…

3D桌面端可视化引擎HOOPS Visualize如何实现3D应用快速开发?

HOOPS Visualize是一个开发平台&#xff0c;可实现高性能、跨平台3D工程应用程序的快速开发。一些主要功能包括&#xff1a; 高性能、以工程为中心的可视化&#xff0c;使用高度优化的OpenGL或DirectX驱动程序来充分利用可用的图形硬件线程安全的C和C#接口&#xff0c;内部利用…

mysql索引相关知识点

1. 索引是什么&#xff1f; 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分)&#xff0c;它们包含着对数据表里所有记录的引用指针。 索引是一种数据结构。数据库索引&#xff0c;是数据库管理系统中一个排序的数据结构&#xff0c;以协助快速查询、更新数…

【Java业务需求解决方案】分布式锁应用详情,多种方案选择,轻松解决,手把手操作(非全数字编码依次加一问题)

背景&#xff1a; 现有编码格式为业务常量数字&#xff0c;每新增一条数据在基础上1,比如&#xff1a; 文件类型1 编码为ZS01 文件类型1下文件1 编码为ZS0101 文件类型1下文件2 编码为ZS0102 文件类型2 编码…

Vue - 3( 15000 字 Vue 入门级教程)

一&#xff1a;初识 Vue 1.1 收集表单数据 收集表单数据在Vue.js中是一个常见且重要的任务&#xff0c;它使得前端交互变得更加灵活和直观。 Vue中&#xff0c;我们通常使用v-model指令来实现表单元素与数据之间的双向绑定&#xff0c;从而实现数据的收集和更新。下面总结了…

Springboot引入swagger

讲在前面&#xff1a;在spring引入swagger时&#xff0c;由于使用的JDK、Spring、swagger 的版本不匹配&#xff0c;导致启动报错&#xff0c;一直存在版本依赖问题。所以在此声明清楚使用版本。JDK 1.8、Spring boot 2.6.13、 Swagger 2.9.2。 引入maven依赖 <dependency&…

【Canvas与艺术】绘制金色Brand Award品牌嘉奖奖章

【成果图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>金色Brand Award品牌嘉奖</title><style type"text/…