XML上的安全不容忽视

在本文中,讲解了XML上一些攻击方式:

(1). XML拒绝服务攻击 -- 通过递归包含自己xml的某一项来实现

(2).XML注入攻击 -- 用户可控部分插入xml注释字符,实现用户不可控变量的控制.

(3). XML外部实体注入攻击 -- XXE(XML_External_Entity),

通过XXE完成非法文件的读取

通过XXE完成SSRF(Server Side Request Forgery)

 

原文链接:http://sec.chinabyte.com/296/12697796.shtml

2013年8月21号第二届KCon Web安全会议在北京金台饭店举行。KCon Web安全大会是一个交流前沿、边缘、主流的Web安全技术、技巧、案例的开放安全会议,在第一届KCon Web安全大会上天融信阿尔法实验室研究人员就做了关于浏览器上的安全议题演讲《浏览器魔术》。本届大会有各个行业安全圈人士500人到场参加,远远超于预期的300人规模。大会上天融信阿尔法实验室张晨做了《Having Fun with XML Hacking》议题演讲,内容非常精彩。本文把演讲的内容稍加整理,与大家分享。

XML的全称是eXtensible Markup Language,意思是可扩展的标记语言,它是标准通用标记语言(Standard Generalized Markup Language,SGML)的一个子集。

在80年代早期,IBM提出在各文档之间共享一些相似的属性,例如字体大小和版面。IBM设计了一种文档系统,通过在文档中添加标记,来标识文档中的各种元素,IBM把这种标识语言称作通用标记语言(Standard Generalized Markup Language,SGML),即GML。经过若干年的发展,1984年国际标准化阻止(ISO)开始对此提案进行讨论,并于1986年正式发布了为生成标准化文档而定义的标记语言标准(ISO 8879),称为新的语言SGML,即标准通用标记语言。

一个简单的XML文件示例,一个XML文件由XML声明、文档类型定义和文档元素组成。

  那么什么是文档类型定义呢?文档类型定义,也叫DTD(Document Type Definition),可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构,DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。

DTD文档的声明及引用

内部DTD文档

      <!DOCTYPE 根元素  [定义内容]>

外部DTD文档

      <!DOCTYPE 根元素  SYSTEM “DTD文件路径“>

内外部DTD文档结合           

      <!DOCTYPE 根元素  SYSTEM “DTD文件路径” [

             定义内容

-]>

XML被设计用来存储和传输数据,任何平台上的程序都可以通过使用XML解析器来处理XML数据,XML的使用范围非常广泛,不仅仅是web应用,还包括数据库软件、浏览器等等。既然XML的覆盖面这么广,那么如果出现安全问题也是相当可怕的。而且很多软件都是使用的同一款XML解析库,如果这套解析库存在漏洞,那么无疑又将影响的范围扩大了。

 

下面这张图很清晰的描述了程序在处理XML时容易出现问题的点,我们逐步来看:应用先将原始数据交给XML生成器,生成XML数据,然后将生成后的XML数据提交到web服务器,web服务器接收到XML数据后,将XML数据交给XML解析器,由XML解析器完成对XML数据的解析后把数据再返回给应用。在整个数据处理流程中,XML生成器和XML解析器这两个点是最容易出现问题的。

  在XML解析的过程中,最常见的有三种漏洞:

拒绝服务漏洞

XML注入

XML外部实体注入

XML拒绝服务漏洞

我们逐个分析,先来看拒绝服务攻击。

下图是一个典型拒绝服务攻击的payload:

  可以看到,payload中先定义了lol实体,值为“lol”字符串,然后下面又定义了lol2实体,在lol2实体中,引用10个lol实体,也就是说现在lol2的值是10个“lol”字符串,下面的lol3又引用了10个lol2实体的值,现在lol3的值是100个“lol”字符串,依此类推,到了最后在lolz元素中引用的lol9中,就会存在上亿个“lol”字符串,如果程序在解析数据时没有做特别的处理,那么极有可能对程序造成拒绝服务攻击。

 XML注入漏洞

再来看XML注入的例子:

  例图这是一个标准的xml文件,即将被提交到web服务器中,我们假设这是一个存有订单信息的xml文件,其中price的值不可控,quantity和address项的值可控,可以由用户自由输入,那么这时,攻击者可以实施xml注入攻击,在quantity的值中构造“<!–”,注释符,然后在address的值中先闭合前面的注释符,然后再重新根据原xml文件结构重新构造xml文件,这时候price元素的值也是可以由攻击者随意构造的,如下图:

可以看到,我们重新构造出了一个xml文件,这时候price的值由原来的25变成了0.01,也就是说,订单的价格被成功的修改了。

XML外部实体注入
在众多xml漏洞中,最容易出现的就是XXE(XML_External_Entity),也就是XML外部实体注入攻击。在文档类定定义部分,可以引用外部的dtd文件,dtd文件的路径可以是URL地址,也可以使用多种协议,所以在这里会出现安全问题。

上图为一个标准的XXE Payload,可以看到,这里引用的外部实体为file:///etc/passwd,如果XML解析库在解析到这里时允许引用外部实体,那么程序将会请求这个地址,并将内容返回。

    对于XXE漏洞,我们还有更多的利用技巧,比如某些场景下,我们需要在一次请求中读取多个文件,那么可以用下面的payload来实现:

XXE不光可以读取文件,还可以通过SSRF来完成更深入的攻击。SSRF(Server Side Request Forgery)被称为服务端请求伪造攻击,与CSRF不同的是它的请求是由服务器发起的,并不是由客户端发起。

             

通过这张图我们可以看到,攻击者先发送包含payload的请求到前端,前端接到数据后将xml数据发送到后端的xml解析器进行解析,这时payload被执行,payload执行后,结果可能会直接被后端返回给攻击者,也可能由payload中所指向的服务器返回给攻击者。

     更常见的攻击场景如下:

攻击者想要访问主机B上的服务,但是由于存在防火墙的原因无法直接访问,这是可以借助主机A来发起SSRF攻击,通过主机A向主机B发起请求,从而完成攻击。
SSRF攻击常用的payload如下:

Portscan实体是通过http协议来访问主机的某些端口,通过返回信息来判断端口是否开放。Smb实体是访问内网中的共享资源,Sqli实体是对内网中web服务器发起sql注入攻击,Syslog实体可以像内网syslog服务器添加垃圾日志。

