反汇编的艺术-----逆向引擎网络教程
相关的例子:下载>>> 作者:CuteDevi & Pegasus[REN]andBen 翻译 梁然生(yuexidong) 于2008-9-19上传 

2003年11月18日
ALPHA RELEASE

翻译 梁然生(yuexidong)

原作 CuteDevi & Pegasus[REN]andBen

3 给框架带来一点活力

3.1 处理我们的菜单源代码

在前面的章节我们定义了框架,但是我们没有写代码处理任何东西。现在,我们开始写菜单处理部分。

我们定义我们的菜单由下面这些组成:

OPEN :打开我们想反汇编的文件,然后我们调用反汇编引擎。

CLOSE:关闭文件

EXIT:离开程序

打开部分是我们需要做的最多的工作包括文件处理和其他部分

3.1.1 处理我们的DlgProc 代码

 

我们知道当我们按一个菜单工程的时候,它会发送WM_COMMAND消息给我们的程序。我们需要处理这个消息。我们知道每个菜单都有他的句柄,我们可以在wParam中得到。所以我们首先添加菜单工程到Skeleton.in:

IDM_OPEN EQU 10002

IDM_CLOSE EQU 10003

IDM_EXIT EQU 10004

和Skeleton.asm中的DlgProc部分:

.elseif eax==WM_COMMAND

mov eax, wParam

.if ax==IDM_OPEN

; We put our code here

.elseif ax==IDM_CLOSE

; .......

.elseif ax==IDM_EXIT

; .......

.endif

.elseif eax==WM_CLOSE

现在我们为我们的三个菜单工程编写了代码。我们现在可以为每一个编写代码。

3.1.2 打开 菜单

 

在这里我们真正需要什么?

一个对话框让用户选择文件

打开文件并把它映射到内存。

为了显示打开文件对话框,首先我们需要添加一些数据到Skeleton.inc

ofn OPENFILENAME <>

buffer db 260 dup(0)

StrFilter db "Executable Files",0,"*.exe",0

db "Dynamic Link Libraries",0,"*.dll",0,0

现在你可以看一下随书附带的源代码。

然后,为了使用我们的OPENFILENAME结构,我们需要先初始化它。在DlgProc我们需要添加下面的代码:

.if eax==WM_INITDIALOG

;-------------------------------------- 初始化整个对话框--------

;===========================================

; 预备ListView控件

;===========================================

invoke PrepareListView

;===========================================

; Initialize the OPENFILENAME structure

;===========================================

mov ofn.lStructSize, SIZEOF ofn

push hWnd

pop ofn.hwndOwner

push hInstance

pop ofn.hInstance

mov ofn.lpstrFilter, OFFSET StrFilter ; ( *.exe & *.dll )

mov ofn.lpstrFile, OFFSET buffer ; ( Store the file name )

mov ofn.nMaxFile, 260

; ( File must exists/ Hide read only files)

mov ofn.Flags, OFN_FILEMUSTEXIST or \

OFN_PATHMUSTEXIST or OFN_LONGNAMES or\

OFN_EXPLORER or OFN_HIDEREADONLY

这段代码只能初始化我们的结构因此我们将它放在WM_INITDIALOG部分。为了弹出这个对话框并显示它我们需要调用GetOpenFileName.

所以返回WM_COMMAND我们添加代码:

.elseif eax==WM_COMMAND

mov eax, wParam

.if ax==IDM_OPEN

;------------------------------------- 打开文件 -------------------

; 显示打开文件对话框

invoke GetOpenFileName, ADDR ofn

;---------------------------------------------------------------------

最好我们有了一个可以正常工作的打开文件对话框。

wpe3.jpg (84909 字节)

图3:带来一点活力的框架

在我们打开这个对话框之后我们需要做一个小的检查看看我们是否真的选择了一个文件或者我们点击了取消按钮或者其他的。

正确的是在 invoke GetOpenFileName, ADDR ofn 之后,我们继续添加:

.if eax==TRUE

invoke CreateFile,ADDR buffer,\

GENERIC_READ or GENERIC_WRITE ,\

FILE_SHARE_READ or FILE_SHARE_WRITE,\

NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\

