别再只当SIM卡用了!用Python脚本和APDU命令,带你亲手“解剖”手机卡里的文件系统
用Python和APDU命令探索USIM卡文件系统的实战指南当你把手机卡插入设备时它不仅仅是一个身份标识——实际上这是一套完整的微型操作系统。本文将带你用Python脚本和APDU命令像安全研究员一样亲手探索USIM卡内的文件系统结构。1. 准备工作搭建USIM卡探索环境要开始探索USIM卡你需要准备以下硬件和软件支持APDU命令的读卡器推荐使用ACR122U或类似型号价格约200-500元一张可读写的USIM卡运营商提供的普通USIM卡即可Python开发环境建议3.8版本必要的Python库pip install pyscard python-apdu注意操作USIM卡有一定风险建议在测试卡或备份重要数据后进行实验连接读卡器后用以下代码检测是否识别到卡片from smartcard.System import readers # 获取可用读卡器列表 reader_list readers() if not reader_list: raise Exception(未检测到读卡器) # 连接第一个读卡器 connection reader_list[0].createConnection() connection.connect() print(f已连接: {reader_list[0]}) print(fATR: {connection.getATR()})2. APDU命令基础与USIM卡对话的语言APDU(Application Protocol Data Unit)是与智能卡通信的基本单位。一个完整的APDU命令包含以下部分字段长度描述CLA1字节指令类别INS1字节指令代码P11字节参数1P21字节参数2Lc0-3字节数据域长度Data变长命令数据Le0-3字节期望响应长度用Python发送APDU命令的通用函数def send_apdu(connection, cla, ins, p1, p2, dataNone, le0): apdu [cla, ins, p1, p2] if data: apdu.append(len(data)) apdu.extend(data) if le 0: apdu.append(le) response, sw1, sw2 connection.transmit(apdu) return response, (sw1 8) | sw2 # 示例选择MF(主文件) response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x3F, 0x00]) print(f响应: {response}, 状态: {hex(status)})3. 探索文件系统从MF到EF的完整遍历USIM卡的文件系统采用树状结构主要包含三种文件类型MF (Master File)根目录固定ID为3F00DF (Dedicated File)专用目录相当于文件夹EF (Elementary File)基础文件存储实际数据3.1 选择并解析MF# 选择MF response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x3F, 0x00]) if status ! 0x9000: raise Exception(选择MF失败) # 获取MF的FCP(文件控制参数) response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x3F, 0x00], 0x10) print(fMF FCP: {bytes(response).hex()})3.2 遍历DF和EF以下函数可以递归遍历USIM卡的文件系统def explore_filesystem(connection, current_path, depth0): indent * depth print(f{indent}探索路径: {/.join(f{b:02X} for b in current_path)}) # 选择当前路径 if current_path: response, status send_apdu(connection, 0x00, 0xA4, 0x08, 0x00, current_path) if status ! 0x9000: print(f{indent}选择失败: {hex(status)}) return # 获取当前目录下的文件列表 (假设有EFDIR) response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x2F, 0x00]) # 尝试选择EFDIR if status 0x9000: # 读取EFDIR内容 response, status send_apdu(connection, 0x00, 0xB0, 0x00, 0x00, le0xFF) print(f{indent}EFDIR内容: {bytes(response).hex()}) # 尝试选择常见DF common_dfs [ ([0x7F, 0x10], DFTELECOM), ([0x7F, 0x20], DFGSM), ([0x7F, 0x21], DFDCS1800), ([0x7F, 0xFF], 当前ADF) ] for df_id, df_name in common_dfs: new_path current_path df_id response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, df_id) if status 0x9000: print(f{indent}发现DF: {df_name} ({bytes(df_id).hex()})) explore_filesystem(connection, new_path, depth1) # 从MF开始探索 explore_filesystem(connection, [])4. 解析关键文件IMSI、ICCID等USIM卡中存储着许多重要信息以下是常见EF文件及其ID文件名称FID描述EF_ICCID2FE2卡唯一标识EF_IMSI6F07用户标识EF_LP6F05语言偏好EF_AD6FAD管理数据4.1 读取IMSI示例def read_imsi(connection): # 选择DFTELECOM response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x7F, 0x10]) if status ! 0x9000: raise Exception(选择DFTELECOM失败) # 选择EF_IMSI response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x6F, 0x07]) if status ! 0x9000: raise Exception(选择EF_IMSI失败) # 读取IMSI (前9字节) response, status send_apdu(connection, 0x00, 0xB0, 0x00, 0x00, le9) if status ! 0x9000: raise Exception(读取IMSI失败) # IMSI格式解析 imsi_bytes bytes(response) imsi_digits [] for b in imsi_bytes: imsi_digits.append(f{(b 4) 0x0F}) imsi_digits.append(f{b 0x0F}) # 第一个字节的高4位是长度 imsi_length int(imsi_digits[0]) imsi .join(imsi_digits[1:imsi_length1]) return imsi print(fIMSI: {read_imsi(connection)})4.2 读取ICCID示例def read_iccid(connection): # 选择MF response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x3F, 0x00]) if status ! 0x9000: raise Exception(选择MF失败) # 选择EF_ICCID response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x2F, 0xE2]) if status ! 0x9000: raise Exception(选择EF_ICCID失败) # 读取ICCID (10字节) response, status send_apdu(connection, 0x00, 0xB0, 0x00, 0x00, le10) if status ! 0x9000: raise Exception(读取ICCID失败) # ICCID格式解析 iccid_bytes bytes(response) iccid_digits [] for b in iccid_bytes: iccid_digits.append(f{(b 4) 0x0F}) iccid_digits.append(f{b 0x0F}) # 去除可能的填充F iccid .join(iccid_digits).rstrip(F) return iccid print(fICCID: {read_iccid(connection)})5. 高级技巧处理TLV格式数据USIM卡中许多数据采用TLV(Tag-Length-Value)格式存储。以下是一个TLV解析工具函数def parse_tlv(data): result {} index 0 while index len(data): tag data[index] index 1 # 处理多字节tag (简化版) if (tag 0x1F) 0x1F: while (data[index] 0x80) 0x80: tag (tag 8) | data[index] index 1 tag (tag 8) | data[index] index 1 # 获取长度 length data[index] index 1 if length 0x81: length data[index] index 1 elif length 0x82: length (data[index] 8) | data[index1] index 2 # 获取值 value data[index:indexlength] index length result[hex(tag)] bytes(value).hex() return result # 示例解析SELECT响应 response, status send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x6F, 0x07]) # 选择EF_IMSI if status 0x9000: tlv_data parse_tlv(response) print(TLV解析结果:) for tag, value in tlv_data.items(): print(f {tag}: {value})6. 安全注意事项与最佳实践在探索USIM卡文件系统时需要注意以下安全事项避免频繁写入USIM卡有有限的擦写次数(通常约10万次)备份重要数据操作前备份EF_IMSI等关键文件谨慎处理鉴权相关文件如EF_Kc、EF_OPc等遵守法律法规仅对自己的USIM卡进行实验以下是一些有用的调试技巧# 启用APDU日志 def logged_send_apdu(connection, cla, ins, p1, p2, dataNone, le0): cmd fCLA: {cla:02X}, INS: {ins:02X}, P1: {p1:02X}, P2: {p2:02X} if data: cmd f, Data: {bytes(data).hex()} if le: cmd f, Le: {le:02X} print(f发送: {cmd}) response, status send_apdu(connection, cla, ins, p1, p2, data, le) print(f响应: {bytes(response).hex() if response else }, 状态: {status:04X}) return response, status # 示例使用 logged_send_apdu(connection, 0x00, 0xA4, 0x00, 0x00, [0x3F, 0x00])在实际项目中我发现最常遇到的问题是无法正确解析TLV数据。一个实用的技巧是先用SELECT命令获取文件的FCP(文件控制参数)其中包含了文件的结构信息。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2603855.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!