rust中的引用为什么可以直接比较大小

Rust 中两个引用(如 &T)可以直接比较大小,是因为 Rust 的标准库为实现了 Ord 或 PartialOrd trait 的类型 T 的引用类型 &T 自动实现了这些 trait。具体来说,当比较 &a 和 &b 时,实际上是在比较它们所指向的底层数据 a 和 b 的值

原因有以下两点:
1. 智能隐式解引用(Dereferencing): Rust 的比较运算符(<, >, <=, >=)会处理引用。编译器在比较时,会自动深入引用层级,比较指向的实际数值,而非引用本身的内存地址。

2. Trait 自动实现: Rust 标准库中存在类似 impl Ord for &T 的定义。即如果类型 T 本身是可以比较顺序的(实现了 Ord),那么 &T 也自动拥有比较顺序的能力。

或者这么来理解: 在 Rust 中,比较运算符(如 >, <, ==)实际上是对标准库中 Trait(特征)的语法糖。比如 > 对应的是 PartialOrd 这个特征(Trait), 如果一个类型 T 可以比较大小,那么它的引用 &T 也可以比较大小;并且,比较两个引用时,Rust 会自动去比较它们所指向的底层数据。

所以,只要引用指向的类型实现了 PartialOrd(如整数、浮点数、字符串等),引用之间就能直接比较。

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let mut largest = &number_list[0];

    for number in &number_list {
        if number > largest {
            largest = number;
        }
    }

    println!("The largest number is {largest}");
}

中 if number > largest 改成 *number > *largest 也是可以的。 这就是手动解引用。

如果我真的想比较引用的“内存地址”怎么办?
因为 Rust 默认比较引用是指向的值,如果你在某些极少数的底层场景下,真的想要比较两个变量是不是存放在内存的同一个位置(比较地址大小),你需要把它们转换成裸指针(Raw Pointers):

if (number as *const i32) > (largest as *const i32)

5G NSA的几种形式

NSA就是LTE与NR的双连接,具体有:
EN-DC (4G核心网,4G基站为主站,5G基站为辅) E_UTRAN New Radio-Dual Connectivity
NE-DC (5G核心网, 5G为主,4G为辅助)
NGEN-DC (5G核心网,4G为主, 5G为辅)

用于反汇编联发科基带的ghidra nanomips isa module

原文:https://www.nccgroup.com/research-blog/ghidra-nanomips-isa-module/
原作者:James Chambers

2023年末至2024年初,NCC集团硬件和嵌入式系统业务部门承接了一项任务,对多款智能手机的基带固件进行逆向工程。这其中包括基于nanoMIPS架构的联发科5G基带固件。我们了解到一些针对Ghidra的nanoMIPS模块是私下开发的,当时并没有公开可用的可靠选项,因此我们自行开发了Ghidra的nanoMIPS反汇编器和反编译器模块。

出于(节省)时间的考虑,我们专注于实现在实际基带固件中遇到的功能和指令,而将尚不需要的复杂P-Code指令模拟保留未实现。虽然该模块仍在开发中,但它仍然能够反编译我们分析过的大部分基带固件。结合一些联发科固件中包含的调试符号信息,该模块在逆向工程过程中发挥了重要作用。

在这里,我们将演示如何将联发科基带固件用我们的 nanoMIPS ISA模块加载到 Ghidra 中,来进行分析。

为了分析固件示例,我们查找了可能搭载带5G支持的联发科SoC的手机。一些相对较新的摩托罗拉机型是不错的选择。(这些设备不属于我们的客户合作范围。)

我们在 https://mirrors.lolinet.com/firmware/lenomola/ 上找到了许多 Android 固件镜像,其中包括摩托罗拉 Moto Edge 2022(代号 Tesla)的固件镜像:
https://mirrors.lolinet.com/firmware/lenomola/tesla/official/。
该型号基于联发科 Dimensity 1050(MT6879)SoC。

固件有一些特定于运营商的版本。我们将随机选择
https://mirrors.lolinet.com/firmware/lenomola/2022/tesla/official/TMO/XT2205-1_TESLA_TMO_12_S2STS32.71-118-4-2-6-3_subsidy-TMO_UNI_RSU_QCOM_regulatory-DEFAULT_cid50_CFC.xml.zip

