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

1.C语言代码

#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的指令集是这样说的:

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

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的整数倍开始存储

备注:一个空格的存储空间为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  

在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之前,查看寄存器

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

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

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

相关文章

PCB进程初识

目录 一、什么是进程 1.课本概念 2.内核观点 二、进程的描述-PCB 1.什么是PCB 2.PCB的组织方式 3.task_struct是Linux操作系统下的PCB 4.task_struct内容分类 三、进程的查看 四、进程的创建 1.查看子进程id和父进程id 演示实例1&#xff1a; 2.fork初识 演示实例…

软件测试学习笔记丨Mitmproxy使用

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/32334 一、简介 Mitmproxy是一款开源、免费的代理工具&#xff0c;支持Mac、Windows、Linux。相比其他代理工具&#xff0c;可以通过Python和Mitmproxy工具本身的插件机制&#xff0c;实现通…

npm运行时出现npm ERR! builtins is not a function报错

项目场景&#xff1a; 项目运行时什么都没动都没改突然运行不起来了&#xff0c;报错 TypeError: builtins is not a function 代码什么都没动&#xff0c;不是代码问题&#xff0c;排查后只有可能是node和npm的问题&#xff0c;所以卸载掉node重装重启 解决方案&#xff1a; …

Hierarchical Cross-Modal Agent for Robotics Vision-and-Language Navigation

题目&#xff1a;用于视觉语言导航的层次化跨模态智能体 摘要 1. 问题背景和现有方法 VLN任务&#xff1a;这是一种复杂的任务&#xff0c;要求智能体基于视觉输入和自然语言指令进行导航。 现有方法的局限性&#xff1a;之前的工作大多将这个问题表示为离散的导航图&#x…

重要的事情说两遍!Prompt「复读机」,显著提高LLM推理能力

【导读】 尽管大模型能力非凡&#xff0c;但干细活的时候还是比不上人类。为了提高LLM的理解和推理能力&#xff0c;Prompt「复读机」诞生了。 众所周知&#xff0c;人类的本质是复读机。 我们遵循复读机的自我修养&#xff1a;敲黑板&#xff0c;划重点&#xff0c;重要的事…

nacos多数据源插件介绍以及使用

概述 在微服务架构中&#xff0c;服务配置的集中管理和动态调整是至关重要的。Nacos 提供了配置管理和服务发现的功能&#xff0c;其中配置管理支持动态数据源的切换&#xff0c;增强了其在复杂环境中的适用性。默认情况下&#xff0c;Nacos 支持 MySQL 和Derby&#xff0c;但…

如何在百度地图上添加自己店铺的位置?

随着互联网的快速发展&#xff0c;如今许多事都可以通过网络去解决&#xff0c;例如线上支付、线上购物、线上订餐等&#xff0c;包括日常出行&#xff0c;人们也可以依靠地图软件去规划路线&#xff0c;然后导航至目的地。其中&#xff0c;百度地图作为国内领先的地图导航平台…

组态图卷起了3D化,这是趋势潮流还是盲目跟风呢?

在当今科技飞速发展的时代&#xff0c;组态图领域也迎来了新的变革 ——3D 化。这一现象引发了人们的广泛关注和思考&#xff1a;这究竟是一种顺应时代的趋势潮流&#xff0c;还是盲目跟风之举呢&#xff1f; 从趋势潮流的角度来看&#xff0c;组态图的 3D 化有着诸多优势。首…

PointNet++网络详解

数据集转换 数据集转换的意义在于将原本的 txt 点云文件转换为更方便运算的npy点云文件&#xff0c;同时&#xff0c;将原本的xyzrgb这 6 个维度转换为xyzrgbc&#xff0c;最后一个c维度代表该点云所属的类别。 for anno_path in anno_paths:print(anno_path)try:elements a…

软件设计之SSM(9)