除了这些,还可以扩展思路,比如对内网中的zabbix_agent进行攻击(在外部实体引用 gopher://ip:10050/1system.run[ls]),即可在该主机执行系统命令。

在PHP环境下,还可以利用封装协议来直接执行系统命令,如expect://。也可以用php://来读取文件内容。
<!ENTITY readfile SYSTEM ‘php://filter/convert.base64-encode/resource=web.xml’>
除了这些,还支持其他很多协议,可以自由发挥编写payload。

随着应用越来越庞大、逻辑越来越复杂,有些需求要用到XML来实现,但很多开发人员还没有充分意识到在使用和解析XML文件时可能会导致安全问题出现,XML漏洞也是一个比较好的研究方向。

天融信阿尔法实验室作为北京天融信前沿安全研究部门,在安全漏洞研究发掘、最新Web安全及浏览器安全问题研究、软件逆向研究等诸多领域积累了大量的成果,获得了国际CVE漏洞编号、中国国家漏洞库漏洞编号的各类型原创漏洞证书。在所发现的不同类型的安全漏洞中,不乏大量的国产软件,阿尔法实验室都在第一时间通知了软件厂商,并提供修补建议,为保障国产软件的安全性做出了贡献。同时作为国家不同部委类型单位的技术服务支撑成员,天融信阿尔法实验室为国家相关部门提供了不同类型技术支持,在安全漏洞研究、安全趋势研究判断、恶意非法软件分析以及移动设备安全等技术领域做出了突出的贡献。

mocking-the-builtin-open-used-as-a-context-manager

转载自:

http://www.voidspace.org.uk/python/mock/compare.html#mocking-the-builtin-open-used-as-a-context-manager

python mock上的事情都可以到这个页面去找解决方案哦~~~

 

Mocking the builtin open used as a context manager

Example for mock only (so far):

>>> # mock
>>> my_mock = mock.MagicMock()
>>> with mock.patch('__builtin__.open', my_mock):
...     manager = my_mock.return_value.__enter__.return_value
...     manager.read.return_value = 'some data'
...     with open('foo') as h:
...         data = h.read()
...
>>> data
'some data'
>>> my_mock.assert_called_once_with('foo')

or:

>>> # mock
>>> with mock.patch('__builtin__.open') as my_mock:
...     my_mock.return_value.__enter__ = lambda s: s
...     my_mock.return_value.__exit__ = mock.Mock()
...     my_mock.return_value.read.return_value = 'some data'
...     with open('foo') as h:
...         data = h.read()
...
>>> data
'some data'
>>> my_mock.assert_called_once_with('foo')
>>> # Dingus
>>> my_dingus = dingus.Dingus()
>>> with dingus.patch('__builtin__.open', my_dingus):
...     file_ = open.return_value.__enter__.return_value
...     file_.read.return_value = 'some data'
...     with open('foo') as h:
...         data = f.read()
...
>>> data
'some data'
>>> assert my_dingus.calls('()', 'foo').once()
>>> # fudge
>>> from contextlib import contextmanager
>>> from StringIO import StringIO
>>> @contextmanager
... def fake_file(filename):
...     yield StringIO('sekrets')
...
>>> with fudge.patch('__builtin__.open') as fake_open:
...     fake_open.is_callable().calls(fake_file)
...     with open('/etc/password') as f:
...         data = f.read()
...
fake:__builtin__.open
>>> data
'sekrets'

[转]定制我的Nexus系统之boot.img的前世今生

在本文中, 关于boot.img的生成,作用,修改方法以及boot.img(kernel.img + ramdisk.img)的头格式解析方法等非常值得学习借鉴!!

转自: http://blog.csdn.net/ttxgz/article/details/7742696

今天,我们来看一下boot.img的生成,作用,和修改方法。

1.首先是build/core/main.mk

 

  1. .PHONY: bootimage
  2. bootimage: $(INSTALLED_BOOTIMAGE_TARGET)

如果我们把out/target/product/crespo/boot.img去掉,再运行make bootimage, 那么会重新生成boot.img。然后我们看build/core/Makefile里

 

 

  1. INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
  2. ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)
  3. tmp_dir_for_image := $(call intermediates-dir-for,EXECUTABLES,boot_img)/bootimg
  4. INTERNAL_BOOTIMAGE_ARGS += –tmpdir $(tmp_dir_for_image)
  5. INTERNAL_BOOTIMAGE_ARGS += –genext2fs $(MKEXT2IMG)
  6. $(INSTALLED_BOOTIMAGE_TARGET): $(MKEXT2IMG) $(INTERNAL_BOOTIMAGE_FILES)
  7.         $(call pretty,”Target boot image: $@”)
  8.         $(hide) $(MKEXT2BOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) –output $@
  9. else # TARGET_BOOTIMAGE_USE_EXT2 != true
  10. $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
  11.         $(call pretty,”Target boot image: $@”)
  12.         $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) –output $@
  13.         $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)
  14. endif # TARGET_BOOTIMAGE_USE_EXT2

可知INSTALLED_BOOTIMAGE_TARGET的对象就是out/target/product/crespo/boot.img, 有可能用2种方式生成,一种是ext2格式,另外一种则不是。根据添加调试打印,crespo采用的不是ext2文件格式,因此我们来看一下$(MKBOOTIMG)。

 

  1. $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
  2.         $(call pretty,”Target boot image: $@”)
  3.         $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) –output $@
  4.         $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)

展开来就是

 

 

  1. out/target/product/crespo/boot.img:out/host/linux-x86/bin/mkbootimg out/target/product/crespo/kernel out/target/product/crespo/ramdisk.img
  2. $(call pretty, “Target boot image: $@”)
  3. $(hide) –kernel out/target/product/crespo/kernel –ramdisk out/target/product/crespo/ramdisk.img –cmdline “console=ttyFIQ0 no_console_suspend” –base 0x30000000 –pagesize 4096 –output $@

其中各变量的含义分别为:

 

 

  1. INSTALLED_BOOTIMAGE_TARGET:out/target/product/crespo/boot.img
  2. MKBOOTIMG:out/host/linux-x86/bin/mkbootimg
  3. INTERNAL_BOOTIMAGE_FILES:out/target/product/crespo/kernel out/target/product/crespo/ramdisk.img
  4. INTERNAL_BOOTIMAGE_ARGS: –kernel out/target/product/crespo/kernel –ramdisk out/target/product/crespo/ramdisk.img –cmdline “console=ttyFIQ0 no_console_suspend” –base 0x30000000 –pagesize 4096
  5. BOARD_BOOTIMAGE_PARTITION_SIZE:

因此,其实boot.img是由两个文件out/target/product/crespo/kernel和out/target/product/crespo/ramdisk.img两个文件组成的。

 

 

2.我们接着看mkbootimg这个工具

这个工具的源码为system/core/mkbootimg/mkbootimg.c, 其include了system/core/mkbootimg/bootimg.h, 我们先来看下bootimg.h的其中一段说明

 

  1. /*
  2. ** +—————–+
  3. ** | boot header     | 1 page
  4. ** +—————–+
  5. ** | kernel          | n pages
  6. ** +—————–+
  7. ** | ramdisk         | m pages
  8. ** +—————–+
  9. ** | second stage    | o pages
  10. ** +—————–+
  11. **
  12. ** n = (kernel_size + page_size – 1) / page_size
  13. ** m = (ramdisk_size + page_size – 1) / page_size
  14. ** o = (second_size + page_size – 1) / page_size
  15. **
  16. ** 0. all entities are page_size aligned in flash
  17. ** 1. kernel and ramdisk are required (size != 0)
  18. ** 2. second is optional (second_size == 0 -> no second)
  19. ** 3. load each element (kernel, ramdisk, second) at
  20. **    the specified physical address (kernel_addr, etc)
  21. ** 4. prepare tags at tag_addr.  kernel_args[] is
  22. **    appended to the kernel commandline in the tags.
  23. ** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
  24. ** 6. if second_size != 0: jump to second_addr
  25. **    else: jump to kernel_addr
  26. */

