HI3515获取U盘序列号

本文介绍了一种通过单片机程序读取U盘序列号的方法。利用读取/proc/scsi/usb-storage/目录下文件的方式,找到并解析U盘序列号。通过for循环遍历目录以应对多次插拔导致的文件路径变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做U盘的加解密,调好RC4后开始获取U盘序列号。

在百度之后试了一些源码,对这个单片机不适用,偶然发现在命令里打入cut /proc/scsi/usb-storage/1可以查看U盘信息,包括序列号。于是想:是不是可以读取这个文件的信息提取出U盘序列号呢?先写个程序试试。

从cut /proc/scsi/usb-storage/1中发现U盘序列号是在第四行,格式是Serial Number:**********,于是用了比较简单的方法读取了第四行Serial Number:后面的内容,发现确实读出了U盘序列号。

之后拔插了U盘后发现了一个问题,随着拔插次数的增加usb-storage目录里的数字会随之增加,但是我们插拔的次数不可能太多,于是用了for循环来读取我们需要的文本,方法虽然笨拙,但是可行。

代码如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void main()
{
	char SN[50];
	char Serial_Number[20];
	char ch[50];
	FILE *fp;
	char pData[512];
	int i = 4,j;
	for(j = 0;j<10;j++)
	{
		sprintf(ch,"/proc/scsi/usb-storage/%d",j);
 		if((fp=fopen(ch,"r+"))!=NULL)
			break;
	}
 	while(i--)
 	{
 		fgets(pData,100,fp);
 	} 
	fclose(fp);
	sscanf(pData, "Serial Number:%[^&]",Serial_Number);
	printf("%s\n",Serial_Number);
}
至此U盘序列号的读取结束

