mkbootimg hacking
1 /********************************************************************** 2 * mkbootimg hacking 3 * 声明: 4 * 1. 本文源代码来自myzr_android4_2_2_1_1_0.tar.bz2中的mkbootimg.c; 5 * 2. 通过阅读该源码,可知Android的boot.img合成原理; 6 * 7 * 深圳 南山平山村 曾剑鋒 Mon May 4 13:09:49 CST 2015 8 **********************************************************************/ 9 10 /* tools/mkbootimg/mkbootimg.c 11 ** 12 ** Copyright 2007, The Android Open Source Project 13 ** 14 ** Licensed under the Apache License, Version 2.0 (the "License"); 15 ** you may not use this file except in compliance with the License. 16 ** You may obtain a copy of the License at 17 ** 18 ** http://www.apache.org/licenses/LICENSE-2.0 19 ** 20 ** Unless required by applicable law or agreed to in writing, software 21 ** distributed under the License is distributed on an "AS IS" BASIS, 22 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 ** See the License for the specific language governing permissions and 24 ** limitations under the License. 25 */ 26 27 typedef struct boot_img_hdr boot_img_hdr; 28 29 #define BOOT_MAGIC "ANDROID!" 30 #define BOOT_MAGIC_SIZE 8 31 #define BOOT_NAME_SIZE 16 32 #define BOOT_ARGS_SIZE 512 33 34 /** 35 * 用于暂存需要的数据的结构体 36 */ 37 struct boot_img_hdr 38 { 39 unsigned char magic[BOOT_MAGIC_SIZE]; //文件类型标识 40 41 unsigned kernel_size; /* size in bytes 内核文件大小 */ 42 unsigned kernel_addr; /* physical load addr 内核文件的起始地址 */ 43 44 unsigned ramdisk_size; /* size in bytes randisk文件的大小*/ 45 unsigned ramdisk_addr; /* physical load addr randisk文件的起始地址 */ 46 47 unsigned second_size; /* size in bytes */ 48 unsigned second_addr; /* physical load addr */ 49 50 unsigned tags_addr; /* physical addr for kernel tags */ 51 unsigned page_size; /* flash page size we assume */ 52 unsigned unused[2]; /* future expansion: should be 0 */ 53 54 unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ 55 56 unsigned char cmdline[BOOT_ARGS_SIZE]; /* 如果U-Boot没有指定bootargs,就会使用这里的参数 */ 57 58 unsigned id[8]; /* timestamp / checksum / sha1 / etc 个人感觉主要用于保存校验数据 */ 59 }; 60 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #include <fcntl.h> 66 #include <errno.h> 67 68 #include "mincrypt/sha.h" 69 #include "bootimg.h" 70 71 /** 72 * 主要用于加载各种需要的文件的函数 73 */ 74 static void *load_file(const char *fn, unsigned *_sz) 75 { 76 char *data; 77 int sz; 78 int fd; 79 80 data = 0; 81 fd = open(fn, O_RDONLY); //带开文件 82 if(fd < 0) return 0; 83 84 sz = lseek(fd, 0, SEEK_END); //跳到文件末尾,这样就知道文件的大小了 85 if(sz < 0) goto oops; 86 87 if(lseek(fd, 0, SEEK_SET) != 0) goto oops; //返回到文件头 88 89 data = (char*) malloc(sz); //分配和文件一样大小的内存空间 90 if(data == 0) goto oops; 91 92 if(read(fd, data, sz) != sz) goto oops; //将文件内容读取到内存中 93 close(fd); 94 95 if(_sz) *_sz = sz; //相当于返回文件的大小 96 return data; //返回文件数据的首地址 97 98 oops: 99 close(fd); 100 if(data != 0) free(data); 101 return 0; 102 } 103 104 /** 105 * mkbootimg使用说明 106 */ 107 int usage(void) 108 { 109 fprintf(stderr,"usage: mkbootimg\n" 110 " --kernel <filename>\n" 111 " --ramdisk <filename>\n" 112 " [ --second <2ndbootloader-filename> ]\n" 113 " [ --cmdline <kernel-commandline> ]\n" 114 " [ --board <boardname> ]\n" 115 " [ --base <address> ]\n" 116 " [ --pagesize <pagesize> ]\n" 117 " -o|--output <filename>\n" 118 ); 119 return 1; 120 } 121 122 123 124 /** 125 * boot.img中每部分数据都是以页为单位存储的,如果数据不足一页的倍数, 126 * 那么就将该页剩下的空间以0填充 127 */ 128 static unsigned char padding[4096] = { 0, }; 129 130 int write_padding(int fd, unsigned pagesize, unsigned itemsize) 131 { 132 /** 133 * pagesize一般都是比较大的整数,例如:1k,2k,4k等等,那么 134 * pagesize-1,就变成了由0开始,后面跟了一堆1,例如: 135 * 1k = 10000000000(二进制); 136 * 1k-1 = 01111111111(二进制); 137 */ 138 unsigned pagemask = pagesize - 1; 139 unsigned count; 140 141 /** 142 * 由上面的解析可知,这里是确保itemsize需要填充的0的个数 143 * 小于pagesize并且大于0 144 * itemsize&pagemask相当于itemsize对pagemask取余 145 */ 146 if((itemsize & pagemask) == 0) { 147 return 0; 148 } 149 150 /** 151 * 计算出需要填充多少个0, itemsize&pagemask相当于itemsize对pagemask取余 152 */ 153 count = pagesize - (itemsize & pagemask); 154 155 if(write(fd, padding, count) != count) { 156 return -1; 157 } else { 158 return 0; 159 } 160 } 161 162 int main(int argc, char **argv) 163 { 164 boot_img_hdr hdr; 165 166 char *kernel_fn = 0; 167 void *kernel_data = 0; 168 char *ramdisk_fn = 0; 169 void *ramdisk_data = 0; 170 char *second_fn = 0; 171 void *second_data = 0; 172 char *cmdline = ""; 173 char *bootimg = 0; 174 char *board = ""; 175 unsigned pagesize = 2048; //默认一页占用2K 176 int fd; 177 SHA_CTX ctx; 178 uint8_t* sha; 179 unsigned base = 0x10000000; //基址 180 unsigned kernel_offset = 0x00008000; 181 unsigned ramdisk_offset = 0x01000000; 182 unsigned second_offset = 0x00f00000; 183 unsigned tags_offset = 0x00000100; 184 185 //可执行文件必须有参数,需知道内核文件在那里,ramdisk在那里等等信息 186 argc--; 187 argv++; 188 189 memset(&hdr, 0, sizeof(hdr)); //清空结构体数据 190 191 /** 192 * 获取命令行参数 193 */ 194 while(argc > 0){ 195 char *arg = argv[0]; 196 char *val = argv[1]; 197 if(argc < 2) { 198 return usage(); 199 } 200 argc -= 2; 201 argv += 2; 202 if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) { 203 bootimg = val; 204 } else if(!strcmp(arg, "--kernel")) { 205 kernel_fn = val; 206 } else if(!strcmp(arg, "--ramdisk")) { 207 ramdisk_fn = val; 208 } else if(!strcmp(arg, "--second")) { 209 second_fn = val; 210 } else if(!strcmp(arg, "--cmdline")) { 211 cmdline = val; 212 } else if(!strcmp(arg, "--base")) { 213 base = strtoul(val, 0, 16); 214 } else if(!strcmp(arg, "--kernel_offset")) { 215 kernel_offset = strtoul(val, 0, 16); 216 } else if(!strcmp(arg, "--ramdisk_offset")) { 217 ramdisk_offset = strtoul(val, 0, 16); 218 } else if(!strcmp(arg, "--second_offset")) { 219 second_offset = strtoul(val, 0, 16); 220 } else if(!strcmp(arg, "--tags_offset")) { 221 tags_offset = strtoul(val, 0, 16); 222 } else if(!strcmp(arg, "--board")) { 223 board = val; 224 } else if(!strcmp(arg,"--pagesize")) { 225 pagesize = strtoul(val, 0, 10); 226 if ((pagesize != 2048) && (pagesize != 4096)) { //页大小只能是两种情况,2k或者4k 227 fprintf(stderr,"error: unsupported page size %d\n", pagesize); 228 return -1; 229 } 230 } else { 231 return usage(); 232 } 233 } 234 hdr.page_size = pagesize; //设置页大小 235 236 /** 237 * 计算各种偏移地址 238 */ 239 hdr.kernel_addr = base + kernel_offset; 240 hdr.ramdisk_addr = base + ramdisk_offset; 241 hdr.second_addr = base + second_offset; 242 hdr.tags_addr = base + tags_offset; 243 244 if(bootimg == 0) { 245 fprintf(stderr,"error: no output filename specified\n"); 246 return usage(); 247 } 248 249 if(kernel_fn == 0) { 250 fprintf(stderr,"error: no kernel image specified\n"); 251 return usage(); 252 } 253 254 if(ramdisk_fn == 0) { 255 fprintf(stderr,"error: no ramdisk image specified\n"); 256 return usage(); 257 } 258 259 if(strlen(board) >= BOOT_NAME_SIZE) { 260 fprintf(stderr,"error: board name too large\n"); 261 return usage(); 262 } 263 264 strcpy(hdr.name, board); //板子类型 265 266 memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); //文件类型 267 268 //kernel命令行参数,如果U-Boot没有给出,将是用这里的参数 269 if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) { 270 fprintf(stderr,"error: kernel commandline too large\n"); 271 return 1; 272 } 273 strcpy((char*)hdr.cmdline, cmdline); 274 275 //加载内核文件,同时获取内核文件的大小 276 kernel_data = load_file(kernel_fn, &hdr.kernel_size); 277 if(kernel_data == 0) { 278 fprintf(stderr,"error: could not load kernel '%s'\n", kernel_fn); 279 return 1; 280 } 281 282 if(!strcmp(ramdisk_fn,"NONE")) { 283 ramdisk_data = 0; 284 hdr.ramdisk_size = 0; 285 } else { 286 ramdisk_data = load_file(ramdisk_fn, &hdr.ramdisk_size); 287 if(ramdisk_data == 0) { 288 fprintf(stderr,"error: could not load ramdisk '%s'\n", ramdisk_fn); 289 return 1; 290 } 291 } 292 293 if(second_fn) { 294 second_data = load_file(second_fn, &hdr.second_size); 295 if(second_data == 0) { 296 fprintf(stderr,"error: could not load secondstage '%s'\n", second_fn); 297 return 1; 298 } 299 } 300 301 /* put a hash of the contents in the header so boot images can be 302 * differentiated based on their first 2k. 303 */ 304 /** 305 * 个人理解这里是产生一些校验数据,可以不用关心,不影响阅读 306 */ 307 SHA_init(&ctx); 308 SHA_update(&ctx, kernel_data, hdr.kernel_size); 309 SHA_update(&ctx, &hdr.kernel_size, sizeof(hdr.kernel_size)); 310 SHA_update(&ctx, ramdisk_data, hdr.ramdisk_size); 311 SHA_update(&ctx, &hdr.ramdisk_size, sizeof(hdr.ramdisk_size)); 312 SHA_update(&ctx, second_data, hdr.second_size); 313 SHA_update(&ctx, &hdr.second_size, sizeof(hdr.second_size)); 314 sha = SHA_final(&ctx); 315 memcpy(hdr.id, sha, 316 SHA_DIGEST_SIZE > sizeof(hdr.id) ? sizeof(hdr.id) : SHA_DIGEST_SIZE); 317 318 /** 319 * 打开输出目标文件,如果文件不存在,那么就创建该文件,如果存在,那么就清空 320 */ 321 fd = open(bootimg, O_CREAT | O_TRUNC | O_WRONLY, 0644); 322 if(fd < 0) { 323 fprintf(stderr,"error: could not create '%s'\n", bootimg); 324 return 1; 325 } 326 327 /** 328 * 这里相当于写入文件头,和写bmp文件差不多的意思 329 */ 330 if(write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) goto fail; 331 if(write_padding(fd, pagesize, sizeof(hdr))) goto fail; 332 333 /** 334 * 接下来按一定顺序写内容 335 */ 336 if(write(fd, kernel_data, hdr.kernel_size) != hdr.kernel_size) goto fail; 337 if(write_padding(fd, pagesize, hdr.kernel_size)) goto fail; 338 339 if(write(fd, ramdisk_data, hdr.ramdisk_size) != hdr.ramdisk_size) goto fail; 340 if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail; 341 342 if(second_data) { 343 if(write(fd, second_data, hdr.second_size) != hdr.second_size) goto fail; 344 if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail; 345 } 346 347 return 0; 348 349 fail: 350 unlink(bootimg); 351 close(fd); 352 fprintf(stderr,"error: failed writing '%s': %s\n", bootimg, 353 strerror(errno)); 354 return 1; 355 }