X86反汇编_深度学习:从 C 指针到汇编逻辑

1. 指针与数据的"暧昧"关系(基础篇)

  • 涵盖内容:

    • 字符指针 : char p = "hello" vs char arr[] = "hello" 的本质区别(常量区 vs 栈区)。

    • 指针数组 : int *arr[5] ------ 存放指针的数组。

    • 数组指针 : int (*p)[5] ------ 指向数组的指针。

  • 攻克目标:

    • 彻底分清"指针数组"和"数组指针"的写法和含义。

    • 理解字符串常量在内存中的位置。

    • 汇编映射点: mov 指令取地址的区别(取栈地址 lea vs 取常量区地址 mov)。

1.1 字符指针

反汇编映射:

  • 编译器优化(反汇编特征)

    • 现象: 没有看到 rep movsloop 循环复制。

    • 本质: 对于短字符串,编译器会将其拆解为多个 DWORD (4字节),使用 MOV 指令直接"暴力填充"到栈中。

    • 实战意义: 在逆向 Shellcode 或还原算法时,看到连续的 MOV [EBP-XX], Imm32,要立刻反应过来这是在初始化字符串或数组。

  • 字符数组 vs 字符指针(内存本质)

    • 字符数组 (char arr\[\]):

      • 动作: 搬运工。把常量区的数据,完整拷贝一份到栈(Stack)上。

      • 关键点: 栈上的数据是可读可写的。你可以修改 arr[0] = 'A',程序正常运行。

      • 汇编特征: mov reg, [地址] -> mov [栈], reg (先读内容,再写内容)。

    • 字符指针 (char *p):

      • 动作: 遥控器。只在栈上保存一个 4 字节的地址编号,直接指向常量区。

      • 关键点: 常量区通常是只读的(Read-Only)。如果你尝试 *p = 'A',程序会直接崩溃 (Access Violation)。

      • 汇编特征: mov [栈], 立即数地址 (直接写地址编号)。

1.2 指针数组

反汇编映射:

  • 从这里我们可以看到指针数组的元素是每个数组首元素的地址,也就是说是把首元素地址当作数据存储到指针数组中。

那么问题来了,在汇编角度中,我们该如何进行取出数组中每个数据(首元素地址 )里面的值(例如:arr_1里面的某个元素)呢?

  • 分析复盘:

    • 第一层:提取"钥匙" (Base Address)

      • 分析:[ebp - 0x4] 取出 arr_3 数组的首元素地址 汇编指令:mov eax, dword ptr [ebp-4]

      • 本质: arr 是一个指针数组,它在栈上存的不是数字,而是地址(即 arr_1, arr_2, arr_3 的首地址)。

      • 动作: [ebp-4] 是数组 arr 的第 3 个元素(arr2)。

      • 结果: 寄存器 EAX 现在拿着一把"钥匙",这把钥匙通向 arr_3 的老家。

    • 第二层:拿着钥匙找"宝藏" (Value Access)

      • 分析: 首元素地址 + 0x10 -> 偏移16个字节,找到 arr_34 的地址。然后取值。

      • 汇编指令: mov ecx, dword ptr [eax+10h]

      • 动作:CPU 以 EAX (arr_3 的基址) 为起点,向后跳 0x10 个字节,把在那里的数据读出来。

    • 核心特征总结:如何在逆向中一眼识别"指针数组"?

      • 指针数组 (int *arr3) 的汇编签名是: "两次 MOV,一次计算"
c 复制代码
mov reg1, [栈地址A]     ; 1. 先读出一个地址 (读到了 arr_3 的基址)
mov reg2, [reg1 + 偏移] ; 2. 再去读那个地址里的内容 (读到了 arr_3[4] 的值)

1.3 数组指针

C语言视角:

c 复制代码
int main()
{
	int arr[10] = { 0 };

	// 实验组 A:打印地址
	printf("%p\n", arr);
	printf("%p\n", &arr);

	// 实验组 B:加法运算(核心!)
	printf("%p\n", arr + 1);
	printf("%p\n", &arr + 1);

	return 0;
}

反汇编视角:

  • 我们可以看到编译器其实还是做了轻微优化,并没有告诉我们[ebp - 0x28]以及[ebp - 0x4]是什么,但是根据我们上下文分析可以推断,

  • [ebp - 0x28] -> arr + 1[ebp - 0x4] -> &arr + 1

  • arr+1是跨了一个字节距离,&arr + 1是跨了一个数组大小的距离。

