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

2018-01-08 11:17:53 浏览数 (1)

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

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

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

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

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

1.设计窗口类

2.注册窗口类

3.创建窗口

4.显示窗口

5.更新窗口

6.建立消息循环

7.窗口过程函数

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

1.设计窗口类

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

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

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

代码语言:javascript复制
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 ;     //窗口类名

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

使用汇编编写:

代码语言:c复制
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.剩余步骤一起执行

代码语言:javascript复制
;对于下方的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
  .....;执行你的代码
 .endifinvoke DefWindowProc,hWnd,uMsg,wParam,lParam;retWindowProc 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 连接到一起即可

代码语言:javascript复制
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的路径,以及导出函数

例子:

代码语言:javascript复制
.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文件了,所以都要自己声明

例子:

代码语言:javascript复制
.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汇编所有资料链接请看所有资料分享的总博客,里面集合了链接

0 人点赞