局部变量,全局变量与内存

news2024/11/24 16:51:58

本文会使用IDA分析局部变量,全局变量在内存的存储

目录

使用IDA分析局部变量

使用IDA分析全局变量

总结


使用IDA分析局部变量

#include <stdio.h>


int main()
{
	int nNum = 1;
	float fNum = 2.5;
	char ch = 'A';

	printf("int %d, float %f, char %c", nNum, fNum, ch);

	return 0;
}

  使用IDA跟进到main函数,F5调出反汇编

摁下tab键,进入汇编界面,Rename函数名为main

Rename函数名为main

 CODE XREF: 当前函数被一个或多个地方引用,摁下CTRL+X可以查看调用main()的地方

局部变量汇编分析

.text:00411760 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00411760 main            proc near               ; CODE XREF: j_main↑j
.text:00411760
.text:00411760 var_FC          = qword ptr -0FCh
.text:00411760 var_24          = byte ptr -24h
.text:00411760 var_1D          = byte ptr -1Dh
.text:00411760 var_14          = dword ptr -14h
.text:00411760 var_8           = dword ptr -8
.text:00411760 argc            = dword ptr  8
.text:00411760 argv            = dword ptr  0Ch
.text:00411760 envp            = dword ptr  10h
.text:00411760
.text:00411760                 push    ebp             ; 把调用者函数的EBP压入堆栈
.text:00411761                 mov     ebp, esp        ; 开辟栈帧
.text:00411763                 sub     esp, 0E4h       ; 开辟函数局部变量空间
.text:00411769                 push    ebx             ; 保存易失性寄存器
.text:0041176A                 push    esi
.text:0041176B                 push    edi
.text:0041176C                 lea     edi, [ebp+var_24] ; ebp+var_24 <==> ebp-24h      lea取局部变量地址到edi        EDI = EBP-36 36个字节要初始化作为局部变量的空间
.text:0041176F                 mov     ecx, 9
.text:00411774                 mov     eax, 0CCCCCCCCh
.text:00411779                 rep stosd
.text:0041177B                 mov     ecx, offset unk_41C003
.text:00411780                 call    sub_41130C      ; CheckForDebuggerJustMyCode  VS系统函数
.text:00411785                 mov     [ebp+var_8], 1  ; int nNum = 1
.text:0041178C                 movss   xmm0, ds:dword_417BE8 ; xmm0寄存器用于处理浮点数,2.5存入寄存器
.text:00411794                 movss   [ebp+var_14], xmm0 ; float fNum = 2.5
.text:00411799                 mov     [ebp+var_1D], 41h ; char ch ='A'
.text:0041179D                 movsx   eax, [ebp+var_1D] ; 把ch扩展为4个字节,为压栈做准备
.text:004117A1                 push    eax             ; ch压栈
.text:004117A2                 cvtss2sd xmm0, [ebp+var_14] ; float拓展为double
.text:004117A7                 sub     esp, 8          ; 开辟堆栈空间
.text:004117AA                 movsd   [esp+0FCh+var_FC], xmm0 ; double入栈
.text:004117AF                 mov     ecx, [ebp+var_8]
.text:004117B2                 push    ecx             ; 把nNum的值保存到ecx后入栈
.text:004117B3                 push    offset aIntDFloatFChar ; 格式化字符串后入栈 offset取地址4个字节大小
.text:004117B8                 call    sub_4113A2      ; 调用printf函数
.text:004117BD                 add     esp, 14h        ; 平衡堆栈
.text:004117C0                 xor     eax, eax        ; 把eax清0,等价于 return 0
.text:004117C2                 pop     edi             ; 恢复易失性寄存器
.text:004117C3                 pop     esi
.text:004117C4                 pop     ebx
.text:004117C5                 add     esp, 0E4h       ; 清理main函数开辟的局部变量空间
.text:004117CB                 cmp     ebp, esp
.text:004117CD                 call    sub_411230      ; CheckEsp()   用于在程序的运行时检查堆栈指针是否被篡改
.text:004117D2                 mov     esp, ebp        ; 恢复栈帧
.text:004117D4                 pop     ebp
.text:004117D5                 retn                    ; 将程序的控制流转移到调用它的指令的下一条指令
.text:004117D5 main            endp