获取UID序列号 VS2005编译通过,源码源自优快云。已经测试好用。 可以获得U名称,制造商ID号 版本号 U序列号及容量 Form1.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.IO; using System.Management; namespace UDiskTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button2_Click(object sender, EventArgs e) { DriveInfo[] s = DriveInfo.GetDrives(); foreach (DriveInfo drive in s) { if (drive.DriveType == DriveType.Removable) { pf.Text = drive.Name.ToString(); break; } } ManagementClass cimobject = new ManagementClass("Win32_DiskDrive"); ManagementObjectCollection moc = cimobject.GetInstances(); foreach (ManagementObject mo in moc) { if (mo.Properties["InterfaceType"].Value.ToString() == "USB") { try { //产品名称 Caption.Text = mo.Properties["Caption"].Value.ToString(); //总容量 Size.Text = mo.Properties["Size"].Value.ToString(); string[] info = mo.Properties["PNPDeviceID"].Value.ToString().Split('&'); string[] xx = info[3].Split('\\'); //序列号 MessageBox.Show("U序列号:" + xx[1]); PNPDeviceID.Text = xx[1]; xx = xx[0].Split('_'); //版本号 REV.Text = xx[1]; //制造商ID xx = info[1].Split('_'); VID.Text = xx[1]; } catch (Exception ex) { MessageBox.Show(ex.Message); } } } } } } 如果编译遇到问题是using System.Management;无法找到引用找到, 项目(p)->添加引用(R)->找到.NET标签页 列表中下部位找到System.Management 双击点确定
Linux读取U或者移动硬序列号获取U或者移动硬分区名。基本原理如下: 当有外置 USB 插入的时候,会产生 /proc/scsi/usb-storage 目录,并在其中产生数字文件(形如 1 2 3 4),此文件存储了设备相关信息。 相应的 /sys/class/scsi_device/ 目录中会有 scsi 设备的目录(ide 硬默认无子目录,sata硬默认有子目录),以数字开头(形如 1:0:0:0 2:0:0:0) 这个数字与前面 /proc/scsi/usb-storage目录中的相对应,子目录表示sata硬。比如 /sys/class/scsi_device/2:0:0:0/device/block 中有USB设备,从该目录下得到U或者移动硬的分区名,比如sda1。 该demo实现了上述过程,先检查 /proc/scsi/usb-storage 目录,看是否有u或者移动硬接入,如果有则读取u或者移动硬序列号,然后在/sys/class/scsi_device/目录下逐级查找,直到找到u或者移动硬的分区。 完整代码,可直接编译和测试,嵌入式环境下也是适应的。 在linux测试如下,id表示序列号,sdb1表示是分区名称: # ./a.out udisk dev num:0 udisk id:055CE21B ret:0 dev num:0 found dir:0:0:0:1 found dev dir:0:0:0:1,full path:/sys/class/scsi_device/0:0:0:1 sub founction found dir:sda found sd device dir:sda,full path:/sys/class/scsi_device/0:0:0:1/device/block/sda found dir:0:0:0:2 found dev dir:0:0:0:2,full path:/sys/class/scsi_device/0:0:0:2 sub founction found dir:sdb found sd device dir:sdb,full path:/sys/class/scsi_device/0:0:0:2/device/block/sdb found sd sub device dir:sdb1 found dir:0:0:0:0 found dev dir:0:0:0:0,full path:/sys/class/scsi_device/0:0:0:0 sub funciton Open Directory /sys/class/scsi_device/0:0:0:0/device/block Error:No such file or directory get name return:0,disk name:sdb1 # 因为有的USB设备会有好几个,比如把CD和U集成到一起,就会出现上面的情况,有多个子目录去查找。
import struct import os import tkinter as tk from tkinter import ttk, messagebox, simpledialog, filedialog, scrolledtext import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import platform import sys import datetime import math class FAT32Parser: def __init__(self, device_path, readonly=True): self.device_path = device_path self.readonly = readonly self.sector_size = 512 self.cluster_size = 0 self.fat_start = 0 self.data_start = 0 self.root_cluster = 0 self.fat = [] self.free_clusters = [] self.total_clusters = 0 try: # 打开设备 mode = 'rb' if readonly else 'r+b' self.fd = open(device_path, mode) except Exception as e: raise ValueError(f"无法打开设备: {e}") try: # 读取引导扇区 self.fd.seek(0) boot_sector = self.fd.read(512) # 检查FAT32签名 if len(boot_sector) < 512 or boot_sector[510] != 0x55 or boot_sector[511] != 0xAA: raise ValueError("无效的引导扇区签名 - 可能不是FAT32格式") # 解析关键参数 self.sectors_per_cluster = boot_sector[13] if self.sectors_per_cluster not in [1, 2, 4, 8, 16, 32, 64, 128]: raise ValueError("无效的每簇扇区数") self.reserved_sectors = struct.unpack('<H', boot_sector[14:16])[0] self.num_fats = boot_sector[16] self.sectors_per_fat = struct.unpack('<I', boot_sector[36:40])[0] self.root_cluster = struct.unpack('<I', boot_sector[44:48])[0] self.total_sectors = struct.unpack('<I', boot_sector[32:36])[0] or struct.unpack('<I', boot_sector[40:44])[0] # 计算关键位置 self.cluster_size = self.sectors_per_cluster * self.sector_size self.fat_start = self.reserved_sectors * self.sector_size self.data_start = self.fat_start + (self.num_fats * self.sectors_per_fat * self.sector_size) # 计算总簇数 self.total_clusters = (self.total_sectors - self.reserved_sectors - self.num_fats * self.sectors_per_fat) // self.sectors_per_cluster # 加载FAT表 self._load_fat_table() # 检查根簇号是否有效 if self.root_cluster < 2 or self.root_cluster >= len(self.fat): raise ValueError(f"无效的根簇号: {self.root_cluster}") # 扫描空闲簇 self._find_free_clusters() except Exception as e: self.fd.close() raise e def _load_fat_table(self): """加载FAT表到内存""" self.fd.seek(self.fat_start) fat_size = self.sectors_per_fat * self.sector_size fat_data = self.fd.read(fat_size) if len(fat_data) != fat_size: raise ValueError("读取FAT表失败") # 解析FAT32表项 (每4字节一个簇) self.fat = [] for i in range(0, len(fat_data), 4): # 只取低28位(FAT32实际用28位) entry = struct.unpack('<I', fat_data[i:i+4])[0] & 0x0FFFFFFF self.fat.append(entry) def _find_free_clusters(self): """查找所有空闲簇""" self.free_clusters = [] for cluster_idx in range(2, len(self.fat)): # 簇0和1保留 if self.fat[cluster_idx] == 0: self.free_clusters.append(cluster_idx) def get_cluster_chain(self, start_cluster): """获取文件的簇链""" if start_cluster < 2 or start_cluster >= len(self.fat): return [] # 无效簇号 chain = [] current = start_cluster visited = set() # 追踪簇链直到结束 while current < 0x0FFFFFF8: # 文件结束标记 if current in visited: break # 防止无限循环 if current >= len(self.fat): break # 越界保护 chain.append(current) visited.add(current) next_cluster = self.fat[current] if next_cluster == 0 or next_cluster >= 0x0FFFFFF8: break current = next_cluster return chain def read_directory(self, cluster): """读取目录内容""" entries = [] chain = self.get_cluster_chain(cluster) if not chain: return entries # 长文件名缓存 lfn_parts = [] for c in chain: # 计算簇对应的扇区 sector_offset = self.data_start + (c - 2) * self.cluster_size if sector_offset < 0 or sector_offset > self.get_disk_size(): break self.fd.seek(sector_offset) data = self.fd.read(self.cluster_size) if len(data) < self.cluster_size: break # 读取不完整 # 解析每个目录项(32字节) for i in range(0, len(data), 32): entry = data[i:i+32] if len(entry) < 32: continue if entry[0] == 0x00: # 空闲 lfn_parts = [] # 重置长文件名缓存 continue if entry[0] == 0xE5: # 删除 lfn_parts = [] # 重置长文件名缓存 continue attr = entry[11] if attr == 0x0F: # 长文件名条目 # 解析长文件名片段 seq = entry[0] name_part = entry[1:11] + entry[14:26] + entry[28:32] # 移除尾部的0x00 name_part = name_part.split(b'\x00')[0] try: name_part = name_part.decode('utf-16le', errors='ignore') except: name_part = '?' lfn_parts.append((seq, name_part)) continue # 短文件名条目 name = entry[0:8].decode('latin-1', errors='ignore').strip() ext = entry[8:11].decode('latin-1', errors='ignore').strip() fullname = name + ('.' + ext if ext else '') # 如果有长文件名,使用它 if lfn_parts: # 排序并组合长文件名片段 lfn_parts.sort(key=lambda x: x[0]) long_name = ''.join(part for _, part in reversed(lfn_parts)) fullname = long_name.strip() lfn_parts = [] # 解析属性 is_dir = bool(attr & 0x10) is_volume = bool(attr & 0x08) # 获取起始簇号 start_cluster_hi = struct.unpack('<H', entry[20:22])[0] start_cluster_lo = struct.unpack('<H', entry[26:28])[0] start_cluster = (start_cluster_hi << 16) | start_cluster_lo # 文件大小 size = struct.unpack('<I', entry[28:32])[0] # 解析时间日期 create_time = struct.unpack('<H', entry[14:16])[0] create_date = struct.unpack('<H', entry[16:18])[0] mod_time = struct.unpack('<H', entry[22:24])[0] mod_date = struct.unpack('<H', entry[24:26])[0] # 跳过卷标 if is_volume: continue # 跳过特殊目录项 if fullname in ['.', '..']: continue entries.append({ 'name': fullname, 'is_dir': is_dir, 'start_cluster': start_cluster, 'size': size, 'create_time': self._parse_dos_datetime(create_date, create_time), 'mod_time': self._parse_dos_datetime(mod_date, mod_time) }) return entries def _parse_dos_datetime(self, date, time): """解析DOS日期时间格式""" try: # 解析日期: 7位年(从1980), 4位月, 5位日 year = ((date & 0xFE00) >> 9) + 1980 month = (date & 0x01E0) >> 5 day = date & 0x001F # 解析时间: 5位时, 6位分, 5位秒(以2秒为单位) hour = (time & 0xF800) >> 11 minute = (time & 0x07E0) >> 5 second = (time & 0x001F) * 2 return f"{year}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}" except: return "未知时间" def read_file_content(self, start_cluster, size): """读取文件内容""" content = b'' chain = self.get_cluster_chain(start_cluster) if not chain: return content bytes_remaining = size for cluster in chain: if bytes_remaining <= 0: break # 计算簇位置 sector_offset = self.data_start + (cluster - 2) * self.cluster_size if sector_offset < 0 or sector_offset > self.get_disk_size(): break self.fd.seek(sector_offset) # 读取簇内容 bytes_to_read = min(bytes_remaining, self.cluster_size) chunk = self.fd.read(bytes_to_read) if not chunk: break content += chunk bytes_remaining -= len(chunk) return content def get_disk_size(self): """获取大小""" self.fd.seek(0, 2) # 移动到文件末尾 return self.fd.tell() def create_file(self, parent_cluster, filename, content): """在指定目录创建文件""" if self.readonly: raise PermissionError("只读模式不允许写操作") # 1. 分配簇链 clusters_needed = math.ceil(len(content) / self.cluster_size) if clusters_needed > len(self.free_clusters): raise IOError("磁空间不足") allocated_clusters = self.free_clusters[:clusters_needed] self.free_clusters = self.free_clusters[clusters_needed:] # 2. 更新FAT表 for i in range(len(allocated_clusters)): cluster = allocated_clusters[i] if i == len(allocated_clusters) - 1: # 最后一个簇标记为EOF self.fat[cluster] = 0x0FFFFFFF else: # 指向下一个簇 self.fat[cluster] = allocated_clusters[i+1] # 3. 写入FAT表 self._write_fat_table() # 4. 写入文件内容 for i, cluster in enumerate(allocated_clusters): offset = self.data_start + (cluster - 2) * self.cluster_size self.fd.seek(offset) # 写入当前簇的数据 start = i * self.cluster_size end = min((i+1) * self.cluster_size, len(content)) self.fd.write(content[start:end]) # 5. 在父目录创建目录项 self._create_directory_entry(parent_cluster, { 'name': filename, 'is_dir': False, 'start_cluster': allocated_clusters[0], 'size': len(content) }) return allocated_clusters[0] def create_directory(self, parent_cluster, dirname): """在指定目录创建子目录""" if self.readonly: raise PermissionError("只读模式不允许写操作") # 1. 分配一个簇给新目录 if not self.free_clusters: raise IOError("磁空间不足") cluster = self.free_clusters[0] self.free_clusters = self.free_clusters[1:] # 2. 更新FAT表 (目录结束) self.fat[cluster] = 0x0FFFFFFF self._write_fat_table() # 3. 初始化目录内容 (创建 . 和 .. 条目) self._initialize_directory(cluster, parent_cluster) # 4. 在父目录创建目录项 self._create_directory_entry(parent_cluster, { 'name': dirname, 'is_dir': True, 'start_cluster': cluster, 'size': 0 }) return cluster def _initialize_directory(self, cluster, parent_cluster): """初始化新目录内容""" # 计算簇位置 offset = self.data_start + (cluster - 2) * self.cluster_size # 创建 . 条目 dot_entry = self._create_dir_entry(".", cluster, True) # 创建 .. 条目 dotdot_entry = self._create_dir_entry("..", parent_cluster, True) # 写入目录内容 self.fd.seek(offset) self.fd.write(dot_entry) self.fd.write(dotdot_entry) # 填充剩余空间为0 remaining = self.cluster_size - len(dot_entry) - len(dotdot_entry) self.fd.write(b'\x00' * remaining) def _create_dir_entry(self, name, cluster, is_dir): """创建目录项字节数据""" # 短文件名格式 (8.3) if len(name) > 8 and '.' not in name: base, ext = name[:8], "" else: parts = name.split('.') base = parts[0].upper()[:8] ext = parts[1].upper()[:3] if len(parts) > 1 else "" # 填充空格 base = base.ljust(8, ' ') ext = ext.ljust(3, ' ') # 创建32字节条目 entry = bytearray(32) # 文件名 (8字节) entry[0:8] = base.encode('latin-1') # 扩展名 (3字节) entry[8:11] = ext.encode('latin-1') # 属性 (目录) entry[11] = 0x10 if is_dir else 0x20 # 目录或存档 # 创建时间和日期 (当前时间) now = datetime.datetime.now() create_time = self._to_dos_time(now) create_date = self._to_dos_date(now) entry[14:16] = struct.pack('<H', create_time) entry[16:18] = struct.pack('<H', create_date) # 修改时间和日期 mod_time = create_time mod_date = create_date entry[22:24] = struct.pack('<H', mod_time) entry[24:26] = struct.pack('<H', mod_date) # 起始簇号 entry[20:22] = struct.pack('<H', (cluster >> 16) & 0xFFFF) # 高16位 entry[26:28] = struct.pack('<H', cluster & 0xFFFF) # 低16位 # 文件大小 (目录为0) entry[28:32] = struct.pack('<I', 0) return entry def _to_dos_time(self, dt): """将datetime转换为DOS时间格式""" return ((dt.hour << 11) | (dt.minute << 5) | (dt.second // 2)) def _to_dos_date(self, dt): """将datetime转换为DOS日期格式""" return (((dt.year - 1980) << 9) | (dt.month << 5) | dt.day) def _create_directory_entry(self, parent_cluster, entry_info): """在父目录中添加新的目录项""" # 获取父目录的所有簇 clusters = self.get_cluster_chain(parent_cluster) if not clusters: raise IOError("父目录无效") # 查找空闲目录槽 for cluster in clusters: offset = self.data_start + (cluster - 2) * self.cluster_size self.fd.seek(offset) data = self.fd.read(self.cluster_size) for i in range(0, len(data), 32): pos = offset + i entry = data[i:i+32] # 找到空闲或已删除的条目 if len(entry) < 32 or entry[0] in [0x00, 0xE5]: # 创建新条目 new_entry = self._create_dir_entry( entry_info['name'], entry_info['start_cluster'], entry_info['is_dir'] ) # 设置文件大小 if not entry_info['is_dir']: new_entry[28:32] = struct.pack('<I', entry_info['size']) # 写入新条目 self.fd.seek(pos) self.fd.write(new_entry) return # 如果没有找到空闲槽,分配新簇 if not self.free_clusters: raise IOError("磁空间不足") new_cluster = self.free_clusters[0] self.free_clusters = self.free_clusters[1:] # 更新FAT表 self.fat[clusters[-1]] = new_cluster # 当前最后一个簇指向新簇 self.fat[new_cluster] = 0x0FFFFFFF # 新簇标记为EOF self._write_fat_table() # 初始化新簇 self.fd.seek(self.data_start + (new_cluster - 2) * self.cluster_size) self.fd.write(b'\x00' * self.cluster_size) # 写入新条目到新簇的第一个位置 new_entry = self._create_dir_entry( entry_info['name'], entry_info['start_cluster'], entry_info['is_dir'] ) if not entry_info['is_dir']: new_entry[28:32] = struct.pack('<I', entry_info['size']) self.fd.seek(self.data_start + (new_cluster - 2) * self.cluster_size) self.fd.write(new_entry) def _write_fat_table(self): """将FAT表写回磁""" # 更新所有FAT副本 for fat_copy in range(self.num_fats): offset = self.fat_start + fat_copy * self.sectors_per_fat * self.sector_size self.fd.seek(offset) # 构建FAT表数据 fat_data = bytearray() for entry in self.fat: fat_data += struct.pack('<I', entry) # 写入FAT表 self.fd.write(fat_data) def close(self): """安全关闭文件句柄""" if hasattr(self, 'fd') and self.fd: self.fd.close() class FAT32Explorer: def __init__(self, root, device_path, readonly=True): self.root = root self.root.title(f"FAT32 文件系统工具 - {device_path}") self.root.geometry("1200x800") self.root.protocol("WM_DELETE_WINDOW", self.on_close) self.device_path = device_path self.readonly = readonly self.current_cluster = None self.current_path = "/" self.selected_file = None self.file_content_cache = None # 创建GUI布局 self._create_widgets() # 在后台加载文件系统 self.loading = True self.status.set("正在加载文件系统...") self.root.after(100, self._load_filesystem) def _create_widgets(self): # 创建主框架 main_frame = tk.Frame(self.root) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 顶部工具栏 toolbar = tk.Frame(main_frame) toolbar.pack(fill=tk.X, pady=(0, 10)) mode_text = "只读模式" if self.readonly else "读写模式" mode_color = "green" if self.readonly else "red" tk.Label(toolbar, text=f"模式: {mode_text}", fg=mode_color, font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=10) # 路径导航 self.path_var = tk.StringVar(value="路径: /") tk.Label(toolbar, textvariable=self.path_var, font=("Arial", 10)).pack(side=tk.LEFT, padx=10) # 操作按钮 btn_frame = tk.Frame(toolbar) btn_frame.pack(side=tk.RIGHT) tk.Button(btn_frame, text="刷新", command=self.refresh).pack(side=tk.LEFT, padx=2) if not self.readonly: tk.Button(btn_frame, text="新建文件", command=self.create_file_dialog).pack(side=tk.LEFT, padx=2) tk.Button(btn_frame, text="新建目录", command=self.create_directory_dialog).pack(side=tk.LEFT, padx=2) # 主分割窗口 self.paned_window = tk.PanedWindow(main_frame, orient=tk.HORIZONTAL) self.paned_window.pack(fill=tk.BOTH, expand=True) # 左侧面板 (目录树) self.left_frame = tk.LabelFrame(self.paned_window, text="目录结构") self.paned_window.add(self.left_frame, width=300) # 目录树 self.tree = ttk.Treeview(self.left_frame, show='tree') self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar = ttk.Scrollbar(self.left_frame, orient=tk.VERTICAL, command=self.tree.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.tree.configure(yscrollcommand=scrollbar.set) self.tree.bind('<<TreeviewSelect>>', self.on_tree_select) # 右侧面板 self.right_frame = tk.PanedWindow(self.paned_window, orient=tk.VERTICAL) self.paned_window.add(self.right_frame) # 上部: 文件列表 file_frame = tk.LabelFrame(self.right_frame, text="当前目录内容") file_frame.pack(fill=tk.BOTH, expand=True) # 文件列表表头 columns = ("name", "size", "type", "cluster", "modified") self.file_tree = ttk.Treeview(file_frame, columns=columns, show="headings") # 设置列 self.file_tree.heading("name", text="名称") self.file_tree.heading("size", text="大小") self.file_tree.heading("type", text="类型") self.file_tree.heading("cluster", text="起始簇") self.file_tree.heading("modified", text="修改时间") self.file_tree.column("name", width=200) self.file_tree.column("size", width=80, anchor='e') self.file_tree.column("type", width=80) self.file_tree.column("cluster", width=80, anchor='e') self.file_tree.column("modified", width=150) self.file_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.file_tree.bind('<<TreeviewSelect>>', self.on_file_select) # 文件列表滚动条 file_scrollbar = ttk.Scrollbar(file_frame, orient=tk.VERTICAL, command=self.file_tree.yview) file_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.file_tree.configure(yscrollcommand=file_scrollbar.set) # 下部: 簇状态可视化 self.cluster_frame = tk.LabelFrame(self.right_frame, text="簇分配图") self.cluster_frame.pack(fill=tk.BOTH, expand=True) self.fig, self.ax = plt.subplots(figsize=(8, 4)) self.canvas = FigureCanvasTkAgg(self.fig, master=self.cluster_frame) self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 底部: 状态栏 self.status = tk.StringVar() self.status.set("就绪" + (" - 只读模式" if self.readonly else " - 读写模式")) status_bar = tk.Label(self.root, textvariable=self.status, bd=1, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) # 添加文件内容查看器 (弹出窗口) self.content_window = None def _load_filesystem(self): """安全加载文件系统""" try: self.parser = FAT32Parser(self.device_path, self.readonly) self.root_cluster = self.parser.root_cluster # 加载根目录 self._show_root_directory() self.status.set(f"就绪 | 总簇数: {len(self.parser.fat)} | 空闲簇: {len(self.parser.free_clusters)}") self.loading = False except Exception as e: self.status.set(f"错误: {str(e)}") messagebox.showerror("初始化错误", f"加载文件系统失败: {str(e)}") self.root.after(100, self.root.destroy) def _show_root_directory(self): """显示根目录""" self.tree.delete(*self.tree.get_children()) self.file_tree.delete(*self.file_tree.get_children()) self.current_path = "/" self.path_var.set(f"路径: {self.current_path}") # 添加根节点 root_id = self.tree.insert('', 'end', text="根目录", values=[self.root_cluster], open=True) self.current_cluster = self.root_cluster # 加载根目录内容 self._load_directory(root_id, self.root_cluster) def _load_directory(self, parent_id, cluster): """加载目录内容到树视图和列表""" try: entries = self.parser.read_directory(cluster) self.current_entries = entries # 清空文件列表 self.file_tree.delete(*self.file_tree.get_children()) # 添加到文件列表 for entry in entries: item_type = "目录" if entry['is_dir'] else "文件" size = f"{entry['size']:,}" if not entry['is_dir'] else "" self.file_tree.insert('', 'end', values=( entry['name'], size, item_type, entry['start_cluster'], entry['mod_time'] )) # 添加到树视图 for entry in entries: if entry['is_dir']: node_id = self.tree.insert(parent_id, 'end', text=entry['name'], values=[entry['start_cluster']]) # 添加一个虚拟节点以便展开 self.tree.insert(node_id, 'end', text="加载中...") # 更新簇图 self._update_cluster_map(cluster) self.status.set(f"显示目录: 簇 {cluster} | 条目: {len(entries)}") except Exception as e: self.status.set(f"错误: {str(e)}") messagebox.showerror("目录错误", f"读取目录失败: {str(e)}") def _update_cluster_map(self, cluster=None): """更新簇状态可视化""" if not hasattr(self, 'parser'): return self.ax.clear() # 获取簇链(如果是目录) if cluster: chain = self.parser.get_cluster_chain(cluster) else: chain = [] # 简化的簇状态展示(只显示前500个簇) max_clusters = min(500, len(self.parser.fat)) cluster_status = [0] * max_clusters # 标记特殊簇 for i in range(max_clusters): if i == 0: cluster_status[i] = 4 # 特殊簇 elif i == 1: cluster_status[i] = 4 # 特殊簇 elif self.parser.fat[i] == 0: cluster_status[i] = 0 # 空闲 elif self.parser.fat[i] >= 0x0FFFFFF8: cluster_status[i] = 1 # 文件结束 elif self.parser.fat[i] == 0x0FFFFFF7: cluster_status[i] = 3 # 坏簇 else: cluster_status[i] = 2 # 使用中 # 高亮显示当前目录的簇链 for c in chain: if c < max_clusters: cluster_status[c] = 5 # 当前选择 # 使用不同颜色 cmap = plt.cm.colors.ListedColormap([ 'green', # 0: 空闲 'red', # 1: 文件结束 'blue', # 2: 使用中 'black', # 3: 坏簇 'gray', # 4: 特殊簇 'yellow' # 5: 当前选择 ]) # 绘制热力图 img = self.ax.imshow([cluster_status], cmap=cmap, aspect='auto', extent=[0, max_clusters, 0, 1]) # 添加颜色条 cbar = self.fig.colorbar(img, ax=self.ax, ticks=[0.4, 1.2, 2.0, 2.8, 3.6, 4.4]) cbar.ax.set_yticklabels(['空闲', '结束簇', '使用中', '坏簇', '特殊簇', '当前选择']) self.ax.set_title(f'簇分配图 (前{max_clusters}个簇)') self.ax.set_xlabel('簇号') self.ax.set_yticks([]) self.canvas.draw() def on_tree_select(self, event): """处理树节点选择事件""" if self.loading: return selected = self.tree.selection() if not selected: return item = self.tree.item(selected[0]) if 'values' in item and item['values']: cluster = item['values'][0] self.current_cluster = cluster # 构建当前路径 path = [] current_item = selected[0] while current_item: item_text = self.tree.item(current_item)['text'] if item_text != "根目录": path.insert(0, item_text) current_item = self.tree.parent(current_item) self.current_path = "/" + "/".join(path) self.path_var.set(f"路径: {self.current_path}") # 如果节点有子节点但只有一个"加载中"节点,则加载实际内容 children = self.tree.get_children(selected[0]) if children and self.tree.item(children[0])['text'] == "加载中...": self.tree.delete(children[0]) self._load_directory(selected[0], cluster) else: self._load_directory(selected[0], cluster) def on_file_select(self, event): """处理文件列表选择事件""" if self.loading: return selected = self.file_tree.selection() if not selected: return item = self.file_tree.item(selected[0]) values = item['values'] if values: # 查找对应的条目 for entry in self.current_entries: if entry['name'] == values[0]: self.selected_file = entry break if self.selected_file: # 显示文件信息 if not self.selected_file['is_dir']: # 打开文件内容查看器 self.show_file_content() def show_file_content(self): """显示文件内容""" if not self.selected_file or self.selected_file['is_dir']: return # 创建弹出窗口 if self.content_window and self.content_window.winfo_exists(): self.content_window.destroy() self.content_window = tk.Toplevel(self.root) self.content_window.title(f"文件内容: {self.selected_file['name']}") self.content_window.geometry("800x600") # 添加文本编辑器 text_frame = tk.Frame(self.content_window) text_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 添加文本区域 self.content_text = scrolledtext.ScrolledText(text_frame, wrap=tk.WORD) self.content_text.pack(fill=tk.BOTH, expand=True) # 添加按钮 btn_frame = tk.Frame(self.content_window) btn_frame.pack(fill=tk.X, padx=10, pady=(0, 10)) tk.Button(btn_frame, text="加载内容", command=self.load_file_content).pack(side=tk.LEFT, padx=5) if not self.readonly: tk.Button(btn_frame, text="保存修改", command=self.save_file_content).pack(side=tk.LEFT, padx=5) tk.Button(btn_frame, text="关闭", command=self.content_window.destroy).pack(side=tk.RIGHT, padx=5) def load_file_content(self): """加载文件内容到编辑器""" if not self.selected_file: return try: content = self.parser.read_file_content( self.selected_file['start_cluster'], self.selected_file['size'] ) # 尝试解码为文本 try: decoded = content.decode('utf-8', errors='replace') self.content_text.delete(1.0, tk.END) self.content_text.insert(tk.END, decoded) self.file_content_cache = content self.status.set(f"已加载文件: {self.selected_file['name']}") except: # 如果是二进制文件,显示十六进制预览 hex_preview = ' '.join(f'{b:02x}' for b in content[:128]) if len(content) > 128: hex_preview += " ..." self.content_text.delete(1.0, tk.END) self.content_text.insert(tk.END, f"二进制文件 (十六进制预览):\n{hex_preview}") self.file_content_cache = content self.status.set(f"已加载二进制文件: {self.selected_file['name']}") except Exception as e: self.status.set(f"错误: {str(e)}") messagebox.showerror("读取错误", f"读取文件内容失败: {str(e)}") def save_file_content(self): """保存修改后的文件内容""" if self.readonly or not self.selected_file or not self.file_content_cache: return # 获取新内容 new_content = self.content_text.get(1.0, tk.END).encode('utf-8') # 检查内容是否变化 if new_content == self.file_content_cache: messagebox.showinfo("保存", "内容未更改") return # 确认保存 confirm = messagebox.askyesno("确认保存", f"确定要保存对文件 '{self.selected_file['name']}' 的修改吗?\n" "此操作将直接写入U!") if not confirm: return try: # 创建新文件 (如果大小变化) if len(new_content) != len(self.file_content_cache): # 创建新文件 new_cluster = self.parser.create_file( self.current_cluster, self.selected_file['name'], new_content ) # 删除旧文件 (标记为删除) # 在实际应用中应该实现文件删除功能 messagebox.showinfo("保存成功", "文件大小已改变,已创建新文件副本") else: # 直接覆盖内容 chain = self.parser.get_cluster_chain(self.selected_file['start_cluster']) bytes_remaining = len(new_content) for i, cluster in enumerate(chain): if bytes_remaining <= 0: break offset = self.parser.data_start + (cluster - 2) * self.parser.cluster_size self.parser.fd.seek(offset) # 写入当前簇的数据 start = i * self.parser.cluster_size end = min((i+1) * self.parser.cluster_size, len(new_content)) self.parser.fd.write(new_content[start:end]) bytes_remaining -= (end - start) self.status.set(f"文件已保存: {self.selected_file['name']}") messagebox.showinfo("保存成功", "文件内容已更新") # 刷新目录 self.refresh() except Exception as e: self.status.set(f"保存错误: {str(e)}") messagebox.showerror("保存失败", f"保存文件失败: {str(e)}") def create_file_dialog(self): """创建新文件对话框""" if self.readonly: messagebox.showwarning("只读模式", "只读模式下无法创建文件") return filename = simpledialog.askstring("新建文件", "请输入文件名:") if not filename: return # 创建内容输入窗口 content_window = tk.Toplevel(self.root) content_window.title(f"创建文件: {filename}") content_window.geometry("600x400") # 添加文本编辑器 text_frame = tk.Frame(content_window) text_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) content_text = scrolledtext.ScrolledText(text_frame, wrap=tk.WORD) content_text.pack(fill=tk.BOTH, expand=True) content_text.focus_set() # 添加按钮 btn_frame = tk.Frame(content_window) btn_frame.pack(fill=tk.X, padx=10, pady=(0, 10)) def create_file(): content = content_text.get(1.0, tk.END).encode('utf-8') content_window.destroy() try: self.parser.create_file( self.current_cluster, filename, content ) self.status.set(f"文件已创建: {filename}") self.refresh() messagebox.showinfo("成功", f"文件 '{filename}' 已创建") except Exception as e: self.status.set(f"创建错误: {str(e)}") messagebox.showerror("创建失败", f"创建文件失败: {str(e)}") tk.Button(btn_frame, text="创建", command=create_file).pack(side=tk.LEFT, padx=5) tk.Button(btn_frame, text="取消", command=content_window.destroy).pack(side=tk.RIGHT, padx=5) def create_directory_dialog(self): """创建新目录对话框""" if self.readonly: messagebox.showwarning("只读模式", "只读模式下无法创建目录") return dirname = simpledialog.askstring("新建目录", "请输入目录名:") if not dirname: return try: self.parser.create_directory(self.current_cluster, dirname) self.status.set(f"目录已创建: {dirname}") self.refresh() messagebox.showinfo("成功", f"目录 '{dirname}' 已创建") except Exception as e: self.status.set(f"创建错误: {str(e)}") messagebox.showerror("创建失败", f"创建目录失败: {str(e)}") def refresh(self): """刷新当前目录""" if self.loading or not self.current_cluster: return selected = self.tree.selection() if selected: self._load_directory(selected[0], self.current_cluster) def on_close(self): """安全关闭窗口""" if hasattr(self, 'parser'): self.parser.close() self.root.destroy() def get_device_path(): """安全获取设备路径""" root = tk.Tk() root.withdraw() # 显示安全警告 messagebox.showwarning("重要提示", "此工具将直接访问您的U设备\n\n" "请确认:\n" "1. 您使用的是测试U,而不是系统\n" "2. 您已备份重要数据\n" "3. 您了解直接操作磁的风险") # 获取操作模式 mode = messagebox.askyesno("选择模式", "请选择操作模式:\n\n是: 读写模式 (允许修改)\n否: 只读模式 (安全分析)") readonly = not mode # 获取设备路径 if platform.system() == 'Windows': # Windows设备路径输入 device_path = simpledialog.askstring("输入U路径", "请输入U符 (例如: F:):\n\n" "Windows路径格式: \\\\.\\[符]", initialvalue="\\\\.\\F:") if device_path: # 标准化路径格式 if not device_path.startswith('\\\\'): device_path = '\\\\.\\' + device_path.replace(':', '') return device_path, readonly else: # Linux/Mac设备路径选择 messagebox.showinfo("选择U", "请选择您的U设备(例如:/dev/sdb1)") device_path = filedialog.askopenfilename( title="选择U设备", filetypes=[("磁设备", "/dev/sd*"), ("所有文件", "*")] ) return device_path, readonly return None, True if __name__ == '__main__': # 安全获取设备路径和模式 device_path, readonly = get_device_path() if not device_path: sys.exit(0) # 最终安全确认 mode_text = "读写" if not readonly else "只读" confirm = messagebox.askyesno("最终确认", f"即将以{mode_text}模式访问设备:\n{device_path}\n\n" "请确认:\n" "1. 这是正确的U设备\n" "2. 您已备份重要数据\n\n" "是否继续?") if not confirm: sys.exit(0) # 创建主窗口 root = tk.Tk() try: app = FAT32Explorer(root, device_path, readonly) root.mainloop() except Exception as e: messagebox.showerror("应用程序错误", f"发生未处理的错误: {str(e)}") sys.exit(1)修改代码使其可以访问目录成功
最新发布
06-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值