修改硬盘SN号

读取硬盘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; /* [i] 'S' for SCSI generic (required) */
int dxfer_direction; /* [i] data transfer direction */
unsigned char cmd_len; /* [i] SCSI command length */
unsigned char mx_sb_len; /* [i] max length to write to sbp */
unsigned short iovec_count; /* [i] 0 implies no scatter gather */
unsigned int dxfer_len; /* [i] byte count of data transfer */
void __user *dxferp; /* [i], [*io] points to data transfer memory
or scatter gather list */
unsigned char __user *cmdp; /* [i], [*i] points to command to perform */
void __user *sbp; /* [i], [*o] points to sense_buffer memory */
unsigned int timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
unsigned int flags; /* [i] 0 -> default, see SG_FLAG... */
int pack_id; /* [i->o] unused internally (normally) */
void __user * usr_ptr; /* [i->o] unused internally */
unsigned char status; /* [o] scsi status */
unsigned char masked_status;/* [o] shifted, masked scsi status */
unsigned char msg_status; /* [o] messaging level data (optional) */
unsigned char sb_len_wr; /* [o] byte count actually written to sbp */
unsigned short host_status; /* [o] errors from host adapter */
unsigned short driver_status;/* [o] errors from software driver */
int resid; /* [o] dxfer_len - actual_transferred */
unsigned int duration; /* [o] time taken by cmd (unit: millisec) */
unsigned int info; /* [o] auxiliary information */
} sg_io_hdr_t; /* 64 bytes long (on i386) */

通过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);

修改硬盘SN号
https://tomwithkernel.github.io/bug/修改硬盘SN号/
作者
Tom
发布于
2024年10月15日
更新于
2025年3月24日
许可协议