首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> C 語言問答

C語言代碼片段深度分析:智能卡指令構建、發送與響應處理流程

     編輯:C 語言問答

代碼片段


unsigned char ins7401[5] = { 0xD0, 0x74, 0x01, 0x00, 0x00 };
int l;
ins7401[3] = 0x80;  // 修改第4字節
ins7401[4] = 0x01;  // 修改第5字節
if ((l = read_cmd_len(reader, ins7401)) < 0) return ERROR;
ins7401[3] = 0x00;
ins7401[4] = l;
if (!write_cmd_vg(ins7401, NULL) || !status_ok(cta_res + l)) {
    cs_log("[videoguard2-reader] failed to read cmd list");
    return ERROR;
}

領域背景:智能卡 / 條件存取系統 (CAS)

  • ins7401:APDU 指令緩衝區 (5 字節),0xD0 0x74 0x01 為 CLA/INS/P1/P2 (ISO 7816-4),後兩字節為參數/長度。
  • reader:讀卡器上下文句柄。
  • read_cmd_len:發送指令並讀取響應長度 (或數據長度)。
  • write_cmd_vg:發送指令 (VideoGuard 專用協議)。
  • status_ok:檢查響應狀態字 (SW1 SW2) 是否 0x9000 (成功)。
  • cta_res:響應緩衝區基址。
  • cs_log:日誌記錄函數。

逐行邏輯解析

  1. 初始化指令模板:ins7401 為固定頭 D0 74 01 + 兩字節參數位。
  2. 首次參數設置:ins7401[3]=0x80; ins7401[4]=0x01; → 構造特定查詢指令 (如「獲取命令列表長度」)。
  3. 發送並獲取長度:l = read_cmd_len(reader, ins7401)。若返回 < 0 表示通訊失敗/無卡/協議錯誤,直接返回 ERROR。
  4. 根據返回長度構造二次指令:ins7401[3]=0x00; ins7401[4]=l; → 參數改為「讀取具體數據」,長度為剛獲取的 l。
  5. 發送數據指令並校驗狀態:write_cmd_vg(ins7401, NULL) 發送讀數據指令;status_ok(cta_res + l) 檢查響應尾部狀態字 (通常在數據後 2 字節)。
  6. 失敗處理:任一步驟失敗 → 記錄日誌 → 返回 ERROR。

關鍵技術細節

潛在風險與改進建議

  1. 緩衝區溢出:ins7401 僅 5 字節,若協議擴展需更長指令將溢出。建議定義 #define MAX_APDU 260 並檢查邊界。
  2. 魔術數字:0xD0, 0x74, 0x01, 0x80, 0x01 應定義為常量/枚舉,增強可讀性。
  3. 返回值未檢查:write_cmd_vg 返回值直接用於邏輯判斷,但未區分「發送失敗」與「卡返回錯誤狀態」。
  4. 並發安全:若 reader/cta_res 為全局/共享,需互斥鎖保護。
  5. 日誌分級:cs_log 應支持級別 (DEBUG/INFO/ERROR) 及上下文 (卡號、指令十六進制)。

重構示例 (更健壯、可讀)


#define CLA_VIDEO_GUARD  0xD0
#define INS_READ_CMD     0x74
#define P1_DEFAULT       0x01

typedef enum {
    CMD_GET_LEN = 0x80,
    CMD_READ_DATA = 0x00
} CmdParam;

int videoguard2_read_cmd_list(ReaderContext *reader, uint8_t *out_buf, size_t *out_len) {
    uint8_t apdu[5] = {CLA_VIDEO_GUARD, INS_READ_CMD, P1_DEFAULT, 0, 0};
    int l;

    // 階段1: 獲取長度
    apdu[3] = CMD_GET_LEN;
    apdu[4] = 0x01;
    l = read_cmd_len(reader, apdu);
    if (l < 0) return ERR_COMM;

    // 階段2: 讀取數據
    apdu[3] = CMD_READ_DATA;
    apdu[4] = (uint8_t)l; // 假設 l <= 255
    if (!write_cmd_vg(reader, apdu, NULL)) return ERR_WRITE;
    if (!status_ok(reader->response_buf + l)) return ERR_STATUS;

    // 成功:拷貝數據給調用者 (假設數據在 reader->response_buf)
    if (out_buf && out_len && *out_len >= (size_t)l) {
        memcpy(out_buf, reader->response_buf, l);
        *out_len = l;
    }
    return SUCCESS;
}

總結

該片段展示了**智能卡雙階段 APDU 交互典型模式**:構造指令 → 發送接收 → 解析響應 → 錯誤處理。代碼緊湊但存在魔術數字、緩衝區固定、錯誤碼粗糙等工程化不足。嵌入式/安全領域開發需建立**協議常量表、動態緩衝區管理、分級錯誤碼、結構化日誌**規範,將此類「能跑通」代碼重構為「可維護、可審計、可擴展」的生產級組件。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
細節說明
APDU 結構CLA(1) | INS(1) | P1(1) | P2(1) | Lc/Le(1/2/3) | Data... | Le(0/1/2/3) | SW1(1) | SW2(1)
緩衝區複用ins7401 重複使用,修改參數字節構造不同階段指令,節省棧空間。
雙階段讀取先查長度 (或獲取元數據),再按長度讀數據,典型「兩次 APDU 交互」模式。
錯誤碼傳遞函數返回 int,負值表錯誤,正常值為長度/狀態,調用者統一判斷 <0。
指針算術cta_res + l 定位響應數據後的狀態字,假設響應格式:[數據 l 字節][SW1][SW2]。