作者归档:softsim

NfcB读取身份证UID

先发送05 00 00询卡命令后,身份证返回12字节卡类型代码,不同身份证返回的数据不一样。比如对两个身份证发送询卡命令后分别返回
50 00 00 00 00 d1 03 86 0c 00 80 80

50 00 00 00 00 d1 03 86 07 00 80 90

询卡成功后,发送SELECT命令:1d 00 00 00 00 00 08 01 08 对二代身份证进行选卡操作。
这时身份证返回的第一个字节应该等于SELECT命令的最后一个字节08,根据这个条件来判断SELECT命令是否操作成功。

最后发送GUID命令:00 36 00 00 08,身份证会返回10字节数据,其中前8字节就是身份证的唯一ID,后两字节是90 00(SW1 SW2)。
=================================
二代证是应答标准的REQB指令的,但Attrib指令是非标的,
二代证的唯一UID可以通过下面的步骤简单读取,
1. PCD发送REQB命令: 05 00 00 二代证收到后会发送ATQB(12 bytes)应答。
例:50 00 00 00 00 d1 03 86 0c 00 80 80
2.PCD发送非标Attrib命令
1d 00 00 00 00 00 08 01 08
二代证返回应答:08 //这里返回08不能认为是错误的,ISO14443标准返回为00

3.PCD发送读取UID命令
00 36 00 00 08 二代证返回应答:xx xx xx xx xx xx xx xx 90 00

Sent bits: 05 00 00
Received bits: 50 00 00 00 00 d1 03 86 0c 00 80 80
Sent bits: 1d 00 00 00 00 00 08 01 08
Received bits: 08
Sent bits: 00 36 00 00 08
Received bits: xx xx xx xx xx xx xx xx 90 00
===========================
读二代证的 寄存器配置信息
WriteRegPN532(CIU_MODE, 0xFF);
WriteRegPN532(CIU_TXAUTO, 0x00);
WriteRegPN532(CIU_TXMODE, 0x03);
WriteRegPN532(CIU_RXMODE, 0x03);
WriteRegPN532(CIU_TYPEB, 0x03);
WriteRegPN532(CIU_DEMOD, 0x4D);
WriteRegPN532(CIU_GSNON, 0xFF);
WriteRegPN532(CIU_CWGSP, 0x3F);
WriteRegPN532(CIU_MODGSP, 0x18);
WriteRegPN532(CIU_RXTHRESHOLD, 0x4D);
WriteRegPN532(CIU_MODWIDTH, 0x68);
WriteRegPN532(CIU_MANUALRCV,0x10);

发送命令 后面2字节是CRC校验
05 00 00 71 ff
1d 00 00 00 00 00 08 01 08 f3 10
00 36 00 00 08 57 44
============================
由于二代证的ATTRIB操作非标,所以不能直接用PN532提供的InListPassiveTarget等上层指令来选卡,
只能靠自己设置寄存器,并通过InCommunicateThru底层通讯,发送ATQB和ATTRIB,完成选卡操作。
—————————-
二代证的ATQB信息里会指明使用了非标准协议,所以 通常的标准type B协议不改造可能就会拒读二代证(类似于目前的普通读写器,只有REQB和ATQB的交互,因为得知卡片后续采用专有协议,而读写器如果不支持,就会停止进一步交互或者重复发送REQB),所以CLF的代码必须要修改以支持读取二代证的。
非接触式前端(CLF)

身份证阅读器通信协议

数据输入格式
Preamble (5个字节,AA AA AA 96 69, 前3个字节用于 帧同步, 后面2个字节,表示帧开始)
数据长度 (2个字节,高位在前,低位在后, 数据长度是指 本帧之后的所有有效数据的长度,CMD+Para+Data+CHKSUM)
CMD (命令,1个字节)
Para (参数,1个字节)
Data (数据,n个字节)
CHK_SUM (校验和,1个字节, 数据帧中除帧头和校验和之外的数据逐字节按位异或的结果)


数据输出格式:
Preamble (5个字节,跟输入格式中的一样,也是 AA AA AA 96 69)
数据长度 (2个字节,高位在前,低位在后, 有效数据=SW1+SW2+SW3+Data+CHK_Sum)
SW1,SW2(各1字节, 阅读器转发证卡 回应的APDU中的SW1,SW2)
SW3 (1字节, 阅读器自身的状态码)