main函数主体堆栈,堆栈存储的是函数的局部变量,函数参数

  1. push EBP    EBP存储当前函数的栈底地址,调用者函数栈底地址压入堆栈
  2. mov EBP,ESP 开辟栈帧
  3. sub     esp, 0xXXX   为函数局部变量开辟空间
  4. push ebx,esi,edi 保存易失性寄存器
  5. 为局部变量空间堆栈初始化为0xCC
  6. 如果main函数有新的局部变量或者调用函数,都会引发堆栈变化
  7. main函数执行完毕开始恢复堆栈
  8.  pop 寄存器 恢复易失性寄存器
  9. add esp,0xXX 恢复局部变量的空间
  10. mov esp,ebp  ESP=EBP=函数调用者的栈底地址
  11. pop ebp 恢复原先的栈底
  12. retn 将执行main后下一条汇编指令地址给EIP寄存器,恢复程序控制流转,也恢复了原先的栈顶

一些细节:

rep stosd:完成对局部变量的初始化

.text:00411769                 push    ebx             ; 保存易失性寄存器
.text:0041176A                 push    esi
.text:0041176B                 push    edi
.text:0041176C                 lea     edi, [ebp+var_24] ; ebp+var_24 <==> ebp-24h      lea取局部变量地址到edi
.text:0041176F                 mov     ecx, 9
.text:00411774                 mov     eax, 0CCCCCCCCh
.text:00411779                 rep stosd               ; 把9个栈空间初始化为0xCC

汇编: 

  1. rep 重复操作,重复操作的次数是ecx的值  
  2. stosd:把EAX值复制到EDI寄存器指向的地址,一次复制4个字节
  3. stosw:把EAX值复制到EDI寄存器指向的地址,一个复制2个字节
  4. stosb:把EAX值复制到EDI寄存器指向的地址,一次复制1个字节

之前的操作中,保存易失性寄存器,每个寄存器8个字节,共计24字节

lea edi, [ebp+var_24] ; ebp+var_24 <==> ebp-24h edi寄存器指向main函数局部变量空间的尾地址

下面代码,给局部变量空间初始化 

000F176F B9 09 00 00 00       mov         ecx,9  
000F1774 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
000F1779 F3 AB                rep stos    dword ptr es:[edi]

"rep stos dword ptr es:[edi]" 的执行步骤如下:

  1. 使用ES:EDI指定目标内存区域的起始地址。
  2. 使用ECX指定要重复执行的次数。
  3. 将EAX中的值作为DWORD数据写入ES:[EDI]指向的内存位置,然后根据DF(方向标志位)决定EDI是递增还是递减,以定位下一个内存位置。
  4. 重复步骤3,直到ECX达到零,或根据DF决定的方向不再满足条件。

因此,"rep stos dword ptr es:[edi]" 指令将会将EAX寄存器中的DWORD数据连续地写入以ES:[EDI]为起点的目标内存区域,并且重复执行次数由ECX指定。这通常用于快速地内存初始化或者进行内存拷贝操作。

但是在64位下,不再使用ES:EDI模式寻址,64位模式下采用了平坦模型(Flat Model),所有线性地址都直接映射到相同的物理地址空间,即,你可以直接使用RDI寄存器作为指针来操作内存地址,而无需使用ES寄存器进行段寻址。

在x64dbg下验证

DF(Direction Flag)是x86处理器的一个标志位,用于指示字符串操作的方向。

当DF标志位被设置为0时,字符串操作是从低地址向高地址进行的,也就是正向(forward)操作。例如,使用REP MOVS指令将数据从源内存区域复制到目标内存区域时,数据会按照从源地址递增到目标地址的顺序进行复制。

当DF标志位被设置为1时,字符串操作是从高地址向低地址进行的,也就是反向(backward)操作。例如,使用REP MOVS指令将数据从目标内存区域复制到源内存区域时,数据会按照从源地址递减到目标地址的顺序进行复制。

得以验证在64位下,直接可以通过EDI寻址操作,不再使用ES:EDI寻址

为什么要每个字节初始化为0xCC

0xCC表示int33异常 0xCC指令是软中断指令,它会导致CPU进入一个中断处理例程。如果程序在调用函数时忘记初始化局部变量,那么在访问这些未初始化的变量时,CPU会立即进入一个中断处理例程,程序会停止运行,并且IDE或调试器会提示有异常产生。

