用 NASM 编写代码(转)

为什么要使用 NASM?

使用 inline asm 固然方便, 但是却不利于代码的移植. 加上 VC 对新指令集(3D Now! ,XMM 等)的支持速度不够, 使用起来很不方便, 所以我们往往采用外部汇编. 如果你以前熟悉 MASM 或是 TASM, 也不必更换, 否则云风推荐 NASM. NASM 及其文档在 http://www.web-sites.co.uk/nasm 可以下载的到. 使用前最好通读一遍文档. 本文只会强调一些重点部分, 而补充一点遗漏, 而不会全盘复述. NASM 支持各种最新的指令集, 有相当强大的宏语言支持. 你将得到比 VC 的 inline asm 更为灵活的使用空间.

选一个好的编程环境

将 VC 当作 NASM 的编辑环境是完全可行的, 只需要在 Tools->Customize->Tools 下加入 NASM.exe 作为外部工具. 这里不详细介绍. 云风推荐 Editplus 这款小巧实用的编辑器. 你可以指定各种关键字的颜色, 云风自己制作的语法文件在这里, 写的还不完备, 很多 nasm 的宏语句尚未加入, 另外还加了一些自己制作的宏. 所以请各位读者酌情修改. 加入 Editplus 的方法是在 Tools-> Preferences->Files->Syntax 里增加一个语法文件. Editplus 支持 output 窗口信息捕获, 在 Tools->Preferences->Tools->User Tools 里增加 NASM.exe, 并选 capture output 就可以了. 当然如果你想制作单独的 win32/pe 文件, 而不是编译成 obj 供 VC link, 那么还需要一个 link 程序. 最好再配合 makefile 使用.

如何编译成可供 VC 连接的 obj

NASM 支持多种输出格式, 虽然 VC 号称是使用的 COFF 格式, 但实际上是有区别的. NASM 除了输出 COFF 格式外, 另外也支持 VC 的目标文件格式, 叫做 win32. 我在 Editplus 的 user tools 里设置的是

	-i I:/nasm/include/ -f win32 -o $(FileNameNoExt).obj $(FileName)
注: 这里的 -i 后是头文件的路径 将编译出来的 obj 加入 VC 的 project 里就可以了. 一般来说, 目标文件有三个段, 分别是 text/data/bss 段.
  • text 段放置代码, 是只读且可运行段
  • data 段放置静态数据, 这些数据会被放置入 exe 文件. 这个段是可读写, 但是不能运行的.
  • bss 段放置动态数据, 这些数据不被放入 exe 文件, 在exe文件被加载入内存后才分配的空间.
一个简单的程序框架是这样的:

[bits 32]

[section .text]
global _func
_func:
; 这里写func 函数的代码

[section .data]

[section .bss]
这里, 用 global 声明了一个可被 C 调用的函数 func (C 函数都有一个下划线前缀) _func: 是这个函数的入口. 在对应的 C 代码里想使用 func 这个函数, 还需要用 extern 声明 func 是外部函数. 如果在 C++ 里使用则还需要将函数说明成 C 调用方式, 方法是用 extern "C" { } 说明

参数的传入和处理

函 数的参数处理和其调用方式有关, C 的缺省调用方式是 _cdel 调用方式, C++ 的非静态成员函数采用的是 _thiscall, 关于各种调用方式的处理堆栈的方法, 在 MSDN 里可以查的到. 写 inline asm 可能很少涉及这些, 但是这里却必须搞清楚.

用纯汇编写 win32 程序等

写 WIN32 下的 win32/pe EXE, 我建议使用 Borland 的 obj 格式, 这是因为导入 DLL 中的 API 比较方便. 但是需要注意一些要点:

  • NASM 缺省使用的segment 是 USE16 的, 而在 win32 下必须使用 USE32, 而且必须指定段的类型. 所以必须在源文件头写上这样几行:

    [section .text class=code use32]
    [section .data class=data use32]
    [section .bss class=bss use32]
    %define __SECT__
  • Borland 的 OBJ 文件里可以指定开始的标签(..start), 一般可以写成这样:

    [section .text]
    ..start
    WinMain:
    你的代码将从 WinMain 这里开始运行.
  • 和 C 不一样, 你的主代码没有什么参数传入, 所以你必须自己来得到必要的参数. 比如:

    push dword 0
    call GetModuleHandle ; 获得 hInstance


    获得 Instance 句柄(到 eax).
  • 退出程序也不能直接用 ret, 因为你的程序是一个进程, 而不是一个函数, 所以必须调用:

    push dword 0 ; 退出码
    call ExitProcess
  • 上面的 GetModuleHandle 是 kernel32.dll 里的 API: GetModuleHandleA, 所以需要在汇编源代码中声明导出:
    	import GetModuleHandle kernel32.dll GetModuleHandleA
    extern GetModuleHandle

    另外 ExitProcess 也是这样:

    import ExitProcess kernel32.dll
    extern ExitProcess


    import 的具体用法请参考 NASM 的文档.

善于使用 NASM 强大的宏指令

这本是一个应该很丰富的 Section, 但是时间有限, 只举一个简单的例子, 定义下面这样一个宏:


%imacro callapi 1-*
%define %%api %1
%rotate -1

%rep %0-1
%rotate -1
push dword %2
%endrep

call %%api
%endmacro
然后, 比如你需要调用 MessageBox, 就可以这样写
callapi MessageBox,[hWnd],lpText,lpCap,0
  • hWnd 是一个全局变量保存的窗口句柄, 当然也可以是 0
  • lpText 是文字的字符串指针
  • lpCap 是MessageBox 的标题字符串
另外我自己还做了许多诸如处理函数参数, 保护堆栈等等方便使用的宏, 这里就不一一列出来了. 希望我引个头, 能让更多的朋友喜欢上 NASM

你可能感兴趣的:(汇编,api,exe,import,makefile,Borland)