说明命令CMD参数Para
复位10FF
状态检测11FF
读SAM模块编号12FF
寻找身份证2001
选取身份证2002
读取基本信息3001
读取最新追加信息3003
设置串口速率115200 bps6000
设置串口速率57600bps6001
设置串口速率38400bps6002
设置串口速率19200bps6003
设置串口速率9600bps6004
命令集

状态

SW1 SW2 SW3Data说明
00 00 90和具体命令有关,可能为空操作成功
00 00 9F卡的IIN返回找卡成功信息
00 00 10接收业务终端数据的校验和错误
00 00 11接收业务终端数据的长度错误
00 00 21接收业务终端的命令错误,包括命令中的各种数值或者逻辑搭配错误
00 00 24无法识别的错误
XX XX 31身份证对阅读器认证失败
00 00 32阅读器对身份证认证失败
00 00 33信息验证错误
00 00 40错误的卡
XX XX 41读身份证失败
XX Xx 47取随机数失败
00 00 60阅读器自检失败,不能接收任何命令
00 00 66阅读器未经过授权,无法使用
00 00 80 搜寻身份证失败
XX XX 81选取身份证失败
00 00 91没有这项内容

读取基本信息的响应

aaaaaa9669 (Preamble)
0508 (长度)
00 00 90 (SW1 SW2 SW3)

data(长度为1284字节的数据, 1284+1+3=1288=0x508)

chksum

data的内容

0100 (文字信息的长度, 256字节)
0400 (照片的长度)

姓名 (30个字节), 也就是说姓名最长的为15个字,不足的补空格

性别(2个字节) 男=1, 女=0

民族 (4个字节)

出生日期(16个字节)

住址(70个字节,最多35个字,不足的补空格)

身份证号(36字节)

签发机关(30个字节,最多15个字,不足的补空格)

有效起始 (16个字节)

有效结束( 16个字节, 年满46周岁的,此处为汉字”长期”, 补空格)

长度36的备用字节, 用空格填充


校验和算法:

def calculate_xor_checksum(message):
    checksum = 0
    for e in message:
        checksum ^= e
    return checksum

TCP_NODELAY与包合并

python需要设置

setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

只有有数据,就会发送出去
不会等到凑到 最大传输单元再发

asyncio的socket选项 TCP_NODELAY 在python 3.6后就被默认设置

https://stackoverflow.com/questions/50790579/prevent-msg-concatenation-with-asyncio-sockets

并行任务concurrent.futures和Coroutines

import time
from concurrent.futures import ProcessPoolExecutor

def print_hello():
    while True:
        print('sleep 1')
        time.sleep(1) 


def print_goodbye():
    while True:
        print('sleep 2')
        time.sleep(2) 
        
with ProcessPoolExecutor(max_workers=2) as e:
     e.submit(print_hello)
     e.submit(print_goodbye)

哪个更好

import asyncio

async def print_hello():
    while True:
        print('sleep 1')
        await asyncio.sleep(1) 


async def print_goodbye():
    while True:
        print('sleep 2')
        await asyncio.sleep(2) 


co1 = print_hello()  
co2 = print_goodbye()  
asyncio.get_event_loop().run_until_complete(asyncio.gather(co1, co2))   

asyncio.run(main()) 比 asyncio.get_event_loop().run_until_complete 更简洁

蓝牙rfcomm

rfcomm的使用,类似tcp, 也有port的概念,在rfcomm中,称为channel, channel可以从1到30
————-
L2CAP, 默认最大包是 672, 但可以协商到65535
在L2CAP中, port 被称作psm(Protocol Service Multiplexers), 从1到32767的奇数。
——————
通过 Service Discovery Protocol 来寻找端口号

Linux下蓝牙工具

$   hciconfig 
hci0:	Type: Primary  Bus: USB
	BD Address: E1:E2:E3:E7:E8:91  ACL MTU: 1021:4  SCO MTU: 96:6
	UP RUNNING PSCAN ISCAN 
	RX bytes:16759 acl:20 sco:0 events:2538 errors:0
	TX bytes:605245 acl:20 sco:0 commands:2498 errors:0

