`
congfeng02
  • 浏览: 203882 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Windows汇编语言简明教程——基础篇

阅读更多

Windows汇编语言简明教程——基础篇
2007新版)

(V0.01

作者:TBsoft Software Studio2007

作者前言

本教程是笔者199912月草作《Win32汇编语言教程》的新版,目前国内关于Win32汇编语言的优秀书籍是罗云彬所著,电子工业出版社出版的《Windows环境下32位汇编语言程序设计》,本教程较为简单,仅作为初学者的参考资料使用。

目前Windows已经从Win32发展到了Win64,因此本教程名称改为《Windows汇编语言简明教程》,但本教程的基础篇仍然只涉及Win32汇编语言。

1引言

WindowsWin32)应用程序一般使用C语言编程,但是在某些需要进行底层编程的情况下,例如Win32应用程序执行机制分析、加密解密、反病毒等底层编程,或者对于某些速度要求较高的程序,需要使用汇编语言(甚至机器语言)直接开发Win32应用程序。Win32应用程序虽然和其他32位应用程序(例如32位保护模式DOS程序)一样可以使用386汇编语言和保护模式编程,但是Win32应用程序的执行机制与其他32位应用程序有一定的差别,例如消息循环、动态链接等,Win32汇编语言也有其特殊的编程方式。

为了使大家能对Win32汇编语言的基本编程方法有一定的了解,笔者草拟了本教程,旨在抛砖引玉,如果本教程能够带领你走进Win32汇编语言世界,笔者心愿足矣。

使用本教程,要求读者具有80386汇编语言和C语言开发Win32应用程序(Win32 SDK编程)的基础。

2进行Win32汇编语言编程的基本软件

进行Win32汇编语言编程,通常需要使用MASM 6.11以上版本的汇编器,以及Win32 SDK中的资源编译器(RC.EXE)和链接器(LINK.EXE),还需要用到Win32 SDK中的引入库文件(KERNEL32.LIBUSER32.LIBGDI32.LIB等)。

目前进行Win32汇编语言编程,准备开发环境较为简单的方法是使用MASM32软件包,可以使用MASM32 V7或者MASM32 V8版本。

MASM32软件包中已经包括了进行Win32汇编语言编程所需的各种软件,包括MASM 6.1x、资源编译器、链接器、包含文件、引入库等,还包括一个可以作为简单的汇编语言集成环境(IDE)的编辑器Quick EditorQEDITOR.EXE),这样在汇编链接Win32汇编语言源程序时可以无需使用命令行工具,很适合初学者使用。

安装MASM32很简单,运行安装文件INSTALL.EXE,选择安装目标驱动器然后确认安装即可完成安装,MASM32通常安装在安装目标驱动器根目录下的masm32子目录中。

使用Quick Editor,可以直接使用资源管理器或者在命令提示符(MS-DOS方式)下运行MASM32安装目录下的QEDITOR.EXE文件。

3 Win32汇编语言与Win32 API

使用Win32汇编语言开发Win32应用程序,使用的应用程序框架仍然是Win32 SDK编程的应用程序框架,只不过将通常使用的C语言换成80386汇编语言。

显然,使用Win32汇编语言开发Win32应用程序同样存在调用Win32 API的问题。和使用C语言进行Win32 SDK编程需要WINDOWS.H头文件以及其他头文件定义常量、数据结构和Win32 API一样,Win32汇编语言也需要包含文件(INC文件)定义常量、数据结构和Win32 API

MASM32中提供了使用Win32汇编语言开发Win32应用程序所需的包含文件,通常位于MASM32安装目录下的INCLUDE子目录中。Win32汇编语言源程序应该包含WINDOWS.INC文件,如果需要调用的Win32 API位于Win32系统DLL中,例如KERNEL32.DLLUSER32.DLLGDI32.DLL等,还需要包含对应的包含文件,例如kernel32.incuser32.incgdi32.inc等。

Win32 API中,凡是与字符或者字符串有关的API都有两种不同的类型:ANSI字符集APIUnicode字符集API,分别对应ANSI字符和Unicode字符,相应与字符或者字符串相关的数据结构也有两种不同的类型。Windows NT(包括Windows 2000XP及其以后版本)支持两种类型的APIWindows 9x通常只支持ANSI字符集APIANSI字符集API或者数据结构的实际名称以API或者数据结构名称末尾加字符“A”表示,Unicode字符集API或者数据结构的实际名称以API或者数据结构名称末尾加字符“W”表示,例如GetModuleHandle函数相应的ANSI字符集函数实际名称为GetModuleHandleA,相应的Unicode字符集函数实际名称为GetModuleHandleW

Win32 SDK中的WINDOWS.H头文件和其他头文件中,凡是与字符有关的API或者数据结构都有两种不同的定义,并使用条件编译和宏定义实现自动根据当前字符集使用对应的API或者数据结构定义。例如,下列程序段是WINBASE.H头文件中对GetModuleHandle函数的定义:

WINBASEAPI

HMODULE

WINAPI

GetModuleHandleA(

LPCSTR lpModuleName

);

WINBASEAPI

HMODULE

WINAPI

GetModuleHandleW(

LPCWSTR lpModuleName

);

#ifdef UNICODE

#define GetModuleHandleGetModuleHandleW

#else

#define GetModuleHandleGetModuleHandleA

#endif // !UNICODE

但是在MASM32提供的包含文件中,API或者数据结构名称标识符通常只对应ANSI字符集API或者数据结构,例如GetModuleHandle函数只对应GetModuleHandleA函数,这样可以保证在Windows 9xWindows NT下的兼容性,如果读者需要改用Unicode字符集API或者数据结构,则需明确使用Unicode字符集API或者数据结构,例如调用GetModuleHandleW函数,初学者可以暂时不去管它。

4非结构化的汇编语言和结构化的汇编语言

读者可能一听到“汇编语言”四个字就觉得十分头疼!汇编语言给人的第一印象就是一大堆难以看懂又不直观的指令,而且不结构化,大量的标号、无条件跳转指令(JMP)和条件跳转指令让你难以看懂程序;过程(或者函数)的调用参数传递又不直观,要么直接使用寄存器传递参数,不符合结构化程序设计原则;要么使用堆栈传递参数,又不能有效地检验参数类型……想必Win32汇编语言更麻烦吧!

汇编语言不是结构化编程语言,但不等于汇编语言不能进行结构化编程,MASM 6.0以上版本的汇编器对汇编语言进行了扩展,提供了很多结构化汇编语言伪指令,可以方便地实现汇编语言结构化程序设计,当你看完本教程以后,你可能会感觉到:Win32汇编语言并不比C语言麻烦多少。(如果读者看不懂本教程中的汇编语言源程序,可以对照MASM32中有关结构化汇编语言语法的帮助看)

下面是一个使用非结构化的汇编语言编写的Win32汇编语言程序,本程序没有使用结构化汇编语言伪指令。本程序的功能很简单:在屏幕上显示一个消息框。本程序只调用了两个Win32 APIMessageBoxExitProcess,源程序如下(MSGBOX1.asm):

.386

.MODEL flat,stdcall

OPTION CASEMAP:NONE

INCLUDE \masm32\include\windows.inc

INCLUDE \masm32\include\kernel32.inc

INCLUDE \masm32\include\user32.inc

INCLUDELIB \masm32\lib\kernel32.lib

INCLUDELIB \masm32\lib\user32.lib

.STACK 4096

.DATA

MsgTextBYTE'This is a simple Win32 application!',0

MsgTitleBYTE'Information',0

.CODE

_start:

moveax,MB_ICONINFORMATION

oreax,MB_OK

pusheax

leaeax,MsgTitle

pusheax

leaeax,MsgText

pusheax

xoreax,eax

pusheax

callMessageBox

xoreax,eax

pusheax

callExitProcess

END _start

使用Quick Editor汇编链接本程序的基本方法如下(下同):

1、启动MASM32中的Quick Editor,编辑汇编语言源程序,将上述汇编语言源程序全部输入或者粘贴,然后选择“File”—“Save As”菜单项将汇编语言源程序保存到一个目录下,注意该目录必须与MASM32的安装目录位于同一个驱动器上,汇编语言源程序文件的扩展名应该是“asm”。

2、选择Quick Editor中的“Project”—“Build All”菜单项即可汇编链接汇编语言源程序。汇编链接时会显示一个命令提示符窗口,如果没有出现错误或者警告信息,说明汇编链接成功,将会生成相应的OBJ文件和EXE文件。

3、选择Quick Editor中的“Project”—“Run Program”菜单项即可运行汇编链接后生成的EXE文件。

本程序汇编链接后,运行生成的MSGBOX1.exe文件,屏幕上将显示出一个消息框,消息框的标题是“Information”,消息框中显示的字符串是“This is a simple Win32 application!”。

Win32汇编语言源程序开始处通常有3条伪指令:

.386

.MODEL flat,stdcall

OPTION CASEMAP:NONE

指示汇编器汇编80386指令,并使用平坦内存模式(Win32内存模式)和stdcall函数调用方式(Win32标准函数调用方式),标识符区分大小写。源程序中使用.STACK.DATA.CODE伪指令分别定义堆栈、数据和代码。

Win32 API通常使用stdcall函数调用方式,stdcall函数调用方式中,函数的参数使用堆栈传递,函数调用之前参数自右向左进栈,函数调用返回时同时将参数出栈,函数返回值通过eax寄存器返回。

因此,程序中调用MessageBox函数和ExitProcess函数之前,都使用push指令将参数自右向左进栈,然后使用call指令直接调用Win32 API的入口点地址。本程序调用MessageBox函数显示消息框以后,调用ExitProcess函数终止程序的执行,ExitProcess函数的作用是终止当前进程。这种直接使用汇编语言指令调用Win32 API开发Win32应用程序的方式直接对应CPU的指令代码,最接近CPU硬件,但并不直观,编写程序一不小心就容易出错。

下面是一个与上述程序等价的Win32汇编语言源程序,使用了结构化汇编语言伪指令,实现了结构化的汇编语言。源程序如下(MSGBOX2.asm):

.386

.MODEL flat,stdcall

OPTION CASEMAP:NONE

INCLUDE \masm32\include\windows.inc

INCLUDE \masm32\include\kernel32.inc

INCLUDE \masm32\include\user32.inc

INCLUDELIB \masm32\lib\kernel32.lib

INCLUDELIB \masm32\lib\user32.lib

.STACK 4096

.DATA

MsgTextBYTE'This is a simple Win32 application!',0

MsgTitleBYTE'Information',0

.CODE

_start:

INVOKEMessageBoxA,0,ADDR MsgText,ADDR MsgTitle,MB_ICONINFORMATION or MB_OK

INVOKEExitProcess,0

END _start

本程序汇编链接后,运行生成的MSGBOX2.exe文件,结果与MSGBOX1.exe文件的运行结果完全相同。

MASM32提供的包含文件中,使用PROTO伪指令定义函数原型(与C语言中函数原型的定义相似),可以定义函数名、调用方式和参数,例如在user32.inc包含文件中定义MessageBox的函数原型:

MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD

MessageBox equ <MessageBoxA>

INVOKE伪指令调用由PROTO伪指令定义的函数,可以方便地传递参数和检查参数类型。包含文件中使用PROTO伪指令定义API函数,MSGBOX2.asm文件中使用INVOKE伪指令调用API函数,极其简单,连一条汇编语言指令也没有用到,可见MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令大大简化了Win32汇编语言编程。

5显示一个窗口的Win32汇编语言程序

学习过Win32SDK编程的读者编写的第一个应用程序可能就是显示一个窗口的C语言程序,笔者也编写了这样一个C语言程序,源程序如下(SDKSIMPL.c):

#include <windows.h>

#include <tchar.h>

static TCHAR szWindowClass[]=_T("SIMPLE");

LRESULT CALLBACK WndProc(HWND hWnd,

UINT message,

WPARAM wParam,

LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nShowCmd)

{

WNDCLASSEX wcex;

HWND hWnd;

MSG msg;

if(!hPrevInstance)

{

wcex.cbSize=sizeof(WNDCLASSEX);

wcex.style=CS_HREDRAW|CS_VREDRAW;

wcex.lpfnWndProc=(WNDPROC)WndProc;

wcex.cbClsExtra=0;

wcex.cbWndExtra=0;

wcex.hInstance=hInstance;

wcex.hIcon=LoadIcon(NULL,IDI_APPLICATION);

wcex.hCursor=LoadCursor(NULL,IDC_ARROW);

wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);

wcex.lpszMenuName=NULL;

wcex.lpszClassName=szWindowClass;

wcex.hIconSm=LoadIcon(NULL,IDI_APPLICATION);

if(!RegisterClassEx(&wcex))

return FALSE;

}

hWnd=CreateWindow(szWindowClass,

_T("Simple"),

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

NULL,

NULL,

hInstance,

NULL);

if(hWnd==NULL) return FALSE;

ShowWindow(hWnd,nShowCmd);

UpdateWindow(hWnd);

while(GetMessage(&msg,NULL,0,0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

}

LRESULT CALLBACK WndProc(HWND hWnd,

UINT message,

WPARAM wParam,

LPARAM lParam)

{

HDC hDC;

PAINTSTRUCT ps;

switch(message)

{

case WM_PAINT:

hDC=BeginPaint(hWnd,&ps);

EndPaint(hWnd,&ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd,message,wParam,lParam);

}

return 0;

}

现在笔者用Win32汇编语言程序实现本程序的功能,源程序如下(SDKSIMP1.asm):

.386

.MODEL flat,stdcall

OPTION CASEMAP:NONE

INCLUDE \masm32\include\windows.inc

INCLUDE \masm32\include\user32.inc

INCLUDE \masm32\include\gdi32.inc

INCLUDE \masm32\include\kernel32.inc

INCLUDELIB \masm32\lib\user32.lib

INCLUDELIB \masm32\lib\gdi32.lib

INCLUDELIB \masm32\lib\kernel32.lib

WinMainPROTO stdcall,:HINSTANCE,:HINSTANCE,:LPSTR,:DWORD

.STACK 4096

.DATA

WindowClassBYTE'SIMPLE',0

WindowTitleBYTE'Simple',0

hInst1HINSTANCE0

lpCmdLine1LPSTR0

.CODE

_start:

INVOKEGetModuleHandle,NULL

movhInst1,eax

INVOKEGetCommandLine

movlpCmdLine1,eax

INVOKEWinMain,hInst1,NULL,lpCmdLine1,SW_SHOWDEFAULT

INVOKEExitProcess,eax

WinMainPROChInst:HINSTANCE,hPrevInst:HINSTANCE,lpCmdLine:LPSTR,nShowCmd:DWORD

LOCALwcex:WNDCLASSEX

LOCALhWnd:HWND

LOCALmsg:MSG

.IF!hPrevInst

movwcex.cbSize,SIZEOF WNDCLASSEX

movwcex.style,CS_HREDRAW or CS_VREDRAW

movwcex.cbClsExtra,0

movwcex.cbWndExtra,0

movwcex.lpfnWndProc,OFFSET WndProc

moveax,hInst

movwcex.hInstance,eax

INVOKELoadIcon,0,IDI_APPLICATION

movwcex.hIcon,eax

INVOKELoadCursor,0,IDC_ARROW

movwcex.hCursor,eax

movwcex.hbrBackground,COLOR_WINDOW+1

movwcex.lpszMenuName,NULL

movwcex.lpszClassName,OFFSET WindowClass

INVOKELoadIcon,0,IDI_APPLICATION

movwcex.hIconSm,eax

INVOKERegisterClassEx,ADDR wcex

.IF!eax

moveax,FALSE

ret

.ENDIF

.ENDIF

INVOKECreateWindowEx,0,ADDR WindowClass,ADDR WindowTitle,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,hInst,NULL

movhWnd,eax

.IF!eax

moveax,FALSE

ret

.ENDIF

INVOKEShowWindow,hWnd,nShowCmd

INVOKEUpdateWindow,hWnd

.WHILETRUE

INVOKEGetMessage,ADDR msg,0,0,0

.BREAK.IF!eax

INVOKETranslateMessage,ADDR msg

INVOKEDispatchMessage,ADDR msg

.ENDW

moveax,msg.wParam

ret

WinMain ENDP

WndProcPROChWnd:HWND,message:UINT,wParam:DWORD,lParam:DWORD

LOCALhDC:HDC

LOCALps:PAINTSTRUCT

.IFmessage==WM_PAINT

INVOKEBeginPaint,hWnd,ADDR ps

movhDC,eax

INVOKEEndPaint,hWnd,ADDR ps

xoreax,eax

ret

.ELSEIF message==WM_DESTROY

INVOKEPostQuitMessage,0

xoreax,eax

ret

.ELSE

INVOKEDefWindowProc,hWnd,message,wParam,lParam

ret

.ENDIF

xoreax,eax

ret

WndProc ENDP

END _start

<spa

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics