这张卡到底有多少内存? (EEPROM 和 ObjectDeletion 游戏!)
How much memory does this card really have? (EEPROM and ObjectDeletion game!)
我写了一个简单的程序来检查 requestObjectDeletion()
方法功能和我的卡的可用内存。
我的小程序响应以下五种不同类型的 APDU 命令:
- SELECT APDU 命令:响应:
0X9000
- 命令:
XX 00 XX XX XX [...]
响应:Return 可用内存字节。
- 命令:
XX 01 XX XX XX [...]
响应:创建一个包含 2000 个元素(即 2000 字节)的本地字节数组。
- 命令:
XX 02 XX XX XX [...]
响应:请求对象删除方法
- 其他命令:响应:
0x9000
为了 return 可用内存我写了一个方法,它使用无限 while
循环来创建大量具有 100
元素的字节数组并增加 counter
同时。在捕获 Not enough memory
异常时,方法 returns counter *100
(即调用此方法之前的空闲内存)
好的,这是程序:
public class ObjDeletion extends Applet {
public static short counter = 0x0000;
private ObjDeletion() {
}
public static void install(byte bArray[], short bOffset, byte bLength)
throws ISOException {
new ObjDeletion().register();
}
public void process(APDU arg0) throws ISOException {
if (selectingApplet()) {
return;
}
byte[] buffer = arg0.getBuffer();
switch (buffer[ISO7816.OFFSET_INS]) {
case 0x00:
counter=0;
ReturnAvailableMem();
break;
case 0x01:
genLocalArray();
break;
case 0x02:
JCSystem.requestObjectDeletion();
break;
default:
return;
}
}
public void genLocalArray() {
byte[] LocalArray = new byte[2000];
}
public void ReturnAvailableMem() {
try {
while (true) {
byte[] dump = new byte[100];
counter += 1;
}
} catch (Exception e) {
ISOException.throwIt((short) (counter * 100));
}
}
}
现在,有一个问题。这是当我向卡发送一些 APDU 命令时 OpenSC-Tool 的输出:
OSC: opensc-tool.exe -s 00a404000b0102030405060708090000 -s 00000000 -s 00020000
-s 00000000 -s 00020000 -s 00010000 -s 00000000 -s 00020000 -s 00000000 -s 0002
0000 -s 00010000 -s 00020000 -s 00000000
Using reader with a card: ACS CCID USB Reader 0
Sending: 00 A4 04 00 0B 01 02 03 04 05 06 07 08 09 00 00
Received (SW1=0x90, SW2=0x00)
//0::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xE3, SW2=0x58)
//1::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0x00, SW2=0x00)
//2::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//3::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xE3, SW2=0x58)
//4::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//5::genLocalArray()
Sending: 00 01 00 00
Received (SW1=0x90, SW2=0x00)
//6::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xDD, SW2=0x18)
//7::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//8::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xE3, SW2=0x58)
//9::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//10::genLocalArray()
Sending: 00 01 00 00
Received (SW1=0x90, SW2=0x00)
//11::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//12::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xE3, SW2=0x58)
命令 0
returns 0xE358
(=58200)。所以调用这个方法之前的内存是58200
字节。
命令1
returns 0x0000
,这意味着可用内存现在少于100
字节。
命令 2
returns 0x9000
,因此 requestObjectDeletion 调用成功。
再次命令3
returns0xE358
,这意味着requestObjectDeletion
运行成功。
命令 4
returns 0x9000
,因此 requestObjectDeletion 调用成功。
命令5
returns 0x9000
,所以现在创建了一个包含2000
个元素的字节数组。
现在我希望卡的空闲内存等于 0xE358 - 2000 = 0xDB88
但在下一个命令中:
命令6
returns 0xDD18
!即我们的卡中有 0xDD18 + 2000 = 0xE4E8
字节内存 space!
这可能是谁?为什么卡在第一个命令中看不到这400字节(0xE4E8 - 0xE358
)?
答案很简单,因为每个数组(以及其他对象)都需要 'header' 来指示其类型、大小等。为此,您需要仔细阅读 JCRE 和 JCVM 规范。
我没有检查过 'header' 的精确大小,但你可以把它想象成 (100 + 'header') * X
,而不是普通的 100 * X
。
这也解释了优化技巧:创建一个大数组比使用许多小数组节省更多的内存——当然也有权衡,例如代码变得不那么可读。
我写了一个简单的程序来检查 requestObjectDeletion()
方法功能和我的卡的可用内存。
我的小程序响应以下五种不同类型的 APDU 命令:
- SELECT APDU 命令:响应:
0X9000
- 命令:
XX 00 XX XX XX [...]
响应:Return 可用内存字节。 - 命令:
XX 01 XX XX XX [...]
响应:创建一个包含 2000 个元素(即 2000 字节)的本地字节数组。 - 命令:
XX 02 XX XX XX [...]
响应:请求对象删除方法 - 其他命令:响应:
0x9000
为了 return 可用内存我写了一个方法,它使用无限 while
循环来创建大量具有 100
元素的字节数组并增加 counter
同时。在捕获 Not enough memory
异常时,方法 returns counter *100
(即调用此方法之前的空闲内存)
好的,这是程序:
public class ObjDeletion extends Applet {
public static short counter = 0x0000;
private ObjDeletion() {
}
public static void install(byte bArray[], short bOffset, byte bLength)
throws ISOException {
new ObjDeletion().register();
}
public void process(APDU arg0) throws ISOException {
if (selectingApplet()) {
return;
}
byte[] buffer = arg0.getBuffer();
switch (buffer[ISO7816.OFFSET_INS]) {
case 0x00:
counter=0;
ReturnAvailableMem();
break;
case 0x01:
genLocalArray();
break;
case 0x02:
JCSystem.requestObjectDeletion();
break;
default:
return;
}
}
public void genLocalArray() {
byte[] LocalArray = new byte[2000];
}
public void ReturnAvailableMem() {
try {
while (true) {
byte[] dump = new byte[100];
counter += 1;
}
} catch (Exception e) {
ISOException.throwIt((short) (counter * 100));
}
}
}
现在,有一个问题。这是当我向卡发送一些 APDU 命令时 OpenSC-Tool 的输出:
OSC: opensc-tool.exe -s 00a404000b0102030405060708090000 -s 00000000 -s 00020000
-s 00000000 -s 00020000 -s 00010000 -s 00000000 -s 00020000 -s 00000000 -s 0002
0000 -s 00010000 -s 00020000 -s 00000000
Using reader with a card: ACS CCID USB Reader 0
Sending: 00 A4 04 00 0B 01 02 03 04 05 06 07 08 09 00 00
Received (SW1=0x90, SW2=0x00)
//0::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xE3, SW2=0x58)
//1::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0x00, SW2=0x00)
//2::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//3::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xE3, SW2=0x58)
//4::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//5::genLocalArray()
Sending: 00 01 00 00
Received (SW1=0x90, SW2=0x00)
//6::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xDD, SW2=0x18)
//7::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//8::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xE3, SW2=0x58)
//9::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//10::genLocalArray()
Sending: 00 01 00 00
Received (SW1=0x90, SW2=0x00)
//11::requestObjectDeletion()
Sending: 00 02 00 00
Received (SW1=0x90, SW2=0x00)
//12::returnAvailableMem()
Sending: 00 00 00 00
Received (SW1=0xE3, SW2=0x58)
命令 0
returns 0xE358
(=58200)。所以调用这个方法之前的内存是58200
字节。
命令1
returns 0x0000
,这意味着可用内存现在少于100
字节。
命令 2
returns 0x9000
,因此 requestObjectDeletion 调用成功。
再次命令3
returns0xE358
,这意味着requestObjectDeletion
运行成功。
命令 4
returns 0x9000
,因此 requestObjectDeletion 调用成功。
命令5
returns 0x9000
,所以现在创建了一个包含2000
个元素的字节数组。
现在我希望卡的空闲内存等于 0xE358 - 2000 = 0xDB88
但在下一个命令中:
命令6
returns 0xDD18
!即我们的卡中有 0xDD18 + 2000 = 0xE4E8
字节内存 space!
这可能是谁?为什么卡在第一个命令中看不到这400字节(0xE4E8 - 0xE358
)?
答案很简单,因为每个数组(以及其他对象)都需要 'header' 来指示其类型、大小等。为此,您需要仔细阅读 JCRE 和 JCVM 规范。
我没有检查过 'header' 的精确大小,但你可以把它想象成 (100 + 'header') * X
,而不是普通的 100 * X
。
这也解释了优化技巧:创建一个大数组比使用许多小数组节省更多的内存——当然也有权衡,例如代码变得不那么可读。