工具
split_bootimg.pl 是一个perl 写的脚本工具, 按照boot.img 格式读取
mkbootfs, mkbootimg, bootsigner: 编译源码会在out/host/linux-x86/bin 下生成, 也可以从官网上下载
具体步䯅
    split_bootimg.pl boot.img # 会得到 boot.img-kernel, boot.img-ramdisk.gz
    mkdir ramdisk | cd ramdisk
    gzip -dc ../boot.img-ramdisk.gz | cpio -i # 会在ramdisk 下将得到所有内容, 做想要的修改
    cd .. # 返回ramdisk 上一层目录
    mkbootfs ramdisk | gzip > ramdisk.img # 制作ramdisk.img
    # 以nubia z18mini 的内核命令打包
    # cmdline : "androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x37 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 sched_enable_hmp=1 sched_enable_power_aware=1 service_locator.enable=1 swiotlb=1 androidboot.configfs=true androidboot.usbcontroller=a800000.dwc3 buildvariant=user"
    # cmdline: "console=ttyMSM0,115200,n8 androidboot.console=ttyMSM0 earlycon=msm_serial_dm,0xc1700000000 androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x237 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 sched_enable_hmp=1 sched_enable_power_aware=1 service_locator.enable=1 swiotlb=1 androidboot.configfs=true androidboot.usbcontroller=a800000.dwc3 buildvariant=userdebug"
    mkbootimg --kernel kernel --ramdisk ramdisk.img --base 0x00000000 --pagesize 4096  --cmdline "androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x37 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 sched_enable_hmp=1 sched_enable_power_aware=1 service_locator.enable=1 swiotlb=1 androidboot.configfs=true androidboot.usbcontroller=a800000.dwc3 buildvariant=user" --os_version 8.1.0 --os_patch_level 2019-08-25 --output boot_new.img
    # 签名 在官方开源的内核源码中有
    boot_signer /boot boot.img verity.pk8 verity.x509.pem boot_signer.img