关闭 
hciconfig hci0 down
开启
hciconfig hci0 up
查看设备名称
 hciconfig hci0 name
hci0:	Type: Primary  Bus: USB
	BD Address: E1:E2:E3:E7:E8:91  ACL MTU: 1021:4  SCO MTU: 96:6
	Name: 'radio'

这笔记本上确实是usb的蓝牙适配器

$  lsusb
Bus 001 Device 004: ID 8087:0a2b Intel Corp. Bluetooth wireless interface

hcitool用来搜索设备,或者显示底层的连接

hcitool scan
...
hcitool con

sdptool用来搜索并浏览 SDP服务, 或者配置本机的SDP

 $ sdptool browse  88:BB:99:11:22:33

Browsing 88:BB:99:11:22:33 ...

Service RecHandle: 0x10001
Service Class ID List:
  "PnP Information" (0x1200)
Protocol Descriptor List:
  "L2CAP" (0x0100)
    PSM: 1
  "SDP" (0x0001)
Language Base Attr List:
  code_ISO639: 0x656e
  encoding:    0x6a
  base_offset: 0x100
Profile Descriptor List:
  "PnP Information" (0x1200)
    Version: 0x0100

Failed to connect to SDP server on 88:BB:99:11:22:33 Connection refused
Service Name: SerialPort
Service RecHandle: 0x10007
Service Class ID List:
  "Serial Port" (0x1101)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 6
Language Base Attr List:
  code_ISO639: 0x656e
  encoding:    0x6a
  base_offset: 0x100
Profile Descriptor List:
  "Serial Port" (0x1101)
    Version: 0x0100

而 hcidump 则类似于 tcpdump
l2ping类似于ping

l2ping  88:BB:99:11:22:33
Ping: 88:BB:99:11:22:33 from E1:E2:E3:E7:E8:91 (data size 44) ...
0 bytes from 88:1B:99:16:F6:AD id 0 time 7.11ms
0 bytes from 88:1B:99:16:F6:AD id 1 time 5.95ms
0 bytes from 88:1B:99:16:F6:AD id 2 time 4.80ms
0 bytes from 88:1B:99:16:F6:AD id 3 time 4.66ms

发送 echo 包到另一台设备
echo包是一个特殊类型的 LCAP包

rfcomm命令

rfcomm bind /dev/rfcomm0 88:BB:99:11:22:33 6

将channel 6 绑定到 /dev/rfcomm0 上,形成一个虚拟 serial port

rfcomm bind 6 98:D3:31:FC:73:2D

将channel 6 绑定到 /dev/rfcomm6上

或者不绑定,直接连接

rfcomm connect /dev/rfcomm0 88:BB:99:11:22:33 6

在 Python 3.3 之后 socket,直接支持蓝牙

import socket
import binascii

macAddr = '88:BB:99:11:22:33'
port = 6

s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
s.connect((macAddr, port))

s.send(binascii.unhexlify('1200AAAAAA9669000B0600FFF20000000000')) 
data = s.recv(4)
print(data)

如果不支持AF_BLUETOOTH, 也可以用串口, 但是必须 先绑定正确的channel

import serial

DEVICE = '/dev/rfcomm6'
BAUD_RATE = 115200

s = serial.Serial(DEVICE, BAUD_RATE)
print('Connect to', DEVICE)

# Send data
s.write(b'hello\n')

# Receive data
data = s.read(3)
print(data)

RSA参数

大质数 p, q 并且p!=q
N=p*q
欧拉函数 φ(n) = r =(p-1)*(q-1) 表示 在小于等于n的正整数之中,有多少个与n构成互质关系

选择一个整数e, e小于r, e与r互质
令e*d= 1%r 也就是  e*d – 1 = k * φ(n)
(N,e)是公钥
(N,d)是私钥
———————
N 称为 public modulus
e 称为 public exponent
d 称为 private exponent
—————
p 称为 secrete prime factor, prime1
q 称为 secrete prime factor, prime2
—–
dmp1 d mod (p-1), exponent1
dmq1 d mod (q-1), exponent2
iqmp q^-1 mod p, coefficient, (inverse of q) mod p

_________________
DER编码

