动态内存管理练习题的反汇编代码分析(底层)

目录

1.练习题回顾

2.反汇编代码

3.分析

lea指令的作用

1.给普通指针赋值

反汇编显示

2.给结构体指针赋值

反汇编显示

mov 指令的作用

1.取普通指针指向地址的值(等价为C语言的*)

反汇编显示

2.取结构体指针指向地址里的值

反汇编显示

3.总结->的作用

4.回到本文分析

细节分析mov ecx,0Ch

解释

对rdi重新恢复为0的解释


1.练习题回顾

求下列代码的执行结果

#include <stdio.h>
char* GetMemory(void)
{char p[] = "hello world";return p;
}void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}int main()
{Test();return 0;
}

 

2.反汇编代码

VS2022+x64+debug

#include <stdio.h>
char* GetMemory(void)
{push        rbp  push        rsi  push        rdi  sub         rsp,110h  lea         rbp,[rsp+20h]  lea         rdi,[rsp+20h]  mov         ecx,0Ch  mov         eax,0CCCCCCCCh  rep stos    dword ptr [rdi]  mov         rax,qword ptr [__security_cookie (07FF68B18D000h)]  xor         rax,rbp  mov         qword ptr [rbp+0E8h],rax  lea         rcx,[__2D923C74_FileName@c (07FF68B192008h)]  call        __CheckForDebuggerJustMyCode (07FF68B181375h)  nop  char p[] = "hello world";lea         rax,[p]  lea         rcx,[string "hello world" (07FF68B18ACA8h)]  mov         rdi,rax  mov         rsi,rcx  mov         ecx,0Ch  rep movs    byte ptr [rdi],byte ptr [rsi]  return p;lea         rax,[p]  
}mov         rdi,rax  lea         rcx,[rbp-20h]  lea         rdx,[__xt_z+1E0h (07FF68B18AC80h)]  call        _RTC_CheckStackVars (07FF68B181311h)  mov         rax,rdi  mov         rcx,qword ptr [rbp+0E8h]  xor         rcx,rbp  call        __security_check_cookie (07FF68B1811B8h)  lea         rsp,[rbp+0F0h]  pop         rdi  pop         rsi  pop         rbp  ret  
............
void Test(void)
{push        rbp  push        rdi  sub         rsp,108h  lea         rbp,[rsp+20h]  lea         rcx,[__2D923C74_FileName@c (07FF68B192008h)]  call        __CheckForDebuggerJustMyCode (07FF68B181375h)  nop  char* str = NULL;mov         qword ptr [str],0  str = GetMemory();call        GetMemory (07FF68B18106Eh)  mov         qword ptr [str],rax  printf(str);mov         rcx,qword ptr [str]  call        printf (07FF68B18119Fh)  nop  
}lea         rsp,[rbp+0E8h]  pop         rdi  pop         rbp  ret  
............
int main()
{push        rbp  push        rdi  sub         rsp,0E8h  lea         rbp,[rsp+20h]  lea         rcx,[__2D923C74_FileName@c (07FF68B192008h)]  call        __CheckForDebuggerJustMyCode (07FF68B181375h)  nop  Test();call        Test (07FF68B18118Bh)  nop  return 0;xor         eax,eax  
}lea         rsp,[rbp+0C8h]  pop         rdi  pop         rbp  ret  

3.分析

在深入讲解之前,补充没有讲过的指令:lea,以及lea和mov指令的对比

8086的指令集是这样说的:

792a3a284e214c0da0e9d7c060d72ebe.png

lea指令的全称:load effective address,加载有效地址(常用于C语言的&取地址)

d33b3afc3237453b93c828eb90298957.png

mov指令的全称:move

lea指令的作用

1.给普通指针赋值

复制以下代码到VS2022以x86+debug环境调试

int main()
{int a = 0;//映射(映射要加粗)到一个内存地址int* p = &a;return 0;
}


反汇编显示

    int a = 0;
mov         dword ptr [a],0  int* p = &a;
lea         eax,[a]  
mov         dword ptr [p],eax

备注:称为普通指针的原因是因为和结构体指针做区别
这里的lea         eax,[a]
[a]代表a的地址,lea的含义:将a的地址加载到eax寄存器中

2.给结构体指针赋值

复制以下代码到VS2022以x86+debug环境调试

typedef struct INFO
{char c;int id;float f;
}INFO;int main()
{INFO info = { 'A',100,6.6f };INFO* pInfo = &info;return 0;
}

