win11下nasm编写汇编及链接方案

一、安装nasm并编写代码

安装nasm添加PATH环境变量

在终端输入nasm -v看到版本即为安装成功

依旧从简单的输出开始,编写如下代码:

asm 复制代码
      global  main  //定义全局符号
        extern  puts  //外部函数
        section .text  //text节区
main:  //符号main的开始,之后程序链接时指定从这开始,OEP将会指向这
        sub     rsp, 28h                        ; 影子空间
        mov     rcx, message                    ; rcx传第一个参数
        call    puts                            ; 调用puts
        add     rsp, 28h                        ; 删除影子空间
        ret
message:  ;变量符号
        db      'Hello', 0                      ;\0结尾的字符串

对于一个可执行文件生成过程:

高级语言-->c语言-->预处理define等宏-->编译为汇编代码-->汇编为obj文件-->链接为可执行文件如exe等

使用命令进行汇编,其中O1表示优化,为可选参数

asm 复制代码
nasm -f win64 hello.asm -o hello.obj         -O1

此时生成的是.obj文件


二、使用gcc.exe或link.exe链接

1.使用gcc链接为例

可以使用电脑上的gcc.exe链接或link.exe链接(在安装一些软件时会附带安装一些gcc,如编译工具和编辑器等)。但是链接后都太大

这里以gcc为例

  • mconsole
    指定 Windows 子系统为控制台程序。这个选项告诉链接器生成一个控制台应用程序(入口函数为 main 或 wmain,启动时会自动分配一个控制台窗口)。与之相对的是 -mwindows(GUI 程序,没有控制台窗口)。
asm 复制代码
gcc hello.obj -o hello.exe -mconsole -lmsvcrt -v
  • lmsvcrt
    链接时去查找并链接 msvcrt 库。具体来说:
    -l 选项表示链接一个库,库文件的实际名称在 Unix 风格下是 lib.a 或 .lib。

msvcrt 对应 Microsoft Visual C++ 运行时库的导入库(例如 MinGW 提供的 libmsvcrt.a),该库用于动态链接到系统自带的 msvcrt.dll(Windows 上最基础的 C 运行时)。

加上这个选项后,程序运行时会使用 MSVC 的 C 运行时(如 printf、malloc 等函数的实现),这对于由 MSVC 编译出的 .obj 文件来说是自然且必要的,否则可能出现符号未定义或运行时冲突。

不显式指定 -lmsvcrt,大多数情况下 GCC(MinGW)仍会自动链接,通常不会出错。但这取决于你具体的 MinGW 发行版和配置

可以使用-v参数查看详细信息,这里我用的是strawberry_per的gcc

大至约64kB,很大一部分原因是因为静态链接了C函数库

查看发现主要是许多节区大小占据大量空间,一方面节区数量多,另一方面文件对齐导致PE文件臃肿

2.链接后大体积原因

为什么会这样?

  1. 链接器默认引入了一大堆库(包括静态库)

    即便有"动态链接 C 库"(-lmsvcrt),这些库里有几个是默认静态链接的:
    libgcc.a、libgcc_eh.a ------ GCC 的低级运行时(除法和模运算、异常展开、栈回溯等)
  • libmingw32.a: MinGW 提供的启动辅助代码
  • libmingwex.a : MinGW 扩展数学函数等
  • libmoldname.a : 一些符号别名
    这些静态库的体积会被直接嵌入你的 hello.exe
  1. 工具链实际上是 UCRT 版本,却链接了 msvcrt

    这是一个 UCRT 版本的 MinGW(名字里有 ucrt),默认运行时应该是 UCRT(libucrt.a),但链接命令里硬编码的是 -lmsvcrt。
    可能导致两种 CRT 的支持代码可能都被部分引入,增大体积
  2. 调试信息------最大的隐形杀手
    hello.obj 由 MSVC 生成,很有可能带有调试信息节(.debugS、.debugS、.debugS、.debugT)。GCC 链接时没有加 -s 或 -Wl,--strip-all,这些调试节会被完整拷贝到 hello.exe 里,让体积成倍增长
  3. 没有优化体积和去除无用节
    链接命令里没有:
  • -Os(优化体积)
  • -s(去除符号表)
  • -Wl,--gc-sections(垃圾收集未用到的节)
  • -fno-ident(去掉 GCC 版本注释节)
  • -fno-exceptions(如果是 C++,去异常表)
体积来源 大约贡献 可优化?
静态链接的 libgcc / libmingwex 20~100 KB 可以强制动态链接 libgcc(-shared-libgcc),但需附带 DLL
CRT 启动文件 (crt2.o 等) 3~10 KB 不可避免
MSVC 的 .obj 调试信息 可能 >100 KB 用 strip hello.exe 或编译时 gcc -s 去除
重复的库 / 多余符号 1~5 KB 加 -Wl,--gc-sections 配合 -ffunction-sections 编译源文件(对 .obj 无效,因为是预编译的)
PE 对齐与元数据 1~2 KB 基本不可减

3.去除符号表后的效果

加上-s去除符号表后仅剩余22kB

以下为符号表去除前后的区别

三、使用golink链接

GoLink是一款专注于提高开发效率和系统可维护性的链接管理工具

为实现极小的大小,这里下载一个golink.exe ,同样添加环境变量

bash 复制代码
golink /entry:main /console kernel32.dll msvcrt.dll hello.obj

其中/entry指定入口点,/console指定控制台应用,之后两dll指定不要静态链接kernel32.dll和msvcrt.dll( Microsoft Visual C++ 运行时库)

此时即可运行程序,仅1.5kB,连文件中的一页4k都不到

调试到入口点,可以看到仅几条机器指令

此时可以看到只有一个start符号的函数

节区数据

最后效果

相关推荐
iCxhust2 小时前
【无标题】8086/8088裸机对于学习微机原理的重要意义
汇编·单片机·嵌入式硬件·嵌入式·微机原理
鸽芷咕3 天前
DOSBox 汇编环境搭建完整教程:安装配置 + MASM/LINK/DEBUG 工具链配置详解
汇编
Gofarlic_OMS3 天前
UG/NX许可证管理高频技术问题解答汇编
java·大数据·运维·服务器·汇编·人工智能
iCxhust3 天前
如何在汇编中修改CS:IP
汇编·单片机·嵌入式硬件·51单片机·微机原理
枷锁—sha4 天前
【CTFshow-pwn系列】03_栈溢出【pwn 073】详解:静态编译下的自动化 ROP 链构建
网络·汇编·笔记·安全·网络安全·自动化
wechatbot8885 天前
极客互动企业微信聚合聊天与接口能力全景展示
汇编·微信·企业微信·ipad
枷锁—sha6 天前
【CTFshow-pwn系列】03_栈溢出【pwn 072】详解:无字符串环境下的多级 Ret2Syscall 与 BSS 段注入
服务器·网络·汇编·笔记·安全·网络安全
李日灐7 天前
<5> Linux 开发工具:包管理 + Vim 实操 + GCC 编译流程 + 静态与动态链接详解
linux·运维·服务器·面试·vim·gcc
iCxhust8 天前
8088汇编测试程序 (MASM/TASM) — 显示 “HELLO 8088!“ + “LCD1602 OK“
汇编·单片机·嵌入式硬件·51单片机·微机原理