美文网首页Armbian
bootimg.py,能编辑boot.img,recovery.

bootimg.py,能编辑boot.img,recovery.

作者: Armlinux | 来源:发表于2020-12-31 11:00 被阅读0次

完全转自https://www.52pojie.cn/thread-558371-1-1.html
这是一个存在已久的的东西,也懒得去翻原出处了,见谅
原本是某大神搞的一个python下的命令行脚本,依赖于Python,好像2.7和3.5都行。
不想每次敲命令搞了个简单的powershell脚本,类似于bat批处理文件,实际比bat强大太多,有待进一步学习
用法:
1.把下面的代码复制到ultraedit分别保存为2个文件bootimg.ps1和bootimg.py,后面需要操作的boot.img和recovery.img也放这个目录下。
2.Python官网下载安装Python,建议下载这个解压可用的https://www.python.org/ftp/python/3.5.2/python-3.5.2-embed-amd64.zip
3.用记事本之类的打开bootimg.ps1文件,将第一行$pypath="d:\python3"的路径改成你自己的python安装路径
4.运行bootimg.ps1就可以了,自行解决文件关联的问题,win10下可以右键使用powershell执行,可能会出现脚本执行权限的问题,自行查找巨硬官方文档解决。
其实这个py脚本还有很多其他的功能只是我不太习惯在这个脚本里使用这些功能,但是这个编辑boot和recovery的很好用!

#!/usr/bin/env python
#fileencoding: utf-8
#Author: Liu DongMiao <[email]liudongmiao@gmail.com[/email]>
#Created  : Sun 17 Oct 2010 11:19:58 AM CST
#Modified : Fri 04 Nov 2016 10:12:42 PM CST
 
import os
import sys
import mmap
import json
import struct
from stat import *
from hashlib import sha1
 
latin = lambda x: x.encode('latin')
 
def write_bootimg(output, kernel, ramdisk, second, dtimg,
        name, cmdline, kernel_addr, ramdisk_addr, second_addr, tags_addr, page_size, padding_size, os_version):
    ''' make C8600-compatible bootimg.
        output: file object
        kernel, ramdisk, second: file object or string
        name, cmdline: string
        base, page_size, padding_size: integer size
 
        official document:
        https://android.googlesource.com/platform/system/core/+/master/mkbootimg/bootimg.h
 
        Note: padding_size is not equal to page_size in HuaWei C8600
    '''
    if not isinstance(page_size, int):
        page_size = 0x800
 
    if not isinstance(padding_size, int):
        padding_size = 0x800
 
    if not hasattr(output, 'write'):
        output = sys.stdout
 
    padding = lambda x: struct.pack('%ds' % ((~x + 1) & (padding_size - 1)), latin(''))
 
    def getsize(x):
        if x is None:
            return 0
        assert hasattr(x, 'seek')
        assert hasattr(x, 'tell')
        x.seek(0, 2)
        return x.tell()
 
    def writecontent(output, x):
        if x is None:
            # sha.update('')
            sha.update(struct.pack('<I', 0))
            return None
 
        assert hasattr(x, 'read')
 
        x.seek(0, 0)
        content = x.read()
        sha.update(content)
        sha.update(struct.pack('<I', len(content)))
        output.write(content)
        output.write(padding(x.tell()))
 
        if hasattr(x, 'close'):
            x.close()
 
    output.write(struct.pack('<8s10I16s512s', latin('ANDROID!'),
        getsize(kernel), kernel_addr,
        getsize(ramdisk), ramdisk_addr,
        getsize(second), second_addr,
        tags_addr, page_size, getsize(dtimg), os_version,
        name, cmdline))
 
    idpos = output.tell()
    # fill with null first
    output.write(struct.pack('32s', latin('')))
    output.write(padding(output.tell()))
    sha = sha1()
    writecontent(output, kernel)
    writecontent(output, ramdisk)
    writecontent(output, second)
    writecontent(output, dtimg)
    # fill back sha1
    output.seek(idpos, 0)
    output.write(sha.digest())
    if hasattr('output', 'close'):
        output.close()
 
