如何获取JavaCard栈信息

继综合评估JavaCard虚拟机文章发布后,跟网友也在讨论获取栈的问题,今天也就此问题提出自己的分析方法,欢迎拍砖。

JavaCard虚拟机的栈的实现结构,每个厂商的可能并不一致,有些可能是是将局部变量与栈帧(frame)分开,有两个不同的栈顶;有些可能是一个大的RAM区,局部变量与frame从两端开始向中间增加,直到两端数据在中间相会,则栈满。

如下图所示:

那我们如何得知每个帧的大小,以及每个局部变量存储几个字节呢?在图中我们分别以x、y来表示栈中Frame的尺寸与short类型局部变量的尺寸。为此我们可以设计一个函数来获取这些信息,我们设计几个函数,分别为只有0、1、2、3个参数的函数,进行递归调用,使用全局变量记录调用次数,得出每个函数调用调用深度,然后再解一个方程就可以获得以上信息了。为了方便后面的表述,我们假定手里拿到的卡的栈RAM的可使用RAM大小为tSize.

具体的实现方式如下:

没有参数的函数递归调用

    /**
     * 没有参数的递归调用,获取调用深度
     */
    public static void sgetJavaCardStack() {
        sCount1++;
        sgetJavaCardStack();
    }

对应的字节码:

P:0   7d 0 7         getstatic_s     sCount1 (S)
P:3   4               sconst_1        
P:4   41               sadd            
P:5   81 0 7         putstatic_s     sCount1 (S)
P:8   8d 0 8         invokestatic    sgetJavaCardStack; ()V
P:b   7a               return  

从字节码中可以看到该函数的确没有局部变量(参数与局部变量在JCA中都叫local变量)

执行结果:

cm>  /send 8000000000
 => 80 00 00 00 00                                     .....
 (335762 usec)
 <= 00 2B 90 00                                        .+..
Status: No Error

那我们可以得出一个方程式1:

0x2B*x = tSize

有一个参数的函数递归调用:

    /**
     * 有一个参数的递归调用,获取调用深度
     */
    public static void sgetJavaCardStack(short s0) {
       sCount1++;
       sgetJavaCardStack(s0);
    }

对应字节码:

P:0   7d 0 7         getstatic_s     sCount1 (S)
P:3   4               sconst_1        
P:4   41               sadd            
P:5   81 0 7         putstatic_s     sCount1 (S)
P:8   1c               sload_0          s0 (S)
P:9   8d 0 b         invokestatic    sgetJavaCardStack; (S)V
P:c   7a               return     

执行结果:

cm>  /send 8001000000
 => 80 01 00 00 00                                     .....
 (279340 usec)
 <= 00 23 90 00                                        .#..
Status: No Error

得出方程式2:

0x23*x + 0x23*(y*1) = tSize – n; (n<x+y) 

有两个参数的递归调用   

 /**
     * 有二个参数的递归调用,获取调用深度
     */
    public static void sgetJavaCardStack(short s0, short s1) {
       sCount1++;
       sgetJavaCardStack(s0, s1);
    }

对应的字节码

P:0   7d 0 7         getstatic_s     sCount1 (S)
P:3   4               sconst_1        
P:4   41               sadd            
P:5   81 0 7         putstatic_s     sCount1 (S)
P:8   1c               sload_0          s0 (S)
P:9   1d               sload_1          s1 (S)
P:a   8d 0 c         invokestatic    sgetJavaCardStack; (SS)V
P:d   7a               return      

 执行结果

cm>  /send 8002000000
 => 80 02 00 00 00                                     .....
 (230688 usec)
 <= 00 1D 90 00                                        ....
Status: No Error

得出方程式3:

0x1D*x +0x1D*(y*2) = tSize – n; (n<x+2y)  

有三个参数的递归调用   

 /**
     * 有三个参数的递归调用,获取调用深度
     */
    public static void sgetJavaCardStack(short s0, short s1, short s2) {
       sCount1++;
       sgetJavaCardStack(s0, s1, s2);
    }

对应的字节码

P:0   7d 0 7         getstatic_s     sCount1 (S)
P:3   4               sconst_1        
P:4   41               sadd            
P:5   81 0 7         putstatic_s     sCount1 (S)
P:8   1c               sload_0          s0 (S)
P:9   1d               sload_1          s1 (S)
P:a   1e               sload_2          s2 (S)
P:b   8d 0 d         invokestatic    sgetJavaCardStack; (SSS)V
P:e   7a               return   

 执行结果

cm>  /send 8003000000
 => 80 03 00 00 00                                     .....
 (197498 usec)
 <= 00 18 90 00                                        ....
Status: No Error

得出方程式4:

0x18*x +0x18*(y*3) = tSize – n; (n<x+3y) 

在解方式的时候,可以把n忽略,通过以上几个方式式解出的结果可能是个小数,但不影响,因为这本身就是一个不严谨的分析方式。 

通过计算得到的结果为:

X = 8;

Y = 2;

tSize = 344;

即每一帧使用8字节,每个局部变量存储为2字节。

这里的tSize就是你调用函数时的可用栈空间,但不是虚拟机的全部栈空间,因为API以及JCRE的实现还会占用一部分。

 

如果感兴趣的话,还可以分析一下其它数据类型的栈使用使用情况,如引用类型,通过实例函数实现等。事实上除了int类型外,其它类型在栈上都使用两个字节存储。关于栈的结构,本博客在深入JavaCard虚拟机系列中还会进一步探讨。

以下是本文分析使用的完整源代码:

  1 package GetJavaCardStackInfoPkg;
  2 
  3 import javacard.framework.APDU;
  4 import javacard.framework.ISO7816;
  5 import javacard.framework.Applet;
  6 import javacard.framework.ISOException;
  7 import javacard.framework.Util;
  8 
  9 /**
 10  * @author SCPlatform@outlook.com
 11  */
 12 public class GetJavaCardStackInfoApp extends Applet {
 13     static short sCount1 = 0;
 14     public static void install(byte[] bArray, short bOffset, byte bLength) {
 15         new GetJavaCardStackInfoApp().register(bArray, (short) (bOffset + 1),
 16                 bArray[bOffset]);
 17     }
 18 
 19     public void process(APDU apdu) {
 20         if (selectingApplet()) {
 21             return;
 22         }
 23         byte[] buffer = apdu.getBuffer();
 24         sCount1 = 0;
 25         switch (buffer[ISO7816.OFFSET_INS]) {
 26         case (byte) 0x00:
 27             sCount1 = (short) 0;
 28             try {
 29                 sgetJavaCardStack();
 30             } catch (Exception e) {
 31             } finally {
 32                 apdu.setOutgoingAndSend((short) 0, Util.setShort(buffer, (short) 0, sCount1));
 33             }
 34             break;
 35 
 36         case (byte) 0x01:
 37             sCount1 = (short) 0;
 38             try {
 39                 sgetJavaCardStack((short) 0);
 40             } catch (Exception e) {
 41             } finally {
 42                 apdu.setOutgoingAndSend((short) 0, Util.setShort(buffer, (short) 0, sCount1));
 43             }
 44             break;
 45 
 46         case (byte) 0x02:
 47             sCount1 = (short) 0;
 48             try {
 49                 sgetJavaCardStack((short) 0, (short) 0);
 50             } catch (Exception e) {
 51             } finally {
 52                 apdu.setOutgoingAndSend((short) 0, Util.setShort(buffer, (short) 0, sCount1));
 53             }
 54             break;
 55         case (byte) 0x03:
 56             sCount1 = (short) 0;
 57             try {
 58                 sgetJavaCardStack((short) 0, (short) 0, (short) 0);
 59             } catch (Exception e) {
 60             } finally {
 61                 apdu.setOutgoingAndSend((short) 0, Util.setShort(buffer, (short) 0, sCount1));
 62             }
 63             break;
 64         case (byte) 0x04:
 65             sCount1 = (short) 0;
 66             try {
 67                 sgetJavaCardStack((short) 0, (short) 0, (short) 0, (short) 0);
 68             } catch (Exception e) {
 69             } finally {
 70                 apdu.setOutgoingAndSend((short) 0, Util.setShort(buffer, (short) 0, sCount1));
 71             }
 72             break;
 73         default:
 74             ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
 75         }
 76     }
 77 
 78     /**
 79      * 没有参数的递归调用,获取调用深度
 80      */
 81     public static void sgetJavaCardStack() {
 82         sCount1++;
 83         sgetJavaCardStack();
 84     }
 85 
 86     /**
 87      * 有一个参数的递归调用,获取调用深度
 88      */
 89     public static void sgetJavaCardStack(short s0) {
 90         sCount1++;
 91         sgetJavaCardStack(s0);
 92     }
 93 
 94     /**
 95      * 有二个参数的递归调用,获取调用深度
 96      */
 97     public static void sgetJavaCardStack(short s0, short s1) {
 98         sCount1++;
 99         sgetJavaCardStack(s0, s1);
100     }
101 
102     /**
103      * 有三个参数的递归调用,获取调用深度
104      */
105     public static void sgetJavaCardStack(short s0, short s1, short s2) {
106         sCount1++;
107         sgetJavaCardStack(s0, s1, s2);
108     }
109 
110     /**
111      * 有四个参数的递归调用,获取调用深度
112      */
113     public static void sgetJavaCardStack(short s0, short s1, short s2, short s3) {
114         sCount1++;
115         sgetJavaCardStack(s0, s1, s2, s3);
116     } 
118 }

作者:SCPlatform
Email:SCPlatform@outlook.com
本文旨在学习、交流使用,任何对文章内容的出版、印刷,对文章中提到的技术的商业使用时,请注意可能存在的法律风险。

原文地址:https://www.cnblogs.com/SCPlatform/p/5088496.html