split_bootimg.pl
#!/usr/bin/perl
######################################################################
#
#   File          : split_bootimg.pl
#   Author(s)     : William Enck <enck@cse.psu.edu>
#   Description   : Split appart an Android boot image created
#                   with mkbootimg. The format can be found in
#                   android-src/system/core/mkbootimg/bootimg.h
#
#                   Thanks to alansj on xda-developers.com for
#                   identifying the format in bootimg.h and
#                   describing initial instructions for splitting
#                   the boot.img file.
#
#   Last Modified : Tue Dec  2 23:36:25 EST 2008
#   By            : William Enck <enck@cse.psu.edu>
#
#   Copyright (c) 2008 William Enck
#
######################################################################
use strict;
use warnings;
# Turn on print flushing
$|++;
######################################################################
## Global Variables and Constants
my $SCRIPT = __FILE__;
my $IMAGE_FN = undef;
# Constants (from bootimg.h)
use constant BOOT_MAGIC => 'ANDROID!';
use constant BOOT_MAGIC_SIZE => 8;
use constant BOOT_NAME_SIZE => 16;
use constant BOOT_ARGS_SIZE => 512;
# Unsigned integers are 4 bytes
use constant UNSIGNED_SIZE => 4;
# Parsed Values
my $PAGE_SIZE = undef;
my $KERNEL_SIZE = undef;
my $RAMDISK_SIZE = undef;
my $SECOND_SIZE = undef;
######################################################################
## Main Code
&parse_cmdline();
&parse_header($IMAGE_FN);
=format (from bootimg.h)
** +-----------------+
** | boot header     | 1 page
** +-----------------+
** | kernel          | n pages
** +-----------------+
** | ramdisk         | m pages
** +-----------------+
** | second stage    | o pages
** +-----------------+
**
** n = (kernel_size + page_size - 1) / page_size
** m = (ramdisk_size + page_size - 1) / page_size
** o = (second_size + page_size - 1) / page_size
=cut
my $n = int(($KERNEL_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);
my $m = int(($RAMDISK_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);
my $o = int(($SECOND_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);
my $k_offset = $PAGE_SIZE;
my $r_offset = $k_offset + ($n * $PAGE_SIZE);
my $s_offset = $r_offset + ($m * $PAGE_SIZE);
(my $base = $IMAGE_FN) =~ s/.*\/(.*)$/$1/;
my $k_file = $base . "-kernel";
my $r_file = $base . "-ramdisk.gz";
my $s_file = $base . "-second.gz";
# The kernel is always there
print "Writing $k_file ...";
&dump_file($IMAGE_FN, $k_file, $k_offset, $KERNEL_SIZE);
print " complete.\n";
# The ramdisk is always there
print "Writing $r_file ...";
&dump_file($IMAGE_FN, $r_file, $r_offset, $RAMDISK_SIZE);
print " complete.\n";
# The Second stage bootloader is optional
unless ($SECOND_SIZE == 0) {
    print "Writing $s_file ...";
    &dump_file($IMAGE_FN, $s_file, $s_offset, $SECOND_SIZE);
    print " complete.\n";
}
######################################################################
## Supporting Subroutines
=header_format (from bootimg.h)
struct boot_img_hdr
{
    unsigned char magic[BOOT_MAGIC_SIZE];
    unsigned kernel_size;  /* size in bytes */
    unsigned kernel_addr;  /* physical load addr */
    unsigned ramdisk_size; /* size in bytes */
    unsigned ramdisk_addr; /* physical load addr */
    unsigned second_size;  /* size in bytes */
    unsigned second_addr;  /* physical load addr */
    unsigned tags_addr;    /* physical addr for kernel tags */
    unsigned page_size;    /* flash page size we assume */
    unsigned unused[2];    /* future expansion: should be 0 */
    unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
    unsigned char cmdline[BOOT_ARGS_SIZE];
    unsigned id[8]; /* timestamp / checksum / sha1 / etc */
};
=cut
sub parse_header {
    my ($fn) = @_;
    my $buf = undef;
    open INF, $fn or die "Could not open $fn: $!\n";
    binmode INF;
    # Read the Magic
    read(INF, $buf, BOOT_MAGIC_SIZE);
    unless ($buf eq BOOT_MAGIC) {
    die "Android Magic not found in $fn. Giving up.\n";
    }
    # Read kernel size and address (assume little-endian)
    read(INF, $buf, UNSIGNED_SIZE * 2);
    my ($k_size, $k_addr) = unpack("VV", $buf);
    # Read ramdisk size and address (assume little-endian)
    read(INF, $buf, UNSIGNED_SIZE * 2);
    my ($r_size, $r_addr) = unpack("VV", $buf);
    # Read second size and address (assume little-endian)
    read(INF, $buf, UNSIGNED_SIZE * 2);
    my ($s_size, $s_addr) = unpack("VV", $buf);
    # Ignore tags_addr
    read(INF, $buf, UNSIGNED_SIZE);
    # get the page size (assume little-endian)
    read(INF, $buf, UNSIGNED_SIZE);
    my ($p_size) = unpack("V", $buf);
    # Ignore unused
    read(INF, $buf, UNSIGNED_SIZE * 2);
    # Read the name (board name)
    read(INF, $buf, BOOT_NAME_SIZE);
    my $name = $buf;
    # Read the command line
    read(INF, $buf, BOOT_ARGS_SIZE);
    my $cmdline = $buf;
    # Ignore the id
    read(INF, $buf, UNSIGNED_SIZE * 8);
    # Close the file
    close INF;
    # Print important values
    printf "Page size: %d (0x%08x)\n", $p_size, $p_size;
    printf "Kernel size: %d (0x%08x)\n", $k_size, $k_size;
    printf "Ramdisk size: %d (0x%08x)\n", $r_size, $r_size;
    printf "Second size: %d (0x%08x)\n", $s_size, $s_size;
    printf "Board name: $name\n";
    printf "Command line: $cmdline\n";
    # Save the values
    $PAGE_SIZE = $p_size;
    $KERNEL_SIZE = $k_size;
    $RAMDISK_SIZE = $r_size;
    $SECOND_SIZE = $s_size;
}
sub dump_file {
    my ($infn, $outfn, $offset, $size) = @_;
    my $buf = undef;
    open INF, $infn or die "Could not open $infn: $!\n";
    open OUTF, ">$outfn" or die "Could not open $outfn: $!\n";
    binmode INF;
    binmode OUTF;
    seek(INF, $offset, 0) or die "Could not seek in $infn: $!\n";
    read(INF, $buf, $size) or die "Could not read $infn: $!\n";
    print OUTF $buf or die "Could not write $outfn: $!\n";
    close INF;
    close OUTF;
}
######################################################################
## Configuration Subroutines
sub parse_cmdline {
    unless ($#ARGV == 0) {
    die "Usage: $SCRIPT boot.img\n";
    }
    $IMAGE_FN = $ARGV[0];
}