反汇编显示

    INFO* pInfo = &info;lea         eax,[info]  mov         dword ptr [pInfo],eax 

注意到lea         eax,[info],为结构体指针赋值

mov 指令的作用

1.取普通指针指向地址的值(等价为C语言的*)

复制以下下代码到VS2022以x86+debug环境调试

int main()
{int iNUM = 0;int* pNUM = &iNUM;int flag = *pNUM;return 0;
}

反汇编显示

int iNUM = 0;
mov         dword ptr [iNUM],0  int* pNUM = &iNUM;
lea         eax,[iNUM]  
mov         dword ptr [pNUM],eax  int flag = *pNUM;
mov         eax,dword ptr [pNUM]  
mov         ecx,dword ptr [eax]  
mov         dword ptr [flag],ecx  

对dword ptr [...]的解释:mov         dword ptr [iNUM],0 把0赋值到iNUM指向的地址中(这里的[pNUM]为普通指针,dword ptr [iNUM]就是普通指针指向地址的值)


注意到最后三个指令:
mov         eax,dword ptr [pNUM]  
mov         ecx,dword ptr [eax]  
mov         dword ptr [flag],ecx  
这里由ecx做中转寄存器(x86环境不支持mov dword ptr [flag],dword ptr [eax])
 

2.取结构体指针指向地址里的值

复制以下代码到VS2022以x86+debug环境调试

typedef struct INFO
{char c;int id;float f;
}INFO;int main()
{INFO info = { 'A',100,6.6f };INFO* pInfo = &info;char c = pInfo->c;return 0;
}


反汇编显示

    INFO info = { 'A',100,6.6f };
mov         byte ptr [info],41h  
mov         dword ptr [ebp-10h],64h  
movss       xmm0,dword ptr [__real@40d33333 (0B77BCCh)]  
movss       dword ptr [ebp-0Ch],xmm0  INFO* pInfo = &info;
lea         eax,[info]  
mov         dword ptr [pInfo],eax  char c = pInfo->c;
mov         eax,dword ptr [pInfo]  
mov         cl,byte ptr [eax]  
mov         byte ptr [c],cl  


注意到最后三个指令均为mov指令
->为结构体的特有的符号,虽然不用*表示,但确有*的作用
备注:mov         cl,byte ptr [eax] (byte对应cl寄存器)

在结尾处添加以下代码重新调试

	char c = pInfo->c; int id = pInfo->id;


对比这两行代码反汇编的不同之处

    char c = pInfo->c;
mov         eax,dword ptr [pInfo]  
mov         cl,byte ptr [eax]  
mov         byte ptr [c],cl  int id = pInfo->id;
mov         eax,dword ptr [pInfo]  
mov         ecx,dword ptr [eax+4]  
mov         dword ptr [id],ecx  


注意到:
mov         cl,byte ptr [eax+0]  
mov         ecx,dword ptr [eax+4]  
发现eax加的偏移量不同(之前讲过结构体的内存对齐,见63.【C语言】再议结构体(上)文章)
对于char c = pInfo->c;结构体首元素从偏移量为0处开始存储
对于int id = pInfo->id;id的对齐数为4,VS的默认对齐数为8,4<8从偏移量为4的整数倍开始存储
bb3826275aa34f5a87039967bec0c543.png
备注:一个空格的存储空间为1个字节


3.总结->的作用

1.取结构体的首地址
2.首地址+成员变量的偏移(例如:eax+?)
3.mov取得到地址里面的值,刚好得到了成员变量的值



4.回到本文分析

由上方lea指令的讲解可知

 lea         rax,[p]  

将p的地址加载到rax中
 lea         rcx,[string "hello world" (07FF68B18ACA8h)]

将已经写入内存的hello world字符串的首字符的地址加载至rcx中
 mov         rdi,rax
 mov         rsi,rcx

rax赋值给rdi,rcx赋值给rsi
 mov         ecx,0Ch

细节分析mov ecx,0Ch

如果调试的的话,会看到以下奇怪的现象

2689308f40cd4c829114897331b3eea9.png

修改ecx会导致rcx整体都变,ecx为rcx的低32位,那rcx的高32位应该保持不变才对

解释

Intel规定:对ecx的写操作不仅修改了rcx的低32位,还会自动将rcx的高32位清零

详见开发手册,网站:http://x86asm.net/articles/x86-64-tour-of-intel-manuals/

下面截取的为关键信息

368997a5c43a43d0bd971d6cd86019c4.png

-------------------------------------------

