python實現數據恢復小軟件 v1版本 簡單實現

首先需要 pip install wmi,psutil,pypiwin32,tkinter 其中的pypiwin32引入時間會比較長等待一下

這個版本是一年半之前的,缺點是當掃描大磁盤時會超出‘list’的長度,導致程序出錯,所以目前只能夠掃描文件較少的磁盤並進行恢復。使用python3可以運行就是會出現亂碼, python2的情況某些會因爲中文會報錯提示‘utf8’不能編碼,需要修改

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# __Author__: 前世是隻狼
import os
import wmi
import platform
import psutil
import tkinter
import binascii
import tkinter.messagebox  # 消息框
from tkinter import ttk  # 導入內部包
win = tkinter.Tk()  # 初始化
win.title("NTFS數據恢復")
win.geometry('450x320')  # 設置窗口大小
win.resizable(False, False)#固定窗體
titelRecoed = tkinter.Label(win, text=" NTFS數據恢復 ", bg="#ff4400", font=("宋體", 14), width=20, height=1).grid(row=0, column=1)
listForTitel = tkinter.Label(win, text="請選擇要恢復的分區:", bg="#ff4400", font=("宋體", 10), width=25, height=1).grid(row=1, column=0)
allDisk = psutil.disk_partitions()  # 獲得所有的磁盤信息
allDiskForList = []
allDiskForChoose = []
for sdiskpart in allDisk:
    if sdiskpart.fstype == 'NTFS':                  # 判定是否是NTFS格式
        disk = sdiskpart.device
        diskForList = disk
        temp = psutil.disk_usage(disk)               # 獲得磁盤的詳細信息
        total = round(temp.total / 1073741824, 2)    # 字節數轉換成GB
        totalForList = (total, "GB")
        allDiskForNtfs = (diskForList, totalForList)
        allDiskForList.append(allDiskForNtfs)
        allDiskForChoose.append(diskForList)
# 雙擊事件
def onDBClick(event):
    askForonDBClick = tkinter.messagebox.askyesno('提示', '確定恢復該文件嗎?')
    if askForonDBClick:
        index = tree.index(tree.selection()[0])
        disks = r"\\.\%s" % (diskList.get()[0:2])  # 打印選中的值
        filename = 'D:\\deletefile\\' + get_name(disks)[index].replace("\x00", "")
        with open(filename, 'wb') as f:
            f.write(binascii.a2b_hex(get_content(disks)[index]))
            f.close()
    else:
        pass
Version1 = platform.platform()
c = wmi.WMI()
for sys in c.Win32_OperatingSystem():
    Version = sys.Caption.encode("UTF8")           # 操作系統
    Vernum = sys.BuildNumber                       # 版本號
    num = sys.OSArchitecture.encode("UTF8")        # 操作系統位數
    Processesnum = sys.NumberOfProcesses           # 進程總數
for processor in c.Win32_Processor():
    CPU = processor.Name.strip().encode("UTF8")    # cpu型號
for Memory in c.Win32_PhysicalMemory():
    Memory = (int(Memory.Capacity) / 1048576)      # 內存大小
disklist = []                                      # 數組
for disk in c.Win32_LogicalDisk(DriveType=3):
    disklist.append(disk.Caption+"%0.2f%% free" % (100.0 * int(disk.FreeSpace) / int(disk.Size)))
tree = ttk.Treeview(win)         # 創建樹狀列表for刪除文件
tree1 = ttk.Treeview(win, show="headings")
tree1["columns"] = ("參數", "內容")
tree1.column("參數", width=55)  # 表示列,不顯示
tree1.column("內容", width=120)  # 表示列,不顯示
tree1.heading("參數", text="參數")  # 顯示錶頭
tree1.heading("內容", text="內容")  # 顯示錶頭
tree["selectmode"] = "browse"       # 按啥都只能同時高亮一行;
tree.bind("<Double-1>", onDBClick) #<Button-1>Double
tree.grid(row=3, column=1)
tree1.insert("", 0, text="操作系統", values=("操作系統", Version1[0: 10]))  # 插入數據,
tree1.insert("", 1, text="版本號", values=("版本號", Vernum))
tree1.insert("", 2, text="操作系統", values=("操作系統", "%s" % num))
tree1.insert("", 3, text="進程總數", values=("進程總數", Processesnum))
tree1.insert("", 4, text="CPU型號", values=("CPU型號", CPU))
tree1.insert("", 5, text="內存大小", values=("內存大小", "%sMB" % Memory))
for disks in disklist:
    tree1.insert("", 6, text="磁盤剩餘", values=("磁盤剩餘", disks))
