GSM SIM卡初始化全过程

SIM上电物理激活后, ME(modem设备)选择一个目录 DF_gsm
并且 可选地尝试选择一个EF_ecc文件(如果EF_ecc可用的花),ME请求紧急呼叫代码
1. ME请求Extended Language Preference(扩展语言首选项)(File ID: 2F05)
如果:
1) EF_PL (2F05) 不可用
2) EF_PL 中的条目 不满足 ISO 639定义的语言
3) ME不支持 EF_PL 中的任何语言
三者满足其一, 那么ME就 只请求 EF_LP (6F05)
如果EF_PL和EF_LP都不存在,或者没有任何语言被ME支持, 那么ME 会选择它默认的语言。

2. 运行CHV1 验证过程;

3. 如果 CHV1 验证成功, ME开始 SIM Phase请求
4. 如果SIM要求Profile Download, 那么ME执行 Profile Download 过程(按照 3GPP TS 51.014标准)
当SIM卡启用BDN事, 那么Profile Download过程将用来 指明 ME是否支持 “SIM决定的呼叫控制”
如果支持, 那么SIM卡能够允许 对 EF_IMSI和 EF_LOCI执行 Rehabilitate 命令
5. 如果 ME检测到 SIM卡是Phase 1的,
它将忽略 FDN相关的过程,继续执行 Administrative Information请求
它也将忽略没有定义在Phase 1阶段的一些特性,比如 Higher Priority PLMN搜索周期请求。

对于Phase 2及以后的SIM卡,只有满足下面两个条件之一时才会继续GSM操作:
1) EF_IMSI和EF_LOCI没有被 invalidated, GSM操作应该立即开始
2) EF_IMSI和 EF_LOCI被invalidated, ME应该rehabiliteate这个两个EF

如果SIM卡上的FDN被启用,没有FDN能力但是有呼叫控制能力的ME, 不应该 rehabilitate 卡上的EF_IMSI和EF_LOCI文件,所以也不能访问这两个EF,
GSM操作应被禁止。

没有FDN能力,也没有呼叫控制能力的ME, 更加不能访问 EF_IMSI和EF_LOCI,GSM同样也被禁止。

SIM卡的FDN和BDN都被启用时,如果ME支持FDN但不支持呼叫控制, rehabilitate操作也不能成功,
因为REHABILITATE命令同BDN特性关联。

6. 接着运行下面的过程:
1) 管理信息(Administrative Information)请求
2) SIM 服务表 请求
3) IMSI 请求
4) 访问控制(Access Control)请求
5) 高优先级 PLMN 搜索周期 请求
6) Investigation scan 请求
7) PLMN selector请求
8) 带访问控制技术的HPLMN选择 请求
9) 用户控制的带访控制技术的PLMN选择器 请求
10) 运营商控制的带访控制技术的PLMN选择器 请求
11) 位置信息(Location Information)请求
12) GPRS位置信息请求
13) Cipher Key请求
14) GPRS Cipher Key请求
15) BCCH 信息请求
16) CPBCCH信息请求
17) 禁止的PLMN请求
18) LSA 信息请求
19) CBMID请求
20) 解个人化控制键请求
21) 告警网络提示 请求
如果在 SST服务表中表明 支持proactrive 服务,ME也支持proactive,
那么在空闲时间,每30秒,应发送一个STATUS命令
在通话过程中,也应发送STATUS命令,来检测sim卡是否存在。

rus jni

参考资料:
https://docs.rs/jni/latest/jni/
https://github.com/jni-rs/jni-rs/tree/master/example

首先,创建一个 HelloWorld.java 文件

class HelloWorld {
    private static native String hello(String input);

    static {
        System.loadLibrary("mylib");
    }

    public static void main(String[] args) {
        String output = HelloWorld.hello("josh");
        System.out.println(output);
    }
}

编译这个文件,并生成 .h 头文件

javac -h . HelloWorld.java
或者用全路径
/usr/lib/jvm/java-11-openjdk-amd64/bin/javac  HelloWorld.java  -h .

可以看到 生成了一个HelloWorld.h文件

3. 创建rust 库

cargo --lib new mylib

4. 编辑Cargo.toml

...

[dependencies]
jni = "*"

[lib]
crate_type = ["cdylib"]

5. 创建 mylib/src/lib.rs

