分类目录归档:汇编&逆向

QQGAME 2009 多开 逆向 源码 讲解

下面来说说具体的,准备工具ollyice
下载地址:http://www.kumouse.com/article.asp?id=46 首先来讲一下常用的防多开的方法: 方法1: 查找窗口法,使用FindWindowA(W)或GetWindowTextA(W)
方法2: 使用互斥对象,用到CreateMutexA(W)
方法3: 使用共享区块 QQ使用的是方法2,具体程序要具体分析(由于我水平有限讲解不了)如果分析不出来可以上google查查,网上有很多牛人的.
先打开一个QQ游戏的窗口然后(原因看下边),我们用ollyice打开qqgame.exe我的文件版本是2.3.103.9大小164808字节,游戏版本QQGAME2009 beta3 sp3 打开程序后,程序会停在这里

00402150 >/$ 55 push ebp ; (initial cpu selection)
00402151 |. 8BEC mov ebp, esp
00402153 |. 6A FF push -1
00402155 |. 68 38314000 push 00403138
0040215A |. 68 D6224000 push <jmp.&MSVCRT._except_handler3> ; SE 处理程序安装
0040215F |. 64:A1 0000000>mov eax, dword ptr fs:[0]
00402165 |. 50 push eax
00402166 |. 64:8925 00000>mov dword ptr fs:[0], esp
00402150这行是程序的开始处,我们在下边的command处输入"bp CreateMutexA"这句的意思是在CreateMutexA这个函数下断,也就是方法2中使用到的函数.
然后我们按F9运行这个程序程序会停在
7C80E9CF > 8BFF mov edi, edi
7C80E9D1 55 push ebp
7C80E9D2 8BEC mov ebp, esp
7C80E9D4 51 push ecx
7C80E9D5 51 push ecx
7C80E9D6 56 push esi
7C80E9D7 33F6 xor esi, esi
7C80E9D9 3975 10 cmp dword ptr [ebp+10], esi
7C80E9DC 74 31 je short 7C80EA0F
7C80E9CF就是CreateMutexA的第一行,我们这时按Alt+F9返回用户代码来到
746824B0 FF75 08 push dword ptr [ebp+8]
746824B3 FF15 88116874 call dword ptr [74681188] ; kernel32.CreateMutexA
746824B9 33C9 xor ecx, ecx
746824BB 85C0 test eax, eax
746824BD 0F95C1 setne cl
746824C0 8906 mov dword ptr [esi], eax
746824C2 5E pop esi
746824C3 8BC1 mov eax, ecx
746824C5 5D pop ebp
746824C6 C2 0800 retn 8
746824B9这行,看看上一行746824B3后边的注解写着什么,kernel32.CreateMutexA也就是刚刚调用了最关键的函数,但是这个函数在程序里调用了很多次,我们怎么知道哪一次的才是不让多开的代码呢?答案很简单,就是试,试也要靠方法的,对于QQ游戏来说,我们开打开一个QQ游戏的登陆窗口,不用登陆,然后再打开第二个,发现什么了?对原来那个窗口在闪,行了,我们就通过判断窗口闪,来得到具体是哪一次调用是禁止打开第二个游戏的代码.下边我们来试,刚才按了一次F9和一次Alt+F9,我们继续重复刚才的工作,直到看到QQ窗口闪动,说明禁止双开的代码以经被执行了,一共按了多少次Alt+F9呢?17次,说明第17次调用才是真正的禁止双开. 好了,我们这时按Ctrl+F2重新打开程序,程序又停在了00402150处,我们按Alt+b打开断点窗口看到有一个地址为7C80E9CF的断点,我们用鼠标选中它,按回车,来到这里
7C80E9CF > 8BFF mov edi, edi
7C80E9D1 55 push ebp
7C80E9D2 8BEC mov ebp, esp
7C80E9D4 51 push ecx
7C80E9D5 51 push ecx
7C80E9D6 56 push esi
眼熟吗?这里就是CreateMutexA的第一行,我们选中7C80E9CF这行,按Shift+F4设置一下条件
暂停程序那里选择"按条件"后边的条件满足次数写16,为什么是16呢?第17次就以经禁止双开了,我们让他停在第16次调用之后,好看看具体是哪个语句禁止双开的.
设置完成后按确定,再按F9运行程序.程序又停在了7C80E9CF这行上,不过这次是第16次,我们按Alt+F9回到用户代码处.
10012886 85C0 test eax, eax
10012888 8946 4C mov dword ptr [esi+4C], eax
1001288B 74 24 je short 100128B1
1001288D FF15 ACC00110 call dword ptr [<&KERNEL32.GetLastErr>; ntdll.RtlGetLastWin32Error
10012893 3D B7000000 cmp eax, 0B7
10012898 8BCE mov ecx, esi
1001289A 75 07 jnz short 100128A3
1001289C E8 BE000000 call 1001295F
100128A1 EB 2A jmp short 100128CD
100128A3 E8 90010000 call 10012A38
100128A8 85C0 test eax, eax
100128AA 74 21 je short 100128CD

程序停在了10012886处,我们按F8步过.执行到10012893这句时看到信息面板,也就是代码窗口下边的小窗口写着eax=000000B7,而10012893这句的代码是cmp eax,0B7 这句的意思就是比较eax和0B7,接着执行按F8,当来到1001289A这行时,注意了, jnz short 100128A3的意思是说,刚才比较的eax和B7如果这两个不相等则跳转到100128A3,否则不跳,刚才eax=B7也就是相等,所以程序接着执行,并不跳转.接着按F8,执行到100128A1这行,发现先前打开的QQ游戏窗口闪了,100128A1上一行call 1001295F,调用了这个子程序后QQ游戏才被禁止运行的.这是关键的部分了. 我们具体分析一下这段代码
10012893 3D B7000000 cmp eax, 0B7
10012898 8BCE mov ecx, esi
1001289A 75 07 jnz short 100128A3
1001289C E8 BE000000 call 1001295F
100128A1 EB 2A jmp short 100128CD
100128A3 E8 90010000 call 10012A38
100128A8 85C0 test eax, eax
100128AA 74 21 je short 100128CD
100128AC 897D FC mov dword ptr [ebp-4], edi
10012893这句把eax和B7做了比较,1001289A这句判断eax和B7不相等则跳转到100128A3,但实际情况是相等,也就是不跳转.如果执行了1001289C这行的子程序,就禁止双开了
我们来简化一下流程
10012893 3D B7000000 cmp eax, 0B7
1001289A 75 07 jnz short 100128A3
1001289C E8 BE000000 call 1001295F
100128A3 E8 90010000 call 10012A38
根据上边所说的,我们如果不执行1001289C这行,就可以双开,最简单的办法有两个,可以跳过1001289C 这行,一是改10012893这行,把eax和B6比,或别的数,反正不是B7就行了.改过后当执行到1001289A时,会直接跳到100128A3去执行,1001289C没有被执行也就是可以双开.二是把1001289A这句的jnz改成je也就是把原来的eax和B7不相等则跳转到100128A3,变成了相等则跳转. 我们用第一种方法试试.
选中10012893,也就是cmp eax,0B7这句,按F2,然后按ALt+B把刚才的7C80E9CF那个断点删除了,只剩下10012893这个,然后按ALT+C,再按Ctrl+F2重新载入程序,按F9运行]] >