实际的 nanoMIPS 固件位于从ZIP包里提取的md1img.img中

为了提取md1img的文件内容,我们还编写了一些 Kaitai 结构定义,并使用简单的 Python 脚本来运行结构解析,并将不同部分输出到单独的文件中。
ksy Kaitai定义还可用于通过Kaitai IDE以交互方式探索这些文件。

使用选项–outdir 运行 md1_extract.py, 将提取其中包含的文件md1img.img:

$ ./md1_extract.py ../XT2205-1_TESLA_TMO_12_S2STS32.71-118-4-2-6-3_subsidy-TMO_UNI_RSU_QCOM_regulatory-DEFAULT_cid50_CFC/md1img.img --outdir ./md1img_out/
extracting files to: ./md1img_out
md1rom: addr=0x00000000, size=43084864
        extracted to 000_md1rom
cert1md: addr=0x12345678, size=1781
        extracted to 001_cert1md
cert2: addr=0x12345678, size=988
        extracted to 002_cert2
md1drdi: addr=0x00000000, size=12289536
        extracted to 003_md1drdi
cert1md: addr=0x12345678, size=1781
        extracted to 004_cert1md
cert2: addr=0x12345678, size=988
        extracted to 005_cert2
md1dsp: addr=0x00000000, size=6776460
        extracted to 006_md1dsp
cert1md: addr=0x12345678, size=1781
        extracted to 007_cert1md
cert2: addr=0x12345678, size=988
        extracted to 008_cert2
md1_filter: addr=0xffffffff, size=300
        extracted to 009_md1_filter
md1_filter_PLS_PS_ONLY: addr=0xffffffff, size=300
        extracted to 010_md1_filter_PLS_PS_ONLY
md1_filter_1_Moderate: addr=0xffffffff, size=300
        extracted to 011_md1_filter_1_Moderate
md1_filter_2_Standard: addr=0xffffffff, size=300
        extracted to 012_md1_filter_2_Standard
md1_filter_3_Slim: addr=0xffffffff, size=300
        extracted to 013_md1_filter_3_Slim
md1_filter_4_UltraSlim: addr=0xffffffff, size=300
        extracted to 014_md1_filter_4_UltraSlim
md1_filter_LowPowerMonitor: addr=0xffffffff, size=300
        extracted to 015_md1_filter_LowPowerMonitor
md1_emfilter: addr=0xffffffff, size=2252
        extracted to 016_md1_emfilter
md1_dbginfodsp: addr=0xffffffff, size=1635062
        extracted to 017_md1_dbginfodsp
md1_dbginfo: addr=0xffffffff, size=1332720
        extracted to 018_md1_dbginfo
md1_mddbmeta: addr=0xffffffff, size=899538
        extracted to 019_md1_mddbmeta
md1_mddbmetaodb: addr=0xffffffff, size=562654
        extracted to 020_md1_mddbmetaodb
md1_mddb: addr=0xffffffff, size=12280622
        extracted to 021_md1_mddb
md1_mdmlayout: addr=0xffffffff, size=8341403
        extracted to 022_md1_mdmlayout
md1_file_map: addr=0xffffffff, size=889
        extracted to 023_md1_file_map

最相关的文件是:
md1rom是 nanoMIPS 固件映像
md1_file_map 提供更多关于 md1_dbginfo 文件的上下文:它的原始文件名是DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31.xz
md1_dbginfo 是一个 XZ 压缩二进制文件,含有md1rom 的调试信息(包括符号)

提取调试符号

md1_dbginfo是包含符号、文件名以及对应的地址的另一种二进制文件格式。我们将根据md1_file_map来的文件名,对其进行重命名和解压缩:

