3. JCOP固件文件系统结构:GP规范与JCOP扩展、文件系统布局(MF、DF、EF)、安全属性与访问控制

好,咱们今天聊聊JCOP固件的文件系统。说实话,这块内容我当年啃了很久才真正吃透。你想想看,一个智能卡里跑着Java Card虚拟机,上面还得兼容GlobalPlatform规范,这文件系统到底怎么组织的?

我个人的理解是:JCOP的文件系统,本质上是一个“带安全属性的树形目录结构”。它既遵守GP规范,又做了不少私货扩展。嗯,咱们一层层拆开看。

3.1 GP规范与JCOP扩展

GlobalPlatform(GP)规范定义了智能卡的基本框架。它规定了卡上该有什么、怎么管理、怎么通信。JCOP作为NXP的商用Java Card实现,自然要遵守GP规范。但问题来了——规范只说了“你要做什么”,没说“你怎么做”。

JCOP在GP基础上做了不少扩展。我记得有一次做项目,客户要求卡上同时跑三个Applet,每个Applet都有自己的安全域。GP规范里只说了“可以有多个安全域”,但没细说怎么隔离。JCOP的扩展就在这里发力了。

核心差异点:

  • GP规范层:定义了ISD(发行者安全域)、SSD(补充安全域)、卡片管理器等逻辑实体
  • JCOP扩展层:增加了私有APDU指令、特殊文件类型、自定义安全属性标记
  • 实际表现:JCOP的GP实现比标准GP多了约30%的私有指令和属性

说白了,JCOP的扩展主要集中在这几个方面:

  • 文件类型扩展:除了标准的MF/DF/EF,JCOP还支持一些特殊文件,比如密钥文件、令牌文件
  • 安全属性扩展:GP规范只定义了基本的ACL(访问控制列表),JCOP加了更细粒度的权限控制
  • 生命周期管理扩展:JCOP对卡片生命周期的状态机做了更精细的划分

我的经验:逆向分析JCOP固件时,千万别只盯着GP规范文档看。JCOP的私有扩展才是真正的“宝藏”。我曾经花了两周时间,才从固件里逆向出一个JCOP特有的文件属性标记——0x9E。这个标记在GP规范里根本找不到,但它在JCOP里控制着Applet的加载权限。

3.2 文件系统布局:MF、DF、EF

JCOP的文件系统,说白了就是一个树。根节点是MF(主文件),下面挂DF(专用文件),DF下面再挂EF(基本文件)。这个结构跟ISO 7816-4里定义的基本一致,但JCOP做了一些“小动作”。

咱们先看一个典型的JCOP文件系统布局:

MF (3F00)
├── DF_GP (A0000000030000)  // GlobalPlatform框架
│   ├── EF_DIR (2F00)       // 应用目录文件
│   ├── EF_ATR (2F01)       // ATR历史字节
│   └── EF_PIN (2F02)       // PIN码文件
├── DF_ISD (A0000000035350) // 发行者安全域
│   ├── EF_KEY (0011)       // 密钥文件
│   └── EF_TOKEN (0012)     // 令牌文件
├── DF_APPLET1 (A00000000101) // 用户Applet1
│   ├── EF_DATA (0001)      // 应用数据
│   └── EF_STATE (0002)     // 状态文件
└── DF_APPLET2 (A00000000102) // 用户Applet2
    └── EF_LOG (0001)       // 日志文件

你可能会问:为什么DF_GP的AID是A0000000030000?嗯,这是GP规范里定义的框架AID。JCOP把它作为默认的GP框架DF。我刚开始逆向时,看到这个AID还以为是某个Applet,后来才发现它是整个GP框架的根DF。

3.3 安全属性与访问控制

这块是JCOP文件系统的精髓。每个文件(MF、DF、EF)都有自己的安全属性。这些属性决定了:谁能读这个文件?谁能写这个文件?需要什么权限才能访问?

JCOP的安全属性结构大致是这样的:

属性字段 长度 说明
ACL_READ 1字节 读取权限:0x00=总是允许,0xFF=总是拒绝,其他值=需要特定密钥
ACL_WRITE 1字节 写入权限:同上
ACL_DELETE 1字节 删除权限:同上
ACL_CREATE 1字节 创建子文件权限:仅DF有效
SEC_ATTR 可变 JCOP扩展安全属性:密钥引用、算法标识、安全通道要求等

