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

news2024/12/25 9:21:49

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.coloradmin.cn/o/2197255.html

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

相关文章

给普通的div或者view等元素添加onblur事件的方式

一般只有input元素有blur事件&#xff0c;但是如果想给普通的元素设置了blur事件之后&#xff0c;它是不会正常直行的。还需要再给元素添加tabindex属性&#xff0c;设置了tabindex属性之后&#xff0c;元素会带有一个下划线&#xff0c;还要去掉下划线&#xff0c;而且聚焦的时…

PostgreSQL学习笔记二:PostgreSQL的系统架构

PostgreSQL 是一种功能强大的开源关系型数据库管理系统&#xff0c;其架构具有以下特点&#xff1a; 一、客户端/服务器架构 客户端 客户端可以是各种应用程序&#xff0c;如 Web 应用、桌面应用等&#xff0c;它们通过网络连接与 PostgreSQL 服务器进行通信。客户端使用标准的…

力扣之603.连续空余座位

文章目录 1. 603.连续空余座位1.1 题干1.2 准备数据1.3 思路分析1.4 解法1.5 结果截图 1. 603.连续空余座位 1.1 题干 表: Cinema ----------------- | Column Name | Type | ----------------- | seat_id | int | | free | bool | ----------------- Seat_id 是该表的自动递…

2024-你自学网络安全的顺序可能一直是反的!

作为一名在网络安全领域工作了八年的技术人员&#xff0c;我想分享一些经验给2024年学习黑客技术的朋友们。 千万不要毫无基础就开始学黑客!一定要先了解相关的信息和知识! 对于刚入行的朋友&#xff0c;我建议先从网络安全或Web安全/渗透测试入手&#xff0c;这些方向市场需求…

【可答疑】基于51单片机的光照强度检测(含仿真、代码、报告、演示视频等)

✨哈喽大家好&#xff0c;这里是每天一杯冰美式oh&#xff0c;985电子本硕&#xff0c;大厂嵌入式在职0.3年&#xff0c;业余时间做做单片机小项目&#xff0c;有需要也可以提供就业指导&#xff08;免费&#xff09;~ &#x1f431;‍&#x1f409;这是51单片机毕业设计100篇…

基于STM32的智能仓库温湿度监控系统设计

引言 本项目设计了一个基于STM32的智能仓库温湿度监控系统&#xff0c;能够实时监测仓库内的温度和湿度&#xff0c;并根据设定的阈值触发报警或启动风扇调节环境。该系统通过DHT11温湿度传感器获取环境数据&#xff0c;结合OLED显示屏、风扇和蜂鸣器&#xff0c;实现对仓库环…

python中的数组模块numpy(一)(适用物联网数据可视化及数据分析)

一、创建数组对象array&#xff0c;认识数组的格式 array函数的格式:np.array(object,dtype,ndmin) 以下是示例代码&#xff1a; # coding:utf-8 import numpy as np d1[1.1,2.1,3.1] d2(1,2,3,4) d3[[a,b],[c,d],[e,f]] print(d1) print(d2) print(d3) print("以上是数…

无法编辑PDF文件?试试这3个解决方法!

PDF文件格式广泛应用于工作中&#xff0c;但有时候我们可能遇到无法编辑PDF文件的情况。这可能导致工作效率降低&#xff0c;特别是在需要修改文件内容时显得尤为棘手。遇到PDF不能编辑时&#xff0c;可以看看是否以下3个原因导致的。 原因一&#xff1a;PDF文件设置了编辑权限…

八大排序--06基数排序(桶排序)

【本质--先排序个位&#xff0c;再排序十位&#xff0c;排百位...依次类推的过程】 获取待排序数组中的最高位数 //取计算最大值的位数int maxarr[0];for(int j0;j<arr.length;j) {if(arr[j]>max) {maxarr[j];}} 存储数据&#xff08;桶排序中除了游标遍历外&#xff…

【数据结构 | PTA】栈

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

分析和解决js运算精度问题,出现多位小数

加减乘除都会出现小数精度错误的问题 (见图) 原因&#xff1a;js进行运算时会将数字先转为二进制再进行运算。 错误思路&#xff1a;之前在做数字运算时都是将数字转化为整数再进行运算&#xff0c;某次突然发现在变整数的时候也会出现精度问题&#xff0c;比如上图中的数据。…

LINUX——内核移植、内核编译教程

Linux内核编译是一个将内核源代码转换成可在特定硬件架构上运行的二进制文件的过程。以下是编译Linux内核的一般步骤&#xff1a; 1、准备工作&#xff1a; 确保安装了必要的编译工具&#xff0c;如gcc、make、ncurses库&#xff08;用于make menuconfig&#xff09;等。 2、…

【读书笔记-《30天自制操作系统》-26】Day27

本篇内容不多&#xff0c;主要是一些优化的工作。首先优化了应用程序&#xff0c;然后引入对应用程序的保护功能&#xff0c;最后引入库的概念。 1. 应用程序优化 首先来解决上一篇中遗留的一个bug:使用ncst命令运行的应用程序&#xff0c;按下ShiftF1或者点击x按钮都无法关…

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

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

『网络游戏』登陆启动框架【05】

将上一章的加载界面隐藏 1.游戏启动逻辑 创建脚本GameRoot.cs &#xff08;该脚本为游戏入口&#xff0c;作用初始化游戏&#xff09; 创建脚本&#xff1a;ResSvc.cs &#xff08;&#xff09; 创建脚本&#xff1a;LoginSys.cs &#xff08;&#xff09; 编写脚本&a…

解析Vue源码中是如何进行模版编译的

模版编译 联系前文&#xff0c;讲了虚拟DOM的patch过程&#xff0c;而虚拟DOM的前提是先有VNode&#xff0c;那么VNode又是从哪里来的&#xff1f;接下来讲的模版编译便是&#xff1a;把用户写的模版进行编译&#xff0c;就会产生VNode。 在日常开发中&#xff0c;我们把写在…

Qt-目录和文件

1. 目录和文件 1.1 目录操作 QDir 类用来处理目录 常用方法&#xff1a; QDir(QString path) &#xff1a; 实例化 absolutePath() : 获取目录绝对路径 dirName() : 获取目录相对路径 exists(dirPath) : 判断目录是否存在 mkdir(QString dirPath) : 创建目录 rmdir(QStr…

经典5级流水线概述

抽象化的流水线结构&#xff1a; 流水线的基本概念 多个任务重叠&#xff08;并发/并行&#xff09;执行&#xff0c;但使用不同的资源流水线技术提高整个系统的吞吐率&#xff0c;不能缩短单个任务的执行时间其潜在的加速比&#xff1d;流水线的级数 流水线正常工作的基本条件…

使用YOLO11实例分割模型进行人物分割【附完整源码】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

开放式蓝牙耳机哪个品牌好用?五大口碑最好开放式耳机力荐!

长时间佩戴传统入耳式耳机有时可能会影响耳道健康&#xff0c;鉴于此&#xff0c;转而选择不入耳设计的开放式耳机就成了不少人的新倾向&#xff0c;它们有助于减少细菌滋生和耳道闷热的烦恼。为了帮助大家找到合适的选项&#xff0c;下面我将列举一些市面上口碑不错的开放式耳…