tree1.grid(row=3, column=0)
chooseDisk = ""            # 掃描的磁盤首先確定
def Hexchange(data):       # 轉換成大寫16進制的自定義函數
    disk_boot = []  # 用來存放$Boot的數組
    boot = []
    for i, c in enumerate(data):
        disk_boot.append(c)
    for key, value in enumerate(disk_boot):
        if key % 2 == 0:
            boot.append(disk_boot[key] + disk_boot[key + 1])
    return boot
def Odisk(disk_choose, start, offset_disk):  # 輸出要絕對讀寫的磁盤盤符,開始位置和偏移量
    with open(disk_choose, 'rb+') as f:
        # 以文件起始位置作爲相對位置,偏移0個長度
        f.seek(start, 0)
        partdata = f.read(offset_disk).hex().upper()
    return partdata
def get_content(disk):     # 恢復文件
    partdata = Odisk(disk, 0, 512)
    disk_boot = Hexchange(partdata)    # 用來存放$Boot的數組
    disk_bpb = disk_boot[11:84]        # 獲取BPB區:數組存放
    sector_bytes = disk_bpb[0:2]       # 扇區字節數
    sector_bytes.reverse()
    cluster_sector = disk_bpb[2]       # 每個簇有幾個扇區
    cluster_sector_data = str(cluster_sector)
    track_sector = disk_bpb[13:15]     # 每磁道的扇區數
    track_sector.reverse()
    track_num = disk_bpb[29:37]        # 扇區總數
    track_num.reverse()
    disk_mft = disk_bpb[37:45]         # $MFT的起始簇號
    disk_mft.reverse()
    mft_track = disk_bpb[53:57]       # 每個MFT記錄的簇數
    mft_track.reverse()
    indexes_track = disk_bpb[57:61]   # 索引的簇數
    indexes_track.reverse()
    offset = int(cluster_sector_data, 16) * int(mft_str(disk_mft), 16) * int(mft_str(sector_bytes), 16)  # $MFT位置(偏移量) := $MFT的邏輯簇號 *每簇扇區數 *每扇區字節數
    mftdata = Odisk(disk, offset, 256 * 1024)  # 存放MFT
    d_mft = Hexchange(mftdata)
    partition_point_array = []          # 嘗試把沒有被刪除的文件給複製出來
    for index, item in enumerate(d_mft):
        condition1 = ['46', '49', '4C', '45']
        condition2 = ['00', '00']      # 文件類型,文件是把01變成00/ // 0已刪除 1正常文件 2已刪除目錄 3目錄正使用
        if d_mft[index:index + 4] == condition1 and d_mft[index + 22:index + 24] == condition2:
            partition_point = index  # 分割點
            partition_point_array.append(partition_point)
    delete_content_name_array = []   # 文件名數組,內容數組
    for ipar in range(len(partition_point_array)):
        structure_size = d_mft[partition_point_array[ipar] + 24:partition_point_array[ipar] + 28]
        structure_size.reverse()
        mft_record = d_mft[      # 屬性  10,30是常駐屬性;80是非常駐屬性
                     partition_point_array[ipar] + 0:partition_point_array[ipar] + int(mft_str(structure_size), 16)]   # 第一個屬性偏移量
        attribute_offset = mft_record[20:22]
        attribute_offset.reverse()
        length_10 = mft_record[int(mft_str(attribute_offset), 16) + 4:int(mft_str(attribute_offset), 16) + 8]
        length_10.reverse()
        start_80 = 0
        attribute_80 = ['80', '00', '00', '00']
        attribute_length_80 = []   # 80屬性的屬性長度
        for index, item in enumerate(mft_record):
            if mft_record[index:index + 4] == attribute_80:
                start_80 = index
                attribute_length_80 = mft_record[start_80 + 4:start_80 + 8]
        attribute_length_80.reverse()   # 存放80屬性
        end = int(mft_str(attribute_length_80), 16)
        mft_80 = mft_record[start_80:start_80 + end]
        attribute_sign = mft_80[8]
        if attribute_sign == "00":
            content = mft_80[24:len(mft_80)]      # 被刪除的內容數組
            delete_content_name = ''
            for icn in range(len(content)):
                delete_content_name += str(content[icn])
            delete_content_name_array.append(delete_content_name)
        else:
            content = mft_80[24:len(mft_80)]
            data_function = content[len(content) - 8:len(content)]
            temp = data_function[0]
            temp_a = str(temp)[0]
            temp_b = str(temp)[1]
            temp_c = int(temp_a) + int(temp_b)
            dataruns_cont = data_function[1:temp_c + 1]
            dataruns_top = dataruns_cont[int(temp_b):int(temp_a) + 1]
            dataruns_top.reverse()
            dataruns_bigen = int(mft_str(dataruns_top), 16) * 8 * 512
            dataruns_size = dataruns_cont[0:int(temp_b)]
            dataruns_size.reverse()
            dataruns_zijie = int(mft_str(dataruns_size), 16) * 8 * 512
            dataruns_temp = Odisk(disk, dataruns_bigen,dataruns_zijie)
            dataruns = Hexchange(dataruns_temp)
            delete_content_name = ''
            for icn in range(len(dataruns)):
                delete_content_name += str(dataruns[icn])
            delete_content_name_array.append(delete_content_name)
    return delete_content_name_array
