让我们学习 x86 - 64 汇编!第 0 部分

这是一篇关于学习 x86 - 64 汇编的系列文章的第一部分,主要内容如下:

  • 背景:大学教授的 x86 汇编在 2008 或 2009 年已过时,作者决定在疫情期间学习并手动编写 x86 - 64 汇编程序,专注于 64 位 Windows 程序,忽略不再相关的遗留内容。
  • 工具介绍

    • 汇编器(Assembler):使用 Flat Assembler(FASM),它体积小、易获取和使用,有良好的宏系统和编辑器,x86 - 64 汇编语言没有统一标准,FASM 有其特点。
    • 调试器(Debugger):使用 WinDbg,它是一个独立的调试器,比 Visual Studio 的集成调试器更适合查看反汇编、内存和寄存器等,可从 Windows 10 SDK 中获取非 Windows - store 版本,对于本文的目的,两个版本基本可以互换。
  • 汇编思维方式

    • 总体架构视图:CPU 只知道做固定数量的事情,指令集是 CPU 设计要做的事情集合,指令简单,如将 8 位值写入内存位置或乘法运算等,本文基于一个简单的架构模型展开。
    • 寄存器(Registers):寄存器是内置在 CPU 中的特殊高速内存,x86 - 64 有 16 个通用寄存器,每个 64 位宽,低字节、字和双字可单独寻址,部分寄存器有特殊用途,如rsp用于栈指针,rsirdi用于字符串操作,还有riprflags等特殊寄存器。
    • 内存和地址(Memory and Addresses):内存可看作字节大小的数组,编号从 0 开始,即内存地址。在旧的 x86 处理器中,寄存器是 16 位宽,地址通过段寄存器和偏移量来获得,而在 x86 - 64 中,这些问题不存在,程序将内存视为连续的字节数组,但实际上是由操作系统和 CPU 共同维护的幻觉,每个进程有自己的虚拟地址空间,通过映射与物理地址关联,防止进程间干扰。
  • 第一个程序

    • 代码分析:使用 FASM 编写的第一个 x86 - 64 汇编程序非常简单,包括设置二进制格式、定义入口点、代码段、入口标签、中断指令int3和返回指令ret等,int3用于设置断点,ret用于返回。
    • 使用调试器:在 WinDbg 中打开程序,通过各个窗口查看程序状态,如反汇编窗口显示当前执行的代码,寄存器窗口显示寄存器内容,内存窗口显示程序内存内容,堆栈窗口显示当前调用栈等,按 F5 运行程序,按 F8 单步执行,可观察到寄存器的变化。但直接使用ret退出程序可能存在问题,因为 Windows 进程的终止条件不仅仅是主线程退出,还可能有其他后台线程,良好的 Windows 程序应调用ExitProcess函数。
  • PE 格式和 DLL 导入:要调用ExitProcess函数,需要导入 KERNEL32.DLL,PE 文件由节组成,导入信息存储在.idata节中,包括导入目录表(IDT)、导入查找表(ILT)和导入地址表(IAT)等,通过 FASM 代码定义了程序的.idata节。
  • 64 位 Windows 调用约定:64 位 Windows 上主要使用 Microsoft x64 调用约定,规则包括栈指针要对齐到 16 字节边界,前四个整数或指针参数通过寄存器传递,额外参数通过栈传递,调用者负责清理栈等,根据这些规则修改了调用ExitProcess的代码,再次在 WinDbg 中调试观察寄存器变化。

总的来说,这部分介绍了学习 x86 - 64 汇编的准备工作、基本概念和第一个简单程序,为后续更有趣的内容打下基础。

阅读 36
0 条评论