use jni::JNIEnv;
use jni::objects::{JClass, JString};
use jni::sys::jstring;

#[no_mangle]
pub extern "system" fn Java_HelloWorld_hello(env: JNIEnv,
                                             _: JClass,
                                             input: JString)
                                             -> jstring {
    let input: String = env.get_string(input).expect("Couldn't get java string!").into();

    let output = env.new_string(format!("Hello, {}!", input))
        .expect("Couldn't create java string!");

    output.into_inner()
}

6. 编译运行

cd mylib
cargo build
cd ..
LD_LIBRARY_PATH=/path/to/mylib/target/debug java HelloWorld
或者
java -Djava.library.path=mylib/target/debug/ HelloWorld

rust自动解引用与强制解引用

Rust 有一个叫 自动引用和解引用(automatic referencing and dereferencing)的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。

他是这样工作的:当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &、&mut 或 * 以便使 object 与方法签名匹配。也就是说,这些代码是等价的:

p1.distance(&p2);
(&p1).distance(&p2);

第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者———— self 的类型。在给出接收者和方法名的前提下,Rust 可以明确地计算出方法是仅仅读取(&self),做出修改(&mut self)或者是获取所有权(self)。


println! 宏也可以自动解引用


强制解引用(Deref coercion) 是一种执行在函数和方法的参数上的便利。它只能用于那些实现了Deref trait的类型。
强制解引用,将一种类型的引用转换成另一种类型的引用。比如它可将 &String 转换成 &str
因为 String实现了 Deref trait, 返回一个 &str

当我们传递一个特定类型的的值的引用给一个函数/方法时,如果同函数/方法中定义的参数类型不匹配时,会自动进行解引用。一系列 deref 方法会被调用,用来在我们提供的类型,和参数所需要的类型之间 实施转换。

程序员在写函数/方法时, 不必要添加 许多 明示的 引用 (&)或者解引用(*)
强制解引用也让我们写的代码更好地同时工作在 引用/智能指针上。

用python给美团walleID设置id

#!/usr/bin/python3

import sys

def getvaluefrombytes(tbytes):
    va = 0
    for j in range(len(tbytes)):
        va += tbytes[j] * 256 ** j
    return va


def decodebytesfromnum(num, lens=4):
    tbytes = bytearray()
    for i in range(lens):
        tbytes.append((num & (0xff << 8 * i)) >> 8 * i)
    return tbytes


def set_cid(readbytes, channelname):
    
    # find End of central directory
    for i in range(len(readbytes) - 22):
        if (readbytes[-22 - i] == 0x50 and readbytes[-21 - i] == 0x4b and readbytes[-20 - i] == 0x05 and readbytes[
                -19 - i] == 0x06):
            break
            

    start_central_directory_value_pos = -6 - i
    start_central_directory = readbytes[start_central_directory_value_pos + 3] * 256 ** 3  \
        + readbytes[start_central_directory_value_pos + 2] * 256 ** 2                      \
        + readbytes[start_central_directory_value_pos + 1] * 256                           \
        + readbytes[start_central_directory_value_pos]

    if readbytes[start_central_directory - 16:start_central_directory] == 'APK Sig Block 42'.encode("utf-8"):
        print("check v2 true")
    else:
        print("check v2 fail")
        return


    sign_block_size = 0
    sgin_block_value_pos = start_central_directory - 16 - 8
    for i in range(8):
        sign_block_size += 256 ** i * readbytes[sgin_block_value_pos + i]

    sign_block_start_pos = start_central_directory - sign_block_size - 8

    k = 0;
    keybytes = {}
    tempbytes = []
        
    strkey = 0
    k = 0
    
    #walleid 0x71777777
    walleIDbytes = bytearray()
    walleIDbytes.append(0x77)
    walleIDbytes.append(0x77)
    walleIDbytes.append(0x77)
    walleIDbytes.append(0x71)

    pairlen = 0
    for i in range(sign_block_start_pos + 8, sgin_block_value_pos):
        tempbytes.append(readbytes[i])
        if k == 7:
            pairlen = getvaluefrombytes(tempbytes)
            tempbytes.clear()
        if k == 11:
            strkey = getvaluefrombytes(tempbytes)
            tempbytes.clear()
        if k == pairlen + 7:
            keybytes[strkey] = bytes(tempbytes)
            tempbytes.clear()
            k = -1
        k += 1


    channel = "{\"channel\":\"%s\"}" % channelname
    channelbytes = bytearray(channel.encode("utf-8"))
    if len(channelbytes) > 252:
        print(" channel str len < 252")
        return
        
    insertbytes = bytearray()
       
    channelbytes_len = len(channelbytes) + 4
    insertbytes.append(channelbytes_len)
    for i in range(7):
        insertbytes.append(0)

    for i in walleIDbytes:
        insertbytes.append(i)
    for i in channelbytes:
        insertbytes.append(i)


    start_central_directory += len(insertbytes)
    dictoffsetbytes = decodebytesfromnum(start_central_directory)
    for index in range(4):
        readbytes[start_central_directory_value_pos + index] = dictoffsetbytes[index]
    sign_block_size += len(insertbytes)
    signsizebytes = decodebytesfromnum(sign_block_size, 8)
    for index in range(8):
        readbytes[sign_block_start_pos + index] = signsizebytes[index]
        readbytes[sgin_block_value_pos + index] = signsizebytes[index]
    for b in insertbytes[::-1]:
        readbytes.insert(sgin_block_value_pos, b)

    outfilepath = "channel_%s.apk" % channelname
    with open(outfilepath, "wb") as fout:
        fout.write(readbytes)