在ecx中设置计数次数为0Ch次"hello world\0"一共12个字符(不要忘记隐含的\0)
 rep movs    byte ptr [rdi],byte ptr [rsi]  

[rdi]和[rsi]为指针,从[rsi]处重复复制(rep movs)字节(一次复制一个字节)至[rdi]处

重复复制rcx(0Ch)次,注意:每复制一次,rdi+1,rsi+1,rcx-1

注意:rep指令这里和8086CPU有所不同,VS中默认每复制一次指针+1(运行环境已经提前设置好了),但是8086CPU要设置方向(cld或std)

............

mov         rdi,rax

............

pop         rdi

虽然恢复了rdi指针的值使其指向了复制过后的字符串的首字符,但是最后出栈的值给了rdi,

rdi重新恢复为0,指针丢失,因此str为野指针

对rdi重新恢复为0的解释

x86+debug环境,VS打开调试模式

有push,就有pop

;保存rbp,rsi,rdi的值
push        rbp  
push        rsi  
push        rdi  
-------------------------------------------------------------------------------------------
;恢复rbp,rsi,rdi原来的值
pop         rdi  
pop         rsi  
pop         rbp  

执行push rbp之前,查看寄存器

a896c80541ae47329021cd3cc2a6b11b.png

注意到rdi=0,因此在GetMemroy函数的最后pop rdi时,rdi的值恢复为0
 

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

回归预测 | Matlab基于SABO-SVR减法平均算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于SABO-SVR减法平均算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SABO-SVR减法平均算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于SABO-SVR减法平均算法优化…

Robust多模态模型的开发

本文所涉及所有资源均在 传知代码平台 可获取。 目录 Robust 多模态模型&#xff1a;寻找遗失的模态&#xff01; 一、研究背景 二、模型结构和代码 三、数据集介绍 六、性能展示 六、实现过程 七、运行过程 Robust 多模态模型&#xff1a;寻找遗失的模态&#xff01; 近年来&a…

最新项目全功能知识付费小程序源码系统 带完整的安装代码包以及搭建部署教程

系统概述 知识付费小程序源码系统是一款基于先进技术架构设计的综合性平台。它旨在为用户提供一站式的知识付费解决方案&#xff0c;涵盖了从内容创作到用户管理的各个环节。 该系统采用了现代化的开发理念和技术手段&#xff0c;确保了系统的稳定性、安全性和高效性。它具有…

ClickHouse 24.9 版本发布说明

本文字数&#xff1a;7295&#xff1b;估计阅读时间&#xff1a;19 分钟 作者&#xff1a;ClickHouse Team 本文在公众号【ClickHouseInc】首发 又到新版本发布的时间了&#xff01; 发布概要 本次ClickHouse 24.9 版本包含了23个新功能&#x1f381;、14项性能优化&#x1f6f…

Golang反射解说

在Go语言中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时检查、修改和操作变量的类型信息。 反射主要用于处理那些在编译时无法确定类型的情况&#xff0c;比如处理接口类型的值、实现通用的函数等。 Go语言中反射的基本…

邮件系统国产化改造: 保障信息安全、提升效率的最佳选择

在当前数字化转型的大背景下&#xff0c;我国政府提出了构建网络强国和数字强国的宏伟蓝图。这一战略的实施&#xff0c;不仅为数字政府的建设提供了坚实的基础&#xff0c;也为政府和企业的数字化升级指明了方向。在这一进程中&#xff0c;邮件系统的国产化改造就显得尤为重要…

Chromium 关闭 Google Chrome 后继续运行后台应用功能分析c++

此功能允许关闭 Google Chrome 后继续运行后台&#xff0c;控制此功能的开关是 // Set to true if background mode is enabled on this browser. //更改此值可以修改默认开启关闭 inline constexpr char kBackgroundModeEnabled[] "background_mode.enabled"; …

前端的全栈混合之路Meteor篇:分布式数据协议DDP深度剖析

本文属于进阶篇&#xff0c;并不是太适合新人阅读&#xff0c;但纯粹的学习还是可以的&#xff0c;因为后续会实现很多个ddp的版本用于web端、nodejs端、安卓端和ios端&#xff0c;提前预习和复习下。ddp协议是一个C/S架构的协议&#xff0c;但是客户端也同时可以是服务端。 什…

STM32F407寄存器操作(DMA+SPI)

