Java Card CAP文件分析

来源:互联网 发布:过期备案域名 编辑:程序博客网 时间:2024/05/25 23:28

JAVA 智能的可执行文件(CAP 文件)是编译多个应用程序(Applet)的生成结果,包含了一个包中定义的所有类和接口,与包之间是一一对应的关系。实际发卡操作时,首先需要将该可执行文件下载至卡片中,并安装需要的应用实例;用户使用该安装的应用实例执行操作功能。

CAP文件包含12个组件:

Component Type

Value

COMPONENT_Header

1

COMPONENT_Directory

2

COMPONENT_Applet

3

COMPONENT_Import

4

COMPONENT_ConstantPool

5

COMPONENT_Class

6

COMPONENT_Method

7

COMPONENT_StaticField

8

COMPONENT_ReferenceLocation

9

COMPONENT_Export

10

COMPONENT_Descriptor

11

COMPONENT_Debug

12

注意:
一个完整的CAP文件,除Applet、Export 和Debug组件是可选外,其他均为必选。每个组件封装成一个CAP包,包含在Jar包中。最后在卡上只保留了5个组件:COMPONET_Method,COMPONET_Class,COMPONET_ConstantPool,COMPONET_StaticField和 COMPONET_Export。其余的组件只是安装时提取有用信息而不在卡中保存。

12个组件中,类class组件保存本应用声明的所有类和接口的信息; 方法method组件保存本应用声明的所有方法和接口,method中利用2字节索引index引用类、方法和域;常数池constant pool组件保存method组件引用的所有类、方法和域信息,分为类、实例域、虚方法、父方法、静态域和静态方法6类,每组信息为4个字节;相关地址reference location组件保存method组件中索引的偏移。

对于JavaCard而言,应用程序的下载过程是即CAP文件写入到EEPROM的过程,即是对CAP文件的下载过程。在CAP文件的下载过程中,需要将一部分组件进行解析,同时对reference location中指定的位置进行链接,能够链接到method组件中的一个索引号,并根据索引号查找constant pool中保存的、与该索引号对应的类、方法或域在 EEPROM中的实际地址,调用实际地址中存储的数据。也就是说,方法的调用其实是需要两个步骤来实现的:

  1. 根据reference location中指定的位置进行链接,获取method组件中的索引号;
  2. 根据索引号查找constant pool中保存的、与该索引号对应的类、方法或域在EEPROM中的实际地址,调用实际地址中存储的数据。

查看一个CAP文件的组件,可以通过两种方法实现:

  • 解压缩软件直接解压得到
  • 通过JCK中的capdump将其分解成各个组件

下面是加压cap的java源码:

[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. import java.io.BufferedInputStream;  
  2. import java.io.BufferedOutputStream;  
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.util.ArrayList;  
  6. import java.util.Enumeration;  
  7. import java.util.List;  
  8. import java.util.zip.ZipEntry;  
  9. import java.util.zip.ZipFile;  
  10. import android.os.Environment;  
  11. import android.util.Log;  
  12.   
  13. public class Cap {  
  14.     static final String TAG = "cap";  
  15.     static public final String CAP_DIRECTORY = "//mnt//sdcard//cap//";  
  16.     static final int READ_LENGTH = 1024;  
  17.   
  18.     private List<String> mCapFileList;  
  19.   
  20.     public void loadCapFile(String CapFileName) {  
  21.         try {  
  22.             if (!Environment.getExternalStorageState().equals(  
  23.                     Environment.MEDIA_MOUNTED)) {  
  24.                 Log.e(TAG, "SD card not available!");  
  25.                 return;  
  26.             }  
  27.             File file = new File(CAP_DIRECTORY + CapFileName);  
  28.             if (!file.exists()) {  
  29.                 Log.e(TAG, CAP_DIRECTORY + "safe.cap not available!");  
  30.                 return;  
  31.             }  
  32.             mCapFileList = new ArrayList<String>();  
  33.             mCapFileList.clear();  
  34.             BufferedOutputStream dest = null;  
  35.             BufferedInputStream is = null;  
  36.             ZipEntry entry;  
  37.             ZipFile zipfile = new ZipFile(file);  
  38.             Enumeration<? extends ZipEntry> e = zipfile.entries();  
  39.             while (e.hasMoreElements()) {  
  40.                 entry = (ZipEntry) e.nextElement();  
  41.                 String fileName = entry.getName();  
  42.                 if (fileName.endsWith(".MF"))  
  43.                     continue;  
  44.   
  45.                 if (fileName.contains("/")) {  
  46.                     int dir = fileName.lastIndexOf("/");  
  47.                     String root = CAP_DIRECTORY + fileName.substring(0, dir);  
  48.                     File path = new File(root);  
  49.                     if (!path.exists()) {  
  50.                         path.mkdirs();  
  51.                     }  
  52.                 }  
  53.                 int count;  
  54.                 byte data[] = new byte[READ_LENGTH];  
  55.                 mCapFileList.add(CAP_DIRECTORY + entry.getName());  
  56.   
  57.                 FileOutputStream fos = new FileOutputStream(CAP_DIRECTORY  
  58.                         + entry.getName());  
  59.                 dest = new BufferedOutputStream(fos, READ_LENGTH);  
  60.   
  61.                 is = new BufferedInputStream(zipfile.getInputStream(entry));  
  62.                 while ((count = is.read(data, 0, READ_LENGTH)) != -1) {  
  63.                     dest.write(data, 0, count);  
  64.                 }  
  65.                 dest.flush();  
  66.                 dest.close();  
  67.                 is.close();  
  68.                 fos.close();  
  69.             }  
  70.         } catch (Exception e) {  
  71.             Log.e(TAG, "loadCap: " + e.getMessage());  
  72.         }  
  73.     }  
  74.   
  75.     public List<String> getCapFileList() {  
  76.         return mCapFileList;  
  77.     }  
  78. }  

使用方法

[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. Cap cap = new Cap();  
  2. cap.loadCapFile("HelloWorld.cap");  
就可以解压出各个组件,他们也是cap文件。

所有的组件均有通用结构格式,如下:

[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. component {  
  2.     u1 tag      //u1表示无符号单字节类型的数据变量类型;tag为组件索引号,按照上面组件名称的顺序从1至12排列  
  3.    
  4.     u2 size     //u2表示无符号双字节的数据变量类型;size为可变长度数组info[]的元素个数  
  5.    
  6.     u1 info[]   //数组info[]中含了组件的所有信息,依据各组件属性不同而各不相同  
  7. }  

比如Applet.cap的十六进制数据是: 
0300090105c0c1c2c3c40085

第一个03是tag,然后是0009是size,说明info为9个字符,01标示了这是包中的第一个applet,05为该Applet的AID的长度,后面的c0c1c2c3c4即为applet的AID,后面的0085是该Applet的install_method_offset,即当前Applet中的install()在Method组件info[]中的偏移。

推荐的CAP组件安装顺序:

  1. COMPONENT_Header
  2. COMPONENT_Directory
  3. COMPONENT_Import
  4. COMPONENT_Applet
  5. COMPONENT_Class
  6. COMPONENT_Method
  7. COMPONENT_StaticField
  8. COMPONENT_Export
  9. COMPONENT_ConstantPool
  10. COMPONENT_ReferenceLocation
  11. COMPONENT_Descriptor (optional)

COMPONENT_Debug组件不需要下载到卡内。

作者:fish

转载:http://www.dreamingfish123.info/?p=818

0 0
原创粉丝点击