Java Card CAP文件分析

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源碼:

  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. }  

使用方法

  1. Cap cap = new Cap();  
  2. cap.loadCapFile("HelloWorld.cap");  
就可以解壓出各個組件,他們也是cap文件。

所有的組件均有通用結構格式,如下:

  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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章