2. 开发环境搭建:NXP JCOP工具链安装、JCVM与JCRE配置、模拟器与真机调试环境、APDU通信基础
说实话,搞JCOP逆向,第一步不是看协议,而是先把环境搭好。我见过太多人拿着数据手册啃了半天,结果连个APDU都发不出去——那感觉,就像你有一把好枪,但没子弹。
这一章,咱们就把吃饭的家伙事儿备齐。我会把我在项目中踩过的坑、绕过的路,都摊开来讲。你跟着走一遍,基本不会出岔子。
2.1 工具链概览:你都需要什么?
先给你一张全景图,看看整个开发环境长什么样。我习惯把工具链分成三层:
说白了,这三层就是:写代码 → 调试 → 验证。每一层都有对应的工具,咱们一个一个来装。
2.2 安装 JCOP Tools 插件
JCOP Tools 是 NXP 官方提供的 Eclipse 插件。我个人习惯用 Eclipse 2020-06 版本,太新的版本反而容易出兼容性问题。
- 下载 Eclipse IDE for Java Developers
- 打开 Help → Install New Software
- 添加 NXP 更新站点:
https://www.nxp.com/jcop-tools/update - 勾选 "JCOP Tools" 和 "JCOP Debugger"
- 一路 Next,接受协议,等待安装完成
装完之后,你会在 Eclipse 里看到一个新的透视图(Perspective),叫 "JCOP"。切换过去,界面会多出几个视图:APDU Console、Card Manager、CAP File Explorer 等。
2.3 JCVM 与 JCRE 配置
JCVM(Java Card Virtual Machine)和 JCRE(Java Card Runtime Environment)是智能卡上的灵魂。在开发机上,我们需要配置它们的模拟版本。
为什么需要模拟器?你想想看,真机调试一次要擦写 Flash,次数多了卡就废了。模拟器可以随便折腾,断点、单步、看变量,都行。
2.3.1 配置 JCVM 模拟器
JCOP Tools 自带了一个 JCVM 模拟器,但需要手动指定 Java Card 规范版本。我个人建议用 3.0.4 或 3.0.5,这两个版本覆盖了大部分实际产品。
在这里设置:
- Java Card Version: 3.0.5
- APDU Buffer Size: 256 bytes(默认就行)
- Transient Object Limit: 1024
嗯,这里要注意:模拟器的内存配置不要改太大,否则真机跑不起来。我见过有人把 Transient Object Limit 设成 65535,结果模拟器跑得飞起,一上真机就报 OutOfMemoryError。
2.3.2 JCRE 启动参数
JCRE 的启动参数藏在项目属性的 "JCOP" 选项卡里。常用的几个参数:
| 参数名 | 说明 | 我的建议值 |
|---|---|---|
-Djcvm.maxAPDULength=256 |
最大 APDU 长度 | 256(兼容性好) |
-Djcvm.secureChannel=SCP02 |
安全通道协议版本 | SCP02_i_05 |
-Djcvm.debug=true |
开启调试日志 | true(必须开) |
-Djcvm.cardManagerAID=A000000003000000 |
Card Manager 的 AID | 默认值即可 |
2.4 模拟器与真机调试环境
调试环境分两种:模拟器调试和真机调试。我建议你前期在模拟器上把逻辑调通,最后再上真机做回归测试。
2.4.1 模拟器调试
模拟器调试其实和普通 Java 调试差不多。右键项目 → Debug As → JCOP Applet。然后就可以设断点了。
但有个坑:模拟器里的 javacard.framework 包和真机上的实现有细微差别。比如 APDU.setIncomingAndReceive() 方法,模拟器里返回的数据长度和真机可能不一样。我遇到过模拟器返回 128 字节,真机只返回 64 字节的情况。
- 在 APDU Console 里直接输入 CLA/INS/P1/P2/LC/Data,模拟器会实时响应
- 用
JCSystem.beginTransaction()和commitTransaction()包裹关键操作,方便回滚 - 日志输出用
Util.arrayCopyNonAtomic()配合ISOException.throwIt()做断言
2.4.2 真机调试环境
真机调试需要硬件:一个 JCOP 开发板(比如 NXP J3D081 或 J3H145)和一个读卡器(推荐 OMNIKEY 5321)。
连接方式很简单:
- 把开发板插到读卡器上
- 读卡器通过 USB 连电脑
- Eclipse 里选择 "JCOP Debugger" 透视图
- 点击 "Connect" 按钮,选择读卡器设备
2.5 APDU 通信基础
APDU(Application Protocol Data Unit)是智能卡和终端之间的通信语言。说白了,就是一组字节流。你发给卡的命令叫 C-APDU,卡返回的叫 R-APDU。
一个标准的 C-APDU 长这样:
CLA INS P1 P2 [LC] [Data] [Le]
CLA = 命令类别(00 是标准,80 是安全通道)
INS = 指令码(比如 A4 是 SELECT,B0 是 READ BINARY)
P1 = 参数1
P2 = 参数2
LC = 后续数据长度(可选)
Data = 数据体(可选)
Le = 期望返回长度(可选)
举个例子,SELECT 命令选择某个 Applet:
发送:00 A4 04 00 07 A0 00 00 00 03 00 00 00
解释:
00 → CLA
A4 → SELECT
04 → P1(按名称选择)
00 → P2(第一个或唯一一个)
07 → LC(后面有7个字节)
A0 00 00 00 03 00 00 → AID(7字节)
00 → Le(不期望返回数据)
返回:90 00
90 00 → 状态字,表示成功
- 抓包:用 APDU Console 的日志功能,把每次交互的字节流都记录下来
- 对比:模拟器和真机的 APDU 日志逐字节对比,差异就是问题所在
- 重放:把抓到的 APDU 保存成脚本,可以反复重放测试
为什么会强调 APDU 基础?因为 JCOP 漏洞挖掘,本质上就是构造畸形的 APDU 去试探卡的边界。你连正常 APDU 都写不对,怎么构造畸形数据?
2.6 环境验证:跑通第一个 Hello World
环境搭好了,咱们跑个最简单的 Applet 验证一下。代码很简单:
package helloworld;
import javacard.framework.*;
public class HelloWorldApplet extends Applet {
private byte[] hello = {'H', 'e', 'l', 'l', 'o'};
public static void install(byte[] bArray, short bOffset, byte bLength) {
new HelloWorldApplet().register();
}
public void process(APDU apdu) {
byte[] buffer = apdu.getBuffer();
if (buffer[ISO7816.OFFSET_CLA] == 0x00 &&
buffer[ISO7816.OFFSET_INS] == 0x00) {
Util.arrayCopy(hello, (short)0, buffer, (short)0, (short)5);
apdu.setOutgoingAndSend((short)0, (short)5);
} else {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
}
编译成 CAP 文件,用 APDU 工具发送 00 00 00 00 05,如果返回 48656C6C6F 9000(即 "Hello" + 成功状态字),说明环境搭建成功。
好了,环境搭好了,工具链也通了。接下来就可以开始真正的漏洞挖掘了。记住,好的开始是成功的一半——环境搭得稳,后面少踩坑。
公众号:蓝海资料掘金营,微信deep3321