软件设计之SSM(9) 路线图推荐&#xff1a; 【Java学习路线-极速版】【Java架构师技术图谱】 尚硅谷新版SSM框架全套视频教程&#xff0c;Spring6SpringBoot3最新SSM企业级开发 资料可以去尚硅谷官网免费领取 学习内容&#xff1a; SpringMVC 概念及核心组件MVC初始化类数据…

POST注入通过sqli-labs靶场less-11

POST注入原理 原理介绍 进入第十一关靶场&#xff0c;我们发现是一个登录窗口&#xff0c;随意提交数据&#xff0c;显示 在url地址进行get提交&#xff0c;发现一直是登录窗口&#xff0c;页面无其他变化&#xff0c;想到post提交注入。 通关原理 打开靶场源码文件。 查看…

SEO(搜索引擎优化)指南

SEO&#xff08;Search Engine Optimization&#xff09;是通过优化网站内容、结构和外部链接&#xff0c;提升网页在搜索引擎结果中的排名&#xff0c;从而增加网站流量的过程。SEO 涉及多个层面&#xff0c;包括技术 SEO、内容优化、外部链接建设等。以下是 SEO 的核心优化策…

FineReport打开报错“配置数据库出错“怎么解决?

配置数据库被锁住&#xff0c;是否重置?将在embed文件夹生成备份并重置 我直接用管理员身份证打开就完美解决了!

AD9361,数据接口

CMOS LVDS Xilinx原语IBUFDS、OBUFDS IBUFDS、和OBUFDS都是差分信号缓冲器&#xff0c;用于不同电平接口之间的缓冲和转换。IBUFDS 用于差分输入&#xff0c;OBUFDS用于差分输出。 IBUFDS https://docs.amd.com/r/en-US/ug953-vivado-7series-libraries/IBUFDS // IBUFDS …

启明智显工业级HMI芯片Model4功耗特性分享

Model4工业级MPU是国产自主面向工业应用的RISC-V架构的应用级芯片&#xff0c;内置玄铁64bit RISC-V CPU C906&#xff0c;主频高达600MHz&#xff0c;算力约1380DMIPS。支持RTOS、linux系统&#xff0c;支持LVGL工具开发UI&#xff1b; Model4系列工业级MPU具有极强的屏显、多…

VL53L0X 测距传感器使用记录

VL53L0X 测距传感器测试使用说明...... by 矜辰所致前言 最近代理商告知以前使用的测距传感器 VL6180 公司已经宣告停产了&#xff0c;那么咱就得找一款替代品作为测距产品的探头了&#xff0c;推荐了 VL53L4 和 VL53L0X 系列&#xff0c;考虑到功耗问题&#xff0c;决定选用低…

【数据结构 | PTA】栈

文章目录 7-1 汉诺塔的非递归实现7-2 出栈序列的合法性**7-3 简单计算器**7-4 盲盒包装流水线 7-1 汉诺塔的非递归实现 借助堆栈以非递归&#xff08;循环&#xff09;方式求解汉诺塔的问题&#xff08;n, a, b, c&#xff09;&#xff0c;即将N个盘子从起始柱&#xff08;标记…

STM32-TIM输入捕获

一、概述 IC&#xff08;Input Capture&#xff09;输入捕获 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变&#xff08;上升沿或下降沿&#xff09;时&#xff0c;当前CNT的值将被锁存到CCR中&#xff0c;可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续…

提示工程、微调和 RAG

自众多大型语言模型&#xff08;LLM&#xff09;和高级对话模型发布以来&#xff0c;人们已经运用了各种技术来从这些 AI 系统中提取所需的输出。其中一些方法会改变模型的行为来更好地贴近我们的期望&#xff0c;而另一些方法则侧重于增强我们查询 LLM 的方式&#xff0c;以提…

【C语言】猜数字小游戏

&#x1f602;个人主页: 起名字真南 &#x1f923;个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 随机数的生成1.1 rand1.2 srand1.3 time1.4 设置随机数范围 2 猜数字游戏实现 前言&#xff1a;我们学习完前面的循环以后可以写一个猜数字小游戏 1 随机数的生成 想要完成…