PE文件代码注入
字数 1641 2025-08-22 12:23:30

PE文件代码注入技术详解

前言

本教程将详细介绍如何在64位Windows系统下对PE文件进行代码注入,实现修改扫雷程序使其弹出自定义消息框的功能。与传统的32位PE注入不同,本教程针对64位程序并考虑了地址随机化(ASLR)的情况。

前置知识

Win64调用约定

  1. 寄存器传参

    • 前四个整数或指针参数通过RCX、RDX、R8和R9寄存器传递
    • 前四个浮点参数通过XMM0、XMM1、XMM2和XMM3寄存器传递
  2. 栈传参

    • 超过四个的参数通过栈传递,从右至左依次入栈
  3. Shadow Space

    • 调用者需在栈上为前四个参数预留32字节空间
    • 即使被调用函数参数少于四个,仍需预留此空间
  4. 返回值处理

    • 整型或指针返回值通过RAX寄存器
    • 浮点返回值通过XMM0寄存器
    • 大返回值(如结构体)通过RCX传递指针,RAX返回该指针
  5. 其他规则

    • 非易失性寄存器(RBX、RBP、RDI、RSI、R12-R15)使用时必须保护
    • 调用者负责清理调用栈
    • 寄存器中整型参数右对齐

注入目标

构造并调用以下MessageBoxA函数:

MessageBoxA(0LL, "You are injected!", "PE injected", 0x1040u);

详细步骤

1. 准备工作

  1. 备份原始程序文件
  2. 准备工具:
    • IDA Pro(用于分析和修改)
    • 010 Editor(用于二进制编辑)
    • 目标程序(扫雷.exe)

2. 写入字符串

  1. 定位.rdata段末尾

    • 在IDA中查看.rdata段的结束地址
    • 或搜索字符串"InitializeSListHead"定位.rdata段
  2. 使用010 Editor添加字符串

    • 在.rdata段末尾添加两个字符串:
      • "PE injected"(标题)
      • "You are injected!"(内容)
  3. 确认字符串位置

    • 重新用IDA打开,检查字符串是否成功添加
    • 记录字符串的实际地址

3. 处理地址随机化(ASLR)

由于程序启用了ASLR,不能直接使用绝对地址,需要使用RIP相对寻址:

  1. 观察原程序如何引用.rdata段变量:

    • 例如:movdqa xmm0, cs:xmmword_140005820
    • 对应字节码:66 0F 6F 05 DE 40 00 00
  2. 计算相对偏移:

    • 偏移量 = 目标地址 - (指令地址 + 指令长度)

4. 添加MessageBoxA函数

原程序只有MessageBoxA的导入表项,没有函数实现:

  1. 参考exit函数实现

    • exit函数通过跳转到.idata段的对应函数实现
    • 字节码示例:FF 25 ...(jmp指令)
  2. 在.text段末尾添加跳转代码

    • 使用010 Editor添加跳转指令的字节码
    • 跳转目标为.idata段中的MessageBoxA地址
  3. 调整跳转地址

    • 先用任意有效地址添加代码
    • 在IDA中使用"Change Byte"功能调整具体地址

5. 注入调用代码

  1. 选择注入位置

    • 在开栈操作(sub rsp, 0E0h)之后注入
    • 确保堆栈平衡
  2. 构造汇编代码

mov r9, 1040h          ; uType = 0x1040
lea r8, [rip + 0x5662]  ; lpCaption = "PE injected"
lea rdx, [rip + 0x5663] ; lpText = "You are injected!"
mov rcx, 0             ; hWnd = NULL
call MessageBoxA
  1. 转换为字节码
49 c7 c1 40 10 00 00   ; mov r9, 1040h
4c 8d 05 62 56 00 00   ; lea r8, [rip + 0x5662]
48 8d 15 63 56 00 00   ; lea rdx, [rip + 0x5663]
48 c7 c1 00 00 00 00   ; mov rcx, 0
  1. 使用IDA修改

    • 使用"Change Byte"功能写入字节码
    • 使用"Assemble"功能添加call指令
  2. 调整偏移量

    • 根据实际字符串位置调整RIP相对偏移
    • 可能需要多次尝试才能正确定位

6. 验证结果

  1. 在IDA中反编译注入的代码,确认是否生成预期的MessageBoxA调用
  2. 运行修改后的程序,检查是否弹出预期消息框