这个地址存储的是浮点数

可以在Edit-->Operand type,选择合适的数据类型,正确的话,就会把16进制数据转换为对应的值。选择floatinteresting point

常见的操作数大小指定符号:

  1. byte ptr:指定一个数据操作数为8位字节(byte)。
  2. word ptr:指定一个数据操作数为16位字(word)。
  3. dword ptr:指定一个数据操作数为32位字 (dword)
  4. mmword ptr 是一个指示符,指示使用 64 位内存操作。

下面这两句代码在不同的工具中解析虽然不同,但意思一样。估计IDA 7.0 解析时是按照x86,而VS2019是按照x64,x64中已经不使用DS:EDI寻址了

VS2019: movss       xmm0,dword ptr [__real@40200000 (0AF7BE8h)]

IDA: movss   xmm0, ds:dword_417BE8

dword ptr [__real@40200000 (0AF7BE8h)] = ds:dword_417BE8 都表示以后面地址为首,取四个字节的数据

  1. xmm0 是 XMM 寄存器中的第一个寄存器
  2. 在 x86 指令集中,有 8 个 xmm 寄存器(xmm0~xmm7),每个寄存器的大小为 128 位(16 字节)

  • movss:将一个单精度浮点数(32 位)从源操作数移动到目标操作数。
  • movsx:将源操作数进行符号扩展后移动到目标操作数,源:8位,目标:16位,从低位存,高 8 位设置为源操作数的符号位,不改变大小,拓展位数 默认情况下,内存操作数被假定为 32 位的双字(DWORD)数据类型。
  • cvtss2sd:将单精度浮点数(4)转换为双精度浮点数(8)
  • xor:用于执行两个二进制数按位异或操作。当两个对应位的值不同时,结果位为 1,否则为 0。

cmp指令:用于比较两个操作数的大小关系,并根据比较结果设置标志位。cmp 指令会将 operand1 减去 operand2 的值,并根据结果设置标志位寄存器(如零标志位、符号标志位等)。

  1. 零标志位(ZF):如果两个操作数相等,则 ZF 被设置为 1;否则被设置为 0。
  2. 符号标志位(SF):如果结果为负数,则 SF 被设置为 1;否则被设置为 0。
  3. 位标志位(CF):用于无符号数比较,如果 operand1 小于 operand2,则 CF 被设置为 1;否则被设置为 0。

使用IDA分析全局变量

#include <stdio.h>

int nNum = 1;
float fNum = 2.5;
char ch = 'A';

int main()
{


	printf("int %d, float %f, char %c", nNum, fNum, ch);

	return 0;
}

局部变量汇编分析

.text:00411760 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00411760 main            proc near               ; CODE XREF: j_main↑j
.text:00411760
.text:00411760 var_D8          = qword ptr -0D8h
.text:00411760 argc            = dword ptr  8
.text:00411760 argv            = dword ptr  0Ch
.text:00411760 envp            = dword ptr  10h
.text:00411760
.text:00411760                 push    ebp
.text:00411761                 mov     ebp, esp        ; 开辟栈帧
.text:00411763                 sub     esp, 0C0h       ; 给局部变量开辟空间
.text:00411769                 push    ebx             ; 保存易失性寄存器
.text:0041176A                 push    esi
.text:0041176B                 push    edi
.text:0041176C                 mov     edi, ebp        ; 从EDI从低地址向高地址初始化局部变量空间
.text:0041176E                 xor     ecx, ecx
.text:00411770                 mov     eax, 0CCCCCCCCh
.text:00411775                 rep stosd
.text:00411777                 mov     ecx, offset unk_41C003
.text:0041177C                 call    sub_41130C
.text:00411781                 movsx   eax, byte_41A03C ; 把字符拓展为4字节,压入堆栈
.text:00411788                 push    eax
.text:00411789                 cvtss2sd xmm0, dword_41A038 ; 把float拓展为double
.text:00411791                 sub     esp, 8
.text:00411794                 movsd   [esp+0D8h+var_D8], xmm0 ; 把xmm0压入堆栈 这种压入堆栈方式相当于先抬升,esp变小,在压入xmm,从低地址填充到高地址
.text:00411799                 mov     ecx, dword_41A034
.text:0041179F                 push    ecx
.text:004117A0                 push    offset aIntDFloatFChar ; 压入的是字符串的地址,共计四个字节
.text:004117A5                 call    sub_4113A2
.text:004117AA                 add     esp, 14h        ; ch(4) + xmm0(8) + ecx(4) + 字符串地址(4) = 20字节 = 0x14 平衡堆栈
.text:004117AD                 xor     eax, eax        ; 清0 eax,相当于 return 0
.text:004117AF                 pop     edi             ; 恢复易失性寄存器
.text:004117B0                 pop     esi
.text:004117B1                 pop     ebx
.text:004117B2                 add     esp, 0C0h       ; 恢复函数局部变量空间
.text:004117B8                 cmp     ebp, esp
.text:004117BA                 call    sub_411230      ; CheckEsp检查当前函数栈帧是否被破坏
.text:004117BF                 mov     esp, ebp        ; 恢复栈帧
.text:004117C1                 pop     ebp
.text:004117C2                 retn
.text:004117C2 main            endp