公元2008年07月27日的宣言

2008年07月27日

我出于兴趣,编写CALL外挂反编译分析时,发现汇编功底太差,当初在大学学到的知识也都快还给老师了,汇编成为我现阶段最大的障碍,一直都懒得看,现在真的要恶补一下了,我在1年前买了一本 罗云彬编写的《Windows 环境下32位汇编语言程序设计》 至今也只看完了第一章。今天我在次宣言,要在半年之内达到独立分析大部分网游Call函数正确地址的水平和破解简单的加密,脱壳等。此版就是为了此目的开的,在学习期间,我会把所学发布出来,请认识我的朋友们见证2009年1月27日,会出现一个全新的我!!!!

_____________________________________________________________________

2009年04月28日

中途关注了一下Linux,但是还在学习中,我并没有放弃,破解方面的知识还要关注看雪学院呀!!~~~~~
加油中~~~~~~~~

动态链接库 - 编写

文件是否是动态链接库取决于它的文件结构,动态链接库和可执行文件同样使用标准的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

驱动器和目录 — 逻辑驱动器操作

1.卷标操作
驱动器创建,修改,删除卷标都可以使用SetVolumeLabel函数

szPath db 'c:',0
szVolume db 'System',0
invoke SetVolumeLabel,addr szPath,szVolume