NULL

mov hFile,eax ; Stores the handle of the file

.endif

注意:我们需要在Skeleton.inc中添加代码:

hFile dd ?

这个CreateFile函数返回文件的句柄然后我们保存它到hFile中。然后我们需要映射文件到内存,简单的处理是用CreateFileMapping创建一个文件映射对象然后用MapViewOfFile映射进内存。但是使用最后一个函数我们需要得到文件的大小,所以我们需要使用GetFileSize为我们工作。

所以简而言之我们需要:

使用CreateFile获取文件句柄

使用CreateFileMapping创建文件映射对象

使用GetFileSize获取文件大小

使用MapViewOfFile将文件映射到内存

我们还应该在每次调用函数后进行检查,如果有错误发生我们应该显示一个信息框。为了声明这些错误信息我们还要创建一个新文件Msgs.inc包含我们需要的错误信息。现在我们只需要两个信息:

Please check the sources now, or you will get lost!

然后我们基本上完成了打开文件部分的代码。也许你已经注意到这些代码有些长,为了看起来比较整齐我们将代码移到一个子程序中。我们叫它MnuFileOpen。我们这样定义:

MnuFileOpen proc

......

ret

MnuFileOpen endp

提示;在RadASM中有代码收缩、展开功能,所以代码看起来非常整洁。

经过简单的移动代码到新子程序我们像下面这样改变代码:

.if ax==IDM_OPEN

invoke MnuFileOpen

.elseif ax==IDM_CLOSE

现在让我们继续,在这一章我们完成了打开文件部分,我们再来完成关闭文件部分。

3.1.3 关闭 菜单

 

我们真的需要做这一部分吗?记住当我们打开一个文件并把它映射到内存,它就在使用内存。所以我们需要在关闭前释放这部分内存。我们使用UnmapViewOfFile关闭映射对象并且我们必须释放掉所有映射的内存。最后我们使用CloseHandle关闭映射的文件映射对象。所以我们需要编写代码来释放这部分内存。

我们增加一个子程序叫MnuFilwClose,像这样调用:

.elseif ax==IDM_CLOSE

invoke MnuFileClose

.elseif ax==IDM_EXIT

 

在我们的MnuFileClose究竟是什么?它是这样的:

MnuFileClose proc

cmp hFileMapping, 0

jnz @f

ret

@@:

invoke UnmapViewOfFile, FileOffset

invoke CloseHandle, hFile

mov FileOffset, 0

mov hFile, 0

ret

MnuFileClose endp

这个子程序做这样的检查如果一个文件映射到内存,就释放并关闭它。

在这一章的最后,我们编写Exit部分的代码。

开程序。

3.1.4 离开 菜单

 

这是非常简单的,我们需要关闭文件,(如果它还开着)然后离开程序。

.elseif ax==IDM_EXIT

invoke MnuFileClose

invoke ExitProcess,0

.endif

调用MnuFileClose子程序确保文件在我们关闭程序之前是释放并且关闭的。然后调用ExitProcess离

在这一章关于菜单处理的代码已经完成,你应该注意源代码:

GetMenu:在初始化对话框当中

EnableMenuItem:在(MnuFile/MnuFileOpen)

在工程当中的MsgBox是一个很小的程序,你可以用它测试我们的反汇编器。

3.2 总结

在这一章我们编写代码处理菜单知道如何打开文件并将它映射到内存。看到ListView如何工作。但我决定在独立的一章用详细的例子讲解ListView.

3.3 下一章简介

我们将:

通过例子理解ListView是如何工作的。

4 ListView

4.1 什么是ListView 控件

ListView是WINDOWS的通用控件就像RichEdit、ProgressBar等。在某些方面它像ListBox但是在能力上有所增强。ListView在显示数据上有4个方法:ICON,SMALL ICON , LIST ,REPORT,但是我们只关注最后一个方法。

wpe4.jpg (28224 字节)

图4:ListView例子

4.2 使用 ListView 控件

在RadASM中我们这样做:

1.新工程:工程名字(ListView)和模板(DialogApp.tpl)

2.我们更改标题为擫istView Example?/P>

3.在ListView.dlg中我们画一个ListView控件,命名为IDC_DSM