显然boot.img由header,kernel,ramdisk,second stage组成,每个段都要补全为page_size的整数倍大小。其中header指明了包含了什么呢?看一下以下的代码(system/core/mkbootimg/bootimg.h):

 

 

  1. typedef struct boot_img_hdr boot_img_hdr;
  2. #define BOOT_MAGIC “ANDROID!”
  3. #define BOOT_MAGIC_SIZE 8
  4. #define BOOT_NAME_SIZE 16
  5. #define BOOT_ARGS_SIZE 512
  6. struct boot_img_hdr
  7. {
  8.     unsigned char magic[BOOT_MAGIC_SIZE];
  9.     unsigned kernel_size;  /* size in bytes */
  10.     unsigned kernel_addr;  /* physical load addr */
  11.     unsigned ramdisk_size; /* size in bytes */
  12.     unsigned ramdisk_addr; /* physical load addr */
  13.     unsigned second_size;  /* size in bytes */
  14.     unsigned second_addr;  /* physical load addr */
  15.     unsigned tags_addr;    /* physical addr for kernel tags */
  16.     unsigned page_size;    /* flash page size we assume */
  17.     unsigned unused[2];    /* future expansion: should be 0 */
  18.     unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
  19.     unsigned char cmdline[BOOT_ARGS_SIZE];
  20.     unsigned id[8]; /* timestamp / checksum / sha1 / etc */
  21. };

可知header里包括了各个段的解压出来后存放的地址,和各个段的大小等信息,那么这些值又是在哪里赋值的呢?我们来看一下system/core/mkbootimg/mkbootimg.c里的main函数片段

 

 

  1. int main(int argc, char **argv)
  2. {
  3.    ……
  4.            /* default load addresses */
  5.     hdr.kernel_addr =  0x10008000;
  6.     hdr.ramdisk_addr = 0x11000000;
  7.     hdr.second_addr =  0x10F00000;
  8.     hdr.tags_addr =    0x10000100;
  9.     while(argc > 0){
  10.         char *arg = argv[0];
  11.         char *val = argv[1];
  12.         if(argc < 2) {
  13.             return usage();
  14.         }
  15.         argc -= 2;
  16.         argv += 2;
  17.         if(!strcmp(arg, “–output”) || !strcmp(arg, “-o”)) {
  18.             bootimg = val;
  19.         } else if(!strcmp(arg, “–kernel”)) {
  20.             kernel_fn = val;
  21.         } else if(!strcmp(arg, “–ramdisk”)) {
  22.             ramdisk_fn = val;
  23.         } else if(!strcmp(arg, “–second”)) {
  24.             second_fn = val;
  25.         } else if(!strcmp(arg, “–cmdline”)) {
  26.             cmdline = val;
  27.         } else if(!strcmp(arg, “–base”)) {
  28.             unsigned base = strtoul(val, 0, 16);
  29.             hdr.kernel_addr =  base + 0x00008000;
  30.             hdr.ramdisk_addr = base + 0x01000000;
  31.             hdr.second_addr =  base + 0x00F00000;
  32.             hdr.tags_addr =    base + 0x00000100;
  33.         } else if(!strcmp(arg, “–board”)) {
  34.             board = val;
  35.         } else if(!strcmp(arg,”–pagesize”)) {
  36.             pagesize = strtoul(val, 0, 10);
  37.             if ((pagesize != 2048) && (pagesize != 4096)) {
  38.                 fprintf(stderr,”error: unsupported page size %d\n”, pagesize);
  39.                 return -1;
  40.             }
  41.         } else {
  42.             return usage();
  43.         }
  44.     }
  45.     hdr.page_size = pagesize;
  46.     ……..
  47. }