30  TAG->Sequence
81  长模式长度
89  长度
      02  TAG -->Integer
      81
      81 
      00 C8 38 4C C5 17 6B D5 80 97 01 B6 37 59 0A EC 3E 5A A5 7E 81 DE 69 6E 5E 09 D5 39 42 01 07 EA A7 A1 C1 18 40 96 72 89 2C 9F 49 3C EE F8 7E 00 EE 73 45 E5 08 EA 80 EE EB 59 4B 6F 96 3E 8D 27 C7 11 BE 4B B9 EB 38 37 71 CB 80 3A B8 D5 F3 6B A8 A7 09 29 C4 44 D1 78 DA 8A DE 70 28 D4 40 D7 10 A2 AB C6 9D 91 AE E2 1D 1B 89 EA 59 55 96 6F BA 43 79 00 E2 38 BC E0 0D 79 D7 4A C7 79 B8 7D 89 

      02 
      03  短模式长度
      01 00 01

私:

30 
82 
02 5E 
        02  (整数)
        01 
           00   (表示版本0)
       
        02   (整数)
        81 
        81    (n)
            00 E9 EA 93 B3 58 D9 54 2C B4 C7 AC E8 56 EA 39 38 F0 C8 1C 2C D5 C9 E3 C2 B2 74 12 E3 B5 35 FE A2 06 FD 6C 3F 9F B0 D0 6F 16 96 46 3E 30 E9 72 6C 1B E5 4F F7 A4 B8 83 60 40 01 B6 D3 E3 64 F4 94 15 46 AD 1A 84 A3 12 5A 0B 5D B0 A4 51 5D FA 9C 60 BA 40 C5 52 49 E9 33 15 CA 7C FD 86 97 7A 09 C4 7D 1B 24 3A 91 C4 17 B9 15 67 0E 5A 32 68 CD 3F 47 E7 A7 A9 CE F1 6F FB 1D 22 AE B3 6B E2 2B 
       
        02 
        03    (e)
            01 00 01 
       
        02 
        81 
        81     (d)
           00 86 7F C6 0B A2 B3 DB ED 94 C9 17 47 C4 EA 0A 9B 0D FF D2 0B 89 A5 FB 5B 30 E7 03 11 81 59 99 9F 2A AB BE F9 5A 65 FD 83 52 96 77 AF 3C CA 8E 6B 51 22 EA 77 37 74 8C BD E4 2F D8 23 3F A6 05 E6 A2 66 B0 DA 19 07 0E E7 33 2F 43 79 7C 7D 6F 39 01 62 CC D9 6F A6 6D 55 B2 97 9B 0B 9E B1 AF 56 2B 2C B4 05 A4 2F 52 69 D7 E0 8E D2 EB 25 FE F6 71 7D FE CB 05 58 99 9D 69 00 E6 9D A6 77 1F 61 
           
        02 
        41   (p)
           00 F5 9F 70 10 A3 C7 D7 8A BC D8 1E 8F FB 16 D7 33 E5 3E B0 E8 17 5F DD 9F FF CE AC FB 20 9F AF 5E A1 6A 79 8D E7 78 8E CA 31 91 D2 F3 6A B1 0A 46 03 24 11 EE 90 DA 36 89 3E 9E 97 8F F9 A1 7C 3B 
           
        02 
        41    (q)
           00 F3 CC 86 BC 6E DF AC 53 E1 AE 02 08 0D F9 9C D8 93 F7 C2 68 04 AB 40 BB A3 09 0A BE 9B 8B DB 2A 05 D5 98 D0 7E FD DA 7C 6C 4A 25 F0 37 D7 E0 8A 20 38 F2 79 1B C1 EA 34 59 16 E9 34 DA 07 02 D1 
           
        02 
        40   (dmp1)
           42 03 BE 0C DA D8 05 38 46 95 17 71 BF 1C 5E 6E 96 38 89 37 77 C0 CE 42 08 70 B5 D1 30 ED 7A 26 C6 DF 65 18 1C 94 2C 5C 6F 82 2F DE 38 58 A8 D8 34 53 E7 75 E4 FC A1 17 B5 FA CD E5 66 44 5E AD 
        
        02 
        41     (dmq1)
           00 C7 9F AA 9B 54 35 B5 87 21 7D 37 90 9C D0 CC 4A D2 2D C6 73 21 8F 8F FB 93 ED 6D 6C D1 34 5D 0A 35 A8 C5 ED C2 6D DD 9E 8F 94 CF 27 B8 B1 FC DB 5C B3 04 67 82 00 EF E2 59 28 57 5F BD 3E 8C 61 
           
        02 
        41     (iqmp)
           00 C9 19 A5 48 F4 BE AD 9A 8C 66 07 C6 86 B4 7A A2 89 E3 76 FA 16 AB A8 0F 57 2F B3 12 5B E0 7D 48 C7 8E B2 DA 6E 40 3F E9 57 71 5E D6 19 C9 D1 28 29 8F 44 99 58 98 D9 DC 5B 0D 65 0B B5 A4 3E 6C