szPath:指出了要设置卷标的逻辑驱动器的根目录
szVolume:指向包含卷标字符串的缓冲区
删除卷标的二种方法

szPath db 'c:',0
szVolume db 0
invoke SetVolumeLabel,addr szPath,szVolume

szPath db 'c:',0
invoke SetVolumeLabel,addr szPath,NULL

如果函数执行成功返回值是TRUE,否则返回FALSE

2.逻辑驱动器的检测
检测系统中当前存在多少个逻辑驱动器可以使用GetLogicalDrives函数,它没有输入参数,它返回一个32位整数,用其中的每一位代表是否存在一个逻辑驱动器.由于系统中可用的盘符公有26个(A~Z),所以用第0到第25位表示A:~Z:

如果认为认为上面的函数做位测试比较麻烦也可以使用GetLogicalDriveStrings,这个函数返回字符串类型的逻辑驱动器列表:

invoke GetLogicalDriverStrings,dwBufferSize,lpBuffer

lpBuffer:指向一个缓冲区,函数在这里返回'A:',0,'B:',0 格式的字符串
dwBufferSize:指出缓存区的大小,如果缓冲区不够大,后面的数据会被截尾

检测驱动器类型的工作可以用GetDriveType函数来完成

szPath db 'c:',0
invoke GetDriveType,addr szPath

szPath指向存放在逻辑驱动器根目录的字符串的缓冲区,函数返回值是驱动器的类型,是下面7个位的一种
0:驱动器类型无法检测
1:指定的根目录不存在
DRIVE_REMOVABLE:可移动介质,如软盘
DRIVE_FIXED:固定盘,如硬盘中的逻辑驱动器
DRIVE_REMOTE:远程驱动器,如网络上映射的驱动器
DRIVE_CDROM:光盘
DRIVE_RAMDISK:内存虚拟盘

如果需要更详细的情况,可以使用GetVolumeInformation函数

invoke GetVolumeInformation,lpRootPathName,lpVolumeNameBuffer,dwVolumeNameSize,lpVolumeSerialNumber,lpMaximumComponentLength,lpFileSystemFlags,lpFileSystemNameBuffer,dwFileSystemNameSize

lpRootPathName:指向需要检测的驱动器根目录字符串,如果是网络驱动器,可以是'\服务器名共享名'
lpVolumeNameBuffer:指向一个字符串缓冲区,用来返回驱动器的卷标,缓冲区长度由dwVolumeNameSize参数指出
lpVolumeSerialNumber:指向一个双字变量,在这里返回逻辑驱动器的序列号,序列号是驱动器被格式化的时候由系统随机产生的一个32位数,它保存在位于驱动器第一个扇区的引导记录中.
lpMaximumComponentLength:指向一个双字变量,在这里返回最大允许的文件名长度,windows中为255
lpFileSystemFlags:指向一个双字变量,在这里返回一些逻辑驱动器的属性标志返回值可能是下列值的组合
FS_CASE_IS_PRESERVED:文件系统保存文件名时保持它的大小写
FS_CASE_SENSITIVE:支持区分大小写的文件名
FS_UNICODE_STORED_ON_DISK:允许存放Unicode格式的文件名
FS_PERSISTENT_ACLS:支持ACL
FS_FILE_COMPRESSION:支持文件压缩
FS_VOL_IS_COMPRESSED:支持卷压缩
lpFileSystemNameBuffer:指向一个字符串缓冲区,用来接收文件系统字符串,类似于'NTFS'或'FAT32'之类的dwFileSystemNameSize指定了这个缓冲区的长度