def get_name(disk):    # 獲取被刪除文件名數組
    partdata = Odisk(disk, 0, 512)
    disk_boot = Hexchange(partdata)    # 用來存放$Boot的數組
    disk_bpb = disk_boot[11:84]        # 獲取BPB區:數組存放
    sector_bytes = disk_bpb[0:2]       # 扇區字節數
    sector_bytes.reverse()
    cluster_sector = disk_bpb[2]       # 每個簇有幾個扇區
    cluster_sector_data = str(cluster_sector)
    track_sector = disk_bpb[13:15]      # 每磁道的扇區數
    track_sector.reverse()
    track_num = disk_bpb[29:37]         # 扇區總數
    track_num.reverse()
    disk_mft = disk_bpb[37:45]         # $MFT的起始簇號
    disk_mft.reverse()
    mft_track = disk_bpb[53:57]        # 每個MFT記錄的簇數
    mft_track.reverse()
    indexes_track = disk_bpb[57:61]    # 索引的簇數
    indexes_track.reverse()
    offset = int(cluster_sector_data, 16) * int(mft_str(disk_mft), 16) * int(mft_str(sector_bytes), 16)  # $MFT位置(偏移量) := $MFT的邏輯簇號 *每簇扇區數 *每扇區字節數
    mftdata = Odisk(disk, offset, 256 * 1024)      # 存放MFT
    d_mft = Hexchange(mftdata)
    partition_point_array = []           # 嘗試把沒有被刪除的文件給複製出來
    for index, item in enumerate(d_mft):
        condition1 = ['46', '49', '4C', '45']
        condition2 = ['00', '00']      # 文件類型,文件是把01變成00/ // 0已刪除 1正常文件 2已刪除目錄 3目錄正使用
        if d_mft[index:index + 4] == condition1 and d_mft[index + 22:index + 24] == condition2:
            partition_point = index  # 分割點
            partition_point_array.append(partition_point)
    delete_file_name_array = []
    delete_content_name_array = []
    for ipar in range(len(partition_point_array)):
        structure_size = d_mft[partition_point_array[ipar] + 24:partition_point_array[ipar] + 28]
        structure_size.reverse()
        mft_record = d_mft[    # 屬性  10,30是常駐屬性;80是非常駐屬性
                     partition_point_array[ipar] + 0:partition_point_array[ipar] + int(mft_str(structure_size), 16)]   # 第一個屬性偏移量
        attribute_offset = mft_record[20:22]
        attribute_offset.reverse()
        length_10 = mft_record[int(mft_str(attribute_offset), 16) + 4:int(mft_str(attribute_offset), 16) + 8]
        length_10.reverse()
        delete_file_name = mft_record[int(mft_str(attribute_offset), 16) + int(mft_str(length_10), 16) + 90:int(mft_str(attribute_offset),16) + int(mft_str(length_10), 16) + 112]
        delete_file_name_data = ""
        for ij in range(len(delete_file_name)):
            delete_file_name_data += chr(int(delete_file_name[ij], 16))
        delete_file_name_array.append(delete_file_name_data)
        start_80 = 0
        attribute_80 = ['80', '00', '00', '00']
        attribute_length_80 = []     # 80屬性的屬性長度
        for index, item in enumerate(mft_record):
            if mft_record[index:index + 4] == attribute_80:
                start_80 = index
                attribute_length_80 = mft_record[start_80 + 4:start_80 + 8]
        attribute_length_80.reverse()     # 存放80屬性
        end = int(mft_str(attribute_length_80), 16)
        mft_80 = mft_record[start_80:start_80 + end]
        content = mft_80[24:len(mft_80)]      # 被刪除的內容數組
        delete_content_name = ''
        for icn in range(len(content)):
            delete_content_name += chr(int(content[icn], 16))
        delete_content_name_array.append(delete_content_name)
    return delete_file_name_array