注意:JCOP的ACL值不是简单的“0x00=允许,0xFF=拒绝”。实际上,0x01-0xFE这些值对应的是安全域里的密钥槽位。比如ACL_READ=0x01,意思是“需要用安全域里索引为01的密钥进行认证后才能读取”。这个设计很巧妙,但也容易让人踩坑。

我曾经遇到过一个案例:一个JCOP卡上的EF文件,ACL_READ设置成了0x00(总是允许),但ACL_WRITE设置成了0x05。我一开始以为0x05是“需要密钥5”,结果怎么认证都不对。后来逆向固件才发现,JCOP把0x05解释为“需要安全通道且密钥索引为5”。这个“安全通道”的要求,在GP规范里是另一个属性字段,但JCOP把它合并到了ACL里。

咱们再深入一点。JCOP的安全属性还有一个“继承机制”。如果一个DF没有显式设置安全属性,它会继承父DF的属性。这个机制在GP规范里也有,但JCOP的实现更复杂——它支持“部分继承”。

举个例子:

MF (3F00): ACL_READ=0x00, ACL_WRITE=0xFF
└── DF_GP (A0000000030000): ACL_READ=0x01, ACL_WRITE=0x02
    └── EF_DIR (2F00): ACL_READ=0x00 (继承自DF_GP? 不,这里JCOP特殊处理了)
    └── EF_KEY (0011): ACL_READ=0xFF, ACL_WRITE=0x03

你看,EF_DIR的ACL_READ=0x00,但它并没有继承DF_GP的0x01。为什么?因为JCOP对“目录文件”做了特殊处理——目录文件总是允许读取。这个细节,GP规范里没写,是我从固件里一点点逆向出来的。

避坑指南:逆向JCOP文件系统时,我建议你重点关注这几个点:

  1. 文件头结构:JCOP的文件头里除了标准FCI(文件控制信息),还有私有标记。这些标记通常以0x9E、0x9F开头
  2. 安全属性存储位置:JCOP有时把安全属性放在文件头里,有时放在单独的“安全属性文件”里。这个取决于固件版本
  3. 密钥引用方式:JCOP的ACL值引用的密钥,不一定在当前安全域里。它可能引用父安全域甚至ISD的密钥

最后,咱们用一张图来总结JCOP文件系统的核心逻辑:

JCOP固件文件系统核心结构 MF (3F00) DF_GP (GP框架) DF_ISD (安全域) EF_DIR (目录) EF_KEY (密钥) EF_TOKEN (令牌) EF_DATA (数据) 安全属性控制层 每个文件都有ACL_READ / ACL_WRITE / ACL_DELETE / ACL_CREATE JCOP扩展:SEC_ATTR(密钥引用、安全通道要求、算法标识) 继承机制:子DF未设置属性时,继承父DF的安全属性

这张图展示了JCOP文件系统的三层结构。最上层是MF,中间是DF(包括GP框架和安全域),最下层是EF。每个文件都受安全属性控制。嗯,这个结构看起来简单,但实际逆向时你会发现,JCOP在每一层都塞了不少私货。

我记得有一次,我逆向一个JCOP 2.4.1的固件,发现它的EF文件头里多了一个字段——0x9F70。这个字段在GP规范里是“保留给私有使用”的。我花了三天时间,通过构造不同的APDU指令,才搞清楚这个字段控制的是“文件是否允许跨安全域访问”。这个功能,GP规范里根本没提。

所以,我的建议是:逆向JCOP文件系统时,别只盯着GP规范。多看看固件里的私有标记,多试试不同的ACL组合。这些“非标”的东西,往往才是JCOP的真正特色。

总结一下本章的核心要点:

  • JCOP文件系统 = GP规范基础 + JCOP私有扩展
  • 文件布局:MF → DF → EF,树形结构
  • 安全属性:ACL控制基本访问,SEC_ATTR控制扩展权限
  • 继承机制:子DF可继承父DF的安全属性,但JCOP有特殊处理
  • 私有标记:0x9E、0x9F开头的字段是JCOP的“宝藏”

好了,这一章就到这里。文件系统这块内容比较多,建议你动手找个JCOP固件,用APDU指令实际读一读文件头,感受一下。下一章咱们聊聊JCOP的密钥管理体系——那才是真正烧脑的地方。


公众号:蓝海资料掘金营,微信deep3321