总结

  1. 全局变量保存在data段,在程序编译链接就确定了;局部变量保存在stack段
  2. 访问局部变量往往是通过[ebp-0xXXX],全局变量往往是一个地址[0xXXX]
  3. 把局部变量或者全局变量压入堆栈需要使用寄存器
  4. 给局部变量赋值,mov [esp-0xXXX],值
  5. 函数有返回值往往借助寄存器,值或者地址

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

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

相关文章

华为云云耀云服务器L实例使用教学

随着云计算时代的进一步深入&#xff0c;越来越多的中小企业企业与开发者需要一款简单易用、高能高效的云计算基础设施产品来支撑自身业务运营和创新开发。 基于这种需求&#xff0c;华为云焕新推出华为云云服务器实例新品。 华为云云服务器具有智能不卡顿、价优随心用、上手更…

Python+Django前后端分离

程序示例精选 PythonDjango前后端分离 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjango前后端分离》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应…

《语义增强可编程知识图谱SPG》白皮书

语义増强可编程图谱框架&#xff1a;新一代知识图谱语义框架/引擎、SPGLLM双驱架构及应用相关进展和应用。《语义增强可编程知识图谱SPG》白皮书 v1.0.pdf: https://url39.ctfile.com/f/2501739-941002398-f8f1f0?p2096 (访问密码: 2096) 参考文献&#xff1a; [1]《语义增强可…

JavaScript学习笔记04

JavaScript笔记04 方法 定义方法 当一个函数是一个对象的属性时&#xff0c;称之为方法。例&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script>let p…

“Vim 退出”使百万程序员无所适从

“程序员一旦进入 Vim &#xff0c;就再难以脱身。”这听起来像是一个恐怖故事&#xff0c;但是却真实的发生在程序员的身上&#xff0c; Stack Overflow 上关于如何退出 Vim 的问题&#xff0c;其点击量已有上百万次了。 问题原文为&#xff1a; Im stuck and cannot escape.…

springboot整合返回数据统一封装

1、MagCode&#xff0c;错误码枚举类 package com.mgx.common.enums;import lombok.*; import lombok.extern.slf4j.Slf4j;/*** 错误码* author mgx*/ Slf4j NoArgsConstructor AllArgsConstructor public enum MsgCode {/*** 枚举标识&#xff0c;根据业务类型进行添加*/Code…

ClickHouse进阶(十八):clickhouse管理与运维-用户配置

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术,IT贫道_大数据OLAP体系技术栈,Apache Doris,Kerberos安全认证-CSDN博客 &#x1f4cc;订阅…

ABB机器人如何修改num数据类型的参数?

ABB机器人如何修改num数据类型的参数? 在实际的生产项目中,机器人中可能会声明一些num数据类型的变量,作为相关的生产参数,例如:生产计数、等待时间、吹气时间、电磁阀动作时间等, 那么,如何在必要时对这些参数进行修改呢? 具体的方法和步骤可参考以下内容: 如下图所示…

《金字塔原理》阅读笔记

我们都希望在思考、沟通交流和解决问题时&#xff0c;重点突出&#xff0c;思路清晰&#xff0c;层次分明。我们评价人时&#xff0c;有一个标准是逻辑思维能力&#xff0c;而逻辑思维能力的标准又是什么&#xff1f;我们指导别人“要逻辑清晰、条理分明”&#xff0c;可怎样才…

