32位汇编第二讲,编写窗口程序,加载资源,响应消息,以及调用C库函数

 

       32位汇编第二讲,编写窗口程序,加载资源,响应消息,以及调用C库函数

(如果想看所有代码,请下载课堂资料,里面有所有代码,这里会讲解怎么生成一个窗口程序)

一丶32位汇编编写Windows窗口程序

首先我们知道32位汇编是可以调用Windows API的,那么今天我们就调用windowsAPI来写一个窗口程序

如果你有windows开发知识,那么就很理解了,如果没有,那么跟着我写,跟着步骤去写,那么也可以写出来

首先我们要编写一个窗口程序(使用SDKAPI编写)有几个步骤

1.设计窗口类

2.注册窗口类

3.创建窗口

4.显示窗口

5.更新窗口

6.建立消息循环

7.窗口过程函数

总共需要这几步,每不单独做个讲解.

1.设计窗口类

设计窗口类,顾名思义,就是你要给你的窗口设置一些属性,比如我窗口的风格,名字,类名,图标,菜单什么的

这里windows为我们提供了一个结构体

WNDCLASS结构体,里面就包含了这些属性,我们只需要依次添加,看下WNDCLASS里面的内容

WNDCLASS 
This structure contains the window class attributes that are registered by the RegisterClass function. 

typedef struct _WNDCLASS {
 UINT style;                   //窗口的风格
 WNDPROC lpfnWndProc;              //窗口消息处理的过程函数
 int cbClsExtra;                 //额外内存申请(不重要)
 int cbWndExtra;                 //额外内存申请(不重要)
 HANDLE hInstance;                //程序的实例句柄
 HICON hIcon;                   //图标
 HCURSOR hCursor;                 //资源光标
 HBRUSH hbrBackground;             //窗口背景
 LPCTSTR lpszMenuName;             //窗口名字
 LPCTSTR lpszClassName; } WNDCLASS ;     //窗口类名

对于上面的结构体,我们只需要里面的参数需要什么内容即可

使用汇编编写:

include windows.inc    
include user32.inc                        ;加载要使用的头文件和lib库,至于这些是什么,下面仔细讲解
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

.386
.model FLAT,stdcall
option casemap:none


.const                                    ;常量区
g_szClassName db "ClassName",0       ;窗口类的类名名称
g_szWndName db "WndName",0       ;窗口的名称
.data ;初始化的数据区 .code ;代码区 WinMain proc ;程序启动的时候执行的入口函数 ;设计我们的窗口类 LOCAL @wc:WNDCLASS     ;定义WNDCLASS,对里面的属性修改
LOCAL @hInstance : HINSTANCE         ;定义程序的实例句柄
LOCAL @hWnd:HWND                 ;定义我们的hWnd,接受创建窗口的时候的返回值\
LOCAL @msg:MSG                 ;定义消息循环的结构体

;思路,第一步,取得窗口的实例句柄,给hInstance
invoke GetModuleHandle,NULL         ;调用API即可获取,返回值默认放在Eax当中
mov @hInstance,eax ;check(为了排版,不写检查了)....
                         ;开始给WNDCLASS各种属性赋值

mov @wc.style, CS_VREDRAW or CS_HREDRAW;   ;默认,垂直和水平拉伸窗口,窗口内容重新布局和绘制
mov @wc.lpfnWndProc, WindowProc;          ;窗口过程函数  
mov @wc.cbClsExtra, 0;              ;额外内存
mov @wc.cbWndExtra, 0;              ;额外内存
mov eax, @hInstance               ;实例句柄的值给eax,下方设置进去,(内存到内存不可以,所以中转)
mov @wc.hInstance, eax;            ;给窗口设置实例句柄
mov @wc.hIcon, NULL;              ;图标资源为NULL
mov @wc.hCursor, NULL;             ;鼠标光标为NULL
mov @wc.hbrBackground, COLOR_ACTIVEBORDER; ;设置背景画刷
mov @wc.lpszMenuName, NULL;          ;设置菜单名称
mov @wc.lpszClassName,offset g_szClassName;;设置窗口类名名称

                          ;这里就设计完成了,下一步就要注册这个窗口类,到系统中,所以这里为中间线,注册窗口的代码我会接着这下面继续写,上面的代码就不重复写了,下面的几个步骤是一样的,最后在把整个的汇编代码贴上

WinMain endp

end WinMain       

2.剩余步骤一起执行