$ cp 018_md1_dbginfo DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31.xz
$ unxz DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31.xz
$ hexdump DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31 | 头
00000000 43 41 54 49 43 54 4e 52 01 00 00 00 98 34 56 00 |CATICTNR.....4V.|
00000010 43 41 54 49 01 00 00 00 00 00 00 00 4e 52 31 36 |CATI........NR16|
00000020 2e 52 32 2e 4d 54 36 38 37 39 2e 54 43 32 2e 50 |.R2.MT6879.TC2.P|
00000030 52 31 2e 53 50 00 4d 54 36 38 37 39 5f 53 30 30 |R1.SP.MT6879_S00|
00000040 00 4d 54 36 38 37 39 5f 4e 52 31 36 2e 54 43 32 |.MT6879_NR16.TC2|
00000050 2e 50 52 31 2e 53 50 2e 56 31 37 2e 50 33 38 2e |.PR1.SP.V17.P38.|
00000060 30 33 2e 32 34 2e 30 33 52 00 32 30 32 33 2f 30 |03.24.03R.2023/0|
00000070 35 2f 31 39 20 32 32 3a 33 31 00 73 00 00 00 2b |5/19 22:31.s...+|
00000080 ed 53 00 49 4e 54 5f 56 65 63 74 6f 72 73 00 4c |.S.INT_Vectors.L|
00000090 08 00 00 54 08 00 00 62 72 6f 6d 5f 65 78 74 5f |...T...brom_ext_|

为了从调试信息文件中提取信息,我们制作了另一个 Kaitai 定义和脚本,来提取符号并以与 Ghidra 脚本ImportSymbolsScript.py兼容的文本格式输出它们:

$ ./mtk_dbg_extract.py md1img_out/DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31 | tee dbg_symbols.txt
INT_Vectors 0x0000084c l
brom_ext_main 0x00000860 l
INT_SetPLL_Gen98 0x00000866 l
PLL_Set_CLK_To_26M 0x000009a2 l
PLL_MD_Pll_Init 0x000009da l
INT_SetPLL 0x000009dc l
INT_Initialize_Phase1 0x027b5c80 l
INT_Initialize_Phase2 0x027b617c l
init_cm 0x027b6384 l
init_cm_wt 0x027b641e l
...

(目前脚本设置为仅输出标签定义而不是函数定义,因为不知道所有符号是否都用于函数。)

安装扩展

首先,我们需要为 Ghidra 安装 nanoMIPS 模块。在 Ghidra 主窗口中,前往“File > Install Extensions”,
点击“Add Extension”加号按钮,然后选择模块的 Zip 文件(例如ghidra_11.0.3_PUBLIC_20240424_nanomips.zip)。
然后重启 Ghidra。

初始加载
加载 md1rom 为原始二进制镜像(raw binary image)。
从md1img.img的解压目录中选择000_md1rom,并保留“Raw Binary”作为格式。
对于语言,点击“Browser”省略号,并使用过滤器 找到小端 32 位 nanoMIPS 选项 (nanomips:LE:32:default ),然后点击“确定”。

我们将在偏移量 0 处加载image,因此无需其他选项。再次单击“确定”即可加载原始二进制文件。

当 Ghidra 询问您是否要进行初始自动分析时,选择“否”。我们必须首先设置镜像内存地址空间0x90000000。

内存映射

打开“内存映射”窗口,然后单击“添加内存块”的加号。

我们将新块命名为“mirror”,将起始地址设置为ram:90000000,长度与基础映像“ram”块的长度(0x2916c40)匹配,权限为读取和执行,并将“块类型”设置为“字节映射”,源地址为 0,映射率为 1:1。

还要将原始“ram”块的权限更改为仅读取和执行。保存内存映射更改并关闭“内存映射”窗口。

请注意,此内存映射是不完整的;它只是使反汇编工作所需的最小设置。

调试符号

接下来,我们将加载调试符号。打开脚本管理器窗口并搜索ImportSymbolsScript.py。运行脚本并选择mtk_dbg_extract.py之前生成的文本文件 ( dbg_symbols.txt)。这将创建一堆标签,其中大部分位于镜像地址空间中。

反汇编

现在我们可以开始反汇编了。地址 0 处有一条跳转指令,可以引导我们开始,因此只需选中地址 0 处的字节,然后按“d”键或右键单击并选择“反汇编”即可。借助调试符号,您可能会注意到该指令跳转到了INT_Initialize_Phase1相应的函数。
基于流程的反汇编现在将开始发现一堆代码。初始反汇编可能需要几分钟才能完成。

然后,我们可以通过“分析 > 自动分析…”运行常规的自动分析。这应该也能发现更多代码,并需要几分钟的时间进行反汇编和反编译。
我们发现,“Non-Returning Functions”分析器在这些固件映像中使用默认配置会产生许多误报,从而扰乱代码流,因此我们建议在初始分析时禁用它。一次性“Decompiler Parameter ID”分析器是下一步运行的一个很好的选择,可以更好地检测函数输入类型。