由上可知,如果有传参数–base, 那么  tag:base + 0x100 –> kernel:base +0x80000 -> second:base + 0x00f0_0000 -> ramdisk:base + 0x0100_0000, 如果没有传参数,则相当于base=0x1000_0000。结合上面的打印参数,crespo把base_addr设为0x3000_0000, pagesize设为4096。所以,我们不妨打开boot.img文件, 看到下面的内容:

 

 

  1. 0000000: 414e 4452 4f49 4421 602d 3300 0080 0030  ANDROID!`-3….0
  2. 0000010: 787f 0200 0000 0031 0000 0000 0000 f030  x……1…….0
  3. 0000020: 0001 0030 0010 0000 0000 0000 0000 0000  …0…………
  4. 0000030: 0000 0000 0000 0000 0000 0000 0000 0000  …………….
  5. 0000040: 636f 6e73 6f6c 653d 7474 7946 4951 3020  console=ttyFIQ0
  6. 0000050: 6e6f 5f63 6f6e 736f 6c65 5f73 7573 7065  no_console_suspe
  7. 0000060: 6e64 0000 0000 0000 0000 0000 0000 0000  nd…………..
  8. 0000070: 0000 0000 0000 0000 0000 0000 0000 0000  …………….
  9. 0000080: 0000 0000 0000 0000 0000 0000 0000 0000  …………….
  10. 0000090: 0000 0000 0000 0000 0000 0000 0000 0000  …………….

可以知道一下信息:

 

 

  1. magic[BOOT_MAGIC_SIZE]       =  “ANDROID!”
  2. kernel_size                  =   0x0033_2d06
  3. kernel_addr                  =   0x3000_8000
  4. ramdisk_size                 =   0x0002_7f78
  5. ramdisk_addr                 =   0x3100_0000
  6. second_size                  =   0x0
  7. second_addr                  =   0x30f0_0000
  8. tags_addr                    =   0x3000_0100
  9. page_size                    =   0x0000_1000

与我们之前的分析是一致的。

 

 

3.这些地址的意义

首先,这个BASE_ADDR是在哪里定义的呢?我们从build/core/Makefile看到下面:

 

  1. ifdef BOARD_KERNEL_BASE
  2.   INTERNAL_BOOTIMAGE_ARGS += –base $(BOARD_KERNEL_BASE)
  3. endif

所以 –base指定的addr是由宏BOARD_KERNEL_BASE定义的,我们查找这个宏,可以得知在device/samsung/crespo/BoardConfigCommon.mk这个文件里定义了的:

 

 

  1. BOARD_NAND_PAGE_SIZE := 4096
  2. BOARD_NAND_SPARE_SIZE := 128
  3. BOARD_KERNEL_BASE := 0x30000000
  4. BOARD_KERNEL_PAGESIZE := 4096
  5. BOARD_KERNEL_CMDLINE := console=ttyFIQ0 no_console_suspend

这是一个关于板子的定义,定义了dram的地址,还定义了cpu abi类型,wifi的驱动,板子名字等等跟板子相关的信息。

 

其次,这里简单地说明一下,这里的kernel_addr, ramdisk_addr, tags_addr分别对应了我们平时对于Linux系统所涉及的ZTEXTADDR,INITRD_PHYS,PARAMS_PHYS。

 

4.ramdisk.img如何解压重做

ramdisk.img其实是 文件系统 -> cpio -> gzip的过程,所以解压的话可以运行一下命令,则ramdisk.img被解压到ramdisk目录

 

  1. cp ramdisk.img ramdisk.cpio.gz
  2. gzip -d ramdisk.cpio.gz
  3. mkdir ramdisk
  4. cd ramdisk
  5. cpio -i -F ../ramdisk.cpio

修改ramdisk文件后重做ramdisk.img,可以运行一下命令

 

 

  1. cd ramdisk
  2. cpio -i -t -F ../ramdisk.cpio > ../list
  3. cpio -o -H newc -O ../ramdisk_new.cpio < list
  4. cd ../
  5. gzip ramdisk_new.cpio
  6. mv ramdisk_new.cpio.gz ramdisk_new.img

上面是鉴于ramdisk里没有新建文件的前提。如果有新建或者删除文件,则需要在重新cpio之前修改list里的文件就可,或者用find来生成说明ramdisk里所有打包文件的文件也可以。

 

 

5. boot.img的作用:

要将boot.img的作用,首先要看下flash的分区和android系统的启动流程。首先用adb登上shell, 在cat /proc/mtd,可以看见:

 

  1. shell@android:/ $ cat /proc/mtd
  2. dev:    size   erasesize  name
  3. mtd0: 00200000 00040000 “bootloader”
  4. mtd1: 00140000 00040000 “misc”
  5. mtd2: 00800000 00040000 “boot”
  6. mtd3: 00800000 00040000 “recovery”
  7. mtd4: 1d580000 00040000 “cache”
  8. mtd5: 00d80000 00040000 “radio”
  9. mtd6: 006c0000 00040000 “efs”

“bootloader”是存放bootloader代码,这段代码在启动中最先运行,负责把boot的东西解包拷贝到ddr里然后把舞台让给boot里的kernel

 

“misc”区是有Bootloader Control Block (BCB) ,用于存放recovery引导信息。(猜是在进入recovery模式才挂载)

        “boot”区是存放引导手机启动时的必要的系统,包含一些硬件底层的驱动,主要分为ramdisk和linux内核两大块。(在系统启动后挂载到根目录下)。

        “recovery”区与boot的相似,但是恢复时启动的系统,比boot多了些恢复用的程序与资源。(只在进入recovery模式才挂载)

        “cache”区是缓存空间,程序或系统用到的缓存数据和指令就存放在这,cpu在调用和执行指令时会优先调用这里的。(在系统启动后会挂载到cache/目录)。

“radio”和“efs”分区还不清楚其作用。

“system”区保存了android系统目录的所有数据,机器启动后的全部系统主要都在这里(在系统启动后会挂载到system/目录),这里没有显示,我猜可能挂到mmc里了
“userdata”区将保存了android数据目录中的所有数据(在系统启动后会挂载到data/目录,里面是会有很多应用数据以及用户的preference之类的配置数据,我们在手机设置里看到的手机内存空间就是指这里)。我猜也有可能是挂到mmc里了。

而andoird的启动流程,是这样的:bootloader 把boot分区里的kernel和ramdisk解压到ddr -> 运行Kernel, 进入built-in驱动程序,然后进入init process,切换到user-space,结束kernel循环,进入process scheduling -> android的init process启动,读取init.rc, Native服务启动,开servicemanager,Zygote和system server

所以到这里,boot.img就很清楚了,boot.img就是boot分区的镜像,用于启动的第二部分,即kernel和一个最小文件系统ramdisk

Linux命令之ar – 创建静态库.a文件

用途说明

创建静态库.a文件。用C/C++开发程序时经常用到,但我很少单独在命令行中使用ar命令,一般写在makefile中,有时也会在shell脚 本中用到。关于Linux下的库文件、静态库、动态库以及怎样创建和使用等相关知识,参见本文后面的相关资料【3】《关于Linux静态库和动态库的分析》。

 

常用参数

格式:ar rcs  libxxx.a xx1.o xx2.o

参数r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。【1】

参数c:创建一个库。不管库是否存在,都将创建。

参数s:创建目标文件索引,这在创建较大的库时能加快时间。(补充:如果不需要创建索引,可改成大写S参数;如果.a文件缺少索引,可以使用ranlib命令添加)

 

格式:ar t libxxx.a

显示库文件中有哪些目标文件,只显示名称。

 

格式:ar tv libxxx.a

显示库文件中有哪些目标文件,显示文件名、时间、大小等详细信息。

 

格式:nm -s libxxx.a

显示库文件中的索引表。

 

格式:ranlib libxxx.a

为库文件创建索引表。

 

使用示例

示例一 在shell脚本中使用

 

Bash代码  收藏代码
  1. OS=`uname -r`
  2. ar rcs libhycu.a.$OS *.o

 

 

示例二 在makefile中使用

Makefile代码  收藏代码
  1. $(BIN1): $(BIN1_OBJS)
  2.         ar rcs $@ $^

 

 

示例三 创建并使用静态库

第一步:编辑源文件,test.h test.c main.c。其中main.c文件中包含main函数,作为程序入口;test.c中包含main函数中需要用到的函数。

vi test.h test.c main.c

第二步:将test.c编译成目标文件。

gcc -c test.c

如果test.c无误,就会得到test.o这个目标文件。

第三步:由.o文件创建静态库。

ar rcs libtest.a test.o

第四步:在程序中使用静态库。

gcc -o main main.c -L. -ltest

因为是静态编译,生成的执行文件可以独立于.a文件运行。

第五步:执行。

./main

 

示例四 创建并使用动态库

第一步:编辑源文件,test.h test.c main.c。其中main.c文件中包含main函数,作为程序入口;test.c中包含main函数中需要用到的函数。

vi test.h test.c main.c

第二步:将test.c编译成目标文件。

gcc -c test.c

前面两步与创建静态库一致。

第三步:由.o文件创建动态库文件。

gcc -shared -fPIC -o libtest.so test.o

第四步:在程序中使用动态库。

gcc -o main main.c -L. -ltest

当静态库和动态库同名时, gcc命令将优先使用动态库。

第五步:执行。

LD_LIBRARY_PATH=. ./main

 

示例五 查看静态库中的文件

[root@node56 lib]# ar -t libhycu.a
base64.c.o
binbuf.c.o
cache.c.o
chunk.c.o
codec_a.c.o

xort.c.o
[root@node56 lib]#
[root@node56 lib]# ar -tv libhycu.a
rw-r–r– 0/0   7220 Jul 29 19:18 2011 base64.c.o
rw-r–r– 0/0   2752 Jul 29 19:18 2011 binbuf.c.o
rw-r–r– 0/0  19768 Jul 29 19:18 2011 cache.c.o

rw-r–r– 0/0   4580 Jul 29 19:18 2011 xort.c.o
[root@node56 lib]#

[root@node56 lib]# nm -s libhycu.a | less

Archive index:
Base64Enc in base64.c.o
GetBase64Value in base64.c.o
Base64Dec in base64.c.o
encode64 in base64.c.o
decode64 in base64.c.o
check64 in base64.c.o
test64 in base64.c.o

chunk_alloc in chunk.c.o
[root@node56 lib]#

 

问题思考

相关资料

【1】CSDN文档中心  linux 的库操作命令 ar和nm

【2】linux ar 打包库到另一个库中[转]

【3】我的嵌入式设计家园 关于Linux静态库和动态库的分析

【4】Linux宝库 ar和nm命令的使用

 

[转载] 通过android源代码编译mkbootimg,unpackbootimg,mkbootfs

来源:https://gist.github.com/1087757

 

get Android source code: http://source.android.com/source/downloading.html

 

$ cd /path/to/android-src

$ cd system/core/libmincrypt/

$ gcc -c *.c -I../include

$ ar rcs libmincrypt.a  *.o

$ cd ../mkbootimg

$ gcc mkbootimg.c -o mkbootimg -I../include ../libmincrypt/libmincrypt.a

$ gcc unpackbootimg.c -o unpackbootimg -I../include ../libmincrypt/libmincrypt.a

$ cd ../cpio

$ gcc mkbootfs.c  -o mkbootfs -I../include

 

copy system/core/mkbootimg/mkbootimg, system/core/mkbootimg/unpackbootimg, system/core/cpio/mkbootfs to a directory in your path.

existential type crisis : Diagnosis of the OpenSSL Heartbleed Bug

When I wrote about the GnuTLS bug, I said that this isn’t the last severe TLS stack bug we’d see. I didn’t expect it to be quite this bad, however.

The Heartbleed bug is a particularly nasty bug. It allows an attacker to read up to 64KB of memory, and the security researchers have said:

Without using any privileged information or credentials we were able steal from ourselves the secret keys used for our X.509 certificates, user names and passwords, instant messages, emails and business critical documents and communication.

How could this happen? Let’s read the code and find out.

The bug

The fix starts here, in ssl/d1_both.c:

int            
dtls1_process_heartbeat(SSL *s)
    {          
    unsigned char *p = &s->s3->rrec.data[0], *pl;
    unsigned short hbtype;
    unsigned int payload;
    unsigned int padding = 16; /* Use minimum padding */

So, first we get a pointer to the data within an SSLv3 record. That looks like this:

typedef struct ssl3_record_st
    {
        int type;               /* type of record */
        unsigned int length;    /* How many bytes available */
        unsigned int off;       /* read/write offset into 'buf' */
        unsigned char *data;    /* pointer to the record data */
        unsigned char *input;   /* where the decode bytes are */
        unsigned char *comp;    /* only used with decompression - malloc()ed */
        unsigned long epoch;    /* epoch number, needed by DTLS1 */
        unsigned char seq_num[8]; /* sequence number, needed by DTLS1 */
    } SSL3_RECORD;

Records have a type, a length, and data. Back to dtls1_process_heartbeat:

/* Read type and payload length first */
hbtype = *p++;
n2s(p, payload);
pl = p;

The first byte of the SSLv3 record is the heartbeat type. The macro n2s takes two bytes from p, and puts them in payload. This is actually the length of the payload. Note that the actual length in the SSLv3 record is not checked.

The variable pl is then the resulting heartbeat data, supplied by the requester.

Later in the function, it does this:

unsigned char *buffer, *bp;
int r;

/* Allocate memory for the response, size is 1 byte
 * message type, plus 2 bytes payload length, plus
 * payload, plus padding
 */
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;

So we’re allocating as much memory as the requester asked for: up to 65535+1+2+16, to be precise. The variable bp is going to be the pointer used for accessing this memory. Then:

/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);

The macro s2n does the inverse of n2s: it takes a 16-bit value and puts it into two bytes. So it puts the same payload length requested.

Then it copies payload bytes from pl, the user supplied data, to the newly allocated bparray. After this, it sends this all back to the user. So where’s the bug?

The user controls payload and pl

What if the requester didn’t actually supply payload bytes, like she said she did? What if pl really is only one byte? Then the read from memcpy is going to read whatever memory was near the SSLv3 record and within the same process.

And apparently, there’s a lot of stuff nearby.

There are two ways memory is dynamically allocated with malloc (at least on Linux): using sbrk(2) and using mmap(2). If the memory is allocated with sbrk, then it uses the old heap-grows-up rules and limits what can be found with this, although multiple requests (especially simultaneously) could still find some fun stuff1.

The allocations for bp don’t matter at all, actually. The allocation for pl, however, matters a great deal. It’s almost certainly allocated with sbrk because of the mmapthreshold in malloc. However, interesting stuff (like documents or user info), is very likely to be allocated with mmap and might be reachable from pl. Multiple simultaneous requests will also make some interesting data available.

And your secret keys will probably be available:

The fix

The most important part of the fix was this:

/* Read type and payload length first */
if (1 + 2 + 16 > s->s3->rrec.length)
    return 0; /* silently discard */
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
    return 0; /* silently discard per RFC 6520 sec. 4 */
pl = p;

This does two things: the first check stops zero-length heartbeats. The second check checks to make sure that the actual record length is sufficiently long. That’s it.

Lessons

What can we learn from this?

I’m a fan of C. It was my first programming language and it was the first language I felt comfortable using professionally. But I see its limitations more clearly now than I have ever before.

Between this and the GnuTLS bug, I think that we need to do three things:

  1. Pay money for security audits of critical security infrastructure like OpenSSL
  2. Write lots of unit and integration tests for these libraries
  3. Start writing alternatives in safer languages

Given how difficult it is to write safe C, I don’t see any other options. I would donate to this effort. Would you?


  1. This section originally contained my skepticism about the feasability of a PoC due to the nature of how the heap works via sbrk. Neel Mehta has validated some of my concerns, but there are many reports of secret key discovery out there. 

Sean is a software engineer who is passionate about doing things right. He is currently working on Squadron: an awesome configuration and release management tool for SaaS applications.

内存分配的原理__进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。

如何查看进程发生缺页中断的次数

         用ps -o majflt,minflt -C program命令查看。

          majflt代表major fault,中文名叫大错误,minflt代表minor fault,中文名叫小错误

          这两个数值表示一个进程自启动以来所发生的缺页中断的次数

发成缺页中断后,执行了那些操作?

当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作: 
1、检查要访问的虚拟地址是否合法
2、
查找/分配一个物理页
3、填充物理页内容(读取磁盘,或者直接置0,或者啥也不干)

4、建立映射关系(虚拟地址到物理地址)
重新执行发生缺页中断的那条指令
如果第3步,需要读取磁盘,那么这次缺页中断就是majflt,否则就是minflt。 

内存分配的原理

从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。

1、brk是将数据段(.data)的最高地址指针_edata往高地址推;

2、mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存

     这两种方式分配的都是虚拟内存,没有分配物理内存在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。


在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap,munmap这些系统调用实现的。
下面以一个例子来说明内存分配的原理:

情况一、malloc小于128k的内存,使用brk分配内存,将_edata往高地址推(只分配虚拟空间,不对应物理内存(因此没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系),如下图:

内存分配的原理__进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。 - 无影 - 专注、坚持、思索
1、进程启动的时候,其(虚拟)内存空间的初始布局如图1所示。
      其中,mmap内存映射文件是在堆和栈的中间(例如libc-2.2.93.so,其它数据文件等),为了简单起见,省略了内存映射文件。
      _edata指针(glibc里面定义)指向数据段的最高地址。
2、进程调用A=malloc(30K)以后,内存空间如图2:
      malloc函数会调用brk系统调用,将_edata指针往高地址推30K,就完成虚拟内存分配。
      你可能会问:只要把_edata+30K就完成内存分配了?
      事实是这样的,_edata+30K只是完成虚拟地址的分配,A这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配了A这块内容,然后从来不访问它,那么,A对应的物理页是不会被分配的。
3、
进程调用B=malloc(40K)以后,内存空间如图3。

情况二、malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空闲内存分配(对应独立内存,而且初始化为0),如下图:

内存分配的原理__进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。 - 无影 - 专注、坚持、思索
4、进程调用C=malloc(200K)以后,内存空间如图4:
      默认情况下,malloc函数分配内存,如果请求内存大于128K(可由M_MMAP_THRESHOLD选项调节),那就不是去推_edata指针了,而是利用mmap系统调用,从堆和栈的中间分配一块虚拟内存
      这样子做主要是因为::
      brk分配的内存需要等到高地址内存释放以后才能释放(例如,在B释放之前,A是不可能释放的,这就是内存碎片产生的原因,什么时候紧缩看下面),而mmap分配的内存可以单独释放。
      当然,还有其它的好处,也有坏处,再具体下去,有兴趣的同学可以去看glibc里面malloc的代码了。
5、进程调用D=malloc(100K)以后,内存空间如图5;
6、进程调用free(C)以后,C对应的虚拟内存和物理内存一起释放。
内存分配的原理__进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。 - 无影 - 专注、坚持、思索
7、进程调用free(B)以后,如图7所示:
        B对应的虚拟内存和物理内存都没有释放,因为只有一个_edata指针,如果往回推,那么D这块内存怎么办呢
当然,B这块内存,是可以重用的,如果这个时候再来一个40K的请求,那么malloc很可能就把B这块内存返回回去了
8、进程调用free(D)以后,如图8所示:
        B和D连接起来,变成一块140K的空闲内存。
9、默认情况下:
       当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩,变成图9所示。

[转]HOWTO: Unpack, Edit, and Repack Boot Images

HOWTO: Unpack, Edit, and Re-Pack Boot Images

Several people have already figured out the details on their own, but I have gotten requests to do a more comprehensive tutorial on how the boot and recovery images are structured, and how you can edit them.

Contents

[hide]

Background

Your phone has several devices which hold different parts of the filesystem:

#cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00040000 00020000 "misc"
mtd1: 00500000 00020000 "recovery"
mtd2: 00280000 00020000 "boot"
mtd3: 04380000 00020000 "system"
mtd4: 04380000 00020000 "cache"
mtd5: 04ac0000 00020000 "userdata"

Note that the order is different for different phones! Check yours to make sure you use the right device.

In this tutorial, we will deal with “recovery” and “boot”. “system” holds everything that gets mounted in your system/ directory, and userdata/ is everything that shows up in data/ (this is all the apps you’ve installed, your preferences, etc).

The recovery and boot partitions are at /dev/mtd/mtd1 and /dev/mtd/mtd2, and before you do anything else you should back these up:

# cat /dev/mtd/mtd1 > /sdcard/mtd1.img
# cat /dev/mtd/mtd2 > /sdcard/mtd2.img

(Note added by lxrose: These commands don’t work if your phone is not rooted and the permissions are not set.)

The other thing you should do is put your favorite update.zip file into the root directory of your sd card so that if you screw up your boot partition you can boot into recovery mode and re-apply the update. You probably want one of the pre-rooted recovery images found elsewhere on the forums.

There is also another important file you should know about. In /system/recovery.img there is a full copy of everything that is loaded on mtd1. This file is automatically flashed onto mtd1 every time you shut down. That means two things: 1. Any changes you make directly to /dev/mtd/mtd1 get blown away on reboot and 2. If you want to change /dev/mtd/mtd1 you’re probably better off just sticking the image in /system/recovery.img and rebooting. When creating your own custom update.zip files (especially when adapting the stock images), you can get tripped up if you forget to replace /system/recovery.img and it ends up overwriting /dev/mtd/mtd1 unbeknownst to you. Watch out.

 

Structure of boot and recovery images

The boot and recovery images are not proper filesystems. Instead, they are a custom android format consisting of a 2k header, followed by a gzipped kernel, followed by a ramdisk, followed by a second stage loader (optional, we have not seen these in the wild yet – except on Sanyo Zio). This structure is outlined in mkbootimg.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

0. all entities are page_size aligned in flash
1. kernel and ramdisk are required (size != 0)
2. second is optional (second_size == 0 -> no second)

A ramdisk is basically a small filesystem containing the core files needed to initialize the system. It includes the critical init process, as well as init.rc, which is where you can set many system-wide properties. If you really want to know more about it, here is the documentation. Here’s a list of files on a typical ramdisk:

./init.trout.rc
./default.prop
./proc
./dev
./init.rc
./init
./sys
./init.goldfish.rc
./sbin
./sbin/adbd
./system
./data

The recovery image typically has a few extra files, which constitute the recovery binary and supporting files (the application that gets run if you hold down home+power when rebooting). These files are:

./res
./res/images
./res/images/progress_bar_empty_left_round.bmp
./res/images/icon_firmware_install.bmp
./res/images/indeterminate3.bmp
./res/images/progress_bar_fill.bmp
./res/images/progress_bar_left_round.bmp
./res/images/icon_error.bmp
./res/images/indeterminate1.bmp
./res/images/progress_bar_empty_right_round.bmp
./res/images/icon_firmware_error.bmp
./res/images/progress_bar_right_round.bmp
./res/images/indeterminate4.bmp
./res/images/indeterminate5.bmp
./res/images/indeterminate6.bmp
./res/images/progress_bar_empty.bmp
./res/images/indeterminate2.bmp
./res/images/icon_unpacking.bmp
./res/images/icon_installing.bmp
./sbin/recovery

 

Unpacking, Editing, and Re-Packing the images

Note: below I give you the details for unpacking and repacking manually, but I created two perl scripts that do most of this for you ([1]).

If you are good with a hex editor, you can open up any of these images and strip off the first 2k of data. Then, look for a bunch of zeroes followed by the hex 1F 8B (which is the magic number of a gzip file). Copy everything from the first line of the file, through the zeroes, and stopping at the 1F 8B. That is the kernel. Everything from the 1F 8B through the end is the ramdisk. You could save each of these files separately. In order to see the contents of the ramdisk, you need to un-gzip it and then un-cpio it. You could use a command like this (ideally after creating a new directory and cd’ing into it):

gunzip -c ../your-ramdisk-file | cpio -i

That will place all of the files from the ramdisk in your working directory. You can now edit them.

In order to re-create the ramdisk, you need to re-cpio them and re-gzip those files, with a command like the following (remember, cpio will include everything in the current working directory, so you probably want to remove any other cruft you might have in there):

find . | cpio -o -H newc | gzip > ../newramdisk.cpio.gz

The final step is to combine the kernel and your new ramdisk into the full image, using the mkbootimg program (which you should download and compile from the git repository):

mkbootimg --cmdline 'no_console_suspend=1 console=null' --kernel your-kernel-file --ramdisk newramdisk.cpio.gz -o mynewimage.img

Now, there’s a lot of hassle in pulling apart files in hex editors and remembering all of these commands, so I wrote unpack and repack perl scripts for you. Hooray.

 

Alternative Method

Download split_bootimg.zip . This Zip file contains one Perl file, split_bootimg.pl, which reads the boot.img header (according to the bootimg.h of the Android source code) to extract the kernel and ramdisk. The script also outputs the kernel command line and board name (if specified).

(Note: Do not use a boot.img image extracted directly from /dev/mtd/mtd2. This image may become corrupted during the read process.)

The following example uses the boot.img from the full TC4-RC28 update:

% ./split_bootimg.pl boot.img 
Page size: 2048 (0x00000800)
Kernel size: 1388548 (0x00153004)
Ramdisk size: 141518 (0x000228ce)
Second size: 0 (0x00000000)
Board name: 
Command line: no_console_suspend=1
Writing boot.img-kernel ... complete.
Writing boot.img-ramdisk.gz ... complete.

Extract the ramdisk.

% mkdir ramdisk
% cd ramdisk
% gzip -dc ../boot.img-ramdisk.gz | cpio -i
% cd ..

Make any changes necessary (e.g., set ro.secure=0 in default.prop).

Recreate the cpio archive using the mkbootfs binary produced from building the Android source code (The cpio utility in OS X does not recognize the newc format, therefore mkbootfs is the best option for OS X users).

% mkbootfs ./ramdisk | gzip > ramdisk-new.gz

Recreate the image file using the mkbootimg binary produced from building the Android source code.

% mkbootimg --cmdline 'no_console_suspend=1 console=null' --kernel boot.img-kernel --ramdisk ramdisk-new.gz -o boot-new.img

For Nexus One : Add –base 0x20000000 to mkbootimg command-line.
(Note: the console=null command line option was introduced in the TC4-RC30 boot images to remove the root shell (TODO: add link))

 

Flashing your new image back onto the phone

You will probably only ever be flashing boot images directly to the phone, given the fact that /system/recovery.img automatically flashes the recovery device for you (as noted above). If you have created a new recovery image, just stick it in /system/recovery.img and reboot. If you are flashing a boot image, stick it on your phone viaadb (a tool included in the Android SDK):

adb push ./mynewimage.img /sdcard

Then, open a shell to your phone via ‘adb shell’, get root, and do the following two commands to flash your new boot image:

# cat /dev/zero > /dev/mtd/mtd2
   write: No space left on device [this is ok, you can ignore]
# flash_image boot /sdcard/mynewimage.img

Reboot.

If your phone starts all the way up, congratulations. If not, you did something wrong and you’ll need to boot into recovery mode and apply your update.zip file (reboot while holding down home+power, when you get the recovery screen press alt+L and then alt+S).

Something fun to do with your new found power

If you place a file titled initlogo.rle in the root directory of your boot image, the phone will display this image upon boot (after the “G1” image and before the Android animation). In order to create this file, you need to create a 320×480 image in Photoshop or Gimp and save it as a “raw image” file. You then need to compress that image with the program to565. More details on that here.

This is not the same thing as applying an update.zip

You will see other places on the forums that describe how to create customized update.zip files, as well as update.zip files that people are sharing. For example, there is a recent update.zip which is a modified version of rc30 (with the anti-root aspects disabled). The update.zip files include new boot images, recovery images, and typically replacements for the entire system/ directory as well as other updates. If you are creating a custom boot or recovery image, it is typically a good idea to start with the image distributed with the most recent update you have applied (flashing an image from an older release could have unintended consequences).
Questions?

rk3188–4.android用initrd文件系统启动流程

转自:

http://blog.chinaunix.net/uid-26009923-id-4074053.html

在init/intramfs.c中

  1. static int __init populate_rootfs(void)
  2. {
  3.     unpack_to_rootfs(__initramfs_start, __initramfs_size);  //1. initramfs的解压
  4.     if (initrd_start) {
  5.         unpack_to_rootfs((char *)initrd_start, initrd_end – initrd_start); //2.initrd的解压
  6.         free_initrd();
  7.     }
  8. }
  9. rootfs_initcall(populate_rootfs);   //这个相当于module_init在系统初始化时会调用

1. initramfs的解压
unpack_to_rootfs(__initramfs_start, __initramfs_size);
rootfs_initcall(populate_rootfs);
–> populate_rootfs
–> unpack_to_rootfs
在init/initramfs.c中

  1. static char * __init unpack_to_rootfs(char *buf, unsigned len)
  2. {
  3.     int i;
  4.     int written, res;
  5.     decompress_fn decompress;
  6.     const char *compress_name;
  7.     static __initdata char msg_buf[64];
  8.     header_buf = kmalloc(110, GFP_KERNEL);
  9.     symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
  10.     name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
  11.     state = Start;
  12.     this_header = 0;
  13.     message = NULL;
  14.     while (!message && len) {
  15.         loff_t saved_offset = this_header;
  16.         //如果开头以字符’0’开始,说明这是cpio格式的ram disk,不用解压直接用复制
  17.         if (*buf == ‘0’ && !(this_header & 3)) {
  18.             state = Start;
  19.             written = write_buffer(buf, len);
  20.             buf += written;
  21.             len -= written;
  22.             continue;
  23.         }
  24.     }
  25.     dir_utime();
  26.     kfree(name_buf);
  27.     kfree(symlink_buf);
  28.     kfree(header_buf);
  29.     return message;
  30. }

在initramfs.cpio中打包了3个文件(2个目录 1个字符设备文件):
/dev   目录
/dev/console 文件
/root    目录

  1. dir /dev 0755 0 0
  2. nod /dev/console 0600 0 0 c 5 1
  3. dir /root 0700 0 0

下面来看一下它们是如何依次解出来的:
buf=__initramfs_start, len=__initramfs_size

  1. static int __init write_buffer(char *buf, unsigned len)
  2. {
  3.     count = len;
  4.     victim = buf;
  5.     while (!actions[state]())
  6.         ;
  7.     return len – count;
  8. }

1.1 do_start
因为在initramfs.cpio的文件长度都为0,所以没有do_copy的过程
write_buffer
–> do_start
在init/initramfs.c中

  1. static int __init do_start(void)
  2. {
  3.     //实际作用是将collect指针移动到打包的cpio每一个文件头处
  4.     read_into(header_buf, 110, GotHeader);
  5.     return 0;
  6. }

注意:这个名虽然叫read_into而且第一个参数又是buf,但实际上这个buf没有起到任何作用

  1. static void __init read_into(char *buf, unsigned size, enum state next)
  2. {
  3.     if (count >= size) {
  4.         collected = victim;
  5.         eat(size);
  6.         state = next;      //下一步要执行do_header
  7.     }
  8. }

1.2 do_header解析110字节的头
write_buffer
–> do_start
–> do_header

  1. static int __init do_header(void)
  2. {
  3.     if (memcmp(collected, “070707”, 6)==0) {
  4.         error(“incorrect cpio method used: use -H newc option”);
  5.         return 1;
  6.     }
  7.     if (memcmp(collected, “070701”, 6)) {
  8.         error(“no cpio magic”);
  9.         return 1;
  10.     }
  11.     parse_header(collected);      //从101个字节的头中解析出inod mode uid gid等
  12.     next_header = this_header + N_ALIGN(name_len) + body_len;   //移到下一个文件的头处
  13.     next_header = (next_header + 3) & ~3;              //cpio的头部都是4字节对齐的
  14.     state = SkipIt;
  15.     if (name_len <= 0 || name_len > PATH_MAX)
  16.         return 0;
  17.     if (S_ISLNK(mode)) {
  18.         if (body_len > PATH_MAX)
  19.             return 0;
  20.         collect = collected = symlink_buf;
  21.         remains = N_ALIGN(name_len) + body_len;
  22.         next_state = GotSymlink;
  23.         state = Collect;
  24.         return 0;
  25.     }
  26.     //注意下面这个 !body_len,目录的body_len为0设备文件的body_len也为0
  27.     //所以这儿代表的是,所有非链接文件
  28.     if (S_ISREG(mode) || !body_len)
  29.         read_into(name_buf, N_ALIGN(name_len), GotName);  //这个实际的作用是,将指针移动到下一个文件的头处
  30.     return 0;                                             //并将状态改为GotName,即要调用do_name
  31. }

1.3 do_name建立目录文件
write_buffer
–> do_start
–> do_header
–> do_name
进行到此处,系统中己存在/与/root两个目录(都是虚拟的),此时再把打包在cpio里面的文件解析到系统的相应位置上.

  1. static int __init do_name(void)
  2. {
  3.     state = SkipIt;
  4.     next_state = Reset;
  5.     if (strcmp(collected, “TRAILER!!!”) == 0) {   //判断是不是结尾
  6.         free_hash();
  7.         return 0;
  8.     }
  9.     clean_path(collected, mode);         //把原先有的路径去掉, 相当于rmdir /dev 或 rm /dev/console
  10.     if (S_ISREG(mode)) {
  11.         int ml = maybe_link();
  12.         if (ml >= 0) {
  13.             int openflags = O_WRONLY|O_CREAT;
  14.             if (ml != 1)
  15.                 openflags |= O_TRUNC;
  16.             wfd = sys_open(collected, openflags, mode);    //如果是普通文件打开sys_open
  17.             if (wfd >= 0) {
  18.                 sys_fchown(wfd, uid, gid);                 //设置权限等
  19.                 sys_fchmod(wfd, mode);
  20.                 if (body_len)
  21.                     sys_ftruncate(wfd, body_len);
  22.                 vcollected = kstrdup(collected, GFP_KERNEL);
  23.                 state = CopyFile;                         //最后调用do_copy将文件内容复制过来
  24.             }
  25.         }
  26.     } else if (S_ISDIR(mode)) {                      // 以/dev为例 
  27.         sys_mkdir(collected, mode);                  // 创建 /dev目录
  28.         sys_chown(collected, uid, gid);              // 设置所有者
  29.         sys_chmod(collected, mode);                  // 设置权限
  30.         dir_add(collected, mtime);                   // 更改/dev目录的mtime
  31.     } else if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
  32.         if (maybe_link() == 0) {                     // 以/dev/console为例 
  33.             sys_mknod(collected, mode, rdev);        // 创建 /dev/console结点
  34.             sys_chown(collected, uid, gid);          // 设置所有者
  35.             sys_chmod(collected, mode);              // 设置权限
  36.             do_utime(collected, mtime);              // 更改时间戳
  37.         }
  38.     }
  39.     return 0;
  40. }

1.3 do_skip
write_buffer
–> do_start
–> do_header
–> do_name
–> do_skip

  1. static int __init do_skip(void)
  2. {
  3.     if (this_header + count < next_header) {
  4.         dbmsg();
  5.         eat(count);
  6.         return 1;
  7.     } else {
  8.         dbmsg();
  9.         eat(next_header – this_header);
  10.         state = next_state;
  11.         return 0;
  12.     }
  13. }

1.5 do_reset
write_buffer
–> do_start
–> do_header
–> do_name
–> do_skip
–> do_reset

  1. static int __init do_reset(void)
  2. {
  3.     dbmsg();
  4.     while(count && *victim == ‘\0’)
  5.         eat(1);
  6.     if (count && (this_header & 3))
  7.         error(“broken padding”);
  8.     return 1;
  9. }

2.initrd的解压
2.1 initrd的起始地址的获取
make menuconfig中
General setup  —>
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support

start_kernel
–>setup_arch
在arch/arm/kernel/setup.c中

  1. void __init setup_arch(char **cmdline_p)
  2. {
  3.     struct machine_desc *mdesc;
  4.     mdesc = setup_machine_fdt(__atags_pointer);
  5.     if (!mdesc)
  6.         mdesc = setup_machine_tags(machine_arch_type);  //读取内核参数
  7.     //uboot的参数: init=/init initrd=0x62000000,0x00130000
  8.     //指定了initrd在内存的起始地址0x62000000,长度0x130000
  9.     parse_early_param();
  10.     arm_memblock_init(&meminfo, mdesc); //将物理地址转为虚地址
  11. }

start_kernel
–>setup_arch
–> arm_memblock_init
在arch/arm/mm/init.c中

  1. void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
  2. {
  3. #ifdef CONFIG_BLK_DEV_INITRD
  4.     if (phys_initrd_size) {
  5.         memblock_reserve(phys_initrd_start, phys_initrd_size);
  6.         initrd_start = __phys_to_virt(phys_initrd_start);     //将物理地址0x62000000转为虚地址
  7.         initrd_end = initrd_start + phys_initrd_size;         //end地址+size=0x00130000  
  8.     }
  9. #endif
  10. }

unpack_to_rootfs((char *)initrd_start, initrd_end – initrd_start);
其中initrd_start是uboot传入的参数0x62000000的虚地址
里面的内容是烧入板子的boot.img去掉头8字节与尾4个字节,即out/target/product/rk3188/ramdisk.img
注: boot.img的生成
目录out/target/product/rk30sdk/root存在
a.将root下的每个文件加上cpio头+每个文件的内容,打包成cpios格式
b. 将这个cpio文件用gzip压缩后写到文件ramdisk.img中
c. mkkrnlimg会对ramdisk.img加上8个字节的头标志,尾部加上4个字节
2.2 解压并释放initrd中的文件目录
rootfs_initcall(populate_rootfs);
–> populate_rootfs
–> unpack_to_rootfs
在init/initramfs.c中

  1. static char * __init unpack_to_rootfs(char *buf, unsigned len)
  2. {
  3.     int i;
  4.     int written, res;
  5.     decompress_fn decompress;
  6.     const char *compress_name;
  7.     static __initdata char msg_buf[64];
  8.     header_buf = kmalloc(110, GFP_KERNEL);
  9.     symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
  10.     name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
  11.     if (!header_buf || !symlink_buf || !name_buf)
  12.         panic(“can’t allocate buffers”);
  13.     state = Start;
  14.     this_header = 0;
  15.     message = NULL;
  16.     while (!message && len) {
  17.         loff_t saved_offset = this_header;
  18.         if (*buf == ‘0’ && !(this_header & 3)) {
  19.             //不是cpio格式,zip压缩过的开头不为字符’0′
  20.             continue;
  21.         }
  22.         this_header = 0;
  23.         //以开头的0x1f, 0x8b判断是zip压缩的,找到gunzip
  24.         decompress = decompress_method(buf, len, &compress_name);
  25.         //调用压缩函数进行解压缩,解压后调用flush_buffer拷贝到各个目录
  26.         decompress(buf, len, NULL, flush_buffer, NULL, &my_inptr, error);
  27.         this_header = saved_offset + my_inptr;
  28.         buf += my_inptr;
  29.         len -= my_inptr;
  30.     }
  31.     dir_utime();
  32.     kfree(name_buf);
  33.     kfree(symlink_buf);
  34.     kfree(header_buf);
  35.     return message;
  36. }

do_start
do_header
do_name
do_copy
do_utime
do_skip
do_reset
这儿的wite_buffer,比initramfs的write_buffer多了一个do_copy的过程
因为initramfs中只有名,没有数据.initrd有数据,所以需要将数据复制过去.

  1. static int __init flush_buffer(void *bufv, unsigned len)
  2. {
  3.     char *buf = (char *) bufv;
  4.     int written;
  5.     int origLen = len;
  6.     if (message)
  7.         return -1;
  8.     while ((written = write_buffer(buf, len)) < len && !message) {
  9.         char c = buf[written];
  10.         if (c == ‘0’) {
  11.             buf += written;
  12.             len -= written;
  13.             state = Start;
  14.         } else if (c == 0) {
  15.             buf += written;
  16.             len -= written;
  17.             state = Reset;
  18.         } else
  19.             error(“junk in compressed archive”);
  20.     }
  21.     return origLen;
  22. }