月度归档:2009年01月

动态链接库 - 编写

文件是否是动态链接库取决于它的文件结构,动态链接库和可执行文件同样使用标准的PE文件格式,公文件头中的属性位不同而以,所以EXE中的一些特征也存在于动态链接库中,比如,可以定义并使用各种资源,可以导入并使用其他动态链接库中的函数等.
要牢记一个概念:动态链接库是被映射到其他应用程序的地址空间中执行的,它和应用程序可以看成是"一体"的,动态链接库可以使用应用程序的资源,它所拥有的资源也可以被应用程序使用,它的任何操作都是代表应用程序进行的,当动态链接库进行打开文件,分配内存和创建窗口等操作后,这些文件,内存和窗口都是为应用程序所拥有的.

例:(罗老板的代码)
Dll.asm

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff Sample.asm
; Link /subsystem:windows /Dll /Def:Sample.def Sample.obj
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .386
    .model flat, stdcall
    option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include    windows.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .data?

dwCounter  dd  ?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; dll 的入口函数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DllEntry  proc  _hInstance,_dwReason,_dwReserved

    mov  eax,TRUE
    ret

DllEntry  Endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 本函数在 dll 内部使用
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CheckCounter  proc

    mov  eax,dwCounter
    cmp  eax,0
    jge  @F
    xor  eax,eax
    @@:
    cmp  eax,10
    jle  @F
    mov  eax,10
    @@:
    mov  dwCounter,eax
    ret

_CheckCounter  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; dll 的导出函数之一
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_IncCounter  proc

    inc  dwCounter
    call  _CheckCounter
    ret

_IncCounter  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; dll 的导出函数之二
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_DecCounter  proc

    dec  dwCounter
    call  _CheckCounter
    ret

_DecCounter  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; dll 的导出函数之三
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Mod    proc  uses ecx edx _dwNumber1,_dwNumber2

    xor  edx,edx
    mov  eax,_dwNumber1
    mov  ecx,_dwNumber2
    .if  ecx
      div  ecx
      mov  eax,edx
    .endif
    ret

_Mod    endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    End  DllEntry

程序仅定义了5个子程序,其中程序的入口由最后一句End定义到了DllEntry处.
程序入口代码什么都没做,仅返回一个TRUE,也没有用到_IncCounter,_DecCounter,_Mod函数,这是因为DLL文件被设计为不是供自己使用的,而是被映射到其他应用程序的地址空间中代表"宿主"程序执行的,这些函数就是供其他程序使用的函数,这些函数对于宿主来说和User32.dll中的CreateWindowEx等极其复杂的函数没有任何区别

1.入口点和初始化代码

DllEntry    proc  hInstDll,dwReason,dwReserved

    mov  eax,dwReason
    .if  eax == DLL_PROCESS_ATTACH
      ;保存hInstDll
      ;初始化库需要的各种资源

      .if   初始化成功
        mov eax,TRUE
      .else
        mov eax,FALSE
      .endif
    .elseif  eax == DLL_THREAD_ATTACH
      ;为新的线程分配资源
    .elseif  eax == DLL_THREAD_DETACH
      ;为线程释放资源
    .elseif  eax == DLL_PROCESS_DETACH
      ;释放库使用的资源
    .endif
    ret

DllEntry    Endp

动态链接库需要一个入口点,这个入口点是一个函数,函数

名并不重要,例子中函数名为DllEntry,可以把它取名为任何合法的名字,但入口函数的格式是有规定的.库的入口函数对调用动态链接库的应用程序来说是不可见的,它仅供操作系统使用.windows在库加载,卸载,进程中线程的创建和结束等时候调用入口函数,以便动态链接库采取相应的动作.在入口函数中可以通过参数来判别本次调用究竟是在哪种情况下发生的.

Windows会传给入口函数3个参数
dwReason:表示本次调用的原国,可能是四种情况之一
DLL_PROCESS_ATTACH:表示动态链接库刚被映射到进程的地址空间,程序可以在这里进行一些初始化的工作,成功返回TRUE,失败返回FALSE.这给了动态链接库一个机会来阻止自己被装入.比如库可以在这里申请并保留一些内存,如果申请失败的话返回FALSE告诉windows,库无法正常工作.