结论

尽管该模块仍在开发中,但其结果已经可用于分析,并使我们能够对基带处理器中的一些关键功能进行逆向工程。

nanoMIPS Ghidra 模块和联发科二进制文件解包器可以在我们的 GitHub 上找到:

https://github.com/nccgroup/ghidra-nanomips
https://github.com/nccgroup/mtk_bp

微信分享如何启动指定的profile

https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/com/android/internal/app/ResolverActivity.java

com.android.internal.app.ResolverActivity的safelyStartActivityInternal

protected void safelyStartActivityInternal(
TargetInfo cti, UserHandle user, @Nullable Bundle options)

UserHandle.of(user_id)
user_id = user_handle.getIdentifier();
————-
com.miui.xspace.utils.XSpaceResolverActivityHelper
private void forward(int userId)

frida新的build方法

debian系统

1. 安装系统依赖
build-essential
git
lib32stdc++-14-dev
libc6-dev-i386

2. 安装nodejs
/etc/apt/sources.list.d/nodesource.sources

Types: deb
URIs: https://deb.nodesource.com/node_23.x/
Suites: nodistro
Components: main
Signed-By: /usr/share/keyrings/nodesource.gpg

apt install nodejs

3. 设置环境变量
export C_INCLUDE_PATH=/usr/include/python3.13

4. 获取源代码
git clone https://github.com/frida/frida.git

5. 编译python库,脚本
./configure –prefix /home/softsim/frida
make
make install

6. 编译android64 server
mkdir build-android
export ANDROID_NDK_ROOT=/home/softsim/Android/Sdk/ndk/25.2.9519653
../frida/configure –host=android-arm64 –prefix /home/softsim/frida-android64
make
make install

7. 编译linux_x64 server
mkdir build-x64
cd build_x64
../frida/configure –host=linux-x86_64 –prefix /home/softsim/frida_x64
make
make install

Qualcomm Baseband Research

https://blog.rickmark.me/untitled/

sbl1.mbn – Secondary Bootloader (after ROM PBL)
qdsp6sw.mbn – Qualcomm Hexagon Digital Signal Processor (non-ARM core)
tz.mbn – Qualcomm TrustZone Implementation – QSEE
hyp.mbn – Qualcomm Hypervisor Execution Environment – QHEE – EL2
xbl_cfg.mbn – For XBL (eXtensible Boot Loader) or EFI based SPL signed static data
restoresbl1.mbn – Secondary program loader (bootloader) for baseband recovery
acdb.mbn – Accessory Calibration Database (seems to be initial)
apps.mbn – Userland baseband applications
rpm.mbn – Rollback prevention manager
wdt.mbn – Watchdog Timer
mba.mbn – QURT – Qualcomm Realtime OS Kernel image

MBN Signature Format

Contains a C struct styled header, followed by hashes, a signature and a certificate chain.

MBNs are ill-designed because the ELF header contains the offset to the signature region, which signs the ELF header
creating a circular dependency.

Header Region

// Likely depends on hash type - samples found stated PK algorithm scep384r1 having a signature size of 384 - deterministic noncing?
// does this lead to a potential leak of private key with double nonce values?
typedef struct {
  char* hash[HASH_TYPE_SIZE]; // Unfortuantly they used all zeros to encode an empty region instead of hash of zeros...
                              // This seems to always be true of the signature area (b01) but also of other regions?
} mbn_hash_row_t;

typedef enum {
  kSHA2_384 = 0x06;
} mbn_hash_type_t;

typedef struct {
  uint32_t hash_rows;         // Number of hash rows - samples with 0 have hashes but no signature... and 0xFFFFFFFF for
                              // pk_hash.  It also has hash rows, perhaps its a problem via multiple verification paths?
  mbn_hash_type_t hash_type;  // 6 - SHA2-384?
  uint32_t = 0
  uint32_t = 0
  uint32_t hash_and_signature_size; // Little endian - data following header and extra
  uint32_t hash_size; // size in bytes of hash type row size * rows - signature follows
  uint32_t pk_hash_one? = 0xFFFFFFFF / 0xA803708F
  uint32_t signature_size; // Size of ASN.1 signature following hash list
  uint32_t pk_hash_two? = 0xFFFFFFFF / 0xA803708F // Usually matches pk_hash_one
  uint32_t some_size;  // Some header item size or possibly align value?
  uint32_t = 0;
  uint32_t extra_size; // Seems to be 0x78 bytes long... 64bit extension?
  char* extra[extra_size];
  mbn_hash_row_t hashes[hash_rows];
} mbn_header_t;