1.前言 前面看B站中有些小伙伴吐槽F4的SPIDMA没有硬件可控的CS引脚&#xff0c;那么今天我就来攻破这个问题 我这边暂时没有SPI的从机芯片&#xff0c;并且接收的过程与发送的过程类似&#xff0c;所以这里我就以发送的过程为例了。 2.理论 手册上给出了如下的描述 我们关注…

Spring Boot 学习之路 -- Thymeleaf 模板引擎

前言 最近因为业务需要&#xff0c;被拉去研究后端的项目&#xff0c;代码框架基于 Spring Boot&#xff0c;后端对我来说完全小白&#xff0c;需要重新学习研究…出于个人习惯&#xff0c;会以 Blog 文章的方式做一些记录&#xff0c;文章内容基本来源于「 Spring Boot 从入门…

微信小程序-分包加载

一.分包的意义 小程序是由多个页面构成&#xff0c;为了因为代码量多&#xff0c;体积大导致用户打开速度变慢&#xff0c;小程序提供了分包加载数据。 分包加载数据&#xff0c;只有在主包调用分包某一个页面时候才会调用加载分包。即就是按需加载。 整个小程序不能超过20M&a…

golang grpc进阶

protobuf 官方文档 基本数据类型 .proto TypeNotesGo Typedoublefloat64floatfloat32int32使用变长编码&#xff0c;对于负值的效率很低&#xff0c;如果你的域有可能有负值&#xff0c;请使用sint64替代int32uint32使用变长编码uint32uint64使用变长编码uint64sint32使用变长…

滚柱导轨适配技巧与注意事项!

滚柱导轨是一种重要的传动元件&#xff0c;它由滚柱作为滚动体。用于连接机床的运动部件和床身基座&#xff0c;其设计旨在提供高承载能力和高刚度&#xff0c;适用于重型机床和精密仪器&#xff0c;而滚柱导轨的适配方法对于确保机械设备的高精度运行至关重要。 滚柱导轨的适配…

大数据分析案例-基于逻辑回归算法构建抑郁非抑郁推文识别模型

🤵‍♂️ 个人主页:@艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ 喜欢大数据分析项目的小伙伴,希望可以多多支持该系列的其他文章 大数据分析案例合集

介绍Java

Java简介 Java是一门由Sun公司&#xff08;现被Oracle收购&#xff09;在1995年开发的计算机编程语言&#xff0c;其主力开发人员是James Gosling&#xff0c;被称为Java之父。Java在被命名为“Java”之前&#xff0c;实际上叫做Oak&#xff0c;这个名字源于James Gosling望向…

Unite Barcelona主题演讲回顾:深入了解 Unity 6

本周&#xff0c;来自世界各地的 Unity 开发者齐聚西班牙巴塞罗那&#xff0c;参加 Unite 2024。本次大会的主题演讲持续了一个多小时&#xff0c;涵盖新功能的介绍、开发者成功案例的分享&#xff0c;以及在编辑器中进行的技术演示&#xff0c;重点展示了 Unity 6 在实际项目中…

学习python自动化——pytest单元测试框架

一、什么是pytest 单元测试框架&#xff0c;unittest&#xff08;python自带的&#xff09;&#xff0c;pytest&#xff08;第三方库&#xff09;。 用于编写测试用例、收集用例、执行用例、生成测试结果文件&#xff08;html、xml&#xff09; 1.1、安装pytest pip instal…

Spring AI 介绍与入门使用 -- 一个Java版Langchain

Langchain 是什么&#xff1f; Langchain 是一个Python 的AI开发框架&#xff0c;它集成了模型输入输出、检索、链式调用、内存记忆&#xff08;Memory&#xff09;、Agents以及回调函数等功能模块。通过这些模块的协同工作&#xff0c;它能够支持复杂的对话场景和任务执行流程…

C语言 | Leetcode C语言题解之第460题LFU缓存

题目&#xff1a; 题解&#xff1a; /* 数值链表的节点定义。 */ typedef struct ValueListNode_s {int key;int value;int counter;struct ValueListNode_s *prev;struct ValueListNode_s *next; } ValueListNode;/* 计数链表的节点定义。 其中&#xff0c;head是数值链表的头…

多点低压差分(M-LVDS)线路驱动器和接收器——MS2111

MS2111 是多点低压差分 (M-LVDS) 线路驱动器和接收器。经过 优化&#xff0c;可运行在高达 200Mbps 的信号速率下。所有部件均符合 M LVDS 标准 TIA / EIA-899 。该驱动器的输出支持负载低至 30Ω 的多 点总线。 MS2111 的接收器属于 Type-2 &#xff0c; 可在 -1…