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

本文会使用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.rhkb.cn/news/138505.html

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

相关文章

opencv dnn模块 示例(16) 目标检测 object_detection 之 yolov4

博客【opencv dnn模块 示例(3) 目标检测 object_detection (2) YOLO object detection】 测试了yolov3 及之前系列的模型&#xff0c;有在博客【opencv dnn模块 示例(15) opencv4.2版本dnn支持cuda加速&#xff08;vs2015异常解决&#xff09;】 说明了如何使用dnn模块进行cuda…

vue2中年份季度选择器(需要安装element)

调用 <!--父组件调用--><QuarterCom v-model"quart" clearable default-current/> 组件代码 <template><div><span style"margin-right: 10px">{{ label }}</span><markstyle"position:fixed;top:0;bottom:0…

路由器ip地址设置

当你使用路由器时&#xff0c;你可以按照以下步骤设置路由器的IP地址。这样可以确保你的网络连接正常并允许其他设备连接到你的路由器。 **步骤一&#xff1a;登录路由器管理界面** 首先&#xff0c;你需要登录到路由器的管理界面。打开你的浏览器&#xff0c;并输入路由器的…

本地Docker Registry远程连接,为你带来高效便捷的镜像管理体验!

Linux 本地 Docker Registry本地镜像仓库远程连接 文章目录 Linux 本地 Docker Registry本地镜像仓库远程连接1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址…

ChatGLM Pytorch从0编写Transformer算法

预备工作 # !pip install http://download.pytorch.org/whl/cu80/torch-0.3.0.post4-cp36-cp36m-linux_x86_64.whl numpy matplotlib spacy torchtext seaborn import numpy as np import torch import torch.nn as nn import torch.nn.functional as F import math, copy, tim…

【漏洞复现】企望制造 ERP命令执行

漏洞描述 由于企望制造 ERP comboxstore.action接口权限设置不当&#xff0c;默认的配置可执行任意SQL语句&#xff0c;利用xp_cmdshell函数可远程执行命令&#xff0c;未经认证的攻击者可通过该漏洞获取服务器权限。 免责声明 技术文章仅供参考&#xff0c;任何个人和组织…

golang入门笔记——pprof性能分析

文章目录 简介runtime/pprof的使用命令行交互网络服务性能分析pprof与性能测试结合压测工具go-wrk 简介 golang性能分析工具pprof的8个指标 1.性能分析的5个方面&#xff1a;CPU、内存、I/O、goroutine&#xff08;协程使用情况和泄漏检查&#xff09;、死锁检测以及数据竟态…

ETHERCAT转MODBUS TCP/IP协议网关

产品介绍 JM-ECT-TCPIP是自主研发的一款EtherCAT从站功能的通讯网关。该产品主要功能是将EtherCAT网络和 TCP/IP 网络连接起来。 本网关连接到EtherCAT总线中做为从站使用&#xff0c;连接到 TCP/IP 网络中做为服务器或客户端使用。 产品参数 技术参数 u 网关做为EtherCAT网…

淘天集团联合爱橙科技开源大模型训练框架Megatron-LLaMA

9月12日&#xff0c;淘天集团联合爱橙科技正式对外开源大模型训练框架——Megatron-LLaMA&#xff0c;旨在让技术开发者们能够更方便地提升大语言模型训练性能&#xff0c;降低训练成本&#xff0c;并保持和LLaMA社区的兼容性。测试显示&#xff0c;在32卡训练上&#xff0c;相…

八、数据类型转换

数据类型转换 1.数据类型转换1.1.隐式类型转换1.2.显式类型转换1.3.训练11.4.训练2 —————————————————————————————————————————————————— 1.数据类型转换 类型转换是将一个值从一种类型更改为另一种类型的过程。例如&…

Haproxy负载均衡群集

HAproxy搭建Web群集一、Web集群调度器1、常见的Web集群调度器2、常用集群调度器的优缺点&#xff08;LVS ,Nginx,Haproxy)2.1 Nginx2.2 LVS2.3 Haproxy 3、LVS、Nginx、HAproxy的区别 二、Haproxy1、简介2、Haproxy应用分析3、HAProxy的主要特性4、Haproxy调度算法&#xff08;…

有了Spring为什么还需要SpringBoot呢

目录 一、Spring缺点分析 二、什么是Spring Boot 三、Spring Boot的核心功能 3.1 起步依赖 3.2 自动装配 一、Spring缺点分析 1. 配置文件和依赖太多了&#xff01;&#xff01;&#xff01; spring是一个非常优秀的轻量级框架&#xff0c;以IOC&#xff08;控制反转&…

命令提示符cmd查询IP地址ipconfig, linux 系统中查看ip地址ifconfig

1.windowR 2.输入cmd----》确定 3.在命令行中输入ipconfig然后按回车。IPv4地址就是电脑的IP地址。 linux系统中查看ip地址 &#xff1a; ifconfig

vue内置组件Transition的详解

1. Transition定义 Vue 提供了两个内置组件&#xff0c;可以帮助你制作基于状态变化的过渡和动画&#xff1a; <Transition>会在一个元素或组件进入和离开 DOM 时应用动画。 <TransitionGroup> 会在一个 v-for 列表中的元素或组件被插入&#xff0c;移动&#xff0…

使用企业订货系统的原因和考虑|网上APP订单管理软件

使用企业订货系统的原因和考虑|网上APP订单管理软件 企业要用订货系统主要如下原因&#xff1a; 第一、在线订货系统能让销售即时看到商品信息。商品售价多少&#xff0c;进货价多少&#xff0c;上次跟客户的成交价是多少&#xff0c;最低可以卖多少钱&#xff0c;用个本子记录…

计算机操作系统 (王道考研)笔记(二)

重点知识点 1 内存1.1 内存的基础知识1.1.1 内存定义、作用1.1.2 指令的工作原理1.1.3 三种装入策略1.1.4 从写程序到程序运行1.1.5 链接的三种方式1.1.6 总结 1.2 内存管理1.2.1 内存空间的分配与回收a&#xff09; 连续分配管理b&#xff09; 非连续分配管理1&#xff09; 基…

线性代数的本质(六)——线性空间

文章目录 线性空间线性空间子空间坐标与同构线性变换与矩阵基变换与坐标变换 线性空间 线性空间 Grant: 普适的代价是抽象。 仔细分析就会发现&#xff0c;关于向量空间的一切概念及有关定理都不依赖于向量的具体表现形式(有序数组)&#xff0c;也不依赖于向量加法、数乘的具体…

notepad++编辑多个位置

在notepad设置多次点击不同的位置&#xff0c;然后同时操作这多个位置的方法&#xff1a; 1、选择编辑&#xff0c;首选项&#xff1a; 2、选择多点编辑&#xff1a;

数据结构学习笔记—— 排序算法总结【ヾ(≧▽≦*)o所有的排序算法考点看这一篇你就懂啦!!!】

目录 一、排序算法总结&#xff08;一&#xff09;排序算法分类&#xff08;二&#xff09;表格比较 二、详细分析&#xff08;最重要考点&#xff01;&#xff01;&#xff01;&#xff09;&#xff08;一&#xff09;稳定性&#xff08;二&#xff09;时间复杂度&#xff08;…

MapRdeuce工作原理

hadoop - (三)通俗易懂地理解MapReduce的工作原理 - 个人文章 - SegmentFault 思否 MapReduce架构 MapReduce执行过程 Map和Reduce工作流程 (input) ->map-> ->combine-> ->reduce-> (output) Map&#xff1a; Reduce