关键词:鸿蒙系统、固件提取、系统分析、逆向工程、底层研究、嵌入式系统、设备固件
摘要:本文深入探讨鸿蒙系统固件的提取与分析技术,系统讲解从固件获取到底层结构解析的完整流程。通过剖析鸿蒙系统的固件架构、启动流程及文件系统特性,结合实际案例演示固件解包、镜像解析和代码审计的关键技术。文章涵盖核心概念、数学原理、实战操作及工具推荐,为嵌入式系统开发者、安全研究员和系统逆向工程师提供系统化的底层研究方法,助力理解鸿蒙系统的底层实现机制与设备固件安全防护体系。
随着物联网设备的普及和鸿蒙生态的快速发展,对鸿蒙系统固件的深入研究成为嵌入式开发、系统优化和安全审计的重要需求。本文聚焦鸿蒙系统固件的全生命周期分析,包括固件获取、解包提取、镜像解析、代码审计及文件系统还原等核心环节,揭示鸿蒙系统在嵌入式设备中的底层运行机制。
研究范围覆盖主流鸿蒙设备(如智能手表、路由器、智能家居终端)的固件结构,重点分析Bootloader、内核镜像(HarmonyOS Kernel)、文件系统(HFS+或自定义文件系统)及硬件抽象层(HALS)的交互逻辑,为读者建立从二进制固件到系统架构的逆向分析能力。
缩略词 | 全称 |
---|---|
HOS | HarmonyOS(鸿蒙系统) |
ELF | Executable and Linkable Format(可执行链接格式) |
GPT | GUID Partition Table(全局唯一标识分区表) |
LZ4 | 高效压缩算法(Lossless Compression Algorithm) |
RSA | Rivest-Shamir-Adleman(非对称加密算法) |
鸿蒙固件遵循"硬件抽象-内核-服务-应用"的分层设计,其核心组件包括:
graph TD
A[硬件层] --> B[Bootloader]
B --> C[内核镜像 (kernel.img)]
C --> D[硬件抽象层 (hals.so)]
D --> E[文件系统 (rootfs)]
E --> F[系统服务 (services)]
F --> G[应用层 (apps)]
style A fill:#f9f,stroke:#333
style B fill:#a9f,stroke:#333
style C fill:#9cf,stroke:#333
style D fill:#9ff,stroke:#333
style E fill:#cff,stroke:#333
style F fill:#f9f,stroke:#333
style G fill:#fff9c,stroke:#333
bootloader.bin
(二进制镜像)、partition_table.bin
(分区描述文件)kernel_type
字段标识kernel
)dtb
文件,描述硬件配置)cmdline
,如内存分配、调试开关)rootfs/
├─ bin/ # 可执行程序
├─ sbin/ # 系统管理工具
├─ lib/ # 动态链接库
├─ etc/ # 配置文件
├─ dev/ # 设备节点
└─ sys/ # 内核参数接口(sysfs)
graph TB
subgraph 启动阶段
1[电源开启] --> 2[Bootloader加载]
2 --> 3[验证分区表签名]
3 --> 4[加载kernel.img到内存]
4 --> 5[解析设备树配置硬件]
5 --> 6[挂载rootfs文件系统]
6 --> 7[启动init进程(PID=1)]
end
subgraph 运行阶段
7 --> 8[启动系统服务(samgr)]
8 --> 9[加载硬件抽象层驱动]
9 --> 10[启动应用框架(AbilityRuntime)]
10 --> 11[用户应用启动]
end
鸿蒙固件常采用定制化封装格式,典型结构如下:
[文件头(512字节)] + [分区表(N个分区描述)] + [各分区数据(按偏移量存储)]
import struct
class FirmwareHeader:
FORMAT = "<4sIIII" # 小端序,4字节魔数,4个32位整数
MAGIC = b"HOSF" # 鸿蒙固件魔数
def __init__(self, data):
self.magic, self.version, self.total_size, self.header_size, self.partition_count = \
struct.unpack(self.FORMAT, data[:struct.calcsize(self.FORMAT)])
if self.magic != self.MAGIC:
raise ValueError("Not a valid HarmonyOS firmware file")
# 解析示例
with open("firmware.bin", "rb") as f:
header_data = f.read(512)
header = FirmwareHeader(header_data)
print(f"Firmware version: {header.version}")
print(f"Partition count: {header.partition_count}")
每个分区包含以下字段(二进制格式):
LZ4算法通过匹配滑动窗口内的重复字节序列,将数据压缩为"令牌(Token)+ 匹配数据"的格式。解压缩时,根据令牌中的长度和偏移量还原原始数据。
import lz4.frame
def decompress_lz4(data):
try:
return lz4.frame.decompress(data)
except lz4.frame.LZ4Error as e:
raise RuntimeError(f"LZ4 decompression failed: {e}")
# 使用示例
compressed_data = firmware_data[offset:offset+size]
decompressed_data = decompress_lz4(compressed_data)
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
def verify_signature(public_key_path, data, signature):
with open(public_key_path, "rb") as f:
public_key = serialization.load_pem_public_key(f.read())
try:
public_key.verify(
signature,
data,
padding.PKCS1v15(),
hashes.SHA256()
)
return True
except:
return False
# 验证分区数据签名
partition_data = firmware_data[offset:offset+size]
signature = firmware_data[signature_offset:signature_offset+256] # RSA-2048签名长度
valid = verify_signature("vendor_public.pem", partition_data, signature)
设固件文件起始地址为 ( 0 ),分区表起始地址为 ( H )(header_size),每个分区描述占用 ( P ) 字节(通常为64字节),则第 ( i ) 个分区的描述地址为:
a d d r i = H + i × P addr_i = H + i \times P addri=H+i×P
分区数据起始地址为分区描述中的偏移量 ( offset_i ),数据大小为 ( size_i ),因此分区数据在固件中的范围是:
[ o f f s e t i , o f f s e t i + s i z e i ) [offset_i, offset_i + size_i) [offseti,offseti+sizei)
以Ext4文件系统为例,inode节点包含文件元数据(权限、大小、数据块指针等),数学表示为:
i n o d e = { m o d e , u i d , g i d , s i z e , b l o c k s , b l o c k p t r [ 0..15 ] } inode = \{ mode, uid, gid, size, blocks, block_ptr[0..15] \} inode={mode,uid,gid,size,blocks,blockptr[0..15]}
其中,block_ptr
前12个直接指向数据块,第13个指向间接块,第14个指向双间接块,第15个指向三间接块。
def traverse_filesystem(root_inode, inode_table, block_device):
stack = [root_inode]
while stack:
inode = stack.pop()
for dirent in parse_dirent(inode, inode_table, block_device):
if dirent.name == "." or dirent.name == "..":
continue
if dirent.is_directory():
child_inode = inode_table[dirent.inode_number]
stack.append(child_inode)
else:
yield dirent.name, dirent.inode_number
router_fw.bin
)工具名称 | 功能 | 安装命令(Linux) |
---|---|---|
binwalk | 固件分析与解包 | sudo apt install binwalk |
dd | 磁盘镜像操作 | 系统自带 |
hexdump | 十六进制查看 | 系统自带 |
Python库 | 解析开发 | pip install lz4 cryptography |
IDA Pro | 二进制反汇编 | 商业软件(需自行获取) |
import struct
from firmware_header import FirmwareHeader
from partition import Partition
from decompressor import decompress_data
from sign_verifier import verify_signature
def unpack_firmware(firmware_path, output_dir):
with open(firmware_path, "rb") as f:
firmware_data = f.read()
# 解析文件头
header = FirmwareHeader(firmware_data[:512])
# 解析分区表
partition_offset = header.header_size
partition_format = "<16sIIBII" # 分区描述格式
partition_size = struct.calcsize(partition_format)
for i in range(header.partition_count):
part_data = firmware_data[partition_offset + i*partition_size : partition_offset + (i+1)*partition_size]
name, offset, size, compress_type, sig_offset, sig_size = struct.unpack(partition_format, part_data)
name = name.decode().strip('\x00')
# 提取分区数据
raw_data = firmware_data[offset:offset+size]
if compress_type == 1:
raw_data = decompress_data(raw_data, "lz4")
elif compress_type == 2:
raw_data = decompress_data(raw_data, "zip")
# 验证签名(假设公钥路径已知)
signature = firmware_data[sig_offset:sig_offset+sig_size]
valid = verify_signature("public_key.pem", raw_data, signature)
print(f"Partition {name} signature: {'VALID' if valid else 'INVALID'}")
# 保存分区文件
output_path = f"{output_dir}/{name}.img"
with open(output_path, "wb") as f_out:
f_out.write(raw_data)
print(f"Extracted {name} to {output_path} ({len(raw_data)} bytes)")
if __name__ == "__main__":
import sys
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} " )
sys.exit(1)
unpack_firmware(sys.argv[1], sys.argv[2])
使用IDA Pro打开kernel.img
,定位关键函数:
kernel_init()
:内核初始化入口,检查是否存在缓冲区溢出漏洞drivers_init()
:设备驱动加载函数,审计指针校验逻辑// 可疑代码片段(示例)
void drivers_init(char* driver_path) {
char buffer[256];
strcpy(buffer, driver_path); // 未检查输入长度,存在溢出风险
load_driver(buffer);
}
/etc/passwd
是否存在弱密码账户/sbin/httpd
等网络服务二进制文件,查找未授权访问接口/dev
目录下设备节点的权限设置,防止越权访问dtb
)作为模板A:加密固件通常包含硬件绑定的密钥,需通过以下途径获取:
A:可能原因包括:
losetup
+mount -t
手动挂载-o loop
选项处理镜像文件A:
通过系统化的固件提取与分析,我们能够深入理解鸿蒙系统在嵌入式设备中的底层实现,这不仅为设备开发提供优化方向,也为构建安全可靠的物联网生态奠定技术基础。随着鸿蒙生态的持续演进,固件分析技术将成为连接硬件与软件、保障系统安全的核心能力。