def parse_bootimg(bootimg):
    ''' parse C8600-compatible bootimg.
        write kernel to kernel[.gz]
        write ramdisk to ramdisk[.gz]
        write second to second[.gz]
        write dtimg to dt.img
        write extra to unknown
 
        official document:
        https://android.googlesource.com/platform/system/core/+/master/mkbootimg/bootimg.h
 
        Note: padding_size is not equal to page_size in HuaWei C8600
    '''
 
    (   magic,
        kernel_size, kernel_addr,
        ramdisk_size, ramdisk_addr,
        second_size, second_addr,
        tags_addr, page_size, dt_size, os_version,
        name, cmdline, id4x8
    ) = struct.unpack('<8s10I16s512s32s', bootimg.read(608))
    bootimg.seek(page_size - 608, 1)
 
    base = kernel_addr - 0x00008000
    assert magic.decode('latin') == 'ANDROID!', 'invald bootimg'
    # assert base == ramdisk_addr - 0x01000000, 'invalid bootimg'
    # assert base == second_addr - 0x00f00000, 'invalid bootimg'
    # assert base == tags_addr - 0x00000100, 'invalid bootimg'
 
    def say(v):
        b7 = 127
        b4 = 15
        a = (v >> 25) & b7
        b = (v >> 18) & b7
        c = (v >> 11) & b7
        y = ((v >>  4) & b7) + 2000
        m = v & b4
        return '%d.%d.%d %s-%s' % (a, b, c, y, m)
    sys.stderr.write('kernel_addr=0x%x\n' % kernel_addr)
    sys.stderr.write('ramdisk_addr=0x%x\n' % ramdisk_addr)
    sys.stderr.write('second_addr=0x%x\n' % second_addr)
    sys.stderr.write('tags_addr=0x%x\n' % tags_addr)
    # sys.stderr.write('base=0x%x\n' % base)
    sys.stderr.write('page_size=%d\n' % page_size)
    sys.stderr.write('os_version=0x%08x(%s)\n' % (os_version, say(os_version)))
    sys.stderr.write('name="%s"\n' % name.decode('latin').strip('\x00'))
    sys.stderr.write('cmdline="%s"\n' % cmdline.decode('latin').strip('\x00'))
 
    while True:
        if bootimg.read(page_size) == struct.pack('%ds' % page_size, latin('')):
            continue
        bootimg.seek(-page_size, 1)
        size = bootimg.tell()
        break
 
    padding = lambda x: (~x + 1) & (size - 1)
    sys.stderr.write('padding_size=%d\n' % size)
    metadata = {
        'kernel_addr': kernel_addr,
        'ramdisk_addr': ramdisk_addr,
        'second_addr': second_addr,
        'tags_addr': tags_addr,
        'page_size': page_size,
        'name': name.decode('latin').strip('\x00'),
        'cmdline': cmdline.decode('latin').strip('\x00'),
        'padding_size': size,
        'os_version': os_version,
    }
    w = open('bootimg.json', 'w')
    w.write(json.dumps(metadata))
    w.close()
 
    gzname = lambda x: x == struct.pack('3B', 0x1f, 0x8b, 0x08) and '.gz' or ''
 
    kernel = bootimg.read(kernel_size)
    output = open('kernel%s' % gzname(kernel[:3]) , 'wb')
    magic = struct.pack('>I', 0xd00dfeed)
    pos = kernel.find(magic)
    if pos > 0:
        output.write(kernel[:pos])
        kerneldt = open('kernel%s.dt' % gzname(kernel[:3]) , 'wb')
        kerneldt.write(kernel[pos:])
        kerneldt.close()
    else:
        output.write(kernel)
    output.close()
    bootimg.seek(padding(kernel_size), 1)
 
    ramdisk = bootimg.read(ramdisk_size)
    output = open('ramdisk%s' % gzname(ramdisk[:3]) , 'wb')
    output.write(ramdisk)
    output.close()
    bootimg.seek(padding(ramdisk_size), 1)
 
    if second_size:
        second = bootimg.read(second_size)
        output = open('second%s' % gzname(second[:3]) , 'wb')
        output.write(second)
        output.close()
        bootimg.seek(padding(second_size), 1)
 
    if dt_size:
        dtimg = bootimg.read(dt_size)
        output = open('dt.img', 'wb')
        output.write(dtimg)
        output.close()
        bootimg.seek(padding(dt_size), 1)
 
    unknown = bootimg.read()
    if unknown:
        output = open('unknown', 'wb')
        output.write(unknown)
        output.close()
    elif os.path.exists('unknown'):
        os.unlink('unknown')
 
    bootimg.close()
 
# CRC CCITT
crc_ccitt_table = []
for crc in range(0, 256):
    for x in range(0, 8):
        if crc & 0x1:
            crc = (crc >> 1) ^ 0x8408
        else:
            crc >>= 1
    crc_ccitt_table.append(crc)
 
def crc_ccitt(data, crc=0xffff):
    for item in data:
        crc = (crc >> 8) ^ crc_ccitt_table[crc & 0xff ^ item]
    return crc
 
def get_crc_ccitt(data):
    crc = crc_ccitt(data) ^ 0xffff
    return struct.pack('<H', crc)
 
POSITION = {0x30000000: 'boot.img',
            0x40000000: 'system.img',
            0x50000000: 'userdata.img',
            0x60000000: 'recovery.img',
            0xf2000000: 'splash.565',}
def parse_updata(updata, debug=False):
    ''' parse C8600 UPDATA binary.
        if debug is true or 1 or yes, write content to [position], else according POSITION
 
        UPDATA.APP Structure (only guess)
        magic                   |       0x55 0xaa 0x5a 0xa5
        header_length           |       unsigned int
        tag1                    |       0x01 0x00 0x00 0x00
        boardname               |       char[8]
        position                |       unsigned int
        content_length          |       unsigned int
        date                    |       char[16] -> YYYY.MM.DD
        time                    |       char[16] -> hh.mm.ss
        INPUT                   |       char[16] -> INPUT
        null                    |       char[16]
        crc                     |       crc-ccitt for header (98bit)
        tag2                    |       0x00 0x10 0x00 0x00
        header                  |       crc-ccitt for every 4096 of content
        content                 |
        padding                 |       padding to 4 bytes
    '''
 
    updatalist = open('updatalist.txt', 'w')
    while True:
        data = updata.read(4)
        if not data:
            break
        if data == struct.pack('4s', latin('')):
            continue
 
        data += updata.read(94)
        assert len(data) == 98, 'invalid updata'
        (   magic,
            header_length,
            tag1,       # \x01\x00\x00\x00
            boardname,
            position,
            content_length,
            date,
            time,
            INPUT,
            null,
            crc,
            tag2,       # \x00\x10\x00\x00
        ) = struct.unpack('<4sI4s8sII16s16s16s16s2s4s', data)
 
        magic, = struct.unpack('!I', magic)
        assert magic == 0x55aa5aa5, 'invalid updata %x' % magic
 
        header_header = list(struct.unpack('98B', data))
        header_header[-5] = header_header[-6] = 0
        assert crc == get_crc_ccitt(header_header)
 
        open('boardname.bin', 'wb').write(boardname)
        open('date.txt', 'wb').write(date)
        open('time.txt', 'wb').write(time)
 
        padding = (~(header_length + content_length) + 1) & 3
 
        remain = header_length - 98
        header = list(struct.unpack('%dB' % remain, updata.read(remain)))
 
        output = open(POSITION.get(position, '0x%x.raw' % position), 'wb')
        sys.stderr.write('%s\t0x%x\n' % (output.name, position))
        updatalist.write('%s\t0x%x\n' % (output.name, position))
 
        remain = content_length
        while remain > 0:
            size = remain > 4096 and 4096 or remain
            data = updata.read(size)
            if debug:
                check = list(struct.unpack('%dB' % size, data))
                check.append(header.pop(0))
                check.append(header.pop(0))
                assert crc_ccitt(check) == 0xf0b8
            output.write(data)
            remain -= size
        output.close()
 
        updata.seek(padding, 1)
 
    updata.close()
    updatalist.close()
 
