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 }

 

posted on 2015-05-05 08:19  zengjf  阅读(1217)  评论(0编辑  收藏  举报

导航