Cryptodome生成

from Cryptodome.PublicKey import RSA

rsa = RSA.generate(1024)
    
PrivKey = rsa.exportKey('DER')
PubKey =  rsa.publickey().exportKey('DER')

输出的DER公钥,实际为X.509规范

SEQUENCE {
	SEQUENCE {
		OBJECT IDENTIFIER
		rsaEncryption (1 2 840 113549 1 1 1)
		NULL
	}
	BIT STRING, encapsulates {
		SEQUENCE {
			INTEGER
			INTEGER 65537
		}
	}
}

实际

30  (SEQUENCE序列 标签)
81  (长度扩展)
9f   (长度)

    30  (序列 标签)
         0d   (长度) 
                 06 (OBJECT IDENTIFIER标签) 09 (长度) 2a864886f70d010101 ( 表示此密钥用于rsa加密)
                 05  (NULL标签)   00 (长度)  NULL
    
    03 (位串 BIT STRING 标签)
    81   (长度扩展)
    8d   (长度)
   00   (bit string未用到的个数, 或者叫填充bit,因为需要按8bit对齐, 不足8bit的, 需要在末尾补0)

        30 (序列 标签)
        81 (长度扩展) 89
         02 (整数标签) 81 (长度扩展) 81    (长度)00d0d0ceefca36275fd389d1602d0794d5ce3e1f93a731751e8e38698e6679ceba145300b8e2276ccdb029e4c83918dd7e6aa8ef54425a3e8ee24c81d542cc0b8d4052f0d3b274b2c8eb3c917169ba5c18ef07c0aac3ae5bc26c732f6e1f5ad0540085343b0cc363dd4c462523c3bd377169a13d7f8166f17f53a2dbe7d4ef703f
         02 (整数标签)03 (长度)         010001

如果生成PKCS#1的公钥语法 RSA Public Key Syntax

    RSAPublicKey ::= SEQUENCE {
      modulus           INTEGER,  -- n
      publicExponent    INTEGER   -- e 
    }

也就是,只有bit string的内容
———————-

ASN.1编码规则

基本编码规则(BER,Basic Encoding Rules)
规范编码规则(CER,Canonical Encoding Rules)
唯一编码规则(DER,Distinguished Encoding Rules)
压缩编码规则(PER,Packed Encoding Rules)
XML编码规则(XER,XML Encoding Rules)

Basic Encoding Rules (BER)是一种自描述的ASN.1数据结构的二进制编码格式。BER编码可以通过一种或多种方式把任意ASN.1值表示成字节字符串(这就增加了一定的不确定性?)。每一个编码后的BER数据依次由数据类型标识(Type identifier),长度描述(Length description), 实际数据(actual Value)排列而成,即BER是一种二进制TLV编码。TLV编码的一个好处,是数据的解析者不需要读取完整的数据,仅从一个不完整的数据流就可以开始解析。

Distinguished Encoding Rules (DER)是BER的子集,主要是消除了BER的一些不确定性的编码规则。DER 用于需要使用唯一的octet string编码的应用程序,例如根据一个ASN.1编码来计算数字签名。

DER在BER规则基础上增加了如下限制:

如果长度在 0 - 127 之间,必须使用短型长度表示法。
如果长度大于等于 128,必须使用长型长度表示法,并且长度必须使用尽可能少的字节表示。
对于简单 string 类型和在其基础上使用隐式标签生成的类型,必须使用简单定长编码方法。
对于结构化类型和在其基础上使用隐式标签生成的类型及在任何类型基础上使用显式标签生成的类型,必须使用结构化定长编码方法。