def write_updata(output):
    '''
        magic                   |       0x55 0xaa 0x5a 0xa5
        header_length           |       unsigned int
        tag1                    |       0x01 0x00 0x00 0x00
        boardname               |       char[8]
        position                |       unsigned int
        content_length          |       unsigned int
        date                    |       char[16] -> YYYY.MM.DD
        time                    |       char[16] -> hh.mm.ss
        INPUT                   |       char[16] -> INPUT
        null                    |       char[16]
        crc                     |       crc-ccitt for header (98bit)
        tag2                    |       0x00 0x10 0x00 0x00
        header                  |       crc-ccitt for every 4096 of content
        content                 |
        padding                 |       padding to 4 bytes
    '''
    from time import strftime
 
    output.write(struct.pack('1s', latin('')) * 92)
    updatalist = open('updatalist.txt', 'r')
    boardname = open('boardname.bin', 'rb').read()
    if os.path.isfile('date.txt'):
        date = open('date.txt').read()
    else:
        date = strftime('%Y.%m.%d')
    if os.path.isfile('time.txt'):
        time = open('time.txt').read()
    else:
        time = strftime('%H.%M.%S')
    for record in updatalist:
        name, position = record.split()[:2]
        data = open(name, 'rb')
        header = latin('')
        content_length = 0
        while True:
            raw4096 = data.read(4096)
            content_length += len(raw4096)
            if not raw4096:
                break
            header += get_crc_ccitt(list(struct.unpack('%dB' % len(raw4096), raw4096)))
        header_length = 98
        header_length += len(header)
        data.close()
 
        header_header = struct.pack('<4sI4s8sII16s16s16s16s2s4s',
                latin('\x55\xaa\x5a\xa5'),
                header_length,
                latin('\x01\x00\x00\x00'),
                boardname,
                int(position, 16),
                content_length,
                latin(date),
                latin(time),
                latin('INPUT'),
                latin(''),
                latin(''),
                latin('\x00\x10\x00\x00'))
        crc = get_crc_ccitt(list(struct.unpack('98B', header_header)))
        output.write(header_header[:-6])
        output.write(crc)
        output.write(header_header[-4:])
        output.write(header)
        data = open(name, 'rb')
        output.write(data.read())
        data.close()
        padding = (~(header_length + content_length) + 1) & 3
        output.write(struct.pack('%ds' % padding, latin('')))
    output.close()
 
def cpio_list(directory, output=None):
    ''' generate gen_cpio_init-compatible list for directory,
        if output is None, write to stdout
 
        official document:
        http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=usr/gen_init_cpio.c
    '''
 
    if not hasattr(output, 'write'):
        output = sys.stdout
    for root, dirs, files in os.walk(directory):
        for file in dirs + files:
            path = os.path.join(root, file)
            info = os.lstat(path)
            name = path.replace(directory, '', 1)
            name = name.replace(os.sep, '/')    # for windows
            if name[:1] != '/':
                name = '/' + name
            mode = oct(S_IMODE(info.st_mode))
            if S_ISLNK(info.st_mode):
                # slink name path mode uid gid
                realpath = os.readlink(path)
                output.write('slink %s %s %s 0 0\n' % (name, realpath, mode))
            elif S_ISDIR(info.st_mode):
                # dir name path mode uid gid
                output.write('dir %s %s 0 0\n' % (name, mode))
            elif S_ISREG(info.st_mode):
                # file name path mode uid gid
                output.write('file %s %s %s 0 0\n' % (name, path, mode))
 
    if hasattr(output, 'close'):
        output.close()
 
def parse_cpio(cpio, directory, cpiolist):
    ''' parse cpio, write content under directory.
        cpio: file object
        directory: string
        cpiolist: file object
 
        official document: (cpio newc structure)
        http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=usr/gen_init_cpio.c
    '''
 
    padding = lambda x: (~x + 1) & 3
 
    def read_cpio_header(cpio):
        assert cpio.read(6).decode('latin') == '070701', 'invalid cpio'
        cpio.read(8) # ignore inode number
        mode = int(cpio.read(8), 16)
        cpio.read(8) # uid
        cpio.read(8) # gid
        cpio.read(8) # nlink
        cpio.read(8) # timestamp
        filesize = int(cpio.read(8), 16)
        cpio.read(8) # major
        cpio.read(8) # minor
        cpio.read(8) # rmajor
        cpio.read(8) # rminor
        namesize = int(cpio.read(8), 16)
        cpio.read(8)
        name = cpio.read(namesize - 1).decode('latin') # maybe utf8?
        cpio.read(1)
        cpio.read(padding(namesize + 110))
        return name, mode, filesize
 
    while True:
        name, mode, filesize = read_cpio_header(cpio)
        if name == 'TRAILER!!!':
            break
 
        if name[:1] != '/':
            name = '/%s' % name
 
        name = os.path.normpath(name)
        path = '%s%s' %(directory, name)
        name = name.replace(os.sep, '/') # for windows
 
        srwx = oct(S_IMODE(mode))
        if S_ISLNK(mode):
            location = cpio.read(filesize)
            cpio.read(padding(filesize))
            cpiolist.write('slink %s %s %s\n' % (name, location, srwx))
        elif S_ISDIR(mode):
            try: os.makedirs(path)
            except os.error: pass
            cpiolist.write('dir %s %s\n' % (name, srwx))
        elif S_ISREG(mode):
            dirname = os.path.dirname(path)
            if not os.path.isdir(dirname):
                os.makedirs(dirname)
            tmp = open(path, 'wb')
            tmp.write(cpio.read(filesize))
            cpio.read(padding(filesize))
            tmp.close()
            cpiolist.write('file %s %s %s\n' % (name, path, srwx))
        else:
            cpio.read(filesize)
            cpio.read(padding(filesize))
 
    cpio.close()
    cpiolist.close()
 