;对于下方的API不熟悉的可以调用MSDN,下载地址在 www.w1x8.com,因为文件太大,所以不上传到课堂资料中了
;
注册窗口类 invoke RegisterClass,addr @wc ;在这里我们使用伪指令addr,他的作用是自动帮我们计算局部变量所在的内存地址,如果对指令不挑明白,可以打开OD找到这个地方看下指令是怎么写的 ;创建窗口 invoke CreateWindowEx, ;这里注意一下只能使用CreateWindowEx,因为.inc文件中没有CreateWindows
0                ;窗口的扩展风格
offset g_szClassName,   ;窗口的类名
offset g_szWndName,     ;窗口的标题名字 WS_OVERLAPPEDWINDOW,     ;窗口的风格 CW_USEDEFAULT,        ;下面4个默认的分别是否是 窗口的高度 宽度 ,窗口的x,y坐标 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,              ;窗口父类的实例句柄 NULL,              ;窗口的菜单 @hInstance,          ;程序的实例句柄 NULL              ;创建窗口的额外参数
mov @hWnd,eax           ;创建窗口后返回一个窗口句柄,返回值地方在eax中,这个上面定义了
;显示窗口
invoke ShowWindow,@hWnd,SW_SHOW ;显示窗口
;更新窗口
invoke UpdateWindow,@hWnd
;建立消息循环
.while TRUE
  invoke GetMessage,addr @Msg,NULL,0,0
  ;判断
  .if (eax == -1)
     .break
  .endif

   invoke TranslateMessage, addr @msg  ;把虚拟键码,转化为键盘按键
   invoke DispatchMessage, addr @msg  ;把msg中的消息,放到窗口过程中执行

.endw
;建立窗口过程
WindowProc proc hWnd:HWND,uMsg:UINT,wParam,WPARAM,lParam:LPARAM
  ;判断消息执行
 .if uMsg == WM_KEYDOWN
  .....;执行你的代码
 .endif
invoke DefWindowProc,hWnd,uMsg,wParam,lParam;
ret
WindowProc endp      ;函数结束
对于上面的代码,不保证能正确执行,因为编写博客,不能把上面代码调试,所以思路代码都是一样的,我会发到课堂资料中
请参考课堂资料中的代码

3.资源的使用

现在我们还不能使用资源,那我们必须编译一个资源文件,.rc结尾,

资源文件,是vc++6.0中常用的资源文件,而编译资源文件的编译器是.rc.exe,这个编译器我都会放到

课堂资料中

首先编译一个资源弄文件

这里使用VC++6.0编写一个

主要代码就是这里,我们使用rc.exe编译这个资源弄文件(这个文件的后缀名是.rc结尾)

编译出来之后是.RES的文件,我们把它当做obj文件使用,连接到PE文件中(exe文件中)即可

但是我们在设计窗口类的时候,需要使用一下这个菜单资源的ID

菜单资源的ID,在资源对应的Result.h的头文件中,我们拿过来即可.

我们要做的就是把资源变为汇编中的即可

比如上面的DIR_MENU1 代表101

那我们用汇编编写为  IDR_MENU1 EQU 101 即可

我们使用link 连接到一起即可

link /subsystem:windows 窗口.obj AAA.RES

然后编译出来就有菜单了,如果响应消息,则在窗口过程函数中捕获WM_COMMAND消息即可

然后资源文件其实是二进制,连接到EXE中(也就是放到EXE当中),那么我们使用WinHex可以再不需要源码的

情况下,把名字修改了

 

我的WinHex没有设置编码,所以看得不太清楚,这里就是存放资源的地方,我们把名字修改了,重新打开我们的窗口

 

改为Y,重新打开窗口

可以看到,已经修改为YIle了,所以逆向是很好玩的.不需要代码,可以直接修改你的程序

二丶.inc文件格式,和.lib文件的说明

1..inc文件说明

上面我们使用了各种.inc文件,我们看下内部是什么,比如windows.inc

对于.inc文件,有个第三方出的工具,可以自动生成,我们看下(MASM32,会打包)

其中上面画框的使我们需要的,下面的我们不太关系,如果关心,可以自动尝试一下(这个工具建议收藏)

我们编写windows程序的时候,只需要包含一个windows.h即可编写代码,是因为windows.h里面有帮我们定义的各种宏,以及函数的声明,在这里我们使用的.inc也是一样的,所以像上面的各种宏,和使用的函数,我们都不用定义了

这里主要介绍一下,lib 转化为.inc文件,首先我们知道,lib文件中存放了各种函数的声明,参数个数,所以这个工具是提取lib,并且转化为对应的.inc文件

我们看一下吧,随便找个lib拷贝过去

拷贝到工具目录下(tools)

可以看到很多工具,这里 我们使用的是 l2inc 正确的读法 是 lib to inc ,这里的2代表是to的意思

可以看到也有inc转化为lib的,自己尝试

我们拷贝到l2inc文件下

打开CMD,进入当前的路径,输入 l2inc lib文件名  回车即可生成

那我们的汇编程序就可以使用了

inc文件中对应的就是函数的声明,可以看出,参数类型都是DWORD类型的

2.lib文件说明

比如昨天我们编译的HelloWord程序,就要手动编译的时候,加上对应的user32.lib,而user32.lib是保存了dll文件中的 名字,还有导出函数,所以加载了这个lib,会找对应的dll和他的导出函数,进而执行我们的程序

