读取硬盘SN号处理方式 用户态发送smartctl -i /dev/sdX命令查询硬盘SN号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 uos@uos-PC [~] ➜ sudo smartctl -i /dev/sda smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.10.0-amd64-desktop] (local build) Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org === START OF INFORMATION SECTION === Model Family: Western Digital Blue Device Model: WDC WD10EZEX-22MFCA0 Serial Number: WD-WCC6Y4DF6V9V LU WWN Device Id: 5 0014ee 268c3dc22 Firmware Version: 01.01A01 User Capacity: 1,000,204,886,016 bytes [1.00 TB] Sector Sizes: 512 bytes logical, 4096 bytes physical Rotation Rate: 7200 rpm Form Factor: 3.5 inches Device is: In smartctl database [for details use: -P show] ATA Version is: ACS-3 T13/2161-D revision 3b SATA Version is: SATA 3.1, 6.0 Gb/s (current: 6.0 Gb/s) Local Time is: Tue Oct 15 14:59:18 2024 CST SMART support is: Available - device has SMART capability. SMART support is: Enabled
sd命令下发流程 可以通过strace先追踪到下发的命令为SG_IO
1 2 3 4 5 6 7 8 9 uos@uos-PC [~] ➜ sudo strace smartctl -i /dev/sda ioctl(3, SG_IO, {interface_id='S', dxfer_direction=SG_DXFER_FROM_DEV, cmd_len=16, cmdp="\x85\x08\x0e\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xec\x00", mx_sb_len=32, iovec_count=0, dxfer_len=512, timeout=60000, flags=0, dxferp="\x7a\x42\xff\x3f\x37\xc8\x10\x00\x00\x00\x00\x00\x3f\x00\x00\x00\x00\x00\x00\x00\x20\x20\x20\x20\ x57\x20\x2d\x44\x43\x57\x36\x43"..., status=0, masked_status=0, msg_status=0, sb_len_wr=0, sbp="", host_status=0, driver_status=0, resid=0, duration=0, info=0}) = 0
1 2 3 4 scsi_cmd_ioctl case SG_IO: get_sg_io_hdr(&hdr, arg); sg_io(q, bd_disk, &hdr, mode);#在此时硬盘的SN数据已经读取出来,存在了hdr中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 typedef struct sg_io_hdr { int interface_id; int dxfer_direction; unsigned char cmd_len; unsigned char mx_sb_len; unsigned short iovec_count; unsigned int dxfer_len; void __user *dxferp; unsigned char __user *cmdp; void __user *sbp; unsigned int timeout; unsigned int flags; int pack_id; void __user * usr_ptr; unsigned char status; unsigned char masked_status; unsigned char msg_status; unsigned char sb_len_wr; unsigned short host_status; unsigned short driver_status; int resid; unsigned int duration; unsigned int info; } sg_io_hdr_t ;
通过strace可以看到最后数据的返回是通过hdr->dxferp返回给用户空间的
感兴趣的小伙伴可以使用ftrace跟踪一下底层调用流程,我看到的是scsi会通过dma_map映射一片空间然后发送inquiry命令给硬盘查询硬盘数据最后返回上来,但是我们魔改没必要修改太底层的代码
此时我们可以解析一下dxferp返回数据的内容,从第5个字节开始是真正的SN号
前面为控制字段,第四个字节为长度控制
0x80 代表单元序列号VPD 页面,0x00为保留字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 int modify_dev_sn (struct sg_io_hdr *hdr) { unsigned char *buffer = NULL ; int i = 0 ,len_id = 0 ; unsigned char *match = NULL ; unsigned char len_value = 0xff ; const unsigned char pattern[] = {0x80 , 0x00 }; if (hdr->dxfer_len > 0x1024 ) { pr_err("hdr info len too long!\n" ); return -EFAULT; } buffer = kzalloc(hdr->dxfer_len, GFP_KERNEL); if (!buffer) { pr_err("Memory allocation failed for buffer\n" ); return -ENOMEM; } if (copy_from_user(buffer, hdr->dxferp, hdr->dxfer_len)) { pr_err("Failed to copy data from user space\n" ); kfree(buffer); return -EFAULT; } while (i <= (hdr->dxfer_len - 3 )) { if (memcmp (&buffer[i], pattern, 2 ) == 0 ) { match = &buffer[i + 3 ]; len_id = i + 2 ; break ; } i++; } if (match) { int sn_len = 33 ; if ((hdr->dxfer_len - 3 ) >= (sn_len + 1 )) { memcpy (&buffer[len_id], &len_value, 1 ); memcpy (match, get_huawei_ufs_cid(), sn_len); match[sn_len] = '\0' ; } } if (copy_to_user(hdr->dxferp, buffer, hdr->dxfer_len)) pr_err("%s: copy from user failed.\n" , __func__); kfree(buffer); return 0 ; }
此时我们就可以通过直接修改返回数据达到修改sn号的效果
sg命令下发流程 1 2 3 4 5 6 7 8 9 sg_ioctl sg_ioctl_common case SG_IO: sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, 1, read_only, 1, &srp); sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);#此时硬盘数据存在p中,需要拷贝回来 copy_from_user(&hdr, p, sizeof(hdr); # 进行数据修改后再传递回去 copy_to_user(p, &hdr, SZ_SG_IO_HDR);