zoukankan      html  css  js  c++  java
  • 如何获取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
    本文旨在学习、交流使用,任何对文章内容的出版、印刷,对文章中提到的技术的商业使用时,请注意可能存在的法律风险。

  • 相关阅读:
    DX 系列之 ComboBoxEdit
    nodejs web API 相关杂项
    解决跨域No 'Access-Control-Allow-Origin' header is present on the requested resource.
    git在本地回退
    docker容器访问宿主机IP
    mongodb+nodejs
    docker-compose控制启动顺序
    angular7 + d3 显示svg
    新建ng工程
    CSS样式
  • 原文地址:https://www.cnblogs.com/SCPlatform/p/5088496.html
Copyright © 2011-2022 走看看