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()  # 进入消息循环

因为换了电脑最后出来的效果如下图:

之前的效果如下:

 

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