战术总结:

  • 汇编里的"类型"就是"步长":

  • 当 CPU 看到 int *,它眼里的步长是 4。

  • 当 CPU 看到 int (*)[10],它眼里的步长是 40 (0x28)

  • 所谓的 "数组指针" ,在汇编层面不过是一次更大跨度的内存偏移计算而已。

1.4 遍历数组指针和指针数组的区别

指针数组遍历核心片段:

总结:先提取数组首元素地址,然后在进行数组内的偏移拿到值。

数组指针遍历核心片段:

总结:以数组首元素为基准,在此基础上进行偏移。

2. 调用函数指针与调用函数区别

C语言:

c 复制代码
void test()
{
	printf("hello world\n");
}

int main()
{
	test();
	void(*pa)() = &test;
	pa();
	return 0;
}

反汇编:

  • 调用函数: 直接call立即数

  • 调用函数指针: 将pa存储的值(函数地址)放入寄存器,call 寄存器

  • 因为立即数是死的,也就代表着调用的是死地址,这种直接的风格也是代表着调用的函数是确定性的;但是函数指针则不确定,我们要知道,指针严格意义上来讲是变量,变量存储什么函数地址是不确定的,所以我们把变量放入寄存器再去call符合间接调用。

2.1 遍历函数指针数组与分析反汇编特征

C语言:

反汇编:

  • 核心复盘:遍历函数指针数组 (The Essence of Function Pointer Arrays)

    • 内存布局:连续的"跳板"

      • 存储形态: 函数指针数组本质上是一个 "地址列表"。

      • 物理特征: 所有函数的入口地址(如 add, sub)在内存中是 紧密排列、互不间隔 的。

      • 数据宽度: 每个元素占用 4 字节(32位环境下),这决定了汇编运算的步长(Scale)。

    • 寻址逻辑:查表法 (Look-up Table)

      • 我们不是通过 if-else 去判断调用谁,而是通过 "计算" 来定位:

      • 输入: 数组下标 i (索引)。

      • 计算: i * 4 (偏移量)。

      • 定位: 基址 + 偏移量 = 目标函数指针在栈上的存储位置。

      • 动作: "下标背后的值,就是函数的地址。"

    • 汇编三部曲 (The Assembly Trilogy)

      • 在底层视角下,一次函数指针数组的调用 (pai) 永远分为这三步:

      • 算 (Calculate): 利用 CPU 的 SIB 寻址 硬件能力,直接计算出内存地址。

        • Raw Assembly: ebp - 基址偏移 + ecx \* 4
      • 取 (Fetch): 从计算出的内存地址中,把目标函数的 入口地址 搬运到寄存器。

        • Instruction: MOV EDX, 算出来的地址
      • 跳 (Indirect Call): CPU 对寄存器内的地址发起"盲跳"。

        • Instruction: CALL EDX (间接调用)
    • 一句话总结:

      • "函数指针数组,就是将'逻辑流'变成了'数据流'。我们通过计算偏移量(Stride),从连续的内存中抓取目标地址,实现精准的间接跳转。"

3. 模拟C语言以及正向汇编实现快速排序

3.1 模拟快排_C语言

3.2 正向汇编_模拟快排

3.2.1 cmp_stu_by_name

3.2.2 Swap

3.2.3 Bubble_Sort

3.2.4 main

  • 以上练习主要从汇编角度手写一个结构体排序算法,内心觉得只有把汇编当作母语般去学习,才能对汇编进行彻底祛魅。

本章完~

相关推荐
ThornArmor8 天前
【工具篇·番外】跨语言生态的主权回收:基于 ISA 说明书的 4-bit 双向汇编系统全线封顶
c语言·开发语言·汇编·c++·重构·架构
是星辰吖~8 天前
WIN32_线程(下)
汇编
是星辰吖~9 天前
WIN32_线程(上)
汇编
AI科技星9 天前
数术工坊 · 第四卷 橡皮泥江湖(拓扑学)【完整定稿】
c语言·开发语言·汇编·electron·概率论·拓扑学
iCxhust10 天前
C# 生成命令行程序 将hex格式烧录程序转换成bin烧录格式
开发语言·汇编·单片机·嵌入式硬件·c#·微机原理
iCxhust10 天前
C#进程管理程序
开发语言·汇编·stm32·单片机·c#·微机原理
hhcgchpspk10 天前
汇编语言传递数据和地址的误区
汇编·笔记·nasm·masm
iCxhust11 天前
MTK8088单板机制作(一)时钟电路
汇编·单片机·嵌入式硬件·微机原理·8088单板机
iCxhust11 天前
8086 汇编位测试使用方法
汇编·单片机·嵌入式硬件·微机原理·8088单板机
iCxhust11 天前
用汇编在8088单板机上创建一个进程
汇编·微机原理