Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1411660
  • 博文数量: 416
  • 博客积分: 13005
  • 博客等级: 上将
  • 技术积分: 3297
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 16:26
文章分类

全部博文(416)

文章存档

2014年(1)

2013年(4)

2012年(46)

2011年(64)

2010年(12)

2009年(4)

2008年(40)

2007年(187)

2006年(58)

分类: WINDOWS

2007-10-30 09:47:32

1.前言:
       Win32ASM的编译器最常用的有两种:Borland公司的Tasm5.0和Microsoft的Masm6.11以上版本,两种编译器各有自己的优缺点
     
     
    但Masm在代码的优化上面好象比Tasm做得好,Masm和Tasm的宏语法有很多的不同,我的这个教程是以Masm格式写的。
 
2. Masm32的环境架设
   <1>下载与安装
       现在Masm32的最新版本是9.0的,我们可以在这里下载:
 
   要说明的是Masm32必须安装在磁盘分区跟目录,最好不要放在系统盘,以免重装系统时数据丢失!具体安装我就不多说了.
    <2>可视化编程
  
         Masm32是命令行的程序,每次写程序都要在命令行里输入这些重复的命令确实很累,能不能像VC那样可视化编程呢?
     
     答案是肯定的, RadASM就是这样的一个一个汇编集成开发工具,虽然名字是这么叫,其实它也是个通用的IDE,不但能做为常用的
   
     Masm,,Nasm,Tasm等编译器的IDE,还能做为VC,BC,LCC,html等的IDE,   下载地址:
        下载会来是个压缩包,直接解要到与Masm32相同的磁盘分区,如: "D:\" ,解压完后打开所在文件夹,找到名为"masm.ini"的配
      置文件,搜索"Paths"找到下面的内容:
     
        [Paths]
        $A=C:\Masm32
        $B=$A\Bin
        $D=$R\AddIns
        $H=$A\Help
        $I=$A\Include
        $L=$A\Lib
        $P=$R\Masm\Projects
        $S=$R\Masm\Sniplets
        $T=$R\Masm\Templates
        $M=$R\Masm\Macro
        $E=C:\OllyDbg
      把上面$A后面的改为Masm32的实际安装文件夹,这里改为\Masm32,如果你装了OD调试器把最后一行改为OD所在文件夹,这样以后的编
    
译的程序就可以直接在调试器中运行,保存文件,OK,一切就序,现在可以开始用汇遍实现可视化编程!
     另外还有一点,如果装有VC的话,Masm32可能会把库文件或头文件放到VC的文件夹里(可以用Windows自带的搜索功能找到这些文
件)这一点要注意!如果出现这种情况,自己把库文件(*.LIB)或(*.INI)头文件分别COPY到Masm32下的的lib和include目录即可!
 
 
3.基本框架
    我发现Iczelion的Win32汇编教程的说的非常详细,我就直接引用了:
    下面的程序段是一个框架, 若您现在还不知道这些指令的确切意义的话,没关系, 随后我就会给大家详细解释。
.386
.MODEL Flat, STDCALL
.DATA
    
     ......
.DATA?
   
    ......
.CONST
   
    ......
.CODE
   

框架就这么简单,好,我现在就给您解释:

.386
这是一个汇编语言伪指令,他告诉编译器我们的程序是使用80386指令集编写的。您还可以使用 .486、.586, 但最安全的还是使用.386。对
于每一种CPU有两套几乎功能相同伪指令: .386/.386P、 486/.486P、 586/.586P。 带P的指令标明您的程序中可以用特权级指令。特权级
指令是保留给操作系统的,如虚拟设备驱动程序。在大多数时间,您的程序都无须运行在RING0层,故用不带后缀P的伪指令已足够了。

.MODEL FLAT,STDCALL
.MODEL 是用来指定内存模式的伪指令,在Win32下,只有一种内存模型,那就是FLAT。 STDCALL 告诉编译器参数的传递约定。参数的传递约
定是指参数传达时的顺序(从左到右或从右到左)和由谁恢复堆栈指针(调用者或被调用者)。在Win16下有两种约定:C 和 PASCAL。C 约定规
定参数传递顺序是从右到左,即最右边的参数最先压栈,由调用者恢复堆栈指针。
例如:为调用函数 foo ( int first_param, int second_param, int third_param ); 按C约定的汇编代码应该是这样的:
push [third_param]
push [second_param]
push [first_param]
call foo
add esp, 3 * 4 ;调用者自己恢复堆栈指针