def main():
    if len(sys.argv) != 3:
        print("i need apk_path and channel_id.")
        exit(-1)

    filepath = sys.argv[1]
    cid      = sys.argv[2]

    readbytes = bytearray()
    with open(filepath, "rb") as f:
        readbytes = bytearray(f.read())

    set_cid(readbytes, cid)


if __name__ == "__main__":
    main()
    
            

Linux下给ccid驱动添加新的读卡器支持

很多PC/SC读卡器都是标准读卡器,但是 作者并没有测试过 https://ccid.apdu.fr/
需要自己手动添加支持。
以Debian系统为例
修改 /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist
添加 ifdVendorID ifdProductID 和 ifdFriendlyName等三项
具体信息,可以用 lsusb命令查看

058c:9590 In Focus Systems EMV Smartcard Reader

这个读卡器在 马大师 的平台上卖20元左右

SPRD有关vsim的at命令更详细的介绍

AT+VIRUALSIMINIT=0
表示使用物理卡
AT+VIRUALSIMINIT=1
表示使用虚拟卡

0:实体卡
1:云卡
2:软卡


AT+VIRTUALSIMINIT=0,0
物理卡, 不写入NV
AT+VIRTUALSIMINIT=0,1
物理卡, 写入nv


AT+VIRTUALSIMINIT?
查询是否开启VSIM


VSIM_CREATE:AT+RSIMRSP=”VSIM”,1
VSIM_INIT:AT+RSIMRSP=”VSIM”,1
开启VSIM功能

前缀不重要, rild会对 这个命令进行处理, 只发送AT+RSIMRSP=”VSIM”,1给基带


AT+RSIMRSP=
发送rAPDU给modem


VSIM_EXIT:AT+RSIMRSP=”VSIM”,0
关闭vsim功能
实际也只是发送 AT+RSIMRSP=”VSIM”,0 给基带


AT+SPVSIMAUTHSET=
设置鉴权数据通过哪个 卡槽的实体卡 发送给卡池


AT+SPVSIMAUTHSET?
查询鉴权通道 通过哪个卡槽的实体卡,发送给云端

RILC_ATCI

socket_local_client “atci_socket0”

“vsim_socket_0”

@vsim_socket_1 (Rild 用 libsprd-ril.so 创建 vism 服务socket)

从 socket里 读取AT命令, 解析成APDU


Android 4.4 采用的socket,而不是8.0之后的 hwbinder

netstat -xunlp
...
unix  2      [ ACC ]     STREAM     LISTENING       5189 595/sprdrild        @atci_socket1
unix  2      [ ACC ]     STREAM     LISTENING       4600 596/sprdrild        @atci_socket2
...
unix  2      [ ACC ]     STREAM     LISTENING       7262 596/sprdrild        @vsim_socket_1

USB主机与集成电路卡接口设备间的通道

1.控制通道(Control pipe)

控制通道消息(包括标准请求,比如GET_DESCRIPTOR和SET_CONFIGURATION)

2. 中断通道(Interrupt pipe)

异步事件(ICC卡存在,ICC卡移除,硬件错误)