typedef struct {

rust字节数组

array和slice的概念

数组(array)是一组拥有相同类型 T 的对象的集合,在内存中是连续存储的。数组使用中括号 [] 来创建,且它们的大小在编译时会被确定。数组的类型标记为 [T; length]T 为元素类型,length 表示数组大小)。

切片(slice)类型和数组类似,但其大小在编译时是不确定的。相反,切片是一个双字对象(two-word object),第一个字是一个指向数据的指针,第二个字是切片的长度。这个 “字” 的宽度和 usize 相同,由处理器架构决定,比如在 x86-64 平台上就是 64 位。slice 可以用来借用数组的一部分。slice 的类型标记为 &[T]

字节数组的几种类型:

(一)      [u8; N](固定大小的字节数组)
这是一个固定长度的数组,长度 N 在编译时确定。
存储在栈上(stack),如果数组过大可能导致栈溢出。
拥有数据的所有权。
大小无法在运行时改变, 如果需要改变,只能释放了再新建。
访问时可以使用 & 获取切片(&[u8])

(二)   &[u8](字节切片 引用)
动态大小,存储在堆上或栈上,通常是借用(reference)。
可通过 as_ref()、deref() 或 slice::from_ref() 从数组转换。
它是一个对字节序列的不可变引用,可以指向[u8; N] 或Vec<u8> 等的一部分
不拥有数据,仅借用;长度在运行时确定。

切片本身 不拥有 任何数据。它只是指向已存在字节数据的指针和长度。数据的所有权属于切片所引用的原始数据。
切片的大小在运行时确定,由其指向的数据的长度决定。

(三)   &mut [u8](可变字节切片)
和 &[u8] 类似,但允许修改数据

(四)   Vec(动态字节数组)
可变长,存储在堆上(heap)。
适用于需要增长或缩短的字节数据
拥有数据的所有权,可以动态调整大小
允许 push、resize、extend 等操作。
通过 push, pop, insert, remove 等方法进行增删元素

(五)  Box<[u8]>(堆分配的字节数组)
固定长度的堆分配数组,与 Vec 类似但不可变长。
适用于数据不需要增长的情况,但仍希望存储在堆上。
let boxed: Box<[u8]> = vec![1, 2, 3, 4].into_boxed_slice();
拥有所有权,在堆上分配,大小固定但可以在运行时创建

(六)   Cow<[u8]>(惰性克隆的字节数组)
Cow(Copy on Write,写时复制)适用于既可能是借用的(&[u8])也可能是拥有的(Vec)数据。
适用于优化性能,避免不必要的复制,只有在需要修改时才会克隆数据。

(七) [u8] 未定大小的字节切片
这是一个抽象的字节切片类型,通常在泛型或 trait 定义中使用。
无法直接实例化,仅作为类型参数出现。
是一个视图(view)而非拥有数据的类型

(八) String和&str
用于文本字符串,以UTF-8编码的字节。本质上也是字节序列。
String拥有数据, &str是一个切片引用。

在某些情况下,你可能会将 String 或 &str 视为字节数组来处理,例如在处理 ASCII 文本或进行底层字符串操作时。但要注意,它们强制 UTF-8 编码,可能不适合处理任意二进制数据。

转换:
[u8; N] 可以转换为 &[u8] 或 &mut [u8](用 &符号 借用)
&[u8] 可以转换为 Vec( 用.to_vec()复制为Vec<u8>)
Vec 可以转换为 Box<[u8]>(比如上面的介绍的into_boxed_slice())
&[u8] 或 Vec 可以转换为 Cow<[u8]>
Vec<u8> 可以通过 .as_slice() 转为 &[u8]
&[u8] 转化 &[u8; N] , 用 try_into()

注意事项:
&[u8] 和 Box<[u8]> 是不可变的(除非使用内部可变性如 Cell 或 RefCell)
&[u8] 和 &mut [u8] 长度由引用的数据决定