PASCAL约定和C约定正好相反,它规定参数是从左向右传递,由被调用者恢复堆栈。Win16采用了PASCAL约定, 因为PASCAL约定产生的代码量
要小。当不知道参数的个数时,C约定特别有用。如在函数wsprintf () 中, wsprintf预先并不知道要传递几个参数,所以它不知道如何恢
复堆栈。STDCALL是C约定和PASCAL约定的混合体,它规定参数的传递是从右到左,恢复堆栈的工作交由被调用者。Win32只用STDCALL约定,
但除了一个特例,即:wsprintf。
.DATA .DATA? .CONST .CODE
上面的四个伪指令是"分段"(SECTION)伪指令。我们上面刚讲过Win32下没有"段"(SEGMENT)的概念,但是您可以把您的程序分成不同的"分段
", 一个"分段"的开始即是上一个"分段"的结束。WIN32中只有两种性质的"分段":DATA和CODE

其中DATA"分段"又分为三种:
.DATA 其中包括已初始化的数据。
.DATA? 其中包括未初始化的数据。比如有时您仅想预先分配一些内存但并不想指定初始值。使用未初始化的数据的优点是它不占据可执行文
件的大小,如:若您要在 .DATA? 段中分配10,000字节的空间,您的可执行文件的大小无须增加10,000字节,而仅仅是要告诉编译器在装
载可执行文件时分配所需字节。
.CONST 其中包括常量定义。这些常量在程序运行过程中是不能更改的。 应用程序并不需要以上所有的三个"分段", 可以根据需要进行定义

.CODE 这是代码"分段"。
<译者注:实际上,分段并不是象在 Dos 下一样,为不同的段分别指出不同的段寄存器,因为 Windows 下只有一个 4GB 的段,Windows 程
序中的分段表现在当程序装载时,赋予不同的分段不同的属性,比如说当你的程序加载时,对于 Ring3 程序来说,.code 段是不可写的,而
.data 段是可写的,如果你尝试象在 Dos 下一样写自己的代码部分,你会得到一个蓝屏错误>

 
4.第一个程序
 
   打开RadASM,文件→新建工程,调出工程向导,如下图所示:

   在工程名称里输入一个名称,比如"MyBox",在工程说明输入一段描述,如:"我的第一个程序",然后点击下一步
随后出现一个"模版"向导,这里选择"无",下一步出现"文件及目录",如果你不想有备份就把"BAK"的勾去掉,然后
再下一步点完成.
    再随后出现的右栏中双击*.Asm,这里是MyBox.Asm,然后把下面的代码COPY到里面
代码:.386
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;         Include 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;          数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
MsgBoxCaption   db "Iczelion Tutorial No.2",0
MsgBoxText        db "Win32 Assembly is Great!",0
.code   ;代码段
start:
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start

  

然后 构建→构建并运行,是不是弹出了一个对话框啊,呵呵,原来W32ASM也是钓API来用的
难道不是吗?看下面几句:
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL

其中,invoke是编译器伪指令,取代PUSH,CALL的调用并做检查如果参数错误就会给出错误提示!

......

下期我们来说说怎样用汇编写个窗口程序
参考资料: Iczelion的Win32汇编教程