3. Bulk-in, Bulk-out pipes


Android API的使用

1. 通过 usbManager.getDeviceList() 来获取设备
android.hardware.usb.UsbManager

2. UsbDevice (表示连接的 USB 设备,并包含用于访问其标识信息、接口和端点的方法。)
getDeviceClass()
getDeviceId()
getDeviceName()
getDeviceProtocol()
getDeviceSubclass()
getManufacturerName()
getProductId()
getSerialNumber()
getVendorId()
得到设备的信息
最重要是可以 通过 getInterface(int index)得到接口

3. UsbInterface 定义设备的一组功能。 设备可以具有一个或多个用于通信的接口。

4. UsbEndpoint 接口端点,是此接口的通信通道。一个接口可以具有一个或多个端点,并且通常具有用于与设备进行双向通信的输入和输出端点。

openDevice(UsbDevice device)

Nubia Watch解锁bootloader安装app

第1代是 Nubia Alpha 型号 SW1002, 高通W2100(MSM8909W),1+8存储组合,重量:很重,电池500mAh,有扬声器,有摄像头,没NFC。
第2代是 Nubia Watch 型号 SW1003, 高通W2100(MSM8909W),1+8存储组合,重98g,电池425mAh,没有扬声器,没有摄像头,没NFC。
Wear 2100是高通2016年发布的U,28nm工艺。 华米Amazfit智能手表2, 华为Watch 2 Pro/2019, Ticwatch Pro 4G 都是用的这款芯片。
———————————
Nubia Watch 基于Android 7.1.1, 默认开启adb, 不过依然可以调用 *#7678# 来打开设置

解锁

adb reboot bootloader
fastboot oem nubia_unlock NUBIA_SW1003
fastboot boot sw1003.img
如果是Nubia Alpha
fast boot oem nubia_unlock NUBIA_SW1002
fast boot recovery-nubia.img

安装Magisk.apk

adb install -r -n nubia  magisk.apk

需要传递一个 -n nubia选项

用recovery来修补boot分区

adb push  magisk.zip /sdcard
adb reboot bootloader
fastboot boot rec.img

其中magisk.zip是由magisk.apk改名而来
启动到recovery后,安装zip文件,会自动对 boot分区patch

华为手表从出厂刷机包和OTA包中生成最终的boot.img

以华为Watch 2 2018为例,
出厂的刷机包 “HUAWEI WATCH 2 Modem_中国版升级_NWD1.170711.022_release.exe”, 这是一个fastboot刷机包

0. 编译 ApplyPatch

apt install libbz2-dev
git clone https://github.com/erfanoabdi/imgpatchtools
cd imgpatchtools
make

1. 从OTA升级包update.zip 获取相关文件和信息

META-INF/com/google/android/updater-script
...
ui_print("Source: huawei/sawshark_sw/sawshark:7.1.1/NWD1.170711.002/4172583:user/release-keys");
ui_print("Target: huawei/sawshark_sw/sawshark:7.1.1/N7I56C/4304100:user/release-keys");
...
apply_patch("EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8062181:ac228af93437500c11504bdaa7f0dcae261f8b90:8064229:3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96",
            "-", 3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96, 8064229,
            ac228af93437500c11504bdaa7f0dcae261f8b90,
            package_extract_file("patch/boot.img.p")) ||
    abort("E3008: Failed to apply patch to EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8062181:ac228af93437500c11504bdaa7f0dcae261f8b90:8064229:3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96");

可以看出原始的 boot.img的sha1为 ac228af93437500c11504bdaa7f0dcae261f8b90, 打好补丁后为 3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96

./ApplyPatch  boot.img  new.img  3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96  8064229  ac228af93437500c11504bdaa7f0dcae261f8b90 patch/boot.img.p

命令行选项

	file = source file from rom zip
	target = target file (use "-" to patch source file)
	tgt_sha1 = target SHA1 Sum after patching
	size = file size
	init_sha1 = file SHA1 sum
	patch = patch file (.p) from OTA zip

验证
sha1sum -b boot.img
ac228af93437500c11504bdaa7f0dcae261f8b90 *boot.img

sha1sum -b new.img
3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96 *new.img

以后的各版本

