惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

W
WeLiveSecurity
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog
The Register - Security
The Register - Security
Stack Overflow Blog
Stack Overflow Blog
博客园 - 三生石上(FineUI控件)
T
Threat Research - Cisco Blogs
S
SegmentFault 最新的问题
V2EX - 技术
V2EX - 技术
Hacker News: Ask HN
Hacker News: Ask HN
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
P
Proofpoint News Feed
J
Java Code Geeks
Microsoft Security Blog
Microsoft Security Blog
M
MIT News - Artificial intelligence
AI
AI
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Proofpoint News Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
B
Blog
N
News and Events Feed by Topic
N
News | PayPal Newsroom
Google DeepMind News
Google DeepMind News
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
WordPress大学
WordPress大学
C
Cybersecurity and Infrastructure Security Agency CISA
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 【当耐特】
U
Unit 42
腾讯CDC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Cloudflare Blog
H
Help Net Security
Recent Announcements
Recent Announcements
P
Privacy & Cybersecurity Law Blog
IT之家
IT之家
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Security Archives - TechRepublic
Security Archives - TechRepublic
L
LINUX DO - 热门话题
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
H
Heimdal Security Blog
博客园 - 聂微东
S
Securelist
大猫的无限游戏
大猫的无限游戏
Cloudbric
Cloudbric
Cisco Talos Blog
Cisco Talos Blog

鱼雨昱

以彼之矛-攻彼之盾:通过伪造运行环境无损解压RedBendEFDPackage固件包 松下 Let's Note RZ5 侧边无线物理开关在Linux下的复活指南 全站升级:正式全面启用 HTTP/3(QUIC)和IPv6 日机拾贝之富士通Felica锁定的强制解除 索尼电脑恢复镜像中MOD文件的处理 Yuu Web Synth Engine Web Office Toolbox(WOT) 基于决策树和线性回归模型以优化深度优先搜索(DFS)性能 PyPianoCatSongDataExtractor
新sdat2img
2024-09-26 · via 鱼雨昱

概要

在Android的FOTA更新包中,因为现在大家都去用payload.bin了,所以旧的Block更新的Sparse Image转Raw Image工具就没人更新了
之前留意到Brotli压缩版本的还有人写工具,但是最近发现在一些低性能电视盒子上使用的是lzma压缩方式就没有人在意了。
所以把这整个工具重构了一遍,使其更加好用且性能更好。

库安装

pip3 install Brotli

代码

#!/usr/bin/env python3
import sys
import os
import lzma
import brotli
from pathlib import Path
from typing import List, Tuple, BinaryIO

BLOCK_SIZE = 4096

def isTransferList(file_path: str, sample_size: int = 1024) -> bool:
    try:
        with open(file_path, 'rb') as f:
            return b'\0' not in f.read(sample_size)
    except IOError:
        return False

def decompressLzmaDat(input_file: str, output_file: str):
    with lzma.open(input_file, 'rb') as lzma_file:
        with open(output_file, 'wb') as decompressed_file:
            decompressed_file.write(lzma_file.read())

def decompressBrotliDat(input_file: str, output_file: str):
    with open(input_file, 'rb') as br_file:
        with open(output_file, 'wb') as decompressed_file:
            decompressed_file.write(brotli.decompress(br_file.read()))

def rangeSet(src: str) -> List[Tuple[int, int]]:
    src_set = [int(item) for item in src.split(',')]
    if len(src_set) != src_set[0] + 1:
        raise ValueError(f'Error parsing data to rangeSet: {src}')
    return [(src_set[i], src_set[i+1]) for i in range(1, len(src_set), 2)]

def parseTransferList(path: str) -> Tuple[int, int, List]:
    if not isTransferList(path):
        raise ValueError(f"The file '{path}' does not appear to be a valid transfer list file.")

    with open(path, 'r') as trans_list:
        version = int(trans_list.readline())
        new_blocks = int(trans_list.readline())

        if version >= 2:
            trans_list.readline()
            trans_list.readline()

        commands = []
        for line in trans_list:
            cmd, *params = line.split()
            if cmd in ['erase', 'new', 'zero']:
                commands.append([cmd, rangeSet(params[0])])
            elif not cmd[0].isdigit():
                raise ValueError(f'Command "{cmd}" is not valid.')

    return version, new_blocks, commands

def processFile(new_data_file: BinaryIO, output_img: BinaryIO, commands: List, max_file_size: int):
    for command in commands:
        if command[0] == 'new':
            for block in command[1]:
                begin, end = block
                block_count = end - begin
                print(f'Copying {block_count} blocks into position {begin}...')

                output_img.seek(begin * BLOCK_SIZE)
                output_img.write(new_data_file.read(block_count * BLOCK_SIZE))
        else:
            print(f'Skipping command {command[0]}...')

    if output_img.tell() < max_file_size:
        output_img.truncate(max_file_size)

def main(transfer_list_file: str, new_data_file: str, output_image_file: str):

    version, new_blocks, commands = parseTransferList(transfer_list_file)

    android_versions = {
        1: 'Android 5.0',
        2: 'Android 5.1',
        3: 'Android 6.0',
        4: 'Android 7.0 or Higher'
    }
    print(f'{android_versions.get(version, "Unknown")} Version Image Detected')

    output_img_path = Path(output_image_file)
    if output_img_path.exists():
        raise FileExistsError(f'Output file "{output_img_path}" already exists')

    decompressed_file = None
    if 'lzma' in new_data_file.lower():
        print("LZMA file detected. Decompressing...")
        decompressed_file = new_data_file + '.decompressed'
        decompressLzmaDat(new_data_file, decompressed_file)
        new_data_file = decompressed_file
        print("Decompression Completed!")
    elif new_data_file.lower().endswith('.br'):
        print("Brotli file detected. Decompressing...")
        decompressed_file = new_data_file + '.decompressed'
        decompressBrotliDat(new_data_file, decompressed_file)
        new_data_file = decompressed_file
        print("Decompression Completed!")

    with open(new_data_file, 'rb') as new_data, output_img_path.open('wb') as output_img:
        all_block_sets = [i for command in commands for i in command[1]]
        max_file_size = max(pair[1] for pair in all_block_sets) * BLOCK_SIZE

        processFile(new_data, output_img, commands, max_file_size)

    print(f'Done! Output image: {output_img_path.resolve()}')

    if decompressed_file:
        os.remove(decompressed_file)
        print("Temporary decompressed file removed.")

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print('Usage: sdat2img_v2.py <transfer.list> <dat|dat.lzma|dat.br> [raw.img]')
        print('<transfer.list>:Transfer List File')
        print('<dat|dat.lzma|dat.br>:New Dat File (Support Uncompressed, LZMA or Brotli)')
        print('[raw.img]:Output File Name of Raw Image\n')
        sys.exit(1)

    transfer_list_file = sys.argv[1]
    new_data_file = sys.argv[2]
    
    if len(sys.argv) > 3:
        output_image_file = sys.argv[3]
    else:
        base_name = os.path.basename(sys.argv[1]).split('.')[0]
        output_image_file = f"{base_name}.raw.img"

    try:
        main(transfer_list_file, new_data_file, output_image_file)
    except Exception as e:
        print(f"An error occurred: {e}", file=sys.stderr)
        sys.exit(1)

可执行文件

For Windows x64