如在BER中Boolean类型true的value字节,可以为任何小于255大于0的整数,而在DER中,value字节只能为255。DER的这种确定性,保证了一个ASN.1数据结构,在编码为DER后,只会有一种正确的结果。这使得DER更适合用在数字签名领域,比如X.509中广泛使用了DER。

————
PKCS#1 私钥定义

    RSAPrivateKey ::= SEQUENCE {
      version           Version,
      modulus           INTEGER,  -- n
      publicExponent    INTEGER,  -- e
      privateExponent   INTEGER,  -- d
      prime1            INTEGER,  -- p
      prime2            INTEGER,  -- q
      exponent1         INTEGER,  -- d mod (p-1)
      exponent2         INTEGER,  -- d mod (q-1)
      coefficient       INTEGER,  -- (inverse of q) mod p
      otherPrimeInfos   OtherPrimeInfos OPTIONAL
    }

frida java StackTrace

Java.performNow(function(){
        var target = Java.use("com.pacakge.myClass")
        var threadef = Java.use('java.lang.Thread')
        var threadinstance = ThreadDef.$new()

        function Where(stack){
            var at = ""
            for(var i = 0; i < stack.length; ++i){
                at += stack[i].toString() + "\n"
            }
            return at
        }

        target.foo.overload("java.lang.String").implementation = function(obfuscated_str){
            var ret = this.foo(obfuscated_str)
            var stack = threadinstance.currentThread().getStackTrace()
            var full_call_stack = Where(stack)
            send("Deobfuscated " + ret + " @ " + stack[3].toString() + "\n\t Full call stack:" + full_call_stack) 
            return ret
        }
})

第2种

var jAndroidLog = Java.use("android.util.Log");
var jException = Java.use("java.lang.Exception");
console.log( jAndroidLog.getStackTraceString( jException.$new() ) );

nfc读取身份证

——-选择文件————EF-ID为6002— 身份证内部序列号, DN码(可能是在户籍部门数据库的主键)—————-
00 a4 00 00 02 6002
9000
80 b0 00 00 20
00 01 02 .. 1c 1d 1e 1f 9000
———-读芯片序列号——- (居民身份证专用集成电路序列号)
00 36 00 00 08
10 e4 10 01 02 03 04 05 9000

————-内部(卡片)认证: 阅读器 验证卡的合法性—————-
00 88 00 52 0a f0007860cd45b884b424 (10字节认证数据)
3e 9a 27 e1 8b 40 b0 b6 (8字节认证响应) 9000
—————获取随机数———————
00 84 00 00 08
43 55 e5 fd a5 11 97 d4 9000
—————-外部(阅读器)认证: 卡片 验证阅读器的合法性———
00 82 00 52 0a f0017658026cf003d446
9000
————选择文件 EF-ID 为 6011—- 或者 6017
00 a4 00 00 02 6011
9000
80 b0 00 00 d6
….
————–
00 a4 00 00 02 6012 或者 6018
9000
80 b0 00 00 e6
80 b0 00 e6 1a
————– 相片数据———————–
00 a4 00 00 02 6013 或者 6019
9000
80 b0 00 00 e6
80 b0 00 e6 e6
80 b0 01 cc e6
80 b0 02 b2 e6
80 b0 03 98 68
—————–指纹数据———————–
00 a4 00 00 02 6021
80b00000e6

===========================

aa aa aa 96 69 00 04 00 00 41 45 表示读取解码失败
00 04 长度: 后续自己的长度为4
00 00 41 应答码: 读身份证操作失败
45 (xor checksum)
—————
aa aa aa 96 69 00 04 00 00 32 36
00 04 长度
00 00 32 应答码: SAM对卡的认证失败, 00 00 31 卡对SAM的认证失败
36 (xor checksum)
—————
身份证阅读器接口使用说明
公安部第一研究所证件技术事业部

avbctl

cat /proc/sys/fs/verity/require_signatures
1

表示开启了fs验证

cat /vendor/etc/fstab.qcom