def write_cpio(cpiolist, output):
    ''' generate cpio from cpiolist.
        cpiolist: file object
        output: file object
    '''
 
    padding = lambda x, y: struct.pack('%ds' % ((~x + 1) & (y - 1)), latin(''))
 
    def write_cpio_header(output, name, mode=0, nlink=1, filesize=0):
        namesize = len(name) + 1
        output.write(latin('070701'))
        output.write(latin('%08x' % 0)) # ino normally only for hardlink
        output.write(latin('%08x' % mode))
        output.write(latin('%08x%08x' % (0, 0))) # uid, gid set to 0
        output.write(latin('%08x' % nlink))
        output.write(latin('%08x' % 0)) # timestamp set to 0
        output.write(latin('%08x' % filesize))
        output.write(latin('%08x%08x' % (3, 1)))
        output.write(latin('%08x%08x' % (0, 0))) # dont support rmajor, rminor
        output.write(latin('%08x' % namesize))
        output.write(latin('%08x' % 0)) # chksum always be 0
        output.write(latin(name))
        output.write(struct.pack('1s', latin('')))
        output.write(padding(namesize + 110, 4))
 
    def cpio_mkfile(output, name, path, mode, *kw):
        if os.path.split(name)[1] in ('su', 'busybox'):
            mode = '4555'
        mode = int(mode, 8) | S_IFREG
        filesize = os.path.getsize(path)
        write_cpio_header(output, name, mode, 1, filesize)
        tmp = open(path, 'rb')
        output.write(tmp.read())
        tmp.close()
        output.write(padding(filesize, 4))
 
    def cpio_mkdir(output, name, mode='755', *kw):
        if name == '/tmp':
            mode = '1777'
        mode = int(mode, 8) | S_IFDIR
        write_cpio_header(output, name, mode, 2, 0)
 
    def cpio_mkslink(output, name, path, mode='777', *kw):
        mode = int(mode, 8) | S_IFLNK
        filesize = len(path)
        write_cpio_header(output, name, mode, 1, filesize)
        output.write(latin(path))
        output.write(padding(filesize, 4))
 
    def cpio_mknod(output, *kw):
        sys.stderr.write('nod is not implemented\n')
 
    def cpio_mkpipe(output, *kw):
        sys.stderr.write('pipe is not implemented\n')
 
    def cpio_mksock(output, *kw):
        sys.stderr.write('sock is not implemented\n')
 
    def cpio_tailer(output):
        name = 'TRAILER!!!'
        write_cpio_header(output, name)
 
        # normally, padding is ignored by decompresser
        if hasattr(output, 'tell'):
            output.write(padding(output.tell(), 512))
 
    files = []
    functions = {'dir': cpio_mkdir,
                 'file': cpio_mkfile,
                 'slink': cpio_mkslink,
                 'nod': cpio_mknod,
                 'pipe': cpio_mkpipe,
                 'sock': cpio_mksock}
    while True:
        line = cpiolist.readline()
        if not line:
            break
        lines = line.split()
        if len(lines) < 1 or lines[0] == '#':
            continue
        function = functions.get(lines[0])
        if not function:
            continue
        lines.pop(0)
        lines[0] = lines[0].replace(os.sep, '/') # if any
        if lines[0] in files:
            sys.stderr.write('ignore duplicate %s\n' % lines[0])
            continue
        files.append(lines[0])
        function(output, *lines)
 
    # for extra in ['/tmp', '/mnt']:
    #    if extra not in files:
    #        sys.stderr.write('add extra %s\n' % extra)
    #        cpio_mkdir(output, extra)
 
    cpio_tailer(output)
    cpiolist.close()
    output.close()
 
def parse_yaffs2(image, directory):
    ''' parse yaffs2 image.
 
        official document: (utils/mkyaffs2image)
        https://android.googlesource.com/platform/external/yaffs2/+/master/yaffs2/
        spare: yaffs_PackedTags2 in yaffs_packedtags2.h
        chunk: yaffs_ExtendedTags in yaffs_guts.h
    '''
 
    path = '.'
    filelist = {1: '.'}
 
    class Complete(Exception):
        pass
 
    def read_chunk(image):
        chunk = image.read(2048)
        spare = image.read(64)
        if not chunk:
            raise Complete
        assert len(spare) >= 16
        return chunk, spare
 
    def process_chunk(image):
        chunk, spare = read_chunk(image)
 
        nil, objectid, nil, bytecount = struct.unpack('<4I', spare[:16])
 
        if bytecount == 0xffff:
            assert len(chunk) >= 460
            (   filetype, parent,
                nil, name, padding, mode,
                uid, gid, atime, mtime, ctime,
                filesize, equivalent, alias
            ) = struct.unpack('<iiH256s2sI5Iii160s', chunk[:460])
 
            # only for little-endian
            # (   filetype, parent,
            #     nil, name, mode,
            #     uid, gid, atime, mtime, ctime,
            #     filesize, equivalent, alias
            # ) = struct.unpack('iiH256sI5Iii160s', chunk[:460])
 
            parent = filelist.get(parent)
            assert parent is not None
 
            name = name.decode('latin').split('\x00')[0]
            path = name and '%s/%s' % (parent, name) or parent
            filelist[objectid] = path
            fullname = '%s/%s' % (directory, path)
 
            if filetype == 0: # unknown
                pass
            elif filetype == 1: # file
                flag = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
                if hasattr(os, 'O_BINARY'):
                    flag |= os.O_BINARY
                output = os.open(fullname, flag, mode)
                while filesize > 0:
                    chunk, spare = read_chunk(image)
                    nil, nil, nil, bytecount = struct.unpack('<4I', spare[:16])
                    size = filesize < bytecount and filesize or bytecount
                    os.write(output, chunk[:size])
                    filesize -= size
                os.close(output)
            elif filetype == 2: # slink
                alias = alias.decode('latin').split('\x00')[0]
                try: os.symlink(alias, fullname)
                except: sys.stderr.write('soft %s -> %s\n' % (fullname, alias))
            elif filetype == 3: # dir
                if not os.path.isdir(fullname):
                    os.makedirs(fullname, mode)
                try: os.chmod(fullname, mode)
                except: sys.stderr.write('directory mode is not supported')
            elif filetype == 4: # hlink
                link = filelist.get(equivalent)
                try: os.link(filelist.get(equivalent), fullname)
                except: sys.stderr.write('hard %s -> %s\n' % (fullname, link))
            elif filetype == 5: # special
                pass
 
    while True:
        try: process_chunk(image)
        except Complete: break
 
    image.close()
 
