mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
1071 字
3 分钟
cpp(九)——内存序,堆栈
2026-10-31

cpp(九)—关于堆栈、字节序#

一、类型与读取#

在ida里,我们经常能看到类型转换比如*(_DWORD *),括号内是指针类型,括号前的星号是用于解引用,这里也就是读取内存的方式,如下

IDA 伪代码 含义
*(_DWORD *)a1 从 a1 地址读取 4 字节
*(_QWORD *)a1 从 a1 地址读取 8 字节
*(_DWORD *)(a1 + 8) 从 a1+8 读取 4 字节
*(_QWORD *)(a1 + 0x10) 从 a1+16 读取 8 字节
a1 + 0x20 地址增加 32 字节
++a1 地址增加一个所指类型的大小(取决于 a1 类型)

二、栈与堆#

栈#

栈主要用于保存函数执行过程中需要的数据,例如:

  • 局部变量
  • 函数调用时需要保存的信息
  • 部分函数参数
  • 返回地址和寄存器保存现场(具体位置取决于 ABI 和编译器)
void test() {
int a = 10;
int b = 20;
int c = a + b;
}

函数执行的时候,编译器会安排空间

比如下面的汇编

SUB SP, SP, #0x30

这通常表示:

将栈指针 SP 减少 0x30,为当前函数分配 48 字节的栈空间。

函数结束时可能看到:

ADD SP, SP, #0x30
RET

这里表示释放空间。

栈在操作系统里布局通常是处在在上方,会往低地址生长。

堆#

堆用于程序运行过程中动态申请和释放内存。

例如:

int* p = new int(100);

这里发生了什么?

  1. 程序向动态内存分配器申请空间。
  2. 分配器返回一块可以存放 int 的内存地址。
  3. 指针变量 p 保存这个地址。
  4. *p 访问这块内存中的整数。

这里隐藏着一个堆栈关系:位于栈上的指针p里保存着堆上new出来的int100的地址。

int* p = new int(100);
std::cout << *p << std::endl;
delete p;
p = nullptr;

这里 delete p 释放的是堆上的对象,不是指针变量 p 本身。

从这里就能看出野指针的来源了,不过之前咱们看了RAll,只能说cpp牛逼吧。

栈与堆的区别#

对比项栈 Stack堆 Heap
管理方式通常由函数调用机制自动管理动态分配器管理
生命周期通常随栈帧退出结束按分配和释放规则决定
典型对象局部变量、调用现场new、malloc 分配的对象
分配特点通常开销较低通常管理开销更高
常见问题栈溢出、返回悬空指针内存泄漏、重复释放

汇编里的栈#

SUB SP, SP, #0x40
STR W0, [SP,#0x10]
LDR W1, [SP,#0x10]
ADD SP, SP, #0x40
RET
指令含义
SUB SP, SP, #0x40为栈帧分配 64 字节
STR W0, [SP,#0x10]将 W0 的 4 字节写入栈偏移 16
LDR W1, [SP,#0x10]从栈偏移 16 读取 4 字节
ADD SP, SP, #0x40回收栈空间
RET函数返回

所以这里也会引出一个判断问题

v1 = *(_DWORD *)(v2 + 0x10);

要判断 v2 是什么:

  • 如果 v2 来自 SP 或 FP,可能是栈上的变量。
  • 如果 v2 是函数参数传入的地址,可能是对象或缓冲区。
  • 如果 v2 来自 new 或 malloc,可能是堆内存。

不要仅凭偏移就判断内存属于栈还是堆。

三、字节序#

字节序决定了字节在内存里的排序。

假设有一个 32 位整数:

uint32_t value = 0x12345678;

它由四个字节组成:

12 34 56 78

但是在内存中,这四个字节究竟按照什么顺序排列?

这就涉及字节序。

主要有两种:

  • 大端序(Big Endian)
  • 小端序(Little Endian)

大端序#

高位放在低地址,这也符合人类的阅读习惯

地址 数据
0x1000 12
0x1001 34
0x1002 56
0x1003 78

小端序#

低位放在低地址。

地址 数据
0x1000 78
0x1001 56
0x1002 34
0x1003 12

在安卓逆向里,通常使用的是arm与thumb指令,都是小端序。

为什么说字节序重要#

前面说了,编译器是按照字节序读取内存里的内容的,所以就会出现运行时读出来的内容大小端序不一致的结果。

首先先说一个前提!字节序不会改变数值大小!比如0x67452301虽然在大小端里视图不一样,但运算时依旧是按照这个数值来运算的。

唯一能体现区别的在于:字节流的读取

比如一个数值被存在内存里,那么编译器按照大小端就能读出两种结果:01 23 45 67和67 45 23 01,这两个玩意儿按照ascii转成字符后是不同的。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

cpp(九)——内存序,堆栈
https://fatdog.20060113.xyz/posts/cpp-again9/
作者
神秘大胖狗
发布于
2026-10-31
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00