这里在文件内部使用的,所以我们连接的时候不用手动去写了

这里的lib文件是 动态的静态加载

什么意思:

  动态的指的就是dll,静态的指的就是dll所对应的lib,这个lib保存了dll的路径信息,还有导出函数信息,当我们连接到EXE中的时候,会从lib中拷贝dll的路径,以及导出函数,然后放到exe当中,

当我们调用的时候,会根据dll的路径,找到对应的dll,根据导出函数,调用dll的导出函数(比如昨天的HELLO信息框)

 静态加载:

  静态加载则是直接把lib连接到exe当中,(这个lib中放的都是代码),相当于把代码拷贝到exe中,这样调用的时候,直接执行代码,而不从dll中去执行这个API了. 确定点是文件大,不容易维护,优点,这个程序任何windows平台上,都能运行,不管你有没有dll

关于静态加载,和动态加载,在下面的调用C库函数中讲解

 

 

三丶动态和静态的使用C库函数

1.首先是动态的使用  

动态的使用我们需要加上 msvcrt.inc然后还需要msvcrt.lib

.inc 我们知道存的是函数的声明, 而.lib则是存放的dll的路径,以及导出函数

例子:

  

.386 
.model FLAT,stdcall 
option casemap:none  
;__UNICODE__ equ

include msvcrt.inc
includelib msvcrt.lib ;crt_ 动态使用

.data
    g_SzBuff  db 100 dup(0)  ;使用Strcpy,拷贝到这里面
    g_SiTile  db  "Hello",0  ;把Hello拷贝到szBuff里面
.const

.code

START:
    invoke crt_strcpy ,offset g_SzBuff,offset g_SiTile ;拷贝字符串,为什么使用crt开头,因为调用约定是C,作者
                                                       ;调用约定是C,那么会有名称粉碎,每次比如strcpy,则在前边加上
                                                       ;_开头,如果是std调用约定,则在后面加上@符号,所以作者为了省事
                                                       ;在_strcpy加上了crt,这样简单
    
    ret
    
end START

看下编译出的程序,使用OD调试查看

 

 我们要拷贝字符串,则看下是否成功拷贝

拷贝后

然后我们 ALT + E 看下模块表,可以找到我们的MSVCRT

 

可以看出调用的是这个.dll的内容

看下Call

Call后面则不一样,表明调用的是Dll中,然后看下面的代码,有个 add ESP,0X8,则表明strcpy是一个C调用约定

因为C调用约定必须外面平栈

2.静态的使用

静态的使用,则用libc.lib,这里面存放了代码,但是需要注意一下,我们提供的工具 MASM32有这个,

而VC++6.0中也有,VS系列也有,至于使用那个版本,就看环境变量谁在前边了,(最好不用MASM32的)

MASM32的libC不全,会导致我们编写代码出错,我们可以从其他位置拷贝一个,放到MASM32的lib文件夹中

(因为我的环境变量他在最前边,所以优先找他,所以我要拷贝,或者你直接拷贝到根目录下)

静态使用分为两步

1.包含lib  includelib libc.lib

2.对你使用的函数声明一下,因为没有inc文件了,所以都要自己声明

例子:

  

.386 
.model FLAT,stdcall 
option casemap:none  
;__UNICODE__ equ

; include msvcrt.inc
; includelib msvcrt.lib ;crt_ 动态使用

includelib libc.lib            ;静态使用


strcpy proto c, :dword, :dword ;声明函数
.data
    g_SzBuff  db 100 dup(0)  ;使用Strcpy,拷贝到这里面
    g_SiTile  db  "Hello",0  ;把Hello拷贝到szBuff里面
.const

.code

START:
    ; invoke crt_strcpy ,offset g_SzBuff,offset g_SiTile ;拷贝字符串,为什么使用crt开头,因为调用约定是C,作者
                                                       ; ;调用约定是C,那么会有名称粉碎,每次比如strcpy,则在前边加上
                                                       ; ;_开头,如果是std调用约定,则在后面加上@符号,所以作者为了省事
                                                       ; ;在_strcpy加上了crt,这样简单
    ;静态使用
    invoke strcpy, offset g_SzBuff,offset g_SiTile
    
    ret
    
end START

看下OD调试(对于编译连接,这里不说了,很常用了,不会的自己多敲几遍,对于以后新增加编译选项则会对应的讲解一下)

我们可以看到,CALL直接成为了地址了,因为代码就在我么我们的EXE文件中,所以直接在对应的地址找到代码的执行位置执行即可.

 第二讲资料:

链接:http://pan.baidu.com/s/1qXIW2sc 密码:zpq1

32汇编所有资料链接请看所有资料分享的总博客,里面集合了链接

posted @ 2017-09-13 00:35  iBinary  阅读(3254)  评论(7编辑  收藏  举报