from gzip import GzipFile
class CPIOGZIP(GzipFile):
    # dont write filename
    def _write_gzip_header(self):
        self.fileobj.write(struct.pack('4B', 0x1f, 0x8b, 0x08, 0x00))
        self.fileobj.write(struct.pack('4s', latin('')))
        self.fileobj.write(struct.pack('2B', 0x00, 0x03))
 
    # don't check crc and length
    def _read_eof(self):
        pass
 
def parse_rle(rle, raw):
    ''' convert 565-rle format to raw file.
 
        official document:
        https://android.googlesource.com/platform/build/+/master/tools/rgb2565/to565.c
    '''
    r = lambda x: int(((x >> 11) & 0x1f) << 3)
    g = lambda x: int(((x >> 5) & 0x3f) << 2)
    b = lambda x: int((x & 0x1f) << 3)
 
    total = 0
    while True:
        data = rle.read(4)
        if not data:
            break
        assert len(data) == 4
        count, color = struct.unpack('<2H', data)
        total += count
        while count:
            count -= 1
            raw.write(struct.pack('3B', r(color), g(color), b(color)))
    rle.close()
    raw.close()
    return total
 
def parse_565(rle, raw):
    ''' convert 565 format to raw file.
 
        official document:
        https://android.googlesource.com/platform/build/+/master/tools/rgb2565/to565.c
    '''
    r = lambda x: int(((x >> 11) & 0x1f) << 3)
    g = lambda x: int(((x >> 5) & 0x3f) << 2)
    b = lambda x: int((x & 0x1f) << 3)
 
    total = 0
    while True:
        data = rle.read(2)
        if not data:
            break
        assert len(data) == 2
        color, = struct.unpack('<H', data)
        total += 1
        raw.write(struct.pack('3B', r(color), g(color), b(color)))
    rle.close()
    raw.close()
    return total
 
def write_rle(raw, rle):
    ''' convert raw file to 565-rle format.
    '''
    x = lambda r, g, b: ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
 
    last = None
    total = 0
    while True:
        rgb = raw.read(3)
        if not rgb:
            break
        total += 1
        assert len(rgb) == 3
        color = x(*struct.unpack('3B', rgb))
        if last is None:
            pass
        elif color == last and count != 0xffff:
            count += 1
            continue
        else:
            rle.write(struct.pack('<2H', count, last))
        last = color
        count = 1
    if count:
        rle.write(struct.pack('<2H', count, last))
    raw.close()
    rle.close()
    return total
 
def write_565(raw, rle):
    ''' convert raw file to 565 format.
    '''
    x = lambda r, g, b: ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
 
    last = None
    total = 0
    while True:
        rgb = raw.read(3)
        if not rgb:
            break
        total += 1
        assert len(rgb) == 3
        color = x(*struct.unpack('3B', rgb))
        rle.write(struct.pack('<H', color))
    raw.close()
    rle.close()
    return total
 