system      /system   ext4    ro,barrier=1,discard                                 wait,slotselect,avb=vbmeta_system,logical,first_stage_mount,avb_keys=/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey
--------------------------------------------------------------
system_ext  /system_ext   ext4    ro,barrier=1,discard                                 wait,slotselect,avb=vbmeta_system,logical,first_stage_mount
--------------------------------------------------------------
product    /product       ext4    ro,barrier=1,discard                                 wait,slotselect,avb=vbmeta_system,logical,first_stage_mount
--------------------------------------------------------------

进入twrp执行关闭

avbctl --force disable-verification
avbctl --force disable-verity
disable-verity
avbctl disable-verification

修改super.img

0.将Android sparse image格式转换为raw image

simg2img super.img   super.img.raw

1.提取 system分区
有两个工具,一个是Android项目中的 lpunpack, 另一个大神的imjtool

lpunpack --partition=system super.img.raw
lpunpack --partition=vendor super.img.raw
lpunpack --partition=product super.img.raw
提取所有
lpunpack super.img.raw

imgtool的命令为

./imjtool.ELF64 super.img.raw extract
MMapped: 0x7f460c400000, imgMeta 0x7f460c401000
liblp dynamic partition (super.img) - Blocksize 0x1000, 3 slots
LP MD Header @0x3000, version 10.2, with 10 logical partitions on block device of 8704 GB, at partition super, first sector: 0x800
Partitions @0x3100 in 3 groups:
	Group 0: default
	Group 1: qti_dynamic_partitions_a
		Name: odm_a (read-only, Linux Ext2/3/4/? Filesystem Image, @0x100000 spanning 1 extents of 1 MB) - extracted
		Name: product_a (read-only, Linux Ext2/3/4/? Filesystem Image, @0x300000 spanning 1 extents of 474 MB) - extracted
		Name: system_a (read-only, Linux Ext2/3/4/? Filesystem Image, @0x1de00000 spanning 1 extents of 5 GB) - extracted
		Name: system_ext_a (read-only, Linux Ext2/3/4/? Filesystem Image, @0x15f600000 spanning 1 extents of 473 MB) - extracted
		Name: vendor_a (read-only, Linux Ext2/3/4/? Filesystem Image, @0x17d000000 spanning 1 extents of 2 GB) - extracted
	Group 2: qti_dynamic_partitions_b
		Name: odm_b (read-only,  empty) - extracted
		Name: product_b (read-only,  empty) - extracted
		Name: system_b (read-only,  empty) - extracted
		Name: system_ext_b (read-only,  empty) - extracted
		Name: vendor_b (read-only,  empty) - extracted


如果遇到错误,请用sudo执行

2. 修改system.img为可写

fallocate -l 2G system.img
/sbin/resize2fs system.img 2G

看实际的system.img的大小,适当大一些
6G

3.0 移除共享块
如果报错 couldn’t mount RDWR because of unsupported optional features (4000)
4000特性就是 EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS

可以用下面的命令 去掉

e2fsck -y -E unshare_blocks  system.img

也可以检查是否有这个feature

/sbin/dumpe2fs vendor_a.img 
...
Filesystem features:      ext_attr dir_index .... extra_isize shared_blocks

3.1 挂载

mount -t ext4 -o loop system.img system
如果是EROFS 只读文件系统
mount -t erofs  -o loop system.ext4.img /mnt

4. 编辑 system目录下的文件

...

5. 卸载 system 目录

umount system

6.修改文件系统错误

e2fsck -yf system.img

7. 让system.img占用尽可能小的空间

resize2fs -M system.img
e2fsck -yf system.img

8. 查看真实分区大小

stat -c '%n %s' system.img

stat -c '%n %s' *
product.img 1596944384
system.img 1128718336
vendor.img 544976896

8.写回

lpmake --metadata-size 65536 --super-name super --metadata-slots 1 
--device super:4294967296 
--group main:3139354624 
--partition system:readonly:1128718336:main --image system=./system.img 
--partition vendor:readonly:544976896:main --image vendor=./vendor.img 
--partition product:readonly:1596944384:main --image product=./product.img 
--sparse --output ./super.new.img

group这里是所有分区的文件大小加起来的和

–metadata-slots 要跟 imjtool 工具输出的一样
实际, device-size设置为8G

