BlogQuy tắc đọc & cắt khúc một gói OTA từ chuỗi hex
Bố cục bài viết
Trang này
Ngày tạo
18/08/2026
Cập nhật
Lượt xem
8
Đọc
7m
Quy tắc đọc & cắt khúc một gói OTA từ chuỗi hex
Hướng dẫn cách tự bổ một chuỗi hex OTA thành các trường mà không cần đoán — vì mỗi trường đều tự khai độ dài của nó, hoặc có độ dài cố định theo chuẩn.
0. Nền tảng: 1 byte = 2 chữ số hex
- 1 byte = 8 bit.
- 1 chữ số hex biểu diễn 4 bit (16 giá trị 0–F, vì 16 = 2⁴).
- Vậy cần 2 chữ số hex mới đủ 1 byte.
- “Nửa byte” (4 bit = 1 chữ số hex) gọi là nibble. 1 byte = 2 nibble.
| Byte (hex) | Nibble cao | Nibble thấp | Thập phân |
|---|---|---|---|
00 | 0 | 0 | 0 (nhỏ nhất) |
0D | 0 | D | 13 |
1F | 1 | F | 31 |
A0 | A | 0 | 160 |
FF | F | F | 255 (lớn nhất) |
Hệ quả thực tế:
TARdài 3 byte → chiếm 6 ký tự hex (A1B2C3).short_message= 36 byte → viết ra 72 ký tự hex.- Số ký tự hex luôn chẵn. Nếu lẻ → chuỗi lỗi (thiếu/thừa 1 nibble).
Cách đọc nhanh: gom 2 ký tự một lần từ trái sang.
02 70 00 00 1F 0D … ✔ mỗi cặp = 1 byte
0270 0000 1F0D … -> SAI: đây là gom 2 byte, dễ nhầm
⚠️ Đừng lẫn “cặp chữ số hex” với “cặp chữ số thập phân của số điện thoại”. Số điện thoại BCD nhét 2 chữ số 0–9 vào 1 byte rồi đảo nibble (“84”→
48) — đó là mã hoá riêng của trường địa chỉ SMS. Quy ước “1 byte = 2 ký tự hex” thì luôn đúng ở mọi nơi.
1. Ba loại “manh mối” để biết chỗ cắt
Bạn không đoán vị trí dấu |. Mỗi trường thuộc một trong ba loại sau:
| Loại | Cách biết độ dài | Ví dụ |
|---|---|---|
| Độ dài cố định | Học thuộc bảng theo chuẩn | CPL 2, CHL 1, TAR 3, CNTR 5… |
| Có byte khai độ dài đứng ngay trước | Đọc byte đó, lấy đúng bấy nhiêu | UDHL, Lc của APDU |
| Suy ra từ trường tổng | Lấy tổng trừ các phần đã biết | Secured Data = CPL − 14; CC = CHL − 13 |
Nói cách khác: UDHL, CPL, CHL, Lc là những “cây thước” nhúng sẵn trong gói. Đọc thước → cắt → thước tiếp theo chỉ cho bạn đoạn kế.
2. Bảng độ dài CỐ ĐỊNH của OTA Command Packet (TS 23.048)
Học thuộc thứ tự và độ dài này là cắt được phần header:
| Thứ tự | Trường | Độ dài (byte) |
|---|---|---|
| 1 | CPL | 2 |
| 2 | CHL | 1 |
| 3 | SPI | 2 |
| 4 | KIc | 1 |
| 5 | KID | 1 |
| 6 | TAR | 3 |
| 7 | CNTR | 5 |
| 8 | PCNTR | 1 |
| 9 | CC (nếu có) | = CHL − 13 |
| 10 | Secured Data | = CPL − 1 − CHL |
Mẹo nhớ dãy header: 2 · 1 · 2 · 1 · 1 · 3 · 5 · 1.
3. Quy trình cắt — 4 bước
Bước 1: UDH — đọc byte đầu (UDHL), lấy đúng bấy nhiêu byte
Bước 2: OTA header — cắt theo bảng cố định 2·1·2·1·1·3·5·1
Bước 3: kiểm chéo — dùng CHL và CPL để xác nhận + tính Secured Data
Bước 4: APDU — mỗi APDU đọc Lc để biết dài bao nhiêu
Bước 1 — UDH: byte đầu tự khai độ dài
02 70 00
│ └──┴─ 2 byte (vì byte đầu = 02)
└─ UDHL = 2
Đọc 02 → UDH gồm 2 byte kế (70 00). Cắt xong UDH.
(70 = Command Packet; chiều SIM trả về dùng 71 = Response Packet.)
Bước 2 — OTA header: cắt theo bảng cố định
00 1F | 0D | 00 00 | 00 | 00 | A1 B2 C3 | 00 00 00 00 01 | 00
2 1 2 1 1 3 5 1 ← byte
CPL CHL SPI KIc KID TAR CNTR PCNTR
Bước 3 — kiểm tra chéo bằng CHL và CPL
CHL = 0D = 13 khai “header dài 13 byte” = SPI..PCNTR.
Đếm lại: 2+1+1+3+5+1 = 13 ✔
→ Nếu có CC, CHL > 13, phần dôi ra = độ dài CC.
CPL = 00 1F = 31 khai “toàn bộ phần sau CPL dài 31 byte”.
→ Secured Data = CPL − (1 byte CHL + CHL) = 31 − (1 + 13) = 17 byte.
CPL (31) − [ 1 byte CHL + CHL=13 ] = Secured Data (17)
Bước 4 — Secured Data: mỗi APDU tự khai Lc
APDU có cấu trúc 4 byte header + 1 byte Lc + đúng Lc byte data:
A0 A4 00 00 02 6F46 Lc=02 → lấy 2 byte → APDU 1 = 7 byte
A0 D6 00 00 05 0056494554 Lc=05 → lấy 5 byte → APDU 2 = 10 byte
Kiểm tra: 7 + 10 = 17 byte = đúng Secured Data ở bước 3 ✔
Vòng lặp đọc từng APDU:
đọc 4 byte (CLA INS P1 P2) → đọc byte thứ 5 (Lc)
→ lấy đúng Lc byte data → xong 1 APDU → còn byte thì lặp lại
4. Ví dụ HOÀN CHỈNH đã cắt
Chuỗi gốc:
027000001F0D00000000A1B2C3000000000100A0A40000026F46A0D60000050056494554
Cắt ra:
02 70 00 | 00 1F | 0D | 00 00 | 00 | 00 | A1 B2 C3 | 00 00 00 00 01 | 00 | A0A40000026F46 | A0D60000050056494554
| Lát cắt | Trường | Ý nghĩa |
|---|---|---|
02 70 00 | UDH | UDHL=2, IEI=70 (Command Packet) |
00 1F | CPL | 31 byte còn lại |
0D | CHL | 13 byte header |
00 00 | SPI | không mã hoá, không ký |
00 | KIc | chỉ số khoá mã hoá |
00 | KID | chỉ số khoá chữ ký |
A1 B2 C3 | TAR | applet đích |
00 00 00 00 01 | CNTR | bộ đếm = 1 |
00 | PCNTR | 0 byte đệm |
A0A40000026F46 | APDU | SELECT EF_SPN (6F46) |
A0D60000050056494554 | APDU | UPDATE BINARY, ghi 00 “VIET” |
Tổng: 3 + 2 + 1 + 13 + 17 = 36 byte = sm_length ✔
5. Tự luyện
Thử tự cắt chuỗi Warehouse này theo đúng 4 bước:
027000002E0D00000000B00010000000000100 0B 3834393832323232323232 48652054686F6E672042696E68205468756F6E67
Gợi ý: UDH 3 byte → CPL=002E=46 → CHL=13 → Secured Data = 46 − 14 = 32 byte.
Trong Secured Data lần này KHÔNG phải APDU mà là dữ liệu applet:
0B = độ dài số (11) → 11 byte ASCII số UE B → phần còn lại là text.
6. Bảng công thức nhanh (tra khi cần)
| Muốn tính | Công thức |
|---|---|
| Số byte của chuỗi hex | (số ký tự hex) ÷ 2 |
| Độ dài UDH | đọc byte UDHL (byte đầu) |
| Vị trí bắt đầu OTA | sau UDH = 1 + UDHL |
| Độ dài CC | CHL − 13 |
| Độ dài Secured Data | CPL − 1 − CHL |
| Độ dài 1 APDU | 5 + Lc (với case 3s có data) |
| Kiểm tra tổng | 1 + UDHL + 2 + CPL == sm_length |
7. Ba dấu hiệu chuỗi bị LỖI (cắt không khớp)
- Số ký tự hex lẻ → thiếu/thừa 1 nibble.
- CPL ≠ số byte thật đứng sau CPL → sai độ dài (lỗi hay gặp nhất).
- CHL < 13 hoặc CHL > cả gói → header vô lý.
- (dấu hiệu phụ) Secured Data đọc ra chữ người-đọc-được trong khi lẽ ra là APDU/nhị phân đã mã hoá → dựng sai hoặc quên bật bảo mật.
Tài liệu học tập. Áp dụng cho OTA Command Packet theo 3GPP TS 23.048 / TS 31.115.