def scan(*args):  # 處理事件,*args表示可變參數
    askForUser = tkinter.messagebox.askyesno('提示', '確定掃描該磁盤嗎?')
    if askForUser:
        disks = r"\\.\%s" % (diskList.get()[0:2])
        for index, item in enumerate(get_name(disks)):
            myid = tree.insert("", index, text=item.replace("\x00", ""), values=index)  # ""表示父節點是根
    else:
        pass
def allRecovery(*args):  # 處理事件,*args表示可變參數
    askForallRecovery = tkinter.messagebox.askyesno('提示', '確定全部恢復嗎?')
    if askForallRecovery:
        disks = r"\\.\%s" % (diskList.get()[0:2])  # 打印選中的值
        for index, item in enumerate(get_name(disks)):
            filename = 'D:\\deletefile\\' + item.replace("\x00", "")
            with open(filename, 'wb') as f:
                f.write(binascii.a2b_hex(get_content(disks)[index]))
                f.close()
    else:
        pass
def recovery(*args):  # 處理事件,*args表示可變參數
    askForUserRec = tkinter.messagebox.askyesno('提示', '確定恢復嗎?')
    if askForUserRec:
        tkinter.messagebox.askyesno('提示', '雙擊恢復')
    else:
        pass
# 轉化成字符串自定義函數:輸入倒敘後的數組;返回字符串
def mft_str(mft_array):
    mft_array_data = ''
    for mft_len in range(len(mft_array)):
        mft_array_data += str(mft_array[mft_len])
    return mft_array_data
# 創建保存恢復出來文件的文件夾
def mkdir(path):
    folder = os.path.exists(path)
    if not folder:  # 判斷是否存在文件夾如果不存在則創建爲文件夾
        os.makedirs(path)  # makedirs 創建文件時如果路徑不存在會創建這個路徑
        tkinter.messagebox.showwarning('提示', 'D:\deletefile創建成功!')
    else:
        tkinter.messagebox.showwarning('提示', 'D:\deletefile存在!')
mkdir("D:\\deletefile")  # 默認保存文件夾
diskList = tkinter.StringVar()
diskListChosen = ttk.Combobox(win, width=26, textvariable=diskList)
diskListChosen['values'] = allDiskForChoose
diskListChosen.current(0)
diskListChosen.grid(row=1, column=1)
listButten = tkinter.Button(win, text="啓動掃描", command=scan).grid(row=1, column=2)  # 綁定事件,點擊按鈕時時,綁定scan()函數)
listButten1 = tkinter.Button(win, text="雙擊恢復", command=recovery).grid(row=3, column=2)
listicon1 = tkinter.Label(win, width=5).grid(row=1, column=5)
listButten2 = tkinter.Button(win, text="全部恢復", command=allRecovery).grid(row=2, column=2)
listiconleft2 = tkinter.Label(win, text="已刪除文件", width=28, height=1, bg="#ff4400").grid(row=2, column=1)
listiconleft3 = tkinter.Label(win, text="電腦系統信息", width=25, height=1, bg="#ff4400").grid(row=2, column=0)
win.mainloop()  # 進入消息循環

因爲換了電腦最後出來的效果如下圖:

之前的效果如下:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章