注意事项

  1. 备份原始文件:修改前务必备份
  2. 地址对齐:确保添加的内容正确对齐
  3. 段大小调整:如果添加内容超出段大小,需要调整PE头中的段大小信息
  4. 校验和:修改后可能需要更新PE头的校验和
  5. 调试技巧:可以使用调试器动态调试,验证地址计算是否正确

总结

本教程详细介绍了64位PE文件代码注入的全过程,重点解决了ASLR带来的地址定位问题。通过RIP相对寻址和正确的调用约定实现,可以在现代64位Windows系统上成功注入代码并实现自定义功能。这种方法不仅适用于消息框注入,也可用于其他类型的代码注入场景。

PE文件代码注入技术详解 前言 本教程将详细介绍如何在64位Windows系统下对PE文件进行代码注入,实现修改扫雷程序使其弹出自定义消息框的功能。与传统的32位PE注入不同,本教程针对64位程序并考虑了地址随机化(ASLR)的情况。 前置知识 Win64调用约定 寄存器传参 : 前四个整数或指针参数通过RCX、RDX、R8和R9寄存器传递 前四个浮点参数通过XMM0、XMM1、XMM2和XMM3寄存器传递 栈传参 : 超过四个的参数通过栈传递,从右至左依次入栈 Shadow Space : 调用者需在栈上为前四个参数预留32字节空间 即使被调用函数参数少于四个,仍需预留此空间 返回值处理 : 整型或指针返回值通过RAX寄存器 浮点返回值通过XMM0寄存器 大返回值(如结构体)通过RCX传递指针,RAX返回该指针 其他规则 : 非易失性寄存器(RBX、RBP、RDI、RSI、R12-R15)使用时必须保护 调用者负责清理调用栈 寄存器中整型参数右对齐 注入目标 构造并调用以下MessageBoxA函数: 详细步骤 1. 准备工作 备份原始程序文件 准备工具: IDA Pro(用于分析和修改) 010 Editor(用于二进制编辑) 目标程序(扫雷.exe) 2. 写入字符串 定位.rdata段末尾 : 在IDA中查看.rdata段的结束地址 或搜索字符串"InitializeSListHead"定位.rdata段 使用010 Editor添加字符串 : 在.rdata段末尾添加两个字符串: "PE injected"(标题) "You are injected !"(内容) 确认字符串位置 : 重新用IDA打开,检查字符串是否成功添加 记录字符串的实际地址 3. 处理地址随机化(ASLR) 由于程序启用了ASLR,不能直接使用绝对地址,需要使用RIP相对寻址: 观察原程序如何引用.rdata段变量: 例如: movdqa xmm0, cs:xmmword_140005820 对应字节码: 66 0F 6F 05 DE 40 00 00 计算相对偏移: 偏移量 = 目标地址 - (指令地址 + 指令长度) 4. 添加MessageBoxA函数 原程序只有MessageBoxA的导入表项,没有函数实现: 参考exit函数实现 : exit函数通过跳转到.idata段的对应函数实现 字节码示例: FF 25 ... (jmp指令) 在.text段末尾添加跳转代码 : 使用010 Editor添加跳转指令的字节码 跳转目标为.idata段中的MessageBoxA地址 调整跳转地址 : 先用任意有效地址添加代码 在IDA中使用"Change Byte"功能调整具体地址 5. 注入调用代码 选择注入位置 : 在开栈操作( sub rsp, 0E0h )之后注入 确保堆栈平衡 构造汇编代码 : 转换为字节码 : 使用IDA修改 : 使用"Change Byte"功能写入字节码 使用"Assemble"功能添加call指令 调整偏移量 : 根据实际字符串位置调整RIP相对偏移 可能需要多次尝试才能正确定位 6. 验证结果 在IDA中反编译注入的代码,确认是否生成预期的MessageBoxA调用 运行修改后的程序,检查是否弹出预期消息框 注意事项 备份原始文件 :修改前务必备份 地址对齐 :确保添加的内容正确对齐 段大小调整 :如果添加内容超出段大小,需要调整PE头中的段大小信息 校验和 :修改后可能需要更新PE头的校验和 调试技巧 :可以使用调试器动态调试,验证地址计算是否正确 总结 本教程详细介绍了64位PE文件代码注入的全过程,重点解决了ASLR带来的地址定位问题。通过RIP相对寻址和正确的调用约定实现,可以在现代64位Windows系统上成功注入代码并实现自定义功能。这种方法不仅适用于消息框注入,也可用于其他类型的代码注入场景。