sim卡引用选择文件

文件标识符(FID)用来定位和识别一个特定的文件。
同一父目录下的任何两个文件都不应具有相同的FID
当前目录的子文件或者子目录,当前目录的父目录, 当前目录的兄弟目录,这三者不能有相同的FID
短文件标识符 (SFI) 编码为 5 位,取值范围为 1 至 30。同一父级下的任何两个文件都不应具有相同的 SFI。
保留的 FID–7FFF可用作给定逻辑通道上当前活动应用程序的 ADF 的 FID。
DF除了有FID外,还可以有名字,名字就是AID, AID在每张卡上都应该唯一。 AID的长度,从1到16个字节。
——
在 UICC 激活和发送ATR给读卡器或者modem后,主文件 (MF) 被隐式选择,并成为当前目录。
(1) 通过FID应用来选择文件或者目录
选择 DF、ADF 或 MF, 都会改变当前目录, 当前EF为空,也就是不存在当前EF
选择EF, 会改变当前EF,但不会当前目录, 目录还是之前的目录,并且这个目录就是当前EF的父目录。

通过文件FID, 可以选择
[1]当前目录的直接子级的任何EF文件
[2]当前 DF 父级的直接子级的任何DF, 也就是当前目录任何兄弟DF
[3]当前目录的父级
[4]当前 DF
[5]当前活动应用的ADF
[6]MF, 也就是3F00
(2)通过 路径 来选择
路径 有3种模式:
[1] 从MF开始
[2] 从ADF开始
[3] 从当前DF开始
MF模式下,终端不得在路径开头使用 MF 的文件标识(即3F00)。
当前DF模式下,终端不得在路径开头使用特殊文件 ID–7FFF。
MF模式或者当前DF模式,终端不得使用当前 DF 的文件标识, 也不得使用空数据字段(也就空选择)。

———-
通过使用 AID 显式选择应用,就可以激活应用,并把选择的应用的 ADF 设置为当前 ADF。
当前 ADF 可以通过 FID 引用,隐式引用值为 7FFF

一个可以被选择的应用,可以通过部分DF名称模式选择,此时应该将P1 设置为 ’04’。部分选择的情况下,DF名称被右截断。
如果卡上有多个应用,它们的AID的开头的内容相同,如果采用部分DF名称模式,具体应该选择那个AID,应取决于P2值。如果P2的值表示”last”,那么选择的应该是与部分 DF 名称匹配的最后一个活动应用。

对于单应用卡,使用部分 DF 名称选择应用是可选的,但多应用卡应支持 部分DF名称选择模式。卡应该在ATR 历史字节的”卡服务数据”和”卡功能”压缩 TLV 对象中指示对此功能的支持.

如果 UICC 不支持使用部分 DF 名称进行选择,则 UICC 应以适当的响应进行响应(例如,不支持命令参数6A86)。
—————–
P1
00 通过FID选择DF, EF 或者 MF
01 选择当前DF下的子DF
03 当前DF的父DF
04 通过DF的名称(也就是AID)来选择
08 通过从MF开始的路径 来选择
09 通过从当前DF开始的路径来选择

如果 P1 = ’00’ 且数据字段为空,则应将 P2 设置为 ‘0C’(“无数据返回”)。然后,MF 被设置为当前目录。

为避免 P1 = ’00’ 时出现歧义,在选择以文件 ID (FID) 为参数的文件时,应遵循以下搜索顺序:
[1]当前 DF 的直接子文件
[2] 父 DF
[3] 父 DF 的直接子文件

当 P1 ≠ ’04’ 时,P2 的位 b2 和 b1 没有意义,应设置为 0。
当 P1 = ’04’(即通过 AID 选择)时,可以在数据字段中指定右截断 AID。

P2=04, 要求返回FCP模板
P2=0C, 不要求返回数据

rust在中国大陆的快速安装途径

之前用的是双不限流量卡,访问国外网络还比较快

换成宽带之后,访问rust网站明显变慢。

rust官方介绍的安装方法

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

几乎无法安装。

改成如下命令即可

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rust.sh
sed -i 's|static.rust-lang.org|mirrors.ustc.edu.cn/rust-static|g' rust.sh
export RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup
chmod a+x rust.sh
./rust.sh
安装完成后
echo "RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup"  >> ~/.cargo/env