NTFS是Windows NT以及之后的Windows 2000、Windows XP、Windows Server 2003、Windows Server 2008、Windows Vista和Windows 7的标准文件系统。NTFS取代了文件分配表(FAT)文件系统,为Microsoft的Windows系列操作系统提供文件系统。NTFS对FAT和HPFS(高性能文件系统)作了若干改进,例如,支持元数据,并且使用了高级数据结构,以便于改善性能、可靠性和磁盘空间利用率,并提供了若干附加扩展功能,如访问控制列表(ACL)和文件系统日志。该文件系统的详细定义属于商业秘密 ,但 Microsoft 已经将其注册为 知识产权产品。
NTFS 提供长文件名、数据保护和恢复,并通过目录和文件许可实现安全性。NTFS 支持大硬盘和在多个硬盘上存储文件(称为卷)。例如,一个大公司的数据库可能大得必须跨越不同的硬盘。NTFS 提供内置安全性特征,它控制文件的隶属关系和访问。从DOS或其他操作系统上不能直接访问 NTFS 分区上的文件。如果要在DOS下读写NTFS分区文件的话可以借助第三方软件;现如今,Linux系统上已可以使用 NTFS-3G进行对 NTFS 分区的完美读写,不必担心数据丢失。
Win 2000采用了更新版本的NTFS文件系统NTFS 5.0,它的推出使得用户不但可以像Win 9X那样方便快捷地操作和管理计算机,同时也可享受到NTFS所带来的系统安全性。 NTFS 允许文件名的长度可达 256 个字符。虽然 DOS 用户不能访问 NTFS 分区,但是 NTFS 文件可以拷贝到 DOS 分区。每个 NTFS 文件包含一个可被 DOS 文件名格式认可的 DOS 可读文件名。这个文件名是 NTFS 从长文件名的开始字符中产生的。
我们来实现在VC++下面实现读取MBR。
C++内联汇编
在C++代码中插入__asm {}即可
我们亲自来分析实现NTFS文件恢复。
;****************************************************** .386 .model flat, stdcall option casemap :none ;****************************************************** ; Include 文件定义 ;****************************************************** include \masm32\include\windows.inc include \masm32\include\user32.inc includelib \masm32\lib\user32.lib include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib ;******************************************************* ; Equ 等值定义 ;******************************************************* ICO_MAIN equ 1000h ;图标ID DLG_MAIN equ 1 ;对话框ID IDC_PARTITION equ 101h ;盘符名输入框ID IDC_FILENAME equ 102h ;文件名ID ;******************************************************* ; 数据段 ;******************************************************* .data hFile_Disk dd 0 ;磁盘文件号 hFile dd 0 ;文件号 FileName db '\\.\' ;打开文件名为\\.\X:形式的文件则打开了X分区 PARTITION db 3 dup (0) ;请注意FileName和等待用户输入的PARTITION共 ;同组成了要打开的文件名(分区) FILENAMEA db 25 dup (0) ;等待用户输入的字符缓存 FILENAMEU db 50 dup (0) ;转换成Unicode字符串的缓存 ErrCap db '失败',00 ;错误对话框的Caption ErrorInfo1 db '可能的原因是:',0dh db '1.该程序不能在除NT以外的系统中执行;',0dh db '2.您输入的盘符无效!',00h ErrorInfo2 db '读盘错误!',00 ErrorInfo3 db '该程序只能恢复NTFS文件系统中的数据!',0dh db '您打开的磁盘不是NTFS文件系统!',00h ErrorInfo4 db '移动文件指针错误!',00h Info1 db '请在当前文件夹下查看已恢复的文件!如果文件扩展名不对,请自行',0dh db '修改扩展名,如果没有文件,则说明您输入的文件没有找到!',00h Recoveried dd 30h ;用来存放已经恢复的文件个数0~z _0 db 00 ;字符串结束 Readed dd 0 System_Id db 'NTFS' ;DBR中NTFS卷的标志 MFT_Flag db 'FILE' ;MFT的标志 StateFindFile dd 0 ;该数据是FindFile过程的返回值 StringLength dd 0 ;该地址用于存放用户输入的文件名的输入长度 MFTTime dd 1024 Temp db 'Text',00 EdiOffset dd 0 ; IndicEdi dd 0 ;用来在比较字符串中存放Edi所指向的字符串的指针 FileNameOffset dd 0 ;用来存放文件名偏移 FileSize dd 0 ;用来存放常驻80H属性的文件大小 FileSize1 dq 0 ;用来存放系统分配给非常驻80H属性的大小 FileSize2 dq 0 ;用来存放非常驻80H属性的文件真实大小 Edi80 dd 0 ;用来保存非常驻80H属性的运行列表偏移 CInfo1 db 0 ;用来保存运行列表的第一个字节的低4位 CInfo2 db 0 ;用来保存运行列表的第一个字节的高4位 ResidentFlag dd 0ffh ;80H属性常驻与非常驻标志,为0表示常驻,为1表示非常驻 ByeofOneC dd 0 ;每簇字节数 RunC dd 0 ;运行相对起始簇号 RunC2 dd 0 ;运行绝对起始簇号 RunByte dd 0 ;运行字节数 RunCN dd 0 ;运行起始簇号 RunFirstAddr dq 0 ;运行起始偏移字节 ;__________________________________________________________________ .data? hInstance dd ? DBR db 512 dup (?) ;512字节作为DBR的缓冲 MFTFirstSector dd ? ;MFT首扇区存放的缓冲 MFTFirstOffset dd 2 dup (?) Filling db 8 dup (?) ;这个是没有用的数据,只是为了让后面的MFT在 ;程序执行时起始偏移在XXXXXXX0上,方便调试 MFT db 1024*1024 dup (?);1M作为MFT的缓冲 DataBuffer db 1024*1024 dup (?);1M字节做为文件数据缓冲 FileNameLong dd ? FileNameBuffer dw 260 dup (?) ;******************************************************* ; 代码段 ;******************************************************* .code ;******************************************************* _ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam mov eax,wMsg .if eax == WM_CLOSE ;如果消息为WM_CLOSE,当按下右上角的关闭按钮 invoke EndDialog,hWnd,NULL ;hWnd为对话框窗口句柄,结束对话框 ret .elseif eax == WM_INITDIALOG ;初始化代码 invoke GetDlgItem,hWnd,IDOK ;取IDOK句柄 invoke EnableWindow,eax,FALSE ;IDOK显示为灰色(确定按钮) invoke LoadIcon,hInstance,ICO_MAIN ;设置标题栏图标 invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax .elseif eax == WM_COMMAND mov eax,wParam ;_____________________________________________________________________ .if ax == IDCANCEL ;如果用户点击“取消”按钮 invoke EndDialog,hWnd,NULL ;关闭对话框 mov eax,TRUE ret .elseif ax== IDOK ;如果用户点击“确定”按钮 invoke CreateFileA,offset FileName,\ ;打开用户输入的盘符 GENERIC_READ OR GENERIC_WRITE,\ FILE_SHARE_READ OR FILE_SHARE_WRITE,\ NULL,OPEN_EXISTING,NULL,NULL mov [hFile_Disk],eax ;保存该分区的文件号 cmp eax,INVALID_HANDLE_VALUE ;判断其是否成功(为-1失败) jz _98CODE ;失败则转 invoke ReadFile,[hFile_Disk],\ offset DBR,512,\ offset Readed,NULL cmp eax,0 jz ReadFail mov edx,dword ptr [DBR+3] ;将该分区的分区标志送edx mov ebx,dword ptr System_Id cmp edx,ebx ;是NTFS分区吗? jnz P_Err ;不是则转 CALL FirstMFTOffset ;定位MFT的起始偏移 invoke SetFilePointer,[hFile_Disk],\ dword ptr [MFTFirstOffset],\ ;移动文件指针的低32位 offset MFTFirstOffset+4,\ ;移动文件指针的高32位 FILE_BEGIN ;从文件(分区)开始处计算 cmp eax,-1 jz MoveFail ReadMFT: mov dword ptr [MFTTime],1024 ;用于计算指针是否指到了内存中的MFT尾 invoke ReadFile,[hFile_Disk],\ ;读1兆MFT offset MFT,1024*1024,\ offset Readed,NULL add dword ptr [MFTFirstOffset],1024*1024 ;保存当前磁盘指针 adc dword ptr [MFTFirstOffset+4],0 cmp eax,0 jz ReadFail mov edi,offset MFT CallFindFile: call FindFile ;查找符合用户输入的已经删除的文件 ;(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( .if [StateFindFile]==0 ;FindFile过程返回状态数据[StateFindFile]为0 ;表示所有的MFT都找完了 invoke MessageBoxA,NULL,offset Info1,offset Temp,MB_OK ret ;(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( .elseif [StateFindFile]==1 ;FindFile过程返回状态数据[StateFindFile]为1 ;表示该文件未被删除或这不是用户要恢复的文件 ReadToo: dec dword ptr [MFTTime] cmp dword ptr [MFTTime],0 jz ReadMFT ;结果为0则表示要读下1兆MFT了 add edi,1024 ;edi向后1K,指向下一个MFT jmp CallFindFile ;继续查找符合用户输入的已经删除的文件 ;(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( .elseif [StateFindFile]==2 ;FindFile过程返回状态数据[StateFindFile]为2 ;表示这是一个用户要恢复的文件(文件名包含用户输入) call ReadData ;这个过程将要恢复文件的数据读入内存中 ;创建要恢复的文件,文件名为原来的文件名 ;______________________________________________________ .if dword ptr [ResidentFlag]==0 ;如果为常驻属性 invoke CreateFileW,offset FileNameBuffer,\ ;创建要恢复的文件 GENERIC_READ OR GENERIC_WRITE,\ ;为读和写打开 0,NULL,\ ;不允许文件再被打开 CREATE_NEW,\ ;创建新文件,如果文件已经存在则返回失败代码 NULL,NULL mov [hFile],eax ;保存文件号 invoke WriteFile,[hFile],\ ;写文件 offset DataBuffer,[FileSize],\ offset Readed,NULL invoke CloseHandle,[hFile] ;关闭文件 jmp ReadToo ;继续找是否还有包含用户输入的文件 ;_____________________________________________________ .elseif dword ptr [ResidentFlag]==1 ;如果为非常驻属性 mov dword ptr [RunC2],0 ;绝对起始簇号清零 jmp ReadToo ;invoke MessageBoxA,NULL,offset ErrorInfo1,offset Temp,MB_OK .endif ;_______________________________________________________ ret .endif ;(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( _98CODE: invoke MessageBoxA,NULL,offset ErrorInfo1,offset ErrCap,MB_OK mov eax,TRUE ret ReadFail: invoke CloseHandle,[hFile_Disk] ;关闭文件(关闭分区) invoke MessageBoxA,NULL,offset ErrorInfo2,offset ErrCap,MB_OK mov eax,TRUE ret P_Err: invoke CloseHandle,[hFile_Disk] ;关闭文件(关闭分区) invoke MessageBoxA,NULL,offset ErrorInfo3,offset ErrCap,MB_OK mov eax,TRUE ret MoveFail: invoke CloseHandle,[hFile_Disk] ;关闭文件(关闭分区) invoke MessageBoxA,NULL,offset ErrorInfo4,offset ErrCap,MB_OK mov eax,TRUE ret ;_____________________________________________________________________ .elseif ax == IDC_PARTITION ;如果用户在盘符文本框中输入 invoke SendDlgItemMessage,hWnd,\ ;限制文本输入为2个字符 IDC_PARTITION,EM_LIMITTEXT,2,NULL invoke GetDlgItemText,hWnd,IDC_PARTITION,\ addr PARTITION,sizeof PARTITION ;取用户输入文本到PARTITION处 invoke GetDlgItem,hWnd,IDOK ;取IDOK句柄(确定按钮) invoke EnableWindow,eax,TRUE ;置确定按钮为可用 mov eax,TRUE ret ;_____________________________________________________________________ .elseif ax == IDC_FILENAME ;如果用在文件名文本框中输入 invoke SendDlgItemMessage,hWnd,\ ;限制文本输入为24个字符 IDC_FILENAME,EM_LIMITTEXT,24,NULL invoke GetDlgItemText,hWnd,IDC_FILENAME,\ ;取用户输入到FILENAMEA处 addr FILENAMEA,sizeof FILENAMEA call CompString ;计算用户输入的字符的长度,并保存在StringLength处 ;将ANSI字符串转换为Unicode字符串 invoke MultiByteToWideChar,1,0,addr FILENAMEA,\ ;AUSI地址FILENAMEA首地址 -1,addr FILENAMEU,\ ;-1为自动大小(为0结束) sizeof FILENAMEU ;转换到FILENAMEU,大小为FILENAMEU的大小 mov edi,offset FILENAMEU call ToCaps ;将FILENAMEU中的Unicode字符串中的小写字母转换为大写 mov eax,TRUE ret .endif ;_____________________________________________________________________ .else mov eax,FALSE ret .endif mov eax,TRUE ret _ProcDlgMain endp ;******************************************************* ;该过程用于计算用户输入的文件名的长度(Unicode的长度),并保存在StringLength处 CompString proc mov eax,00h mov edi,offset FILENAMEU CmpString: cmp byte ptr [edi],00 ;字符串是否结束 jz ExitProc inc eax inc edi inc edi jmp CmpString ExitProc: mov [StringLength],eax ret CompString endp ;******************************************************* ;小写的Unicode字母转大写 ToCaps proc Char: mov bx,word ptr [edi] ;将字符串中的一个字送BX .while bx>=61h && bx<=7ah ;这个字是UNICODE小写字母么,是则执行循环体 sub bx,20h ;将其转变为大小的Unicode字符 mov word ptr [edi],bx ;再将这个转变后的字符送回字符串中 inc edi ;指针加2 inc edi mov bx,word ptr [edi] ;继续将下一个字符串中的一个字送BX .break .if bx==00h ;如果是00则表示已经到了字符串尾,则退出循环 .endw cmp bx,00 ;bx为0表示已经到了字符串的尾部 jz Okchar ;到了字符串的尾部转返回处 inc edi ;指针加2(因为是Unicode字符) inc edi jnz Char ;不是小写Unicode字母则跳过这个字符继续做下一个字符的判断 Okchar: ret ToCaps endp ;******************************************************* ;计算$MFT起始偏移,并存放在MFTFirstOffset FirstMFTOffset proc mov edi,offset DBR mov eax,dword ptr [edi+30h] ;MFT起始簇号送eax mov cl,[edi+0dh] movzx ebx,cl ;每簇扇区数送ebx mul ebx ;MFT起始簇号*每簇扇区数=MFT起始扇区号,高位保存在edx中 ;低位保存在eax中 mov edi,offset MFTFirstSector mov [edi],eax ;将MFT起始扇区号保存 mov ebx,512 ;每扇区字节数送ebx mul ebx ;每扇区字节数*MFT起始扇区号=MFT起始偏移 mov edi,offset MFTFirstOffset ;放MFT起始偏移的地址送edi mov [edi],eax ;保存MFT起始偏移(低位) mov [edi+4],edx ;保存MFT起始偏移(高位) ;经过以上算法后,MFTFirstOffset保存的就是MFT起始偏移了 ret FirstMFTOffset endp ;******************************************************* ;判断当前MFT是否是用户要恢复的文件 FindFile proc push edi NextFindFile: ;_____________________________________________________ .if dword ptr [edi] != 454c4946h ;如果这不是一个合法的MFT,则表明已经读完了卷中所有的MFT cmp dword ptr [edi],44414142h ;在windows 2K中,有可能出现标志为“BAAD”的空MFT jnz NoMft mov [StateFindFile],1 ;[StateFindFile]赋1后退出,跳过这个MFT jmp ExitProc NoMft: mov [StateFindFile],0 ;[StateFindFile]赋0后退出 jmp ExitProc ;_____________________________________________________ .elseif word ptr [edi+16h] != 0 ;如果这个文件是未被删除的 mov [StateFindFile],1 ;[StateFindFile]赋1后退出 jmp ExitProc ;_____________________________________________________ .else add di,word ptr [edi+14h] mov dword ptr [EdiOffset],edi ;保存属性头开始偏移 ;(((((((((((((((((((((((((((((((((((((((((((((((((((((( FindAttribute: .if dword ptr [edi]==30h ;是30h属性的话 CmpFileNameLeng: ;比较文件名长度 push edx pop edx add di,word ptr [edi+14h] ;将30H属性头后的属性偏移送edi ;(属性头14H处为该属性开始的相对偏移) mov edx,dword ptr [edi+30h] ;保存文件大小,为后面恢复文件做准备 mov dword ptr [FileSize2],edx ;30H属性偏移30H后的8个字节是文件的实际大小 mov edx,dword ptr [edi+34h] mov dword ptr [FileSize2+4],edx movzx eax,byte ptr [edi+40h] ;edi+40中存放的是30H属性的文件名Unicode长度 mov dword ptr [FileNameLong],eax ;保存文件名长度 cmp eax,dword ptr [StringLength] ;该长度和用户输入的文件名长度进行比较 jc Next ;如果CF=1(有借位)则转,有借位则表示用户输 ;入的字符串的长度大于该文件的文件名长度, ;这肯定不是用户要恢复的文件了 jmp CmpFileName Next: mov edi,dword ptr [EdiOffset] ;将属性头开始偏移送回edi add edi,dword ptr [edi+4h] ;edi为下一个属性的偏移(属性头04~07为该属性的长度) mov dword ptr [EdiOffset],edi ;保存下一个属性头开始偏移 jmp FindAttribute ;继续找下一个属性 CmpFileName: movzx eax,byte ptr [edi+40h] ;该文件30H属性的文件名长度送eax add edi,42h ;30H属性开始的偏移+42后为该文件文件名的开始偏移送edi(源串地址) mov dword ptr [IndicEdi],edi ;保存文件名的开始偏移 mov dword ptr [FileNameOffset],edi ;保存文件名开始偏移!!!!!!!!!!! sub eax,dword ptr [StringLength] ;eax=文件名长度-用户输入的串长度 inc eax ;eax加1,结果为串比较时移动源串指针的次数 push edi call ToCaps ;将小写的Unicode码转为大写,为比较串做准备 pop edi mov ecx,dword ptr [StringLength] ;计数器=用户输入的串的长度 mov ebx,ecx ;保存用户输入串长度到ebx mov esi,offset [FILENAMEU] ;目的串地址 Compare: repz cmpsw ;比较源串和目的串 jz Alike ;一样则说明这就是用户要恢复的文件,一样则转 dec eax cmp eax,0 ;为0表示比较完毕都没有发现文件名中包含了用户输入的字符串 jz Next ;继续找下一个属性 mov esi,offset [FILENAMEU] ;重新指向用户输入串的首地址目的串地址 add dword ptr [IndicEdi],2 ;文件名字符串向后移动两个单位,指向下一个字符 mov edi,dword ptr [IndicEdi] mov ecx,ebx ;比较的次数=用户输入的串的长度 jmp Compare ;转比较处 Alike: xor eax,eax mov edi,offset FileNameBuffer mov ecx,128 ;循环128次,将文件名缓冲区清零 ClearBuffer: mov dword ptr [edi],eax add edi,4 loop ClearBuffer ;清除文件名缓冲区 mov esi,dword ptr [FileNameOffset] ;源地址为30H属性中文件名的开始 mov edi,offset FileNameBuffer ;目标地址为FileNameBuffer mov ecx,dword ptr [FileNameLong] ;传送次数为文件名长度 rep movsw ;拷贝文件名到FileNameBuffer中 mov [StateFindFile],2 ;这就是用户要恢复的文件[StateFindFile]赋值2后退出 jmp ExitProc ;((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( .elseif dword ptr [edi]==0ffffffffh ;是属性结束的话,说明这个文件不是用户要恢复的 mov [StateFindFile],1 ;[StateFindFile]赋1后退出 jmp ExitProc ;((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( .else ;是其他属性的话直接将指针加上属性长度得到后面的属性开始,重新判断 jmp Next .endif ;((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( .endif ExitProc: pop edi ;将该MFT的起始偏移写回edi ret FindFile endp ;******************************************************* ReadData proc push edi ;保存要恢复文件的MFT的开始地址 add di,word ptr [edi+14h] FindNext: mov dword ptr [EdiOffset],edi ;保存属性头开始偏移 .if dword ptr [edi]==80h ;是80h属性的话 cmp byte ptr [edi+9h],0 ;该80H属性是否有属性名[edi+09H]为0表示没有属性名 jnz Next ;有属性名的80H属性不是用户数据属性,跳过这个属性,比较下一个 .if byte ptr [edi+08]==0 ;[edi+08]==0(一个字节) 表示该属性为常驻属性 movzx eax,word ptr [edi+14h] ;属性开始的偏移(属性头的长度)送eax mov ebx,dword ptr [edi+4h] ;包括属性头长度在内的属性长度 sub ebx,eax ;ebsx=包括属性头长度在内的属性长度 - 属性头的长度=用户数据的长度(文件大小) mov dword ptr [FileSize],ebx ;文件大小 mov ecx,ebx ;循环次数为文件大小 mov esi,edi ;把80H属性头开始的偏移送esi add si,word ptr [edi+14h] ;把80H属性开始的偏移送esi(源地址) mov edi,offset DataBuffer ;把数据缓存区的地址送edi(目的地址) rep movsb ;将要恢复的用户数据传到DataBuffer中 inc dword ptr [Recoveried] ;每恢复一个文件将[Recoveried]加1 mov dword ptr [ResidentFlag],0 ;给常驻与非常驻标志置0,表示常驻 jmp ExitProc .elseif byte ptr [edi+08]==1 ;[edi+08]==1(一个字节)表示该属性为非常驻属性 ;获取文件的大小 mov edx,dword ptr [edi+30h] ;保存文件大小,为后面恢复文件做准备 mov dword ptr [FileSize1],edx ;80H属性头偏移30H后的8个字节是文件的真实大小 mov edx,dword ptr [edi+34h] mov dword ptr [FileSize1+4],edx invoke CreateFileW,offset FileNameBuffer,\ ;创建要恢复的文件 GENERIC_READ OR GENERIC_WRITE,\ ;为读和写打开 0,NULL,\ ;不允许文件再被打开 CREATE_NEW,\ ;创建新文件,如果文件已经存在则返回失败代码 NULL,NULL mov [hFile],eax ;保存文件号 push edi ;保存edi push eax ;保存eax mov edi,offset DBR mov cl,byte ptr [edi+0dh] movzx ebx,cl ;每簇扇区数送ebx mov eax,512 mul ebx ;每簇扇区数乘以每扇区字节数=每簇字节数 mov dword ptr [ByeofOneC],eax ;每簇字节数保存到ByeofOneC pop eax pop edi xor ebx,ebx mov bx,word ptr [edi+20h] add edi,ebx ;把80H属性运行列表开始的偏移送edi Recover: call RunInfo ;获取运行起始字节偏移(在RunFirstAddr处),和该运行占有的字节数(在RunByte处) invoke SetFilePointer,[hFile_Disk],\ ;移动文件指针到运行数据开始处 dword ptr [RunFirstAddr],\ ;移动文件指针的低32位 offset RunFirstAddr+4,\ ;移动文件指针的高32位 FILE_BEGIN ;从文件(分区)开始处计算 ReadWrite: .if dword ptr [RunByte]>100000H ;如果运行大小大于1M字节 invoke ReadFile,[hFile_Disk],\ ;读1兆字节的运行数据 offset DataBuffer,1024*1024,\ offset Readed,NULL invoke WriteFile,[hFile],\ ;写1M数据到文件 offset DataBuffer,1024*1024,\ offset Readed,NULL sub dword ptr [RunByte],1024*1024 ;RunByte减1M字节 sub dword ptr [FileSize1],1024*1024 ;文件大小减1M字节 jmp ReadWrite ;继续读数据写进文件 .elseif dword ptr [RunByte]<=100000H ;如果运行大小小于或等于1M字节 invoke ReadFile,[hFile_Disk],\ ;读运行大小字节的运行数据 offset DataBuffer,[RunByte],\ offset Readed,NULL invoke WriteFile,[hFile],\ ;写运行最后剩下的实际文件大小字节数据到文件 offset DataBuffer,dword ptr [FileSize1],\; [FileSize1]总是小于[RunByte]的 offset Readed,NULL mov al,byte ptr [CInfo1] movzx ecx,al add edi,ecx mov al,byte ptr [CInfo2] movzx ecx,al add edi,ecx inc edi ;这样edi就指向了下一个运行列表的开始 cmp byte ptr [edi],0 ;有下一个运行列表吗? jnz Recover ;不是0表示还有下一个运行,继续处理下一个运行 invoke CloseHandle,[hFile] ;没有下一个运行则关闭文件 invoke SetFilePointer,[hFile_Disk],\ ;还原磁盘指针 dword ptr [MFTFirstOffset],\ ;移动文件指针的低32位 offset MFTFirstOffset+4,\ ;移动文件指针的高32位 FILE_BEGIN ;从文件(分区)开始处计算 .endif ExitReadWrite: mov dword ptr [ResidentFlag],1 ;给常驻与非常驻标志置1,表示非常驻 jmp ExitProc .endif .elseif dword ptr [edi]==0ffffffffh ;是属性结束的话,就退出 jmp ExitProc .else ;是其他属性的话 Next: mov edi,dword ptr [EdiOffset] ;将属性头开始偏移送回edi add edi,dword ptr [edi+4h] ;edi为下一个属性的偏移(属性头04~07为该属性的长度) jmp FindNext ;继续找下一个属性 .endif ExitProc: pop edi ;将该MFT的起始偏移写回edi ret ReadData endp ;******************************************************* RunInfo proc ;获取文件起始字节偏移,和该运行占有的字节数 mov dword ptr [Edi80],edi ;将属性运行列表偏移保存到Edi80 mov al,byte ptr [edi] ;把运行列表的第一个字节送al,其高 ;4位表示多少运行列表中多少个字节为起始簇 ;低4位表示多少个字节表示簇大小 push eax mov cl,4 shr al,cl ;将al逻辑右移4位,结果al为该运行起始的簇号所占字节数 mov byte ptr [CInfo1],al pop eax and al,0fh ;清al高4位后al为该运行的簇数占有的字节 mov byte ptr [CInfo2],al ;计算运行的总共的字节数 push edi mov esi,edi inc esi ;源地址为运行所占簇数偏移 mov edi,offset RunCN ;目标地址 xor ecx,ecx mov cl,byte ptr [CInfo2] rep movsb ;将运行簇数保存到RunCN处 mov eax,dword ptr [RunCN] ;将运行簇数送eax mov ebx,dword ptr [ByeofOneC] ;将每簇字节数送ebx mul ebx mov dword ptr [RunByte],eax ;该运行所占字节数偏移送RunByte处 pop edi ;计算运行的起始偏移字节 push edi mov esi,edi inc esi xor ecx,ecx mov cl,byte ptr [CInfo2] add esi,ecx ;esi指向运行起始簇偏移(源地址) mov edi,offset RunC ;目标地址 mov cl,byte ptr [CInfo1] ;循环次数为运行列表中起始簇所占字节数 rep movsb ;将运行的起始簇号保存到RunC处 mov eax,dword ptr [RunC] ;将相对运行起始簇号送eax add dword ptr [RunC2],eax ;计算绝对起始簇号 mov eax, dword ptr [RunC2] ;绝对起始簇号送eax mov ebx,dword ptr [ByeofOneC] ;将每簇字节数送ebx mul ebx mov dword ptr [RunFirstAddr],eax mov dword ptr [RunFirstAddr+4],edx ;保存运行起始偏移到RunFirstAddr处 pop edi ret RunInfo endp ;******************************************************* start: invoke GetModuleHandle,NULL ;得到模块句柄(NULL为本模块) mov hInstance,eax ;保存模块句柄 invoke DialogBoxParam,hInstance,\ ;创建模块对话框,从hInstance指定模块装入 DLG_MAIN,NULL,\ ;装入DLG_MAIN参数指定的对话框,父对话框为NULL offset _ProcDlgMain,NULL ;过程地址为_ProcDlgMain的首地址, ;当作WM_INITDIALOG消息的lParam传给过程对话框定义为NULL(未定义) invoke ExitProcess,NULL ;退出程序 end start