Posts

    提取内核DTB

    Pre

    在新内核3.5.7以后有了DTB,所以要进行提取。之前没有dtb是uboot传递给内核设备信息。

    提取思路

    DTB_HEADER是= b”\xd0\x0d\xfe\xed” 搜索dtb的头标志。然后如出来保存。

    具体实现

    Py,shell等。

    #!/usr/bin/env python3
    """
    Copyright 2017-2021 Pablo Castellano
    
    extract-dtb is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    extract-dtb is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with extract-dtb.  If not, see <http://www.gnu.org/licenses/>.
    """
    
    import argparse
    import os
    import string
    
    __version__ = "1.3.dev0"
    
    DTB_HEADER = b"\xd0\x0d\xfe\xed"
    
    
    def dump_file(filename, content):
        with open(filename, "wb") as fp:
            fp.write(content)
    
    
    def safe_output_path(output_dir, dtb_filename_new):
        """Safely combines the output folder with the relative path of the dtb
        (which may contain subfolders) and creates the necessary folder
        structure.
    
        :returns: the resulting file name
        """
        if "../" in dtb_filename_new + "/":
            raise RuntimeException(
                "DTB file path points outside of extraction"
                " directory: " + dtb_filename_new
            )
        ret = os.path.join(output_dir, dtb_filename_new)
        os.makedirs(os.path.dirname(ret), exist_ok=True)
        return ret
    
    
    def split(args):
        """Reads a file and looks for DTB_HEADER occurrences (beginning of each DTB)
        Then extract each one. If possible, use the device model as filename.
        """
        positions = []
    
        with open(args.filename, "rb") as fp:
            content = fp.read()
    
        dtb_next = content.find(DTB_HEADER)
        while dtb_next != -1:
            positions.append(dtb_next)
            dtb_next = content.find(DTB_HEADER, dtb_next + 1)
    
        if len(positions) == 0:
            print("No appended dtbs found")
            return
    
        if args.extract:
            os.makedirs(args.output_dir, exist_ok=True)
            begin_pos = 0
            for n, pos in enumerate(positions, 0):
                dtb_filename = get_dtb_filename(n)
                filepath = os.path.join(args.output_dir, dtb_filename)
                dump_file(filepath, content[begin_pos:pos])
                if n > 0:
                    dtb_name = get_dtb_model(filepath)
                    if dtb_name:
                        dtb_filename_new = get_dtb_filename(n, dtb_name)
                        dtb_filename_new_full = safe_output_path(
                            args.output_dir, dtb_filename_new
                        )
                        os.rename(filepath, dtb_filename_new_full)
                        dtb_filename = dtb_filename_new
                print("Dumped {0}, start={1} end={2}".format(dtb_filename, begin_pos, pos))
                begin_pos = pos
    
            # Last chunk
            dtb_filename = get_dtb_filename(n + 1)
            filepath = os.path.join(args.output_dir, dtb_filename)
            dump_file(filepath, content[begin_pos:])
            dtb_name = get_dtb_model(filepath)
            if dtb_name:
                dtb_filename_new = get_dtb_filename(n + 1, dtb_name)
                os.rename(
                    os.path.join(filepath), os.path.join(args.output_dir, dtb_filename_new)
                )
                dtb_filename = dtb_filename_new
            print(
                "Dumped {0}, start={1} end={2}".format(
                    dtb_filename, begin_pos, len(content)
                )
            )
            print(
                "Extracted {0} appended dtbs + kernel to {1}".format(
                    len(positions), args.output_dir
                )
            )
        else:
            print("Found {0} appended dtbs".format(len(positions)))
    
    
    def get_dtb_filename(n, suffix=""):
        if n == 0:
            return "00_kernel"
        n = str(n).zfill(2)
        basename = "{0}_dtbdump".format(n)
        if suffix != "":
            basename += "_" + suffix.replace(" ", "_").replace("/", "_")
        return basename + ".dtb"
    
    
    def get_dtb_model(filename, min_length=4):
        """Finds the first printable string in a file with length greater
        than min_length. Replaces spaces with underscores.
        """
        with open(filename, errors="ignore") as f:
            result = ""
            for c in f.read():
                if c in string.printable:
                    result += c
                    continue
                if len(result) >= min_length:
                    return result.replace(" ", "_").replace("\t", "_").replace("\n", "_").replace("\r", "_")
                result = ""
            if len(result) >= min_length:  # catch result at EOF
                return result.replace(" ", "_").replace("\t", "_").replace("\n", "_").replace("\r", "_")
        return None
    
    
    def main():
        parser = argparse.ArgumentParser(description="Extract dtbs from kernel images.")
        parser.add_argument("filename", help="Android kernel image")
        parser.add_argument(
            "-o", dest="output_dir", default="dtb", required=False, help="Output directory"
        )
        parser.add_argument(
            "-n",
            dest="extract",
            action="store_false",
            default=True,
            required=False,
            help="Do not extract, just output information",
        )
        parser.add_argument("-V", "--version", action="version", version=__version__)
    
        args = parser.parse_args()
    
        split(args)
    
    
    if __name__ == "__main__":
        main()