实际上手尝试破解公交卡
为了提高公交卡的安全性,只为学习研究性质发表!
为什么想到破解公交卡,因为其使用fm1208卡片我正好研究中,也是现阶段的银行卡卡片芯片类型。下面我将直接介绍其破解过程而忽略原理的讲解。
一.抓包数据解析
🌟说明:黄色代表读卡机,白色代表卡片
除去26之类的响应和防冲突的指令不说了直接步入正题
@😊1
02 00 A4 04 00 08 A0 00 00 06 32 01 01 05 00 D4 23
这里从00开始表示选择文件,文件名为08后8字节数据(创建文件环境)
在交互中出现2次!响应报文数据域即一些简单信息不赘述了对破解没啥用
@😊2
02 00 B2 08 C4 17
这里从00开始,从00一直到结尾,中间的08好像是从01开始一直读数据
这里读的数据都是一些充值数据应该是做本地记录充值和消费信息(明文)
下面展示一段:
03 00 03 00 00 00 00 00 00 64 02 80 01 00 01 00 00 20 24 11 15 13 45 18 90 00 B0 DE
这一段就表示2024年11月15日13点45分18秒充值64(hex)表示十进制数100即1元
对于该数据不仅保存在卡片上也可能会保存在终端上,所以修改卡片上数据有可能可以刷
也有可能下次消费时直接被服务器端发现,这可能需要伪造tac数据发送至服务器防止被发现
目前没有研究,我主要研究更改卡上数据
还有类似的一两个指令都是查看一些信息就不再赘述了
@😊3
初始化圈存命令
03 80 50 00 02 0B 01 00 00 00 64 80 01 00 01 00 00 27 24
从80开始,cla 80 ; ins 50 ;p1 00 ;p2 02 表示电子钱包;lc 0B ;
01表示密钥标识符;00 00 00 64表示交易金额1元;1表示终端机编号。
响应报文数据域
03 00 00 01 68 00 04 01 00 9D F3 84 7C 2A 39 E0 5E 90 00 C1 AF
从00开始,00 00 01 68表示卡片余额为十六进制数168(十进制为360)即3.60元人民币
00 04表示电子钱包联机交易序号;01表示密钥版本号;00表示算法标识;9df3847c是卡片产生的伪随机数;2A39E05E为MAC1。
二.圈存原理解析
picc表示读卡机(主机)pcd表示卡片
picc和卡片都有一个load key但不直接使用
MAC是使用session key再通过pboc-mac加密得到
session key 是通过明文数据和随机数等通过load key
采用3des(2keys)加密得到的结果,其主要作用是生成MAC
MAC分为MAC1和MAC2,它们的作用是让picc和pcd各自验证彼此
然后完成圈存后卡片返回一个TAC到服务器,这里就先不研究了
先看卡片上数据如何更改,服务器方面有它的破解方法不一样
以上就是其原理的一般解析,下面我们理清数据实际破解一下
三.破解前的分析和数据整理
(1)第一小节
session key的明文数据00000168(旧余额)+00000064(交易金额)+02(交易类型电子钱包圈存)+800100010000(终端机编号)+80(凑够16字节加密使用)
session key的明文:00000168000000640280010001000080
session key的密文MAC1:2A39E05E(实际应该为16字节,由于只需要验证故只取前4字节)
使用的加密算法:ANSI x99
(2)第二小节
假设已经破解得到了session key,将其表示为SK
load key 的密文即经过3des加密得到的SK
load key 的加密算法3des加密
load key 的明文部分为 inputdata
明文为:9df3847c(卡片产生的随机数)+0004(联机交易序号)+8000(凑够8字节)
(3)第三小节
简单解释一下3des加密和ansi x99加密的原理
3des:密钥前半部分为k1后半部分为k2加密过程为先用k1加密再用k2解密最后再k1加密
解密的过程即将加密的过程反转。
ansix99:将数据打包分成几个八字节块然后与初始值进行异或后再des加密最后得出结果
from numba import cuda
import numpy as np
import time
import binascii
import sys
# CUDA 核心函数:PBOC-MAC 加密和密钥检查
@cuda.jit
def pboc_mac_kernel_with_progress(iv, plaintext, target, keys_per_thread, key_offset, results, found_flag, progress_counter):
idx = cuda.grid(1)
start_key = idx * keys_per_thread + key_offset
for key_index in range(keys_per_thread):
if found_flag[0]: # 如果找到密钥,提前退出
return
current_key = start_key + key_index
key = current_key.to_bytes(8, byteorder='big') # 转换为 8 字节密钥
# Step 1: 初始向量异或明文前 8 字节
xor_result = cuda.local.array(8, dtype=np.uint8)
for i in range(8):
xor_result[i] = plaintext[i] ^ iv[i]
# Step 2: DES 加密第一阶段
des1 = des_encrypt_device(key, xor_result)
# Step 3: 异或明文后 8 字节
xor_result2 = cuda.local.array(8, dtype=np.uint8)
for i in range(8):
xor_result2[i] = des1[i] ^ plaintext[8 + i]
# Step 4: DES 加密第二阶段
des2 = des_encrypt_device(key, xor_result2)
# 检查密文前 4 字节是否匹配目标
match = True
for i in range(4):
if des2[i] != target[i]:
match = False
break
if match:
# 记录结果
for i in range(8):
results[idx * 8 + i] = key[i]
found_flag[0] = 1
# 更新进度计数器
if idx 0 and key_index % 256 0:
cuda.atomic.add(progress_counter, 0, 1)
# 主程序:利用 GPU 并行破解密钥
def gpu_brute_force_with_progress(iv, plaintext, target_prefix):
num_keys = 2**24 # 测试较小的密钥空间(完整 2^64 空间需分片处理)
keys_per_thread = 256 # 每个线程尝试的密钥数量
threads_per_block = 256
blocks_per_grid = (num_keys + keys_per_thread threads_per_block - 1) // (keys_per_thread threads_per_block)
# 初始化数据
iv = np.frombuffer(iv, dtype=np.uint8)
plaintext = np.frombuffer(plaintext, dtype=np.uint8)
target_prefix = np.frombuffer(target_prefix, dtype=np.uint8)
results = np.zeros(blocks_per_grid threads_per_block 8, dtype=np.uint8)
found_flag = np.array([0], dtype=np.uint8)
progress_counter = np.array([0], dtype=np.uint32)
# 分配 GPU 内存
d_iv = cuda.to_device(iv)
d_plaintext = cuda.to_device(plaintext)
d_target = cuda.to_device(target_prefix)
d_results = cuda.to_device(results)
d_found_flag = cuda.to_device(found_flag)
d_progress_counter = cuda.to_device(progress_counter)
# 启动 CUDA 核心函数
start_time = time.time()
pboc_mac_kernel_with_progress[blocks_per_grid, threads_per_block](
d_iv, d_plaintext, d_target, keys_per_thread, 0, d_results, d_found_flag, d_progress_counter
)
cuda.synchronize()
end_time = time.time()
# 获取结果
results = d_results.copy_to_host()
found_flag = d_found_flag.copy_to_host()
progress_counter = d_progress_counter.copy_to_host()
# 汇总匹配密钥
matched_keys = []
for i in range(blocks_per_grid * threads_per_block):
if results[i 8: (i + 1) 8].any():
matched_keys.append(results[i 8: (i + 1) 8])
# 打印结果
print("\n--- 计算完成 ---")
print(f"总耗时: {end_time - start_time:.2f} 秒")
print(f"尝试密钥数: {num_keys}")
print(f"每秒尝试密钥: {num_keys / (end_time - start_time):.2f}")
print("匹配密钥列表:")
for key in matched_keys:
print(f"- {binascii.hexlify(key)}")
print("计算进度: 100%")
# 示例数据
iv = b'\x00' * 8 # 初始向量全为0
plaintext = b'000001680000006402800100010000' # 明文
partial_ciphertext = b'\x2A\x39\xE0\x5E' # 密文前4字节
# 执行优化后的穷举破解
gpu_brute_force_with_progress(iv, plaintext, partial_ciphertext)