ui_print("Source: huawei/sawshark_sw/sawshark:7.1.1/N7I56C/4304100:user/release-keys");
ui_print("Target: huawei/sawshark_sw/sawshark:7.1.1/N7I92E/4403039:user/release-keys");
...
apply_patch("EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8064229:3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96:8064229:91a8457809ca7534d87b46ec44bf7991d3b53179",
            "-", 91a8457809ca7534d87b46ec44bf7991d3b53179, 8064229,
            3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96,
            package_extract_file("patch/boot.img.p")) ||
    abort("E3008: Failed to apply patch to EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8064229:3f29ba5b40328b1e3dc16515ff4a4e3bbe2ddb96:8064229:91a8457809ca7534d87b46ec44bf7991d3b53179");
-----
ui_print("Source: huawei/sawshark_sw/sawshark:7.1.1/N7I92E/4403039:user/release-keys");
ui_print("Target: huawei/sawshark_sw/sawshark:7.1.1/NWD1.171123.001/4467331:user/release-keys");
...
apply_patch("EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8064229:91a8457809ca7534d87b46ec44bf7991d3b53179:8064229:8d05322cc211052be5d0264f6e8671e0f5ea28e1",
            "-", 8d05322cc211052be5d0264f6e8671e0f5ea28e1, 8064229,
            91a8457809ca7534d87b46ec44bf7991d3b53179,
            package_extract_file("patch/boot.img.p")) ||
    abort("E3008: Failed to apply patch to EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8064229:91a8457809ca7534d87b46ec44bf7991d3b53179:8064229:8d05322cc211052be5d0264f6e8671e0f5ea28e1");
---------------------------------
ui_print("Source: huawei/sawshark_sw/sawshark:7.1.1/NWD1.171123.001/4467331:user/release-keys");
ui_print("Target: huawei/sawshark_sw/sawshark:8.0.0/OWDE.180122.014/4586409:user/release-keys");
...
apply_patch("EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8064229:8d05322cc211052be5d0264f6e8671e0f5ea28e1:8594662:07c12ecbf36151a617ff05677f4896405e421ebc",
            "-", 07c12ecbf36151a617ff05677f4896405e421ebc, 8594662,
            8d05322cc211052be5d0264f6e8671e0f5ea28e1,
            package_extract_file("patch/boot.img.p")) ||
    abort("E3008: Failed to apply patch to EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8064229:8d05322cc211052be5d0264f6e8671e0f5ea28e1:8594662:07c12ecbf36151a617ff05677f4896405e421ebc");
-------------------

ui_print("Source: huawei/sawshark_sw/sawshark:8.0.0/OWDE.180122.014/4586409:user/release-keys");
ui_print("Target: huawei/sawshark_sw/sawshark:8.0.0/OWDE.180215.020/4633085:user/release-keys");

....

apply_patch("EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8594662:07c12ecbf36151a617ff05677f4896405e421ebc:8594662:17625d3b58ca903ec0ad8cdf3c8fe872ad8e11d3",
            "-", 17625d3b58ca903ec0ad8cdf3c8fe872ad8e11d3, 8594662,
            07c12ecbf36151a617ff05677f4896405e421ebc,
            package_extract_file("patch/boot.img.p")) ||
    abort("E3008: Failed to apply patch to EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8594662:07c12ecbf36151a617ff05677f4896405e421ebc:8594662:17625d3b58ca903ec0ad8cdf3c8fe872ad8e11d3");
-----------------------------------
ui_print("Source: huawei/sawshark_sw/sawshark:8.0.0/OWDE.180215.020/4633085:user/release-keys");
ui_print("Target: huawei/sawshark_sw/sawshark:8.0.0/OWDE.180707.001.E1/4958530:user/release-keys");
....
apply_patch("EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8594662:17625d3b58ca903ec0ad8cdf3c8fe872ad8e11d3:8604902:4946f61ba431ec844dae06afe2e572f491a99a5a",
            "-", 4946f61ba431ec844dae06afe2e572f491a99a5a, 8604902,
            17625d3b58ca903ec0ad8cdf3c8fe872ad8e11d3,
            package_extract_file("patch/boot.img.p")) ||
    abort("E3008: Failed to apply patch to EMMC:/dev/block/platform/soc/7824900.sdhci/by-name/boot:8594662:17625d3b58ca903ec0ad8cdf3c8fe872ad8e11d3:8604902:4946f61ba431ec844dae06afe2e572f491a99a5a");