内存映射文件

使用内存映射文件
1.内存映射文件函数
内存映射文件包括:CreateFileMapping,OpenFileMapping,MapViewOfFile,UnmapViewOfFile和FlushViewOfFile.
创建一个内存映射文件对象使用CreateFileMapping这个步骤内存映射文件的用途(是在磁盘上建立内存映射文件,还是在页文件中建立进程间共享的映射

invoke CreateFileMapping,hFile,lpFileMappingAttributes,flProtect,dwMaximumSizeHigh,dwMaximumSizeLow,lpName
.if eax
mov hFileMap,eax

hFile:指定一个文件句柄.如果句柄是属于一个已经打开的文件的,那么内存映射文件将在这个文件上面建立;如果要建立存在于页文件中的内存映射供不同进程共享,那个hFile指定为-1
lpFileMappingAttributes:指向一个SECURITY_ATTRIBUTES结构,用来定义内存映射文件对象是否可继承
SECURITY_ATTRIBUTES结构,该结构定义如下

SECURITY_ATTRIBUTES STRUCT
nLength DWORD ? ;本结构的长度
lpSectrityDescriptor DWORD ?
bInheritHandle DWORD ? ;是否允许继承
SECURITY_ATTRIBUTES ENDS

将nLength设置为结构的长度,将bInheritHandle设置为TRUE就可以使句柄能够被子进程继承
不继承可把这个参数设置为NULL
flProtect:指定内存映射文件的保护类型,有3个取值
PAGE_READONLY:内存映射文件提交的内存页面是只读的,对应的CreateFile也要设置GENERIC_READ标志
PAGE_READWRITE:内存页面可读写,对应CreateFile设置GENERIC_READ和GENERIC_WRITE
PAGE_WRITECOPY:内存页可以有Copy on Write属性.对应CreateFile设置GENERIC_READ和GENERIC_WRITE
dwMaximumSizeHigh和dwMaximumSizeLow:这两个参数组合指定了一个64位的内存映射文件长度,如果内存映射文件用于磁盘文件的时候,这个长度如大于磁盘文件长度,磁盘文件将扩展到这个长度.如小于磁盘文件长度,那么只能存取磁盘文件的一部分,一种简单的方法是全设置成0,系统当自动调整磁盘文件大小
lpName:用来给定内存映射文件的名字.当内存映射文件作用于磁盘文件的时候,不需要给它起名;如用于进程间共享内存,那就必须为该对象命名,因为在其它进程中只有使用这个名称才能打开这个内存映射文件对象,该名字字符串不能和其他进程已创建的对象同名.

当一个进程创建内存共享文件用于和其他进程共享的时候,其它进程不能再使用CreateFileMapping去创建同样的内存映射文件对象,而是要用OpenFileMapping函数打开创建好的对象

invoke OpenFileMapping,dwDesiredAccess,bInheritHandle,lpName
.if eax
mov hFileMap,eax
.endif

dwDesiredAccess:参数指定保护类型,有3个取值
FILE_MAP_WRITE(或FILE_MAP_ALL_ACCESS):可写属性
FILE_MAP_READ:可读属性
FILE_MAP_COPY:Copy on write属性
lpName:指向的名字就是创建对象时使用的名字
如果CreateFileMapping或OpenFileMapping函数执行成功返回内存映射文件句柄,如果执行失败返回NULL

MapViewOfFile函数用来映射内存映射文件的一个视图,这个函数的用法

invoke MapViewOfFile,hFile,hFileMap,dwDesiredAccess,dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfBytesToMap
.if eax
mov lpMemory,eax
.endif

hFileMap:CreateFileMapping或OpenFileMapping返回的句柄
dwDesiredAccess:指定保护类型
FILE_MAP_WRITE(或FILE_MAP_ALL_ACCESS):可写属性
FILE_MAP_READ:可读属性
FILE_MAP_COPY:Copy on write属性
dwFileOffsetHigh和dwFileOffsetLow:组成64位的偏移量,用来指定视图的基地址是从文件的哪个位置开始映射
dwNumberOfBytesToMap:指定映射的字节数,如果设置为0则映射的是整个文件,同时偏移地址被忽略
如果映射成功,返回一个地址,如果映射失败,则返回NULL

当不再使用映射文件后,通过UnmapViemOfFile函数撤销映射并使用CloseHandle函数关闭内存映射文件对象句柄

invoke UnmapViewOfFile,lpMemory
invoke CloseHandle,hFileMap

视图中的内存修改后,系统会在视图撤销映射或文件映射对象被删除时自动将数据写到磁盘上,但可以根据需要将对文件的修改立即写到磁盘上,该功能的函数FlushViewOfFile

invoke FlushViewOfFile,lpMemory,dwFileSize

该函数将从指定地址开始,指定大小的数据块中的脏页面写到磁盘,指定的内存范围必须位于视图的边界之内

2使用内存映射文件读写文件
内存映射文件读写文件的步骤
(1) 调用CreateFile打开想要映射的文件,得到hFile
(2) 调用CreateFileMapping函数生成一个建立在CreateFile函数创建的文件对象基础上的内存映射对象,得到hFileMap
(3) 调用MapViewOfFile把整个文件的一个区域或整个文件映射到内存中,得到指向映射到内存的第一个字节的指针lpMemory
(4) 用该指针来读写文件
(5) 调用UnmapViewOfFile来解除文件映射,传入参数为lpMemory
(6) 调用CloseHandle来关闭内存映射文件,传入参数为hFileMap
(7) 调用CloseHandle来关闭文件,传入参数为hFile
例程: …………….

3使用内存映射文件在进程间共享数据
进程间共享数据的步骤如下:
(1) 调用OpenFileMapping打开一个命名的内存映射文件对象,得到hFileMap,打开成功跳到(3),否则表示此程式是第一个副本,继续执行步骤(2)
(2) 调用CreateFileMapping函数创建一个命名的内存映射对象,得到hFileMap
(3) 调用MapViewOfFile函数映射对象的一个视图,得到lpMemory
(4) 用该指针来读写共享的内存区域.
(5) 调用UnmapViewOfFile来解锁视图映射,传入参数为lpMemory
(6) 调用CloseHandle来关闭内存映射文件,传入参数为hFileMap
例程: …………….

驱动器和目录 — 目录操作

1.创建和删除目录
创建目录使用CreateDirectory函数,例如:

szDir db 'c:dir1dir2',0

invoke CreateDirectory,addr szDir,NULL

如果创建成功返回TRUE,失败返回FALSE
注意:创建目录的上层目录必须存在,创建目录名不能与同目录下的目录或文件同名

删除目录使用RemoveDirectory函数

szDir db 'c:dir1dir2',0

invoke RemoveDirectory,addr szDir

如删除成功返回TRUE,否则返回FALSE
注意:被删除的是参数中指出的最后一级目录,删除目录必须是一个空目录,删除的文件不会进入回收站

2.一些特殊目录
当前目录:所有未指定路径的文件名均默认使用这个目录
Windows目录:Windows操作系统的安装目录
系统目录:Windows安装目录下存放系统文件的目录(9X下是system,NT下是system32)
临时目录:存放临时文件的目录,系统可以在磁盘空间不足的时候自动删除里面的文件.
Win32中专门设置了几个函数来获取这些目录的位置

invoke GetCurrentDirectory,dwBufferSize,lpBuffer ;获取当前目录
invoke GetTempPath,dwBufferSize,lpBuffer ;获取临时目录
invoke GetWindowsDirectory,lpBuffer,dwBufferSize ;获取Windows目录
invoke GetSystemDirectory,lpBuffer,dwBufferSize ;获取系统目录

lpBuffer:指向一个缓冲区,用来接收返回的路径字符串
dwBufferSize:指出了缓冲区的大小,一般设置为MAX_PATH
注意:四个函数的参数顺序,GetTempPath返回值包括'' 而其它三个没有,当程序运行的时候,默认的当前路径是执行程序所在目录,但不是绝对的,比如使用GetOpenFileName等函数,会设置当前目录为指定的目录

修改当前路径用SetCurrentDirectory

szDir db 'c:dir1dir2',0

invoke SetCurrentDirectory,addr szDir