检测逻辑驱动器剩余空间GetDiskFreeSpace用法如下

invoke GetDiskFreeSpace,lpRootPathName,lpSectorsPerCluster,lpBytesPerSector,lpNumberOfFreeClusters,lpTotalNumberOfClusters

lpRootPathName:指向驱动器根目录字符串
lpSectorsPerCluster:返回每簇的扇区数
lpBytesPerSector:返回每扇区的字节数
lpNumberOfFreeClusters:返回驱动器中未使用的簇的数量
lpTotalNumberOfClusters:返回驱动器中簇的总数

Windows 文件操作 — 其它文件操作

1.拷贝文件
拷贝文使用CopyFile或CopyFileEx

invoke CopyFile,lpExistingFileName,lpNewFileName,bFailIfExists

函数将lpExistingFileName指定文件拷贝为lpNewFileName指定的文件
bFailIfExists指定目标存在时的动作,指定为TRUE,拷贝失败;指定为FALSE则覆盖掉原来的文件,拷贝出来的新文件的属性和原来文件的属性一样
CopyFileEx函数可以完成同样的功能,只不过函数可以指定一个回调函数.

2.移动文件
移动文件使用MoveFile或MoveFileEx

invoke MoveFile,lpExistingFileName,lpNewFileName

将lpExistingFileName指定的文件移动到lpNewFileName指定的目标位置,文件名可以同时改变或不改变,目标文件必须不存在,否则函数调用失败.当函数执行成功的时候返回非0值,否则函数返回0
注意:MoveFile函数可以移动文件或目录.当移动一个文件时,源文件和目标文件可以处于不同的驱动器上
当移动对象是一个文件夹时,源目录与目标目录必须在一个驱动器上

invoke MoveFileEx,lpExistingFileName,lpNewFileName,dwFlags

MoveFileEx函数增加了一个dwFlags参数来时行移动控制,参数可以是下面取值的组合
MOVEFILE_COPY_ALLOWED:允许拷贝数据,如果不指定,文件不能移动到不同的驱动器上,不能与REBOOT同时使用
MOVEFILE_DELAY_UNTIL_REBOOT:在Winnt上执行时,函数在下一次启动时再进行移动操作
MOVEFILE_REPLACE_EXISTING:如果目标存在,将它替换掉(与MoveFile相比的优点)
MoveFileEx还有个特殊用法

invoke MoveFileEx,lpExistingFileName,NULL,MOVEFILE_DELAY_UNTIL_REBOOT

将在下一次启动时将lpExistingFileName指定的文件删除(Win95不支持)

3.删除文件
删除文件使用DeleteFile函数

invoke DeleteFile,lpFileName

lpFileName指向一个包含要删除文件名的字符串.在Win95下执行时,可以删除打开状态的文件,在NT下执行,不能对一个打开的文件进行删除.

Windows 文件操作 — 文件属性

1.Win32的文件函数可以操作多种对象,用GetFileType函数可以得到操作对象的类型.

invoke GetFileType,hFile

该函数返回值有4种
FILE_TYPE_UNKNOWN 文件类型未知
FILE_TYPE_DISK 磁盘文件
FILE_TYPE_CHAR 字符设备
FILE_TYPE_PIPE 管道

2.获取文件长度
使用GetFileSize函数

invoke GetFileSize,hFile,lpFileSizeHigh

lpFileSizeHigh指向一个用来接收高32位长度的变量,一般设置为NULL,长度的低32位在函数的返回值中返回,GetFileSize返回的是当前长度

3.获取和修改文件日期
获取文件日期使用GetFileTime

invoke GetFileTime,hFile,lpCreationTime,lpLastAccessTime,lpLastWriteTime

hFile:操作文件句柄
lpCreationTime:创建日期
lpLastAccessTime:最后存取日期
lpLastWriteTime:最后写入日期
如果不需要某个日期可将它设置为NULL
返回的日期是个FILETIME 结构