def unpack_simg(simg=None, img=None):
    '''unpack sparse image
 
    official document:
    https://android.googlesource.com/platform/system/core/+/master/libsparse/
    '''
    sys.stderr.write('arguments: [sparse image] [raw image]\n')
    sys.stderr.write('simg file: %s\n' % simg)
    sys.stderr.write(' img file: %s\n' % img)
    if simg is None or img is None:
        return False
 
    SPARSE_HEADER_MAJOR_VER = 1
    SPARSE_HEADER_LEN = 28
    CHUNK_HEADER_LEN = 12
    CHUNK_TYPE_RAW = 0xCAC1
    CHUNK_TYPE_FILL = 0xCAC2
    CHUNK_TYPE_DONT_CARE = 0xCAC3
    CHUNK_TYPE_CRC32 = 0xCAC4
    NULL = latin('')
 
    class SparseImageError(Exception):
        pass
 
    magic = struct.pack('<I', 0xed26ff3a)
    wb = open(img, 'wb')
    rb = open(simg, 'rb+')
    mm = mmap.mmap(rb.fileno(), 0)
    pos = -1
    while True:
        pos = mm.find(magic, pos + 1)
        if pos < 0:
            break
        mm.seek(pos, 0)
        (
            _,
            major_version,
            minor_version,
            file_hdr_sz,
            chunk_hdr_sz,
            blk_sz,
            total_blks,
            total_chunks,
            image_checksum
        ) = struct.unpack('<I4H4I', mm.read(SPARSE_HEADER_LEN))
        if major_version != SPARSE_HEADER_MAJOR_VER:
            continue
        if file_hdr_sz < SPARSE_HEADER_LEN:
            continue
        if chunk_hdr_sz < CHUNK_HEADER_LEN:
            continue
 
        # finally, get it
        if file_hdr_sz > SPARSE_HEADER_LEN:
            mm.seek(file_hdr_sz - SPARSE_HEADER_LEN, 1)
        for _ in range(0, total_chunks):
            (
                chunk_type,
                reserved,
                chunk_sz,
                total_sz
            ) = struct.unpack('<2H2I', mm.read(CHUNK_HEADER_LEN))
            if file_hdr_sz > SPARSE_HEADER_LEN:
                mm.seek(file_hdr_sz - SPARSE_HEADER_LEN, 1)
            chunk_data_size = total_sz - chunk_hdr_sz
            chunk_block_size = chunk_sz * blk_sz
            if chunk_type == CHUNK_TYPE_RAW:
                if chunk_data_size != chunk_block_size:
                    raise SparseImageError()
                while chunk_data_size > 0:
                    if chunk_data_size > 8192:
                        size = 8192
                    else:
                        size = chunk_data_size
                    chunk_data_size -= size
                    wb.write(mm.read(size))
            elif chunk_type == CHUNK_TYPE_FILL:
                if chunk_data_size != 4:
                    raise SparseImageError()
                while chunk_block_size > 0:
                    if chunk_block_size > 8192:
                        size = 8192
                    else:
                        size = chunk_block_size
                    chunk_block_size -= size
                    wb.write(mm.read(4) * (size / 4))
            elif chunk_type == CHUNK_TYPE_DONT_CARE:
                if chunk_data_size != 0:
                    raise SparseImageError()
                while chunk_block_size > 0:
                    if chunk_block_size > 8192:
                        size = 8192
                    else:
                        size = chunk_block_size
                    chunk_block_size -= size
                    wb.write(struct.pack('%ss' % size, NULL))
            elif chunk_type == CHUNK_TYPE_CRC32:
                if chunk_data_size != 4:
                    raise SparseImageError()
                mm.read(4)
            else:
                raise SparseImageError()
        # should break?
        pos = mm.tell()
 
    rb.close()
    wb.close()
 
__all__ = [ 'parse_updata',
            'write_updata',
            'parse_bootimg',
            'write_bootimg',
            'parse_cpio',
            'write_cpio',
            'parse_yaffs2',
            'parse_rle',
            'write_rle',
            'parse_565',
            'write_565',
            'cpio_list',
            'POSITION',
            'unpack_simg',
            ]
 
# above is the module of bootimg
# below is only for usage...
 
def repack_bootimg(kernel_addr=None, ramdisk_addr=None, second_addr=None, tags_addr=None, cmdline=None, page_size=None, padding_size=None):
 
    if os.path.exists('ramdisk.cpio.gz'):
        ramdisk = 'ramdisk.cpio.gz'
    else:
        ramdisk = 'ramdisk.gz'
 
    if os.path.exists('second.gz'):
        second = 'second.gz'
    elif os.path.exists('second'):
        second = 'second'
    else:
        second = ''
 
    if os.path.exists('dt.img'):
        dtimg = 'dt.img'
    else:
        dtimg = ''
 
    sys.stderr.write('arguments: [kernel_addr] [ramdisk_addr] [second_addr] [tags_addr] [cmdline] [page_size] [padding_size]\n')
    metadata = {}
    if os.path.exists('bootimg.json'):
        try:
            metadata = json.loads(open('bootimg.json').read())
        except:
            pass
 
    if 'kernel_addr' in metadata:
        kernel_addr = metadata.get('kernel_addr', 0)
    else:
        kernel_addr = int(kernel_addr, 16)
 
    if 'ramdisk_addr' in metadata:
        ramdisk_addr = metadata.get('ramdisk_addr', 0)
    else:
        ramdisk_addr = int(ramdisk_addr, 16)
 
    if 'second_addr' in metadata:
        second_addr = metadata.get('second_addr', 0)
    else:
        second_addr = int(second_addr, 16)
 
    if 'tags_addr' in metadata:
        tags_addr = metadata.get('tags_addr', 0)
    else:
        tags_addr = int(tags_addr, 16)
 
    if 'name' in metadata:
        name = latin(metadata.get('name'))
    elif name is None:
        name = latin('')
 
    if 'cmdline' in metadata:
        cmdline = latin(metadata.get('cmdline'))
    elif cmdline is None:
        cmdline = latin('mem=211M console=null androidboot.hardware=qcom')
 
    if 'page_size' in metadata:
        page_size = metadata.get('page_size', 2048)
    else:
        page_size = int(str(page_size))
 
    if 'padding_size' in metadata:
        padding_size = metadata.get('padding_size', 4096)
    else:
        padding_size = int(str(padding_size))
 
    os_version = metadata.get('os_version', 0)
 
    sys.stderr.write('kernel: kernel\n')
    sys.stderr.write('ramdisk: %s\n' % ramdisk)
    sys.stderr.write('second: %s\n' % second)
    sys.stderr.write('dtimg: %s\n' % dtimg)
    sys.stderr.write('kernel_addr: 0x%x\n' % kernel_addr)
    sys.stderr.write('ramdisk_addr: 0x%x\n' % ramdisk_addr)
    sys.stderr.write('second_addr: 0x%x\n' % second_addr)
    sys.stderr.write('tags_addr: 0x%x\n' % tags_addr)
    sys.stderr.write('name: %s\n' % name)
    sys.stderr.write('cmdline: %s\n' % cmdline)
    sys.stderr.write('page_size: %d\n' % page_size)
    sys.stderr.write('padding_size: %d\n' % padding_size)
    sys.stderr.write('output: boot_repack.img\n')
 
    if os.path.isfile('kernel.gz'):
        kernel = open('kernel.gz', 'rb')
    else:
        kernel = open('kernel', 'rb')
    if os.path.isfile(kernel.name + '.dt'):
        dt = open(kernel.name + '.dt', 'rb')
 
        kerneldt = open(kernel.name + '.bundle', 'wb')
        kerneldt.write(kernel.read())
        kerneldt.write(dt.read())
        kerneldt.close()
 
        dt.close()
        kernel.close()
        kernel = open(kerneldt.name, 'rb')
    options = { 'kernel_addr': kernel_addr,
                'ramdisk_addr': ramdisk_addr,
                'second_addr': second_addr,
                'tags_addr': tags_addr,
                'name': name,
                'cmdline': cmdline,
                'output': open('boot_repack.img', 'wb'),
                'kernel': kernel,
                'ramdisk': open(ramdisk, 'rb'),
                'second': second and open(second, 'rb') or None,
                'dtimg': dtimg and open(dtimg, 'rb') or None,
                'page_size': page_size,
                'padding_size': padding_size,
                'os_version': os_version,
                }
    write_bootimg(**options)
    if kernel.name.endswith('.bundle'):
        os.remove(kernel.name)
 