待续......
 
 
更新内容:
一个简单的对话框窗口程序
   在传统的教程中,写一个窗口程序对新手来说是非常复杂的,必须调用大量API,通过以下几个步逐:
     1.得到您应用程序的句柄(必需);
     2.得到命令行参数(如果您想从命令行得到参数,可选);
     3.注册窗口类(必需,除非您使用 Windows 预定义的窗口类,如 MessageBox 或 dialog box;
     4.产生窗口(必需);
     5.在桌面显示窗口(必需,除非您不想立即显示它);
     6.刷新窗口客户区;
     7.进入无限的获取窗口消息的循环;
     8.如果有消息到达,由负责该窗口的窗口回调函数处理;
     9.如果用户关闭窗口,进行退出处理。
    对比那些高极编程语言,ASM算是比较复杂的,也因此吓跑了不少新手,在我的这篇文章中我将尽量
避开这个,以通俗易懂类VC的编程方法解决这个问题,当然我还是建议大家看一下完全用API建一个窗
口的方法参考一下"汇编全接触"里的Iczelion的Win32汇编教程,里面有详细的资料,我将在这里告诉
大家怎样用资源文件结合RadAsm实现可视化编程,下面让我们来了解下资源文件:
Windows 的资源文件
  
  不管在Dos下编程还是在Windows下编程,我们总是要用到除了可执行文件外的很多其他数据,
如声音数据,图形数据,文本等等,在Dos下编程,我们可以自己定义这些文件的格式,但这样一来
就造成了很多资源共享的问题,大家可能还记的Dos下的很多游戏,它们的图形都是按自己的格式存
放的,你无法用标准的看图软件来看。也无法把它另存为其他格式。虽然在Win32编程中,我们仍然
可以这样做,但Win32编程给了我们一个方案 ---- 就是格式统一的资源文件,把字符串、图形、对
话框包括上面的按钮,文本等定义到一个资源文件中,就可以方便的在不同的文件中使用它,最重
要的是,如果我们用自己的文件格式,使用时就要涉及到这些文件的读写操作,比较复杂,但使用
资源文件时,Windows提供了一系列的API来装入资源。非常方便。
......

在程序中使用资源
     在程序中,要使用资源之前必须先装如内存,Windows定义了一系列的API来装入资源,如
LoadMenu,LoadString,LoadBitmap 等等,);   这些Load函数的返回值是一个句柄,调用参数中一
般至少为两项: hInstance 和ResouceName,这个 ResouceName(如BitmapName,MenuName)就是在资
源文件中的 #define 指定的值,如果你用 #define MY_ICON 10/ MY_ICON ICON "Main.ico" 定义
了一个图标,那么在程序中要使用 Main.ico 图标就可以用 LoadIcon(hInstance,10) 来装入已经
定义为10号的图标文件。另一个参数 hInstance 是执行文件的句柄,它对应资源所在的文件名,你
可以在程序开始执行时用 invoke GetModuleHandle,NULL 获得 hInstance。另外一些资源并不是显
式地装入的,如对话框资源,它是在建立对话框的函数中由Windows自己装入的,如下面例子中的
invoke DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,0 ,是在屏幕上显示一
个资源文件中已经定义好了的对话框,就并不存在 LoadDialogBox 之类的API来先装入对话框
介绍了这么多相关的东西,现在让我们来进入正题:

1.打开第一节练习的工程,方法是:文件-打开,选择"D:\RadASM\Masm\Projects\MyBox\MyBox.rap",
 如果已经没有了,请新建一个!
2.再随后出现的右栏中双击*.Asm(MyBox.Asm),把下面的代码覆盖到里面:
代码:    
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;   Programmed by hacker0058,
;   Website:
;              第一个窗口程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 
.486
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;         Include 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include kernel32.inc
include shell32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
includelib shell32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;         Equ 数据 RC资源
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN         equ              1000
.data
.data?
hInstance HINSTANCE ?  
.code
;********************************************************************
_ProcDlgMain         proc         uses ebx edi esi, \
                 hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD
             mov eax,wMsg
    
         cmp eax,WM_CLOSE          ;对话框关闭时
         je   boxClose
         cmp eax,WM_INITDIALOG     ;对话框初始化时
         je   boxStart
         cmp eax,WM_COMMAND       ;按下对话框上的按钮时
         je commoand
   
    retFalse:
            mov         eax,FALSE
            ret    
                  
    boxClose:
           invoke EndDialog,hWnd,NULL
           jmp retTrue
          
    boxStart:
                       
               jmp retTrue
                   
    commoand:
            mov eax,wParam
        
           ;代码
            
      retTrue:
        
            mov         eax,TRUE
            ret        