FILETIME STRUCT
dwLowDateTime DWORD ? ;文件日期低32位
dwHighDateTime DWORD ? ;文件日期高32位
FILETIME ENDS

这个结构中无法直接得到日期的年月日时分秒等数据.转换FILETIME为SYSTEMTIME结构,使用FileTimeToSystemTime进行转换

invoke FileTimeToSystemTime,lpFileTime,lpSystemTime

设置日期

invoke SetFileTime,hFile,lpCreationTime,lpLastAccessTime,lpLastWriteTime

填写FILETIME结构的时候,最简便的方法

invoke SystemTimeToFileTime,lpSystemTime,lpFileTime

4.获取和修改文件属性
创建文件是,可以在CreateFile的dwFlagsAndAttributes参数指定,也可以在以后用SetFileAttributes修改属性

invoke SetFileAttributes,lpFileName,dwFileAttributes

调用SetFileAttributes不需要打开文件上,只要指定全路径的文件名就可以了
dwFileAttributes与dwFlagsAndAttributes的参数是一样的

获取文件的只读,隐含与系统等属性,可以使用GetFileAttributes

invoke GetFileAttributes,lpFimeName

如果函数执行失败返回-1 成功返回文件的属性

Windows 文件操作 — 查找文件

开始查找文件,首先使用FindFirstFile函数,如果函数执行成功,返回一个句柄hFindFile来对应这个寻找操作,接下来可以利用这个句柄循环调用FindNextFile继续查找其它文件,直到FindNextFile返回失败为止.最后关闭hFindFile句柄.一般查找文件的代码结构:

invoke FindFirstFile,lpFindFile,lpFindFileDate
.if eax != INVALID_HANDLE_VALUE
mov hFindFile,eax
.repeat
;处理本次找到的文件
invoke FindNextFile,hFindFile,addr lpFindFileData
.until eax==FALSE
invoke FindClose,hFindFile
.endif

FindFirstFile的参数lpFindFile指向一个字符串,代表要寻找的文件名,如果文件名中不锯含路径,将在当前目录中查找文件.在文件名中可以包含'*'或'?'通配符
FindFirstFile函数和FindNextFile函数中的lpFindFileData参数则指向一个缓冲区,函数会在缓冲区中返回一个WIN_FIND_DATA结构,结构中包括了Windows查找过程中临时使用的数据和找到的文件名与属性等数据,该结构定义如下:

WIN32_FIND_DATA STRUCT
dwFileAttributes DWORD ? ;文件属性
ftCreationTime FILETIME <> ;文件的创建日期
ftLastAccessTime FILETIME <> ;文件的最后存取日期
ftlastWriteTime FILETIME <> ;文件的最后修改日期
nFileSizeHigh DWORD ? ;文件长度的高32位
nFileSizeLow DWORD ? ;文件长度的低32位
dwReserved0 DWORD ? ;内部使用
dwReserved1 DWORD ? ;内部使用
cFileName BYTE MAX_PATH dup(?) ;本次找到的文件名
cAlternate BYTE 14 dup(?) ;文件的8.3结构的短文件名
WIN32_FIND_DATA ENDS

dwFileAttributes字段可以是下面取值的组合,通过它可以检查找到的是一个文件还是一个子目录,以及属性
FILE_ATTRIBUTE_ARCHIVE:文件包含归档属性
FILE_ATTRIBUTE_COMPRESSED:文件和目录被压缩
FILE_ATTRIBUTE_DIRECTORY:找到的是一个目录
FILE_ATTRIBUTE_HIDDEN:文件包含隐含属性
FILE_ATTRIBUTE_NORMAL:文件没有其它属性
FILE_ATTRIBUTE_READONLY:文件包含只读属性
FILE_ATTRIBUTE_SYSTEM:文件包含系统属性
FILE_ATTRIBUTE_TEMPORARY:文件是一个临时文件
注意:cFileName字段中包括了找到的文件名,这个文件名中并不包含路径.