4.改变ListView的报告类型(Report Type)

编译并运行程序。显然我们现在有了一个很好的空白ListView程序。

在ListView(Report View)中有一行或多于一行。数据的安排像表一样在安排行和列。我们通过向ListView控件发送LVM_INSERTCOLUMN。

所以我们确切明白如何发送这个消息。

LVM_INSERTCOLUMN

wParam = iCol ;行序号

lParam = pointer to a LV_COLUMN structure ;指向LV_COLUMN指针

什么是LV_COLUMN结构?明显的它是一个结构包含我们需要插入到的ListView行的信息。

LV_COLUMN STRUCT

imask dd ?

fmt dd ?

lx dd ?

pszText dd ?

cchTextMax dd ?

iSubItem dd ?

iImage dd ?

iOrder dd ?

LV_COLUMN ENDS

我们还需要更多的细节。

Imask包含一些具体的结构成员标志是合法的。因为一些成员不是每次都使用,但是仅仅在有些情况会。

LVCF_FMT: 它意味着fmt成员是合法的

LVCF_SUBITEM: iSubItem成员是合法的

LVCF_TEXT: pszText 成员是合法的

LVCF_WIDTH: lx 成员是合法的

fmt 是在行中条目的或分条目的排列方式。它可以是以下几种值:

LVCFMT_CENTER: 文字居中

LVCFMT_RIGHT:文字右对齐

LVCFMT_LEFT:文字左对齐

lx包含行的宽度(像素)(这个行宽度以后可以在运行时使用LVM_SETCOLUMNWIDTH消息改变)

pszText是指向我们想要插入的行的名字的指针。如果一个消息被发送以获取行的属性,这个条目包含一个指向大的存储名字的缓冲区的指针并且cchTextMax区域必须包含这个缓冲区的大小。

cchTextMax包含缓冲区的大小(字节)。这个条目只是在我们获取行的属性时被使用。

iSubItem:指向分条目的索引

iImage:在image队列中指向一个image的索引(从0开始)

iOrder: 从0开始的行偏移。它是一个从左到右的序列。

基本上在ListView被创建后我们应该插入一行或多行。我们必须这样做因为我们要使用ListView的报告模式(Report View)。为了做到这些,我们需要创建一个合法的LV_COLUMN结构。

填充一些必要的信息然后发送LVM_INSERTCOLUMN消息给我们的ListView.

当我们添加多于一行时,我们需要使我们的程序尽所能的简洁,我们将添加一个新的添加行的子程序。

InsertColumn proc pszHeading:DWORD, dwWidth:DWORD

;=========================================================

; pszHeading: 指向行名字的指针

; dwWidth: 插入行的宽度

;=========================================================

LOCAL lvc:LV_COLUMN

mov lvc.imask, LVCF_TEXT + LVCF_WIDTH

push pszHeading

pop lvc.pszText

push dwWidth

pop lvc.lx

invoke SendMessage,hDsm, LVM_INSERTCOLUMN,dwIndex,addr lvc

inc dwIndex

ret

InsertColumn endp

这个子程序只是简单的构建一个LV_COLUMN结构,然后填充必需的字段(文本和宽度)再发送LVM_INSERTCOLUMN消息来插入行,这个子程序有两个参数,一个使指向行名字的指针,第二个是行的宽度。

注意:dwIndex是一个插入行的计数器,所以新行应该在旧行的右面。

注意:这个消息被发送给ListView的句柄,所以我们必须在对话框初始化的时候使用GetDlgitem获取ListView的句柄并将它保存到hDsm。

现在我们可以插入任意我们想插入的行了。

invoke InsertColumn,addr TextOfTheColumn,WidthOfTheColumn

请检查你的代码,否则你要掉队了。

现在我们知道如何插入行,但是如何在行中添加数据呢?首先我们需要知道关于ListView的一些事情。

在ListView中,字段是主要的输入点。在报告模式(Report View),这里有字段和分字段。字段是最左边的行,分字段是剩余的行。

Column1 Column2 Column3 Column4 Column5

Item1 Subitem1 Subitem2 Subitem3 Subitem4

