You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

x64汇编调用WinExec:calc.exe无效仅calc有效的问题排查

问题:WinExec调用中字符串构造的异常问题

背景

正在学习Shellcode编写,曾接触过汇编但知识点不够系统,容易遗忘。

环境配置

gcc --version
gcc.exe (Rev3, Built by MSYS2 project) 13.2.0
nasm --version
NASM version 2.16.01 compiled on Mar 20 2023

# 编译命令
nasm -fwin64 -o file.obj file.asm
gcc -o file.exe file.obj

问题描述

编写调用WinExec的汇编程序时,传入参数calc.exe无法启动计算器,但仅传入calc时可以正常运行。尝试多种栈上构造字符串的方式,多数程序能运行但无法启动计算器,部分会触发段错误。

示例代码

; define arch
bits 64
; make pic adresses
default rel

segment .data

segment .text
global main
extern ExitProcess
extern WinExec

extern printf

main:
    push    rbp
    mov     rbp,rsp
    sub     rsp, 32

    push    0
    push    0x636C6163
    
    ; 用于添加'.exe'的变体代码
    ;mov     dword [rsp + 4], 0x6578652e

    mov     rcx, rsp
    mov     rdx, 1
    call    WinExec

    xor     rax,rax
    call    ExitProcess

尝试的构造方式及结果

  • 先push calc,再用mov指令添加.exe:程序正常退出但未启动计算器
  • 构造完整路径C:\Windows\System32\calc.exe:触发KernelBase.dll段错误
  • 按word块push ca、lc、.e、xe:程序正常退出但未启动计算器
  • 按word块push ca、lc:触发KernelBase.dll段错误
  • 按dword push calc:唯一能正常启动计算器的方式

疑问

为何会出现这种差异?是否与LSB(最低有效字节)有关?


解答

核心问题:64位栈操作特性、字节序与栈对齐的三重错误

你的问题本质是对x86_64(Win64)环境下的栈操作、字节序规则以及调用约定理解不到位,具体拆解如下:

1. 64位push指令的隐性扩展

Win64下,push指令对32位/16位操作数会自动零扩展为64位,压入8字节数据,而非你预期的4/2字节。

  • 你写的push 0实际压入8字节全0,push 0x636C6163实际压入0x00000000636C6163(8字节)。
  • 这导致栈中字符串的布局完全偏离预期:比如你以为只加了一个空终止符,实际栈中会有大量冗余空字节;按word块push时,每个操作都会额外添加6个空字节,直接破坏字符串结构。

2. 小端序构造错误

x86架构采用小端序存储,即字符串的字节会按反向顺序存入双字/字中。你在构造长字符串时,要么字节顺序搞反,要么push顺序错误:

  • 比如构造.exe时,你用mov dword [rsp+4], 0x6578652e是正确的(小端序下,该双字会在栈中生成.exe的正确字节序列),但结合前面的8字节push操作,最终字符串会变成calc.exe后跟着4个冗余空字节——虽然这本身不影响终止符,但结合栈对齐问题就会触发异常。
  • 按word块pushca、lc时,你错误地按正向顺序push,导致栈中生成的字符串是cacl而非calc,WinExec自然找不到对应程序。

3. Win64调用约定的栈对齐要求

Win64规定,调用任何API前,栈指针rsp必须保持16字节对齐。你的代码中:

  • push rbp后rsp已偏离16字节对齐,后续sub rsp,32、push操作进一步破坏对齐,调用WinExec时栈处于不对齐状态,触发未定义行为——部分场景下运气好能运行(比如仅pushcalc),但多数情况会出现段错误或功能异常。

正确构造calc.exe的示例代码

直接在预留的栈空间中精确写入字符串,避免push指令的隐性扩展问题,同时保证栈对齐:

bits 64
default rel

segment .text
global main
extern ExitProcess
extern WinExec

main:
    push    rbp
    mov     rbp,rsp
    sub     rsp, 32        ; 预留阴影空间

    ; 在阴影空间中构造calc.exe字符串(含空终止符)
    mov     dword [rsp+16], 0x636C6163  ; 小端序存储"calc"
    mov     dword [rsp+20], 0x6578652E  ; 小端序存储".exe"
    mov     byte  [rsp+24], 0x00        ; 空终止符

    mov     rcx, rsp+16                 ; 字符串首地址
    mov     rdx, 1                      ; SW_SHOWNORMAL

    sub     rsp, 8                      ; 调整栈至16字节对齐
    call    WinExec
    add     rsp, 8                      ; 恢复栈

    xor     rax,rax
    call    ExitProcess

内容的提问来源于stack exchange,提问作者Andrei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 07:19:56