这张卡到底有多少内存? (EEPROM 和 ObjectDeletion 游戏!)

How much memory does this card really have? (EEPROM and ObjectDeletion game!)

我写了一个简单的程序来检查 requestObjectDeletion() 方法功能和我的卡的可用内存。

我的小程序响应以下五种不同类型的 APDU 命令:

  1. SELECT APDU 命令:响应:0X9000
  2. 命令:XX 00 XX XX XX [...] 响应:Return 可用内存字节。
  3. 命令:XX 01 XX XX XX [...]响应:创建一个包含 2000 个元素(即 2000 字节)的本地字节数组。
  4. 命令:XX 02 XX XX XX [...]响应:请求对象删除方法
  5. 其他命令:响应: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 调用成功。

再次命令3returns0xE358,这意味着requestObjectDeletion运行成功。

命令 4 returns 0x9000,因此 requestObjectDeletion 调用成功。

命令5 returns 0x9000,所以现在创建了一个包含2000个元素的字节数组。

现在我希望卡的空闲内存等于 0xE358 - 2000 = 0xDB88

但在下一个命令中:

命令6returns 0xDD18!即我们的卡中有 0xDD18 + 2000 = 0xE4E8 字节内存 space!

这可能是谁?为什么卡在第一个命令中看不到这400字节(0xE4E8 - 0xE358)?

答案很简单,因为每个数组(以及其他对象)都需要 'header' 来指示其类型、大小等。为此,您需要仔细阅读 JCRE 和 JCVM 规范。

我没有检查过 'header' 的精确大小,但你可以把它想象成 (100 + 'header') * X,而不是普通的 100 * X

这也解释了优化技巧:创建一个大数组比使用许多小数组节省更多的内存——当然也有权衡,例如代码变得不那么可读。