_ProcDlgMain         endp
;********************************************************************
start:
   invoke GetModuleHandle,NULL
   mov hInstance,eax
   invoke DialogBoxParam,eax,DLG_MAIN,NULL,offset _ProcDlgMain,0    ;显示对话框
   invoke ExitProcess,NULL
;********************************************************************
end         start

3.在右栏中选择"MyBox.Asm",然后单击右键,新建→对话框,接着会弹出一个"添加新对话框"的对话
   框,随便输入一个文件名(如:dlg),点保存,就到了对话框修改界面,如图:
4.你可以给对话框改个名字,方法是:右下角属性里的"caption"项,如:"我的第一个窗口程序"
   当然改完后别忘了"回车"保存!
最后,构建→构建并运行,是不是看到了一个标题为"我的第一个窗口程序"的窗口程序,如图:

下面我们就重点来讲解一下:

         DLG_MAIN         equ              1000
 
   定以资源文件,其中1000是对话框属性里的ID,前面的"DLG_MAIN"可以随便取个名字,只要不违反
MASM的命名规则就行
    _ProcDlgMain         proc         uses ebx edi esi, \
    proc 的语句应该是不陌生的,要重复讲解一下的是 uses 和 下面的参数,uses 下的寄存器表
示要编译器自动插入保存及恢复这些寄存器的指令,\ 是在 Masm32 中接下一行的符号,表示下一
行是本行的继续内容,以避免一行中的内容过长。下面的 hWnd:DWORD 等语句定义了调用这个子程
序的参数,如果有以下定义 MyProc proc dwPara1:DWORD,dwPara2:DWORD,dwPara3:DWORD,然后你
用 invoke MyProc 1,2,3 来调用它,那么,1,2,3 将分别被赋值给 dwPara1,dwPara2,dwPara3,你
可以在子程序中使用这些传递过来的参数。如果参数的类型是双字,那么:DWORD可以省略
     最后要讲到的就是 DialogBoxParam 这个API了,在Windows中,所有的窗口都要指定一个子程
序,当Windows检测到鼠标、定时器等和这个窗口有关的动作时,它回调用这个子程序,这就是
Windows基于消息的体系的最基本的概念,换句话说,在Dos下,我们通过INT指令调用系统,而在
Windows 下,有很多时候是你指定子程序地址让Windows来调用你。
    invoke DialogBoxParam,hInstance,DLG_MAIN,NULL,offset
  _ProcDlgMain,0中的 offset _ProcDlgMain 就指定了如果有消息发生,Windows就来执行这个
子程序,参数中的 DLG_MAIN 就是在资源文件中定义的对话框模板ID。 hInstance 是对话框所在的
资源文件的句柄。
   
  另外,在_ProcDlgMain 子程序中,Windows传给我们4个参数hWnd,wMsg,wParam,lParam,其
中,hWnd是对话框的窗口句柄,wMsg表示现在发生的消息事件,如这个对话框初始化时Windows会以
WM_INITDIALOG为消息调用,关闭时为WM_CLOSE,按下对话框上的按钮时为WM_COMMAND等,wParam和
lParam是附加的参数,对应不同的消息对应不同定义,具体可以参考:
   Win32Programmer'sreference。

课后练习:  

根据下面的提示写一个"QQ任我聊"程序,下次再见时我会仔细讲解:

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;         Equ 数据 RC资源
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

DLG_MAIN         equ              1000
IDC_EDT          equ              1001          ;编辑框
IDC_IDOK         equ              1002          ;按纽
QQ_N             equ              12            ;设置QQ号的最大位数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
temp db "tencent://message/?uin=",'%s',"&Site=im.qq.com&Menu=yes",0

.data?
posBuffer   db 50+QQ_N dup (?)
qqtemp db QQ_N dup (?)
hInstance HINSTANCE ? 

 invoke GetDlgItemText,hWnd,IDC_EDT ,addr qqtemp,QQ_N   ;取用户输入到文本
                            
   invoke wsprintf,addr posBuffer,addr temp,addr qqtemp ;连接文本串
                               
   invoke ShellExecute,NULL,NULL,addr posBuffer,NULL,NULL,SW_HIDE   ;执行IE命令
阅读(2715) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~