鸿蒙5解包

鸿蒙5的系统包UPDATE.APP用的还是老版本安卓包的结构,但是现有的工具好像不能识别分区名,分块的分区数据导出也有问题,参考YKG/huawei_UPDATE.APP_unpacktool的解包工具重新实现了下。
写好的代码放在GitHub了。

系统包的文件头首先是92字节的0x00,接着是每个分区的数据块,数据块开头是55AA5AA5,数据块结构如下。

偏移 大小 数据
0x0 4 Magic 55AA5AA5
0x4 4 数据头长度
0x8 4 Magic 0x00000001
0xC 8 硬件ID
0x14 4 序号 (应该是废弃了)
0x18 4 分区数据长度
0x1C 16 日期
0x2C 16 时间
0x3C 16 分区名
0x4C 16 0x00
0x5C 2 头部自定义CRC16
0x5E 2 0x1000 数据块计算CRC16时每块的大小
0x60 2 0x00
0x62 数据头长度-0x62 数据块CRC16表 (按上面定义的块大小分块计算)
数据头长度 分区数据长度 分区数据
数据头长度+分区数据长度 pad将分区数据对其到4字节

按数据块结构循环解析到EOF即可,需要注意的是如果记录中存在相同的分区名,数据要append到前一个已经导出的分区数据后面。老版本的工具无法解包主要就是没有处理这一步。

用ImHex写了个Pattern。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ImHex Pattern for UPDATE.APP

import std.mem;
import type.magic;

char ZeroPadding[92] @ 0x00;
struct BlockHeader {
type::Magic<"\x55\xAA\x5A\xA5"> [[name("Signature")]];
u32 headerLength [[name("Header Length")]];
type::Magic<"\x01\x00\x00\x00"> [[name("Magic Number")]];
char hardwareId[8] [[name("Hardware ID")]];
u32 sequence [[name("Sequence out of use?")]];
u32 dataLength [[name("Partition Data Length")]];
char date[16] [[name("Date")]];
char time[16] [[name("Time")]];
char partitionName[16] [[name("Partition Name")]];
char blank1[16] [[name("Blank1 16 bytes 0x00")]];
char headerChecksum[2] [[name("Header Checksum")]];
u16 chunkSize [[name("Data chunk size for CRC")]];
char blank2[2] [[name("Blank 2 bytes 0x00")]];
char checksum[headerLength - 98] [[name("Data Checksum")]];
};

struct Block {
BlockHeader blockHeader;
char data[blockHeader.dataLength];
char Padding[(4 - ($ % 4)) % 4];
} [[name(blockHeader.partitionName)]];

Block block[while($ < std::mem::size())] @ $;

忘了从XDA哪个上古贴子翻出来的算法了,算出来是对的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class UpdateCrc16:
"""
Customized CRC16
"""
def __init__(self, initial_sum=0xFFFF, polynomial=0x8408, xor_value=0xFFFF):
self._polynomial = polynomial
self._xor_value = xor_value
self._initial_sum = initial_sum
self._table = self._initialize_table()

def _initialize_table(self):

table = [0] * 256
for i in range(256):
value = 0
temp = i
for _ in range(8):
if ((value ^ temp) & 0x0001) != 0:
value = (value >> 1) ^ self._polynomial
else:
value >>= 1
temp >>= 1
table[i] = value
return table

def compute_sum(self, mv: memoryview) -> int:

current_sum = self._initial_sum
for byte in mv:
current_sum = ((current_sum >> 8) ^ self._table[(current_sum ^ byte) & 0xFF]) & 0xFFFF
final_sum = (current_sum ^ self._xor_value) & 0xFFFF

return final_sum

鸿蒙6解包

最近更新了鸿蒙6,发现包结构变了,现在用的是OpenHarmony_L2的结构,解包工具代码可以直接从openharmony的仓库找到。

包结构现代了不少,基本用TLV结构记录数据,校验值现在直接用了SHA-256。写了下ImHex的Pattern方便分析。代码同样放在GitHub了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// ImHex Pattern for update.bin

#include <std/string.pat>

#define UPGRADE_COMPINFO_SIZE_L2 87
#define COMPONENT_ADDR_SIZE_L2 32

enum HeaderTLVType : u16 {
header_tlv_type_not_l2 = 0x11,
header_tlv_type_l2 = 1,
};

enum ComponentType : u8 {
IMG = 0,
ZIP = 1,
UNKNOWN = 0xFF
};

struct ComponentInfo {
char component_name[COMPONENT_ADDR_SIZE_L2];
u16 component_id;
u8 component_res_type;
u8 component_flag;
ComponentType component_type;
char component_version[10];
u32 component_size;
u32 component_original_size;
char digest[32]; // sha256 of component
};

struct FileHeader {
HeaderTLVType header_tlv_type; // phone should always be openharmony_l2 0x1
u16 upgrade_pkg_header_size;
u32 pkg_info_length;
u32 update_file_version;
char product_update_id[64];
char software_version[64];
u16 time_tlv_type; // time type is 2
u16 upgrade_pkg_time_size;
char date[16];
char time[16];
u16 compinfo_tlv_type; // compinfo type is 5
u16 compinfo_len;
u8 component_count = compinfo_len / UPGRADE_COMPINFO_SIZE_L2;
ComponentInfo component_infos[component_count];
};

struct HashInfoHeader {
u16 sign_tlv_type; // sign_tlv_type is 8
u32 signdata_len;
char signdata[signdata_len];
};

FileHeader file_header @ 0x0;
char describe_package_id[16] @ $;
HashInfoHeader hash_info_header @ $;

u8 chunk_index;

struct ChunkData {
char chunk_data[file_header.component_infos[chunk_index].component_size] [[name(std::string::to_string(file_header.component_infos[chunk_index].component_name))]];
chunk_index += 1;
};

ChunkData chunk_datas[file_header.component_count] @ $;