x64汇编语言与逆向工程基础指南(二)

1. 前置知识

1.1 大、小端序存储

小端序(Little-endian)和大端序(Big-endian)是计算机存储数据时两种不同的字节序排列方式。这两种方式定义了在内存中多字节数据(如整数、浮点数)的字节顺序。

  • 大端序(Big-endian)

    • 在大端序存储中,数据的高字节(即最重要的字节)存储在低地址处,而低字节(即最不重要的字节)存储在高地址处。
    • 例如,假设有一个32位的整数0x12345678,其字节排列在内存中的顺序是:
      地址       数据
      0x00       0x12
      0x01       0x34
      0x02       0x56
      0x03       0x78
      
    • 大端序的优点是它符合人类阅读的自然顺序(从左到右)。在网络协议中,通常采用大端序来标准化数据格式,称为网络字节序。
  • 小端序(Little-endian)

    • 在小端序存储中,数据的低字节(即最不重要的字节)存储在低地址处,而高字节(即最重要的字节)存储在高地址处。
    • 例如,对于同样的32位整数0x12345678,其字节排列在内存中的顺序是:
      地址       数据
      0x00       0x78
      0x01       0x56
      0x02       0x34
      0x03       0x12
      
    • 小端序的优点是对较小的数据(如一个字节)进行读写操作时,无需重新排列字节,从而可以提高效率。许多现代的计算机体系结构(如Intel x86系列处理器)使用小端序。

具体区别可以参见下图:
在这里插入图片描述

总结

  • 大端序:高字节在低地址,符合人类的自然阅读顺序。
  • 小端序:低字节在低地址,常被认为对处理器更高效,也是编译器比较常用的。

1.2 汇编风格

Intel汇编风格AT&T汇编风格是两种不同的汇编语言语法格式,它们在语法和操作数顺序上有所不同。

1.2.1 Intel汇编风格

  • 操作数顺序:指令格式为 操作码 目的操作数,源操作数。即先写目的操作数,再写源操作数。
  • 示例MOV AX, BX 表示将 BX 的值移动到 AX 寄存器中。
  • 寄存器名:使用简短的寄存器名,如 AX, BX, CX 等。
  • 符号:使用 [] 表示内存地址,如 MOV AX, [1234h] 表示从内存地址 1234h 读取数据到 AX 寄存器。

1.2.2 AT&T汇编风格

  • 操作数顺序:指令格式为 操作码 源操作数, 目标操作数。即先写源操作数,再写目标操作数,即操作数的顺序与 Intel 是反向的。
  • 示例MOV %bx, %ax 表示将 bx 的值移动到 ax 寄存器中。
  • 寄存器名:寄存器名前加 % 符号,如 %ax, %bx, %cx 等。
  • 符号:使用 () 表示内存地址,如 MOV %ax, 1234(%bx) 表示将 1234 加上 bx 寄存器的值所指向的内存地址中的数据移动到 ax 寄存器中。

总结

  • Intel风格:操作数顺序为目标在前,源在后;寄存器名简短,无符号标记。
  • AT&T风格:操作数顺序为源在前,目标在后;寄存器名带有 % 符号,内存地址使用 () 符号。

显然,对于示例编译得到的exe程序,windows采用了小端序存储格式和Intel汇编风格

1.3 .bss段与.data段

在程序内存布局中,.bss段和.data段都是存储全局变量和静态变量的重要区域。它们在程序编译和运行时的行为有很大不同。

1.3.1 .bss

功能

  • .bss(Block Started by Symbol)段用于存储那些在程序开始运行前未被初始化的全局变量和静态变量。
  • 在程序启动时,.bss段中的数据会被自动初始化为零。

特点

  • 未初始化数据:这些变量在源代码中没有被显式初始化,例如 int globalVar;。它们默认值为零。
  • 节省空间:由于.bss段的数据在程序文件中不占用实际的空间(只在内存中存在),这可以减少程序的文件大小。
  • 内存分配:虽然.bss段在程序的二进制文件中不占用空间,但在程序加载到内存时,操作系统会为这些变量分配足够的内存空间,并将其初始化为零。

例子

int globalVar;  // 一个.bss段中的变量,因为它没有初始化赋值

1.3.2 .data

功能

  • .data段用于存储已初始化的全局变量和静态变量。这些变量在程序启动前已经有了确定的值。

特点

  • 已初始化数据:这些变量在源代码中有明确的初始值,例如 int initializedVar = 10;
  • 占用空间.data段中的数据在程序文件中是有实际大小的,因为它们的初始值被存储在程序的二进制文件中。
  • 内存分配:当程序被加载到内存中,操作系统会将这些变量的初始值从程序文件中读取到内存中。

例子

int initializedVar = 10;  // 一个.data段中的变量,具有有一个初始值

总结

  • .bss:存储未初始化的全局变量和静态变量;在程序启动时被初始化为零;节省程序文件空间,但在内存中占用空间。
  • .data:存储已初始化的全局变量和静态变量;程序文件中有实际的初始值;在内存中保留这些初始值。

2. 在内存中存储数据

若想在内存中存储常量,则需要将数据存放在.data段

首先打开程序内存布局,双击.data段,将.data段数据转储到内存1或2的地方。
在这里插入图片描述
在这里插入图片描述

2.1 存储纯数字常数

  • Step1:在转存的.data段选中某地址部分数据【以四字节为例】,右键进行编辑内存数据。
    在这里插入图片描述

假设需要将0x12345678存入指定地址0x7FF7D380B170,由于程序采用小端序存储模式,需要将数据编写为 \x78\x56\x34\x12如下图所示:

在这里插入图片描述

  • Step 2:接着将写入数据的地址内存复制给rax寄存器,改写汇编指令如下:
    在这里插入图片描述

  • Step 3:最后步过运行,即可在寄存器窗口查看到指定的数据已经被成功传递给rax寄存器。

在这里插入图片描述

  • Step 4 :最后尝试将rax寄存器的数据存储到另外一个地址0x7FF7D38B00,修改汇编指令最后查得该地址数据成功被改写。
    在这里插入图片描述
    在这里插入图片描述

最后,如果需要保存修改,导出补丁文件即可。之后将该补丁导入初始程序可以直接看到 “手动修改过的“ 数据已经固化在那个地址。【以上步骤中只有0x7FF7D380B170处数据满足,其他通过代码修改的数据则需要运行才会变化】

2.2 存储字符串常量

  • Step 1:编辑某个地址数据,修改为字符串”hello world“

tips:字符串hex编码末尾需要加\x00作为标识符。
在这里插入图片描述

  • Step 2:修改汇编代码将字符串所在地址复值给rax寄存器
    在这里插入图片描述
  • Step 3:查看寄存器窗口,rax寄存器值为存储字符串的地址,并且自动将字符串”hello world“解析出来了
    在这里插入图片描述

总结

  • 由于 .data段数据是固定的常量,因此如果手动修改了某个.data地址的数据,那么程序再次启动后该地址数据依然保存着被修改过的数据。
  • 相对的,.bss段数据保存的是变量,每次程序启动都会进行刷新,即修改后的数据再次启动会被清除

3. 在内存中存储变量

若想在内存中设置,则需要将数据存放在.bss段

  • Step 1:内存布局中双击.bss段,将数据转储到内存框区域,接着选定地址进行更改数据。
    在这里插入图片描述
  • Step 2:修改.bss地址数据,编写指令将数据赋值给rax寄存器
    在这里插入图片描述在这里插入图片描述
  • Step 3:接着尝试将rax寄存器的数据复制到另一个地址,成功修改.bss地址数据。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暮夜--

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值