Item2 Subitem1 Subitem2 Subitem3 Subitem4

Item3 Subitem1 Subitem2 Subitem3 Subitem4

所以我们该如何添加字段呢?为了添写一行我们需要填充一个LV_ITEM结构然后用LVM_INSETITEM消息发送它。

这个LV_ITEM结构是这样定义的:

LV_ITEM STRUCT

imask dd ?

iItem dd ?

iSubItem dd ?

state dd ?

stateMask dd ?

pszText dd ?

cchTextMax dd ?

iImage dd ?

lParam dd ?

iIndent dd ?

LV_ITEM ENDS

具体说明:

imask:一些合法结构成员的标志,跟上面的imask有些类似。

iItem:字段结构的索引(列号,从0开始)

iSubItem:在字段后面的分字段的索引(从0开始),它被认为包含与行

state:体现字段状态的标志(选择、高亮、焦点)它也包含覆盖图片的索引(从1开始)或这张图片是否被字段使用。

stateMask:我们说成员状态包含状态标志或覆盖图片的索引,所以我们需要详细的我们所感兴趣的值

pszText:在LV_COLUMN结构中

cchTextMax:在LV_COLUMN结构中

iImage:为ListView控件使用的ICON的图片列表索引。

lParam:一个在ListView控件中用户如何整理使用字段的值。

iIndent:我们没有事情让它做,请参考MSDN获取更多信息。

注意:我们添加一个字段使用LVM_LINSETITEM但是添加一个分字段我们使用LVM_SETITEM。这是因为分字段被认为是字段集的属性。它意味着如果没有字段就不能有分字段,它们是联系在一起的。

InsertItem proc uses ecx Row:DWORD, Column:DWORD, pszCaption:DWORD

;===============================================================

; Row: 从0开始的索引(列号)

; Column: 从0开始的索引(行号)

; pszCaption: 指向欲插入字段的名字的指针

;===============================================================

LOCAL lvc:LV_ITEM

mov lvc.imask, LVCF_TEXT

push row

pop lvc.iItem

push Column

pop lvc.pszText

.if Column==0

invoke SendMessage,hDsm, LVM_INSERTITEM,0,addr lvc

.elseif

invoke SendMessage,hDsm, LVM_SETITEM,0,addr lvc

.endif

inc dwIndex

ret

InsertItem endp

这个子程序很简单的构造了一个LV_ITEM结构然后填充必要信息(行、列、名字)再发送LVM_INSERTITEM消息来插入一个字段。如果(行区域)不为0它意味着我们需要插入一个分字段所以我们需要发送LVM_SETITEM来代替。这个子程序有三个参数,一个是列号,第二个是行号,第三个是一个指向我们要插入的字段名字的指针。现在我们可以插入任何我们想使用的字段和分字段了。

invoke InsertItem,Row,Column,addr TextOfTheItem

请检查你的代码,否则你要掉队了。

在我们学习完ListView如何工作、如何插入行、字段、分字段之后,让我们快速的浏览一遍ListView,我们是这样做的:

invoke SendMessage, hDsm, LVM_SETTEXTCOLOR, 0, 00E41030h

invoke SendMessage, hDsm, LVM_SETBKCOLOR,0,00DEF5F3h

invoke SendMessage, hDsm, LVM_SETTEXTBKCOLOR,0,00DEF5F3h

invoke SendMessage, hDsm, LVM_SETEXTENDEDLISTVIEWSTYLE, 0,\

LVS_EX_FULLROWSELECT or \

LVS_EX_GRIDLINES + LVS_EX_FLATSB

首先它发送一个消息来改变文本颜色,然后改变ListView的背景颜色,再改变文本的背景颜色。确保文本背景颜色和ListView的背景颜色是一致的。最后的消息改变ListView的扩展风格,添加Girdlines &Flat Scrollbars &允许我们选择整列。

4.3 总结

在这一章我们学习了ListView是如何工作的,我们也学习了一个我们所需要功能的ListView的演示程序。

4.4 下一章简介

我们将:

进行一段PE的旅程

添加事件报告代码

 



<<<上一篇
欢迎访问AoGo汇编小站:http://www.aogosoft.com
下一篇>>>