def unpack_bootimg(bootimg=None):
    if bootimg is None:
        bootimg = 'boot.img'
    sys.stderr.write('arguments: [bootimg file]\n')
    sys.stderr.write('bootimg file: %s\n' % bootimg)
    sys.stderr.write('output: kernel[.gz] ramdisk[.gz] second[.gz]\n')
    parse_bootimg(open(bootimg, 'rb'))
 
def unpack_updata(updata=None, debug=False):
    if updata is None and os.path.exists('UPDATA.APP'):
        updata = 'UPDATA.APP'
    sys.stderr.write('arguments: [updata file]\n')
    sys.stderr.write('updata file: %s\n' % updata)
    sys.stderr.write('output: splash.565 (565 file)\n')
    sys.stderr.write('output: boot.img recover.img (bootimg file)\n')
    sys.stderr.write('output: system.img userdata.img (yaffs2 image)\n')
    parse_updata(open(updata, 'rb'), debug)
 
def unpack_ramdisk(ramdisk=None, directory=None):
    if ramdisk is None:
        ramdisk = 'ramdisk.gz'
 
    if directory is None:
        directory = 'initrd'
 
    sys.stderr.write('arguments: [ramdisk file] [directory]\n')
    sys.stderr.write('ramdisk file: %s\n' % ramdisk)
    sys.stderr.write('directory: %s\n' % directory)
    sys.stderr.write('output: cpiolist.txt\n')
 
    if os.path.lexists(directory):
        raise SystemExit('please remove %s' % directory)
 
    tmp = open(ramdisk, 'rb')
    magic = tmp.read(6)
    if magic[:3] == struct.pack('3B', 0x1f, 0x8b, 0x08):
        tmp.close()
        cpio = CPIOGZIP(ramdisk, 'rb')
    elif magic.decode('latin') == '070701':
        tmp.seek(0, 0)
        cpio = tmp
    else:
        tmp.close()
        raise IOError('invalid ramdisk')
 
    cpiolist = open('cpiolist.txt', 'w')
    parse_cpio(cpio, directory, cpiolist)
 
def repack_updata(updatalist=None, debug=False):
    if updatalist is None:
        updatalist = 'updatalist.txt'
    sys.stderr.write('arguments: [updatalist file]\n')
    sys.stderr.write('updatalist file: %s\n' % updatalist)
    sys.stderr.write('output: updata_repack.app\n')
    output = open('updata_repack.app', 'wb')
    write_updata(output)
 
def repack_ramdisk(cpiolist=None):
    if cpiolist is None:
        cpiolist = 'cpiolist.txt'
 
    sys.stderr.write('arguments: [cpiolist file]\n')
    sys.stderr.write('cpiolist file: %s\n' % cpiolist)
    sys.stderr.write('output: ramdisk.cpio.gz\n')
 
    cpiogz = CPIOGZIP('ramdisk.cpio.gz', 'wb')
    output = open(cpiolist, 'r')
    write_cpio(output, cpiogz)
    cpiogz.close()
 
def unpack_yaffs(image=None, directory=None):
    if image is None:
        image = 'userdata.img'
    if directory is None and image[-4:] == '.img':
        directory = image[:-4]
 
    sys.stderr.write('arguments: [yaffs2 image] [directory]\n')
    sys.stderr.write('yaffs2 image: %s\n' % image)
    sys.stderr.write('directory: %s\n' % directory)
 
    if os.path.lexists(directory):
        raise SystemExit('please remove %s' % directory)
 
    parse_yaffs2(open(image, 'rb'), directory)
 
SIZE = {320*480: (320, 480),        # HVGA
        240*320: (240, 320),        # QVGA
        240*400: (240, 400),        # WQVGA400
        240*432: (240, 432),        # WQVGA432
        480*800: (480, 800),        # WVGA800
        480*854: (480, 854),        # WVGA854
        }
def unpack_rle_565(rlefile, rawfile, function):
    if rawfile is None:
        if rlefile[-4] == '.':
            rawfile = rlefile[:-4] + '.raw'
        else:
            rawfile = rlefile + '.raw'
 
    if rawfile[-4] == '.':
        pngfile = rawfile[:-4] + '.png'
    else:
        pngfile = rawfile + '.png'
 
    sys.stderr.write('output: %s [%s]\n' % (rawfile, pngfile))
 
    rle = open(rlefile, 'rb')
    raw = open(rawfile, 'wb')
    total = function(rle, raw)
 
    try: import Image
    except ImportError: return
 
    size = SIZE.get(total)
    if size is None: return
 
    data = open(rawfile, 'rb')
    Image.fromstring('RGB',  size, data.read(), 'raw').save(pngfile)
    data.close()
 
def unpack_rle(rlefile=None, rawfile=None):
    if rlefile is None:
        rlefile = 'initlogo.rle'
    sys.stderr.write('arguments: [rle file] [raw file]\n')
    sys.stderr.write('rle file: %s\n' % (rlefile))
    unpack_rle_565(rlefile, rawfile, parse_rle)
 