lpmake --metadata-size 65536    --device-size=8589934592   --metadata-slots=3   
--group=qti_dynamic_partitions_a:8053952512   
--partition=odm_a:none:1470464:qti_dynamic_partitions_a   
--partition=product_a:none:407822336:qti_dynamic_partitions_a   
--partition=system_a:none:4835311616:qti_dynamic_partitions_a  
--partition=system_ext_a:none:496226304:qti_dynamic_partitions_a   
--partition=vendor_a:none:2313121792:qti_dynamic_partitions_a   
--image=odm_a=./odm_a.img  --image=product_a=./product_a.img  --image=system_a=./system_a.img  --image=system_ext_a=./system_ext_a.img   --image=vendor_a=./vendor_a.img   
--group=qti_dynamic_partitions_b:0  
--partition=odm_b:none:0:qti_dynamic_partitions_b  
--partition=product_b:none:0:qti_dynamic_partitions_b  
--partition=system_b:none:0:qti_dynamic_partitions_b  
--partition=system_ext_b:none:0:qti_dynamic_partitions_b  
--partition=vendor_b:none:0:qti_dynamic_partitions_b  
--image=odm_b=./odm_b.img  --image=product_b=./product_b.img  --image=system_b=./system_b.img  --image=system_ext_b=./system_ext_b.img  --image=vendor_b=./vendor_b.img  
--sparse  --output /media/3/tmp/super.new.img

lpmake I 10-14 19:06:49 11155 11155 builder.cpp:1031] [liblp]Partition odm_a will resize from 0 bytes to 1470464 bytes
lpmake I 10-14 19:06:49 11155 11155 builder.cpp:1031] [liblp]Partition product_a will resize from 0 bytes to 407822336 bytes
lpmake I 10-14 19:06:49 11155 11155 builder.cpp:1031] [liblp]Partition system_a will resize from 0 bytes to 4835311616 bytes
lpmake I 10-14 19:06:49 11155 11155 builder.cpp:1031] [liblp]Partition system_ext_a will resize from 0 bytes to 496226304 bytes
lpmake I 10-14 19:06:49 11155 11155 builder.cpp:1031] [liblp]Partition vendor_a will resize from 0 bytes to 2313121792 bytes
Invalid sparse file format at header magic
Invalid sparse file format at header magic
Invalid sparse file format at header magic
Invalid sparse file format at header magic
Invalid sparse file format at header magic
Invalid sparse file format at header
Invalid sparse file format at header
Invalid sparse file format at header
Invalid sparse file format at header
Invalid sparse file format at header
lpmake --metadata-size 65536\
 --device-size=4294967296\
 --metadata-slots=3\
 --group=google_system_dynamic_partitions_a:2222931968\
 --partition=odm_a:none:700416:google_system_dynamic_partitions_a\
 --partition=product_a:none:266579968:google_system_dynamic_partitions_a\
 --partition=system_a:none:1363767296:google_system_dynamic_partitions_a\
 --partition=system_ext_a:none:359391232:google_system_dynamic_partitions_a\
 --partition=vendor_a:none:232493056:google_system_dynamic_partitions_a\
 --image=odm_a=./odm_a.img\
 --image=product_a=./product_a.img\
 --image=system_a=./system_a.img\
 --image=system_ext_a=./system_ext_a.img\
 --image=vendor_a=./vendor_a.img\
 --group=google_system_dynamic_partitions_b:24563712\
 --partition=odm_b:none:0:google_system_dynamic_partitions_b\
 --partition=product_b:none:0:google_system_dynamic_partitions_b\
 --partition=system_b:none:24563712:google_system_dynamic_partitions_b\
 --partition=system_ext_b:none:0:google_system_dynamic_partitions_b\
 --partition=vendor_b:none:0:google_system_dynamic_partitions_b\
 --image=odm_b=./odm_b.img\
 --image=product_b=./product_b.img\
 --image=system_b=./system_b.img\
 --image=system_ext_b=./system_ext_b.img\
 --image=vendor_b=./vendor_b.img\
 --sparse \
 --output ./super.new.img

参考资料:
https://forum.xda-developers.com/t/editing-system-img-inside-super-img-and-flashing-our-modifications.4196625/
https://blog.senyuuri.info/posts/2022-04-27-patching-android-super-images/