JavaWeb 学习笔记 3:Servlet

JavaWeb 学习笔记 3&#xff1a;Servlet 1.简介 Servlet 是 JavaEE 定义的一套 Web 应用开发标准&#xff08;接口&#xff09;&#xff0c;实现了该技术的 Web 服务器软件&#xff08;如 Tomcat&#xff09;上可以运行一个 Servlet 容器&#xff0c;只要我们使用 Servlet 技…

【Kubernetes二进制安装】

Kubernetes二进制安装 cluster-health&#xff1a;检查etcd集群的运行状况也就是说 kubelet 首次访问 API Server 时&#xff0c;是使用 token 做认证&#xff0c;通过后&#xff0c;Controller Manager 会为 kubelet 生成一个证书&#xff0c;以后的访问都是用证书做认证了。在…

RS485以及MODBUS学习

学习目的&#xff1a; 1、什么是485&#xff1f; 2、485如何通信&#xff1f; 3、如何使用熟能生巧&#xff1f; RS485是一种四总线通信&#xff0c;分别是VCC、GND、485_A、485_B。两根负责通信&#xff0c;两根负责进行供电。 RS485通信 硬件层&#xff1a;解决的是数据传输问…

【ELFK】之消息队列kafka

一、kafka的定义 Kafka 是一个分布式的基于发布/订阅模式的消息队列&#xff08;MQ&#xff0c;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域。Kafka 是最初由 Linkedin 公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff0…

Arduino程序设计(十)8×8 LED点阵显示(MAX7219)

88 LED点阵显示 前言一、MAX7219点阵模块二、点阵显示实验1、点阵显示部分图形2、点阵显示数字 0 ~ 9 及 26 个英文字母 总结 前言 本文主要介绍88 LED点阵显示实验&#xff0c;分别是&#xff1a;1、介绍MAX7219点阵模块&#xff1b;2、点阵显示部分图形&#xff1b;3、点阵显…

探索以太坊 Layer 2 解决方案的后起之秀——Starknet

作者: stellafootprint.network 数据来源: Starknet Dashboard “区块链三难题”&#xff0c;或“可扩展性三难题”&#xff0c;强调了区块链平台想要去平衡安全性、去中心化和可扩展性将面临的挑战。通常情况下&#xff0c;区块链架构只能有效地优先考虑其中两个难题。例如&…

蓝牙核心规范(V5.4)10.2-BLE 入门笔记之CIS篇

LE CIS 同步通信 同步通信提供了一种使用蓝牙LE在设备之间传输有时间限制的数据的方式。它提供了一个机制,允许多个接收器设备在不同的时间从相同的源接收数据,以同步它们对该数据的处理。LE AUDIO使用同步通信。 当使用同步通信时,数据具有有限的时间有效期,在到期时被认…

Nacos服务注册中心

1.引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency> 2. application.properties中配置 #-------------- Nacos配置 spring.cloud.nacos.…

项目开发过程中,客户提出了新的需求,怎么办?

由于用户已经签署用户需求报告&#xff0c;面对他们提出的新需求&#xff0c;要初步评估判断是否涉及相关需求的变更&#xff0c;作为项目经理&#xff0c;我会采取以下步骤&#xff1a; 1. 与客户进行沟通&#xff1a;首先&#xff0c;我会与客户进行进一步的沟通和讨论&…

【视觉SLAM入门】9.1 建图1---SLAM任务,稠密地图构建,立体视觉,RGBD,八叉树,点云地图等各种不同地图

"讷为君子&#xff0c;寡为吉人 ” 1. 立体稠密地图1.1 地图构建1.2 分析立体相机稠密建图效果 2. RGB-D稠密地图2.1 地图对比2.1.1 八叉树地图 3. 建图&#xff1f;定位&#xff1f;孰轻孰重3.1 鬼影问题3.2 三维重建 4. 总结 SLAM的功能&#xff1a; 直到现在我们可以知…

嵌入式:驱动开发 Day7

作业&#xff1a;基于GPIO子系统&#xff0c;编写LED的驱动程序和应用程序 驱动程序&#xff1a;myled.c #include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #inc…