def unpack_565(rlefile=None, rawfile=None):
    if rlefile is None:
        rlefile = 'splash.565'
    sys.stderr.write('arguments: [565 file] [raw file]\n')
    sys.stderr.write('565 file: %s\n' % (rlefile))
    unpack_rle_565(rlefile, rawfile, parse_565)
 
def repack_rle_565(rawfile, rlefile, function):
 
    if rawfile[-4:] != '.raw':
        try: import Image
        except ImportError:
            sys.stderr.write('Please Install PIL (python-imaging)\n')
            return None
        try:
            img = Image.open(rawfile)
        except:
            sys.stderr.write('Cannot Open Image File')
            return None
 
        from JpegImagePlugin import RAWMODE
        if 'transparency' in img.info or img.mode == 'RGBA':
            new = img.mode == 'RGBA' and img or img.convert('RGBA')
            img = Image.new('RGB', new.size)
            img.paste(new, (0, 0), new)
        elif img.mode not in RAWMODE:
            img = img.convert('RGB')
 
        if img.size not in list(SIZE.values()):
            sys.stderr.write('warning: Image is not HVGA, [W]QVGA, WVGA\n')
 
        rawfile = rlefile[:-4] + '.raw'
        data = open(rawfile, 'wb')
        data.write(img.tostring())
        data.close()
 
    raw = open(rawfile, 'rb')
    rle = open(rlefile, 'wb')
    function(raw, rle)
 
def repack_rle(rawfile=None, rlefile=None):
    if rawfile is None:
        rawfile = 'initlogo.raw'
 
    if rlefile is None:
        if rawfile[-4] == '.':
            rlefile = rawfile[:-4] + '.rle'
        else:
            rlefile = rawfile + '.rle'
 
    sys.stderr.write('arguments: [raw file] [rle file]\n')
    sys.stderr.write('raw file: %s\n' % rawfile)
    sys.stderr.write('rle file: %s\n' % rlefile)
    repack_rle_565(rawfile, rlefile, write_rle)
 
def repack_565(rawfile=None, rlefile=None):
    if rawfile is None:
        rawfile = 'splash.raw'
 
    if rlefile is None:
        if rawfile[-4] == '.':
            rlefile = rawfile[:-4] + '.565'
        else:
            rlefile = rawfile + '.565'
 
    sys.stderr.write('arguments: [raw file] [565 file]\n')
    sys.stderr.write('raw file: %s\n' % rawfile)
    sys.stderr.write('565 file: %s\n' % rlefile)
    repack_rle_565(rawfile, rlefile, write_565)
 
if __name__ == '__main__':
 
    functions = {
                 '--unpack-updata': unpack_updata,
                 '--repack-updata': repack_updata,
                 '--unpack-bootimg': unpack_bootimg,
                 '--unpack-ramdisk': unpack_ramdisk,
                 '--unpack-yaffs': unpack_yaffs,
                 '--unpack-yaffs2': unpack_yaffs,
                 '--unpack-yafffs': unpack_yaffs,
                 '--unpack-rle': unpack_rle,
                 '--unpack-565': unpack_565,
                 '--repack-ramdisk': repack_ramdisk,
                 '--repack-bootimg': repack_bootimg,
                 '--repack-rle': repack_rle,
                 '--repack-565': repack_565,
                 '--cpio-list': cpio_list,
                 '--unpack-simg': unpack_simg,
                }
 
    def usage():
        sys.stderr.write('supported arguments:')
        sys.stderr.write('\n\t')
        sys.stderr.write('\n\t'.join(sorted(functions.keys())))
        sys.stderr.write('\n')
        raise SystemExit(1)
 
    if len(sys.argv) == 1:
        usage()
 
    sys.argv.pop(0)
    name = sys.argv[0]
    function = functions.get(name, None)
    sys.argv.pop(0)
    if not function:
        usage()
    function(*sys.argv)
 
# vim: set sta sw=4 et:

$pypath="d:\python3"
 
function bootimg
{
""
""
""
"bootimg交互脚本"
""
"1.unpack boot.img"
"2.unpack recovery.img"
"3.unpack 输入的img文件名"
""
"4.unpack ramdisk"
""
"5.repack ramdisk"
"6.repack bootimg"
""
$choice=Read-Host "请输入数字选择需要的功能"
switch($choice){
1{unpackboot}
2{unpackrecovery}
3{unpackfile}
4{unpackramdisk}
5{repackramdisk}
6{repackbootimg}
default{bootimg}
}
}
 
function unpackboot
{
Start-Process -FilePath $pypath\python.exe "$PSScriptRoot\bootimg.py --unpack-bootimg"
bootimg
}
 
function unpackrecovery
{
Start-Process -FilePath $pypath\python.exe "$PSScriptRoot\bootimg.py --unpack-bootimg recovery.img"
bootimg
}
 
function unpackramdisk
{
Start-Process -FilePath $pypath\python.exe "$PSScriptRoot\bootimg.py --unpack-ramdisk"
bootimg
}
 
function unpackfile
{
$imgfile=Read-Host "请输入文件名,如boot.img"
Start-Process -FilePath $pypath\python.exe "$PSScriptRoot\bootimg.py --unpack-bootimg $imgfile"
bootimg
}
 
function repackramdisk
{
Start-Process -FilePath $pypath\python.exe "$PSScriptRoot\bootimg.py --repack-ramdisk"
bootimg
}
 
function repackbootimg
{
Start-Process -FilePath $pypath\python.exe "$PSScriptRoot\bootimg.py --repack-bootimg"
bootimg
}
 
cd $PSScriptRoot
 
bootimg

相关文章

网友评论

    本文标题:bootimg.py,能编辑boot.img,recovery.

    本文链接:https://www.haomeiwen.com/subject/veyloktx.html