winston 发表于 2012-3-7 00:07:22

[原]VisualC++信息安全编程(2)内联汇编实现NTFS文件恢复



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   3dup (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的标志
StateFindFiledd   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
.elseifeax == 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   
.elseifeax == WM_COMMAND
    mov       eax,wParam
   ;_____________________________________________________________________
      .ifax == IDCANCEL                         ;如果用户点击“取消”按钮
          invoke    EndDialog,hWnd,NULL         ;关闭对话框
          mov       eax,TRUE
          ret
      .elseifax== IDOK                           ;如果用户点击“确定”按钮
          invoke    CreateFileA,offset FileName,\;打开用户输入的盘符
                  GENERIC_READ OR GENERIC_WRITE,\
                  FILE_SHARE_READ OR FILE_SHARE_WRITE,\
                  NULL,OPEN_EXISTING,NULL,NULL
          mov       ,eax               ;保存该分区的文件号
          cmp       eax,INVALID_HANDLE_VALUE       ;判断其是否成功(为-1失败)
          jz      _98CODE                        ;失败则转
          invoke    ReadFile,,\
                  offset DBR,512,\
                  offset Readed,NULL
          cmp       eax,0
          jz      ReadFail
          mov       edx,dword ptr           ;将该分区的分区标志送edx
          mov       ebx,dword ptr System_Id      
          cmp       edx,ebx                        ;是NTFS分区吗?
          jnz       P_Err                        ;不是则转
          CALL      FirstMFTOffset               ;定位MFT的起始偏移
          invoke    SetFilePointer,,\
                  dword ptr ,\   ;移动文件指针的低32位
                  offset MFTFirstOffset+4,\      ;移动文件指针的高32位
                  FILE_BEGIN                     ;从文件(分区)开始处计算
          cmp       eax,-1
          jz      MoveFail
       ReadMFT:
          mov       dword ptr ,1024       ;用于计算指针是否指到了内存中的MFT尾
          invoke    ReadFile,,\      ;读1兆MFT
                  offset MFT,1024*1024,\
                  offset Readed,NULL
          add       dword ptr ,1024*1024;保存当前磁盘指针
          adc       dword ptr ,0
          cmp       eax,0
          jz      ReadFail
         
          mov       edi,offset MFT
       CallFindFile:                     
          call      FindFile             ;查找符合用户输入的已经删除的文件
            ;((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
            .if   ==0 ;FindFile过程返回状态数据为0
                                       ;表示所有的MFT都找完了
                   invoke    MessageBoxA,NULL,offset Info1,offset Temp,MB_OK
                   ret
            ;((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((      

            .elseif ==1 ;FindFile过程返回状态数据为1
                                       ;表示该文件未被删除或这不是用户要恢复的文件
                ReadToo:
                  dec dword ptr
                  cmp dword ptr ,0
                  jzReadMFT         ;结果为0则表示要读下1兆MFT了
                  add edi,1024          ;edi向后1K,指向下一个MFT
                  jmp CallFindFile      ;继续查找符合用户输入的已经删除的文件
            ;((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((                                       
            .elseif ==2 ;FindFile过程返回状态数据为2
                                       ;表示这是一个用户要恢复的文件(文件名包含用户输入)
                  call ReadData         ;这个过程将要恢复文件的数据读入内存中
                  ;创建要恢复的文件,文件名为原来的文件名
               ;______________________________________________________
                     .if   dword ptr ==0       ;如果为常驻属性
                        invoke    CreateFileW,offset FileNameBuffer,\;创建要恢复的文件
                            GENERIC_READ OR GENERIC_WRITE,\ ;为读和写打开
                            0,NULL,\                        ;不允许文件再被打开
                            CREATE_NEW,\               ;创建新文件,如果文件已经存在则返回失败代码
                            NULL,NULL


                        mov       ,eax                  ;保存文件号
                        invoke    WriteFile,,\          ;写文件
                                  offset DataBuffer,,\      
                                  offset Readed,NULL
                        invoke    CloseHandle,         ;关闭文件
                        jmp       ReadToo                     ;继续找是否还有包含用户输入的文件
                  ;_____________________________________________________
                      .elseifdword ptr ==1    ;如果为非常驻属性
                        mov       dword ptr ,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,      ;关闭文件(关闭分区)
          invoke    MessageBoxA,NULL,offset ErrorInfo2,offset ErrCap,MB_OK
          mov          eax,TRUE
          ret
      P_Err:
          invoke    CloseHandle,      ;关闭文件(关闭分区)
          invoke    MessageBoxA,NULL,offset ErrorInfo3,offset ErrCap,MB_OK
          mov          eax,TRUE
          ret
      MoveFail:
          invoke    CloseHandle,      ;关闭文件(关闭分区)
          invoke    MessageBoxA,NULL,offset ErrorInfo4,offset ErrCap,MB_OK
          mov          eax,TRUE
          ret      
      ;_____________________________________________________________________
      .elseifax == 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
      ;_____________________________________________________________________
      .elseifax == 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,offsetFILENAMEU
          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
    moveax,00h
    movedi,offset FILENAMEU
CmpString:
    cmpbyte ptr ,00      ;字符串是否结束
    jz   ExitProc
    inceax
    incedi
    incedi
    jmpCmpString
ExitProc:
    mov,eax
    ret
CompString    endp
;*******************************************************
;小写的Unicode字母转大写
ToCaps        proc
Char:
    movbx,word ptr        ;将字符串中的一个字送BX
.while   bx>=61h && bx<=7ah       ;这个字是UNICODE小写字母么,是则执行循环体
    subbx,20h                  ;将其转变为大小的Unicode字符
    movword ptr ,bx       ;再将这个转变后的字符送回字符串中
    incedi                     ;指针加2
    incedi                     
    movbx,word ptr        ;继续将下一个字符串中的一个字送BX
.break .if bx==00h                ;如果是00则表示已经到了字符串尾,则退出循环
.endw
    cmpbx,00                   ;bx为0表示已经到了字符串的尾部
    jz   Okchar                  ;到了字符串的尾部转返回处
    incedi                     ;指针加2(因为是Unicode字符)
    incedi                              
    jnzChar                  ;不是小写Unicode字母则跳过这个字符继续做下一个字符的判断   
Okchar:
    ret
ToCaps    endp
;*******************************************************
;计算$MFT起始偏移,并存放在MFTFirstOffset
FirstMFTOffsetproc
    movedi,offset DBR
    moveax,dword ptr ;MFT起始簇号送eax
    movcl,
    movzx ebx,cl       ;每簇扇区数送ebx
    mulebx         ;MFT起始簇号*每簇扇区数=MFT起始扇区号,高位保存在edx中
                     ;低位保存在eax中
    movedi,offset MFTFirstSector
    mov,eax   ;将MFT起始扇区号保存
    movebx,512       ;每扇区字节数送ebx
    mulebx         ;每扇区字节数*MFT起始扇区号=MFT起始偏移
    movedi,offset MFTFirstOffset ;放MFT起始偏移的地址送edi
    mov,eax   ;保存MFT起始偏移(低位)
    mov,edx   ;保存MFT起始偏移(高位)
    ;经过以上算法后,MFTFirstOffset保存的就是MFT起始偏移了
    ret
FirstMFTOffsetendp
;*******************************************************
;判断当前MFT是否是用户要恢复的文件
FindFileproc
    pushedi
NextFindFile:
;_____________________________________________________
.if   dword ptr != 454c4946h ;如果这不是一个合法的MFT,则表明已经读完了卷中所有的MFT
    cmp   dword ptr ,44414142h    ;在windows 2K中,有可能出现标志为“BAAD”的空MFT
    jnz   NoMft
    mov   ,1            ;赋1后退出,跳过这个MFT
    jmp   ExitProc
NoMft:
    mov   ,0       ;赋0后退出
    jmp   ExitProc
;_____________________________________________________
.elseif word ptr != 0                        ;如果这个文件是未被删除的
    mov   ,1                              ;赋1后退出
    jmp   ExitProc
;_____________________________________________________
.else
    add   di,word ptr
    mov   dword ptr ,edi                            ;保存属性头开始偏移
      ;((((((((((((((((((((((((((((((((((((((((((((((((((((((
      FindAttribute:
      .if   dword ptr ==30h             ;是30h属性的话
      CmpFileNameLeng:                     ;比较文件名长度
            pushedx
            pop   edx
            add   di,word ptr       ;将30H属性头后的属性偏移送edi
                                             ;(属性头14H处为该属性开始的相对偏移)
            mov   edx,dword ptr       ;保存文件大小,为后面恢复文件做准备                                             
            mov   dword ptr ,edx    ;30H属性偏移30H后的8个字节是文件的实际大小
            mov   edx,dword ptr       
            mov   dword ptr ,edx
            movzx eax,byte ptr        ;edi+40中存放的是30H属性的文件名Unicode长度
            mov   dword ptr ,eax ;保存文件名长度
            cmp   eax,dword ptr ;该长度和用户输入的文件名长度进行比较
            jc    Next                         ;如果CF=1(有借位)则转,有借位则表示用户输
                                             ;入的字符串的长度大于该文件的文件名长度,
                                             ;这肯定不是用户要恢复的文件了
            jmp   CmpFileName
      Next:
            mov   edi,dword ptr     ;将属性头开始偏移送回edi
            add   edi,dword ptr        ;edi为下一个属性的偏移(属性头04~07为该属性的长度)
            mov   dword ptr ,edi    ;保存下一个属性头开始偏移   
            jmp   FindAttribute                ;继续找下一个属性
      CmpFileName:
            movzx eax,byte ptr        ;该文件30H属性的文件名长度送eax
            add   edi,42h                      ;30H属性开始的偏移+42后为该文件文件名的开始偏移送edi(源串地址)
            mov   dword ptr ,edi   ;保存文件名的开始偏移
            mov   dword ptr ,edi ;保存文件名开始偏移!!!!!!!!!!!
            sub   eax,dword ptr ;eax=文件名长度-用户输入的串长度
            inc   eax                        ;eax加1,结果为串比较时移动源串指针的次数
            pushedi
            callToCaps                     ;将小写的Unicode码转为大写,为比较串做准备
            pop   edi
            mov   ecx,dword ptr ;计数器=用户输入的串的长度
            mov   ebx,ecx                      ;保存用户输入串长度到ebx   
            mov   esi,offset        ;目的串地址
      Compare:
            repz cmpsw                        ;比较源串和目的串
            jz    Alike                        ;一样则说明这就是用户要恢复的文件,一样则转
            dec   eax
            cmp   eax,0                        ;为0表示比较完毕都没有发现文件名中包含了用户输入的字符串                     
            jz    Next                         ;继续找下一个属性
            mov   esi,offset        ;重新指向用户输入串的首地址目的串地址
            add   dword ptr ,2       ;文件名字符串向后移动两个单位,指向下一个字符
            mov   edi,dword ptr
            mov   ecx,ebx                      ;比较的次数=用户输入的串的长度
            jmp   Compare                      ;转比较处                     
      Alike:
            xor   eax,eax
            mov   edi,offset FileNameBuffer
            mov   ecx,128                      ;循环128次,将文件名缓冲区清零
      ClearBuffer:
            mov   dword ptr ,eax
            add   edi,4                        
            loopClearBuffer                  ;清除文件名缓冲区
            mov   esi,dword ptr ;源地址为30H属性中文件名的开始
            mov   edi,offset FileNameBuffer      ;目标地址为FileNameBuffer
            mov   ecx,dword ptr    ;传送次数为文件名长度
            rep   movsw                        ;拷贝文件名到FileNameBuffer中
            mov   ,2            ;这就是用户要恢复的文件赋值2后退出
            jmp   ExitProc   
      ;(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((      
      .elseif dword ptr ==0ffffffffh      ;是属性结束的话,说明这个文件不是用户要恢复的
            mov   ,1            ;赋1后退出
            jmp   ExitProc
      ;(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
      .else                                     ;是其他属性的话直接将指针加上属性长度得到后面的属性开始,重新判断
            jmp   Next
         
      .endif
      ;(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
.endif
ExitProc:
    popedi                        ;将该MFT的起始偏移写回edi
    ret      
FindFileendp
;*******************************************************
ReadDataproc
    pushedi                     ;保存要恢复文件的MFT的开始地址
    add   di,word ptr
FindNext:
    mov   dword ptr ,edi   ;保存属性头开始偏移
.if      dword ptr ==80h      ;是80h属性的话
    cmp   byteptr ,0      ;该80H属性是否有属性名为0表示没有属性名
    jnz   Next                        ;有属性名的80H属性不是用户数据属性,跳过这个属性,比较下一个
      .if   byte ptr ==0      ;==0(一个字节) 表示该属性为常驻属性
            movzxeax,word ptr    ;属性开始的偏移(属性头的长度)送eax
            mov    ebx,dword ptr    ;包括属性头长度在内的属性长度
            sub    ebx,eax                  ;ebsx=包括属性头长度在内的属性长度 - 属性头的长度=用户数据的长度(文件大小)
            mov    dword ptr ,ebx ;文件大小
            mov    ecx,ebx                  ;循环次数为文件大小
            mov    esi,edi                  ;把80H属性头开始的偏移送esi
            add    si,word ptr     ;把80H属性开始的偏移送esi(源地址)
            mov    edi,offset DataBuffer    ;把数据缓存区的地址送edi(目的地址)
            rep    movsb                  ;将要恢复的用户数据传到DataBuffer中
            inc    dword ptr    ;每恢复一个文件将加1
            mov    dword ptr ,0 ;给常驻与非常驻标志置0,表示常驻
            jmp    ExitProc   
      .elseif byte ptr ==1      ;==1(一个字节)表示该属性为非常驻属性
            ;获取文件的大小
            mov    edx,dword ptr       ;保存文件大小,为后面恢复文件做准备                                             
            mov    dword ptr ,edx    ;80H属性头偏移30H后的8个字节是文件的真实大小
            mov    edx,dword ptr       
            mov    dword ptr ,edx
            invoke    CreateFileW,offset FileNameBuffer,\;创建要恢复的文件
                      GENERIC_READ OR GENERIC_WRITE,\ ;为读和写打开
                      0,NULL,\                        ;不允许文件再被打开
                      CREATE_NEW,\               ;创建新文件,如果文件已经存在则返回失败代码
                      NULL,NULL
            mov       ,eax                  ;保存文件号
            
            push   edi                        ;保存edi
            push   eax                        ;保存eax            
            mov    edi,offset DBR
            mov    cl,byte ptr
            movzxebx,cl       ;每簇扇区数送ebx
            mov    eax,512
            mul    ebx         ;每簇扇区数乘以每扇区字节数=每簇字节数
            mov    dword ptr ,eax ;每簇字节数保存到ByeofOneC
            pop    eax
            pop    edi
            xor    ebx,ebx
            mov    bx,word ptr
            add    edi,ebx    ;把80H属性运行列表开始的偏移送edi

         Recover:
            call   RunInfo   ;获取运行起始字节偏移(在RunFirstAddr处),和该运行占有的字节数(在RunByte处)
            invoke    SetFilePointer,,\ ;移动文件指针到运行数据开始处
                      dword ptr ,\   ;移动文件指针的低32位
                      offset RunFirstAddr+4,\      ;移动文件指针的高32位
                      FILE_BEGIN                     ;从文件(分区)开始处计算               

         ReadWrite:
            .if   dword ptr >100000H ;如果运行大小大于1M字节
               invoke    ReadFile,,\      ;读1兆字节的运行数据
                           offset DataBuffer,1024*1024,\
                           offset Readed,NULL               
               invoke    WriteFile,,\          ;写1M数据到文件
                           offset DataBuffer,1024*1024,\      
                           offset Readed,NULL
               sub    dword ptr ,1024*1024;RunByte减1M字节
               sub    dword ptr ,1024*1024 ;文件大小减1M字节
               jmp    ReadWrite                      ;继续读数据写进文件
            .elseif dword ptr <=100000H ;如果运行大小小于或等于1M字节
               invoke    ReadFile,,\      ;读运行大小字节的运行数据
                           offset DataBuffer,,\
                           offset Readed,NULL               
               invoke    WriteFile,,\          ;写运行最后剩下的实际文件大小字节数据到文件
                           offset DataBuffer,dword ptr ,\; 总是小于的
                           offset Readed,NULL
               mov       al,byte ptr
               movzx   ecx,al
               add       edi,ecx
               mov       al,byte ptr
               movzx   ecx,al
               add       edi,ecx
               inc       edi               ;这样edi就指向了下一个运行列表的开始
               cmp       byte ptr ,0 ;有下一个运行列表吗?
               jnz       Recover    ;不是0表示还有下一个运行,继续处理下一个运行
               invoke    CloseHandle, ;没有下一个运行则关闭文件
               invoke    SetFilePointer,,\;还原磁盘指针
                           dword ptr ,\   ;移动文件指针的低32位
                           offset MFTFirstOffset+4,\      ;移动文件指针的高32位
                           FILE_BEGIN                     ;从文件(分区)开始处计算
                  
            .endif
          ExitReadWrite:
            mov    dword ptr ,1 ;给常驻与非常驻标志置1,表示非常驻
            jmp    ExitProc   
      .endif
.elseifdword ptr ==0ffffffffh ;是属性结束的话,就退出
    jmp   ExitProc
.else                              ;是其他属性的话
Next:
    mov   edi,dword ptr    ;将属性头开始偏移送回edi
    add   edi,dword ptr       ;edi为下一个属性的偏移(属性头04~07为该属性的长度)
    jmp   FindNext                  ;继续找下一个属性
.endif
ExitProc:
    popedi                        ;将该MFT的起始偏移写回edi
    ret
ReadDataendp
;*******************************************************
RunInfo   proc   
    ;获取文件起始字节偏移,和该运行占有的字节数
    mov    dword ptr ,edi    ;将属性运行列表偏移保存到Edi80
    mov    al,byte ptr       ;把运行列表的第一个字节送al,其高
                                          ;4位表示多少运行列表中多少个字节为起始簇
                                          ;低4位表示多少个字节表示簇大小
    push   eax
    mov    cl,4
    shr    al,cl                  ;将al逻辑右移4位,结果al为该运行起始的簇号所占字节数
    mov    byte ptr ,al
    pop    eax
    and    al,0fh                   ;清al高4位后al为该运行的簇数占有的字节
    mov    byte ptr ,al
    ;计算运行的总共的字节数
    push   edi
    mov    esi,edi
    inc    esi                      ;源地址为运行所占簇数偏移
    mov    edi,offset RunCN         ;目标地址
    xor    ecx,ecx
    mov    cl,byte ptr
    rep    movsb                  ;将运行簇数保存到RunCN处
    mov    eax,dword ptr    ;将运行簇数送eax      
    mov    ebx,dword ptr ;将每簇字节数送ebx
    mul    ebx                     
    mov    dword ptr ,eax;该运行所占字节数偏移送RunByte处
    pop    edi
    ;计算运行的起始偏移字节
    push   edi
    mov    esi,edi
    inc    esi
    xor    ecx,ecx
    mov    cl,byte ptr
    add    esi,ecx               ;esi指向运行起始簇偏移(源地址)
    mov    edi,offset RunC         ;目标地址
    mov    cl,byte ptr     ;循环次数为运行列表中起始簇所占字节数
    rep    movsb                   ;将运行的起始簇号保存到RunC处
    mov    eax,dword ptr    ;将相对运行起始簇号送eax
    add    dword ptr ,eax    ;计算绝对起始簇号
    mov    eax, dword ptr     ;绝对起始簇号送eax   
    mov    ebx,dword ptr ;将每簇字节数送ebx
    mul    ebx                     
    mov    dword ptr ,eax
    mov    dword ptr ,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

作者:yincheng01 发表于2012-1-6 6:39:49 原文链接


页: [1]
查看完整版本: [原]VisualC++信息安全编程(2)内联汇编实现NTFS文件恢复