import os
import sys
import ctypes
import fcntl
KNOCK_SEQUENCE_LBAS = [256, 4096, 768, 2048, 1280, 1536, 21376, 29556]
BLOCK_SIZE = 512
SIG_OFFSET = 162
SIG_VALUE = b'\x09\x33'
HEALTH_OFFSET = 192
FW_OFFSET = 224
SCSI_READ_10 = 0x28
SCSI_LUN_1_MASK = 0x20
SG_IO = 0x2285
SG_DXFER_FROM_DEV = -3
class SgIoHdr(ctypes.Structure):
_fields_ = [
("interface_id", ctypes.c_int),
("dxfer_direction", ctypes.c_int),
("cmd_len", ctypes.c_ubyte),
("mx_sb_len", ctypes.c_ubyte),
("iovec_count", ctypes.c_ushort),
("dxfer_len", ctypes.c_uint),
("dxferp", ctypes.c_void_p),
("cmdp", ctypes.c_void_p),
("sbp", ctypes.c_void_p),
("timeout", ctypes.c_uint),
("flags", ctypes.c_uint),
("pack_id", ctypes.c_int),
("usr_ptr", ctypes.c_void_p),
("status", ctypes.c_ubyte),
("masked_status", ctypes.c_ubyte),
("msg_status", ctypes.c_ubyte),
("sb_len_wr", ctypes.c_ubyte),
("host_status", ctypes.c_ushort),
("driver_status", ctypes.c_ushort),
("resid", ctypes.c_int),
("duration", ctypes.c_uint),
("info", ctypes.c_uint)
]
def get_sd_details(device_path):
print(f"[*] Opening {device_path} (SCSI Generic Mode)")
try:
fd = os.open(device_path, os.O_RDWR)
except Exception as e:
print(f"[!] Error: {e}")
return
data_buffer = (ctypes.c_ubyte * BLOCK_SIZE)()
sense_buffer = (ctypes.c_ubyte * 32)()
result_data = None
for lba in KNOCK_SEQUENCE_LBAS:
cdb = (ctypes.c_ubyte * 10)(
SCSI_READ_10,
SCSI_LUN_1_MASK,
(lba >> 24) & 0xFF, (lba >> 16) & 0xFF, (lba >> 8) & 0xFF, lba & 0xFF,
0x00, 0x00, 0x01, 0x00
)
header = SgIoHdr(
interface_id=ord('S'),
dxfer_direction=SG_DXFER_FROM_DEV,
cmd_len=ctypes.sizeof(cdb),
mx_sb_len=ctypes.sizeof(sense_buffer),
dxfer_len=BLOCK_SIZE,
dxferp=ctypes.addressof(data_buffer),
cmdp=ctypes.addressof(cdb),
sbp=ctypes.addressof(sense_buffer),
timeout=2000
)
if fcntl.ioctl(fd, SG_IO, header) != 0:
continue
if lba == KNOCK_SEQUENCE_LBAS[-1]:
result_data = bytes(data_buffer)
os.close(fd)
if result_data:
sig = result_data[SIG_OFFSET : SIG_OFFSET + 2]
if sig != SIG_VALUE:
print(f"[!] Fail: Incorrect signature {sig.hex()}. SMART page locked.")
return
health = result_data[HEALTH_OFFSET]
fw_raw = result_data[FW_OFFSET:]
fw_str = ""
for b in fw_raw:
if 32 <= b <= 126: fw_str += chr(b)
else:
break
print("\n" + "="*50)
print(f"{'TEAMGROUP SMART SD REPORT':^50}")
print("="*50)
print(f" Health Remaining: {health}%")
print(f" Firmware Version: {fw_str}")
print("="*50 + "\n")
else:
print("[!] Fail: Device did not respond to sequence.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: sudo python3 tg_info.py /dev/sgX")
sys.exit(1)
get_sd_details(sys.argv[1])