android root权限破解分析

破解android的root权限的本质是:在系统中加入一个任何用户都可能用于登陆的su命令。或者说替换掉系统中的su程序,因为系统中的默认su程序需要验证实际用户权限,只有root和 shell用户才有权运行系统默认的su程序,其他用户运行都会返回错误。而破解后的su将不检查实际用户权限,这样普通的用户也将可以运行su程序,也可以通过su程序将自己的权限提升。  

root破解没有利用什么Linux内核漏洞(Linux内核不可能有这么大的漏洞存在),可以理解成root破解就是在你系统中植入“木马su”,说它是“木马”一点儿都不为过,假如恶意程序在系统中运行也可以通过su来提升自己的权限的这样的结果将会是灾难性 的。所以一般情况下root过手机都会有一个SuperUser应用程序来让用户管理允许谁获得root权限,也算是给系统加了一层保险吧! 


Android的应用程序入口肯定是Java程序。应用程序的启动者是由系统临时根据Androidmanifest.xml中定义的权限而创建的临时用户。而不像linux那样是使用登陆者的身份启动,从而使得进程具有登陆者的所有权限。这也是Android的安全机制之一。 
新的权限机制也带来新的问题,Android给应用程序的权限是按功能来分,java虽然可以访问文件系统。但由于应用程序本身是临时用户启动,这个临时用户权限十分有限。因此诞生了<越狱/root机器>这样的产物。 

其实root机器不是真正能让你的应用程序具有root权限。它原理就跟linux下的像sudo这样的命令。在系统的bin目录下放个su程序并且属主是root并有suid权限。则通过su执行的命令都具有Android root权限。  

当然使用临时用户权限想把su拷贝的/system/bin目录并改属性并不是一件容易的事情。这里用到2个工具跟2个命令。 
工具就是busybox。不熟悉的同学可以去网上google下。这个太有名了我就不多说了。把busybox拷贝到你有权限访问的目录然后给他赋予4755权限,你就可以用它做很多事了。 
当然busybox不能提升权限,真正提升权限的是ratc这个程序,这个程序中一键root包里面可以找到,作用是rooting在adb的shell。 
网上介绍Ratc的文章不多,它是rage against the cage 的缩写。是真正的提升权限的破解程序。虽然我没看过源代码,但估计是利用adb源代码部分内容来实现的, 
原理估计跟模拟器使用adb shell登陆可以获得root shell差不多。(因为它运行需要adb连接才会成功)。 

使用busybox前先运行ratc,这样运行busybox的UID将是0,也就是root。 
首先把system目录改成可读性的:busybox mount -o remount,rw /system, 
当然你还不能改下面的文件,因为system下文件的所有者都不是你。但你可以偷梁换柱把system下的目录给换掉。 
使用命令Busybox mount -t tmpfs none /system/xbin,呵呵这下xbin目录你随便写了。 
将su跟busybox弄过去cp /data/data/xxx/su /system/xbin。然后赋权限chmod 4755 /system/xbin/su。 
然后使目录生效busybox --install -s /system/xbin,别忘善后busybox mount -o remount,ro /system去掉system可写。 
这样只是临时的,只能用su跟busybox能执行一些原来系统没有权限执行的命令而已。当系统重启后/system/xbin又变为原来的文件。 
真正要改系统的话需要自己写内核代码(相当于windows的驱动程序)。内核文件拥有所有权限。使用busybox命令insmod /data/data/xxx/xxx.ko装载内核文件,你想干嘛就可以干嘛了。 
当然我们不是搞破解的没必要去改别人的机器,我们只是想让自己应用程序具有root权限而已。所以临时的su就可以了。 
我们用c++写一个可执行文件。使用socket可以跟java的程序通讯。然后将需要使用root权限才能执行的代码放在c++程序里,然后java程序中创建新的su进程, 
将c++程序带全路径作为参数1。启动后就可以通过socket调用c++函数去执行你想干的事了。最后程序执行完了别忘了善后busybox umount /system/xbin。 
最后说说要注意的事情,如果机器已经拥有Android root权限的话就不需要做这些事情了,但root过的机器都有装有个权限管理的程序。会弹出对话框。但这个程序管理能力有限,如果不想让他弹出的话。也许可以通过改su文件名来解决。有兴趣的同学不妨试试。 


-------------------------------------------------------------------------------------------------------------------------------------------------------- 


破解手机Root权限是比较简单及安全的,破解Root权限的原理就是在手机的/system/bin/或/system/xbin/目录下放置一个可执行文件“su”,这是一个二进制文件,仅仅在系统中置入这个“su”文件是不会给手机的软件或硬件造成任何故障。 

下面的代码是android系统原版的su中的部分代码,可以看出只允许getuid()为AID_ROOT和AID_SHELL的进程可以使用su进行登陆。 
也就是说,Android系统默认的su程序只能root和shell可以用运行su,这个是安全的。如果把这个限制拿掉,就是root破解了,获取了root权限
。 
/* Until we have something better, only root and the shell can use su. */ 
myuid = getuid(); 
if (myuid != AID_ROOT && myuid != AID_SHELL) { 
fprintf(stderr,"su: uid %d not allowed to su\n", myuid); 
return 1; 


  在Superuser(一个破解程序)这个android程序中的su不再有上面的一部分,这样任何进程都可以使用su进行登陆了。 

还有一部分android程序要使用root权限可能的用法类似于(这个也是Superuser中的一部分代码): 

Process process = Runtime.getRuntime().exec("su"); 
DataOutputStream os = new DataOutputStream(process.getOutputStream()); 
os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system\n"); 
os.writeBytes("busybox cp /data/data/com.koushikdutta.superuser/su /system/bin/su\n"); 
os.writeBytes("busybox chown 0:0 /system/bin/su\n"); 
os.writeBytes("chmod 4755 /system/bin/su\n"); 
os.writeBytes("exit\n"); 
os.flush(); 


而在上面提到的Superuser和android原生的su源码中都有这部分代码: 

if(setgid(gid) || setuid(uid)) { 
fprintf(stderr,"su: permission denied\n"); 
return 1; 


从出上面的分析可以认为破解android的root权限的实质是:在系统中加入一个任何用户都可能用于登陆的su命令。当然这首先要取得root权限才能做到。 
在z4root这个android下的破解android的root权限的程序中有一个rageagainstthecage,这就是破解的关键。 




-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

root破解过程的终极目标是替换掉系统中的su程序。但是要想替换掉系统中su程序本身就是需要root权限的,怎样在root破 解过程中获得root权限,成为我们研究的重点了。 
下面我们先清点一下我们需要破解系统情况,假设需要破解的Android系统具备如下条件: 

    1、可以通过adb连接到设备,一般意味着驱动程序已经安装。 
    2、但是adb获得用户权限是shell用户,而不是root。 

要想理解root破解过程我们首先需要了解一下adb工具,SDK中包含adb(android debug briage)工具,设备端有adbd服务程序后台运行,为开发机的adb程序提供服务,adbd的权限,决定了adb的权限。 
具体用户可查看/system/core/adb下的源码,查看 Android.mk你将会发现adb和adbd其实是一份代码,然后通过宏来编译。 

查看adb.c的adb_main函数你将会发现adbd中有如下代码: 

    int adb_main(int is_daemon) 

   { 

        ...... 

        property_get("ro.secure", value, ""); 

       if (strcmp(value, "1") == 0) { 

           // don't run as root if ro.secure is set... 

           secure = 1; 

           ...... 

       } 

   

      if (secure) { 

           ...... 

           setgid(AID_SHELL); 

           setuid(AID_SHELL); 

           ...... 

       } 

   } 

从中我们可以看到adbd会检测系统的ro.secure属性,如果该属性为1则将会把自己的用户权限降级成shell用户。一般设备出厂的时候在/default.prop文件中都会有: 

    ro.secure=1 

这样将会使adbd启动的时候自动降级成shell用户。 

然后我们再介绍一下adbd在什么时候启动的呢?答案是在init.rc中配置的系统服务,由init进程启动。我们查看init.rc中有如下内容: 

    # adbd is controlled by the persist.service.adb.enable system property 

    service adbd /sbin/adbd 

    disabled 

  在init.rc中配置的系统服务启动的时候都是root权限(因为init进行是root权限,其子程序也是root)。由此我们可以知道在adbd程序在执行: 

    /* then switch user and group to "shell" */ 

    setgid(AID_SHELL); 

    setuid(AID_SHELL); 

代码之前都是root权限,只有执行这两句之后才变成shell权限的。这样我们就可以引出root破解过程中获得root权限的方法了,那就是让以上面setgid和setuid函数执行失败,也就是降级失败,那就继续在root权限下面运行了 。 

这其实利用了一个RageAgainstTheCage漏洞。这里面做一个简单说明: 

    1、出厂设置的ro.secure属性为1,则adbd也将运行在shell用户权限下; 

    2、adb工具创建的进程ratc也运行在shell用户权限下; 

    3、ratc一直创建子进程(ratc创建的子程序也将会运行在shell用户权限下),紧接着子程序退出,形成僵尸进程,占用shell用户的进程资源,直到到达shell用户的进程数为 RLIMIT_NPROC的时候(包括adbd、ratc及其子程序),这是ratc将会创建子进程失败。 
    这时候杀掉adbd,adbd进程因为是 Android系统服务,将会被Android系统自动重启,这时候ratc也在竞争产生子程序。在adbd程序执行上面setgid和setuid之 前,ratc已经创建了一个新的子进程,那么shell用户的进程限额已经达到,则adbd进程执行setgid和setuid将会失败。根据代码我们发 现失败之后adbd将会继续执行。 
    这样adbd进程将会运行在root权限下面了。 

    3、这时重新用adb连接设备,则adb将会运行在root权限下面了。 

通过上面的介绍我们发现利用RageAgainstTheCage漏洞,可以使adbd获得root权限,也就是adb获得了root权限。 
拿到root权限剩下的问题就好办了,复制破解之后的su程序到系统中,都是没有什么技术含量的事情了。 

其实堵住adbd的这个漏洞其实也挺简单的: 

    /* then switch user and group to "shell" */ 

    if (setgid(AID_SHELL) != 0) { 

       exit(1); 

  } 

    if (setuid(AID_SHELL) != 0) { 

      exit(1); 

  } 

如果发现setgid和setuid函数执行失败,则adbd进程异常退出,就把这个漏洞给堵上了。 
为什么这么多设备都没有堵上这个漏洞呢?我觉得是设备厂商的策略,虽然知道怎么封堵漏洞但是就是留着个后门给大家,让第三方给自己定制 rom,提高自己系统的易用性。 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

去年的Android adb setuid提权漏洞被用于各类root刷机,漏洞发现人Sebastian Krahmer公布的利用工具RageAgainstTheCage(rageagainstthecage-arm5.bin)被用于z4root等提权工具、Trojan.Android.Rootcager等恶意代码之中。下面我们来分析这一漏洞的产生原因。 

The Android Exploid Crew小组在后来发布了一份PoC代码:rageagainstthecage.c。从这份代码开始着手。 
在main(:72)函数中,首先获取了RLIMIT_NPROC的值(:83),这个值是Linux内核中定义的每个用户可以运行的最大进程数。 
然后,调用find_adb()函数(:94)来搜索Android系统中adb进程的PID,具体而言,该函数读取每个进程对应的文件的/proc/<pid>/cmdline,根据其是否等于”/sbin/adb”来判断是否adb进程。 
接下来,fork了一个新的进程(:109),父进程退出,而子进程继续。接下来,在113行创建一个管道。 

rageagainstthecage.c 
代碼: 

if (fork() > 0) 
    exit(0); 

setsid(); 
pipe(pepe); 

重头戏发生在下面的122到138行,代码如下: 

rageagainstthecage.c 

代碼:if (fork() == 0) { 
    close(pepe[0]); 
    for (;;) { 
        if ((p = fork()) == 0) { 
            exit(0); 
        } else if (p < 0) { 
            if (new_pids) { 
                printf("\n[+] Forked %d childs.\n", pids); 
                new_pids = 0; 
                write(pepe[1], &c, 1); 
                close(pepe[1]); 
            } 
        } else { 
            ++pids; 
        } 
    } 

新建一个进程后,在子进程之中,exploit代码不断地fork()(:125),而新的子进程不断退出,从而产生大量的僵尸进程(占据shell用户的进程数)。 
最终,进程数达到上限,fork()返回小于0,于是打印当前已经创建多少子进程,并向管道输入一个字符(:131)。
在这里,管道的作用是和(:122)fork出来的父进程同步,该进程在141行read这一管道,因而阻塞直至僵尸进程已经达到上限(:131)。 
进一步的,exploit杀掉adb进程,并在系统检测到这一现象并重启一个adb之前,再一次fork(),将前一个adb留下的进程空位占据。 
最后,在152行,exploit调用wait_for_root_adb(),等待系统重启一个adb,这个新建的adb就会具有root权限。 


为什么在shell用户的进程数达到上限RLIMIT_NPROC以后,新建的adb会具有root权限?我们来看adb的源码。 
在<android_src>/system/core/adb/adb.c的第918行,我们可以看到如下代码: 

android_src/system/core/adb/adb.c 
代碼:/* then switch user and group to "shell" */ 
if (setgid(AID_SHELL) != 0) { 
    exit(1); 

if (setuid(AID_SHELL) != 0) { 
    exit(1); 

这已经是漏洞修补以后的代码。在漏洞最初被发现时,代码如下: 

android_src/system/core/adb/adb.c 
代碼:/* then switch user and group to "shell" */ 
setgid(AID_SHELL); 
setuid(AID_SHELL); 
简而言之,原来没有检查setuid()函数的返回值。事实上,在此之前,adb.c中的代码都是以root权限运行,以完成部分初始化工作。 
在这一行,通过调用setuid()将用户从root切换回shell,但setuid()在shell用户进程数达到上限RLIMIT_NPROC时,会失败,因此adb.c继续以root身份运行,而没有报错。 

我们来看setuid()的man手册(man 2 setuid),其中有如下说明: 
man 2 setuid 
代碼:RETURN VALUE 
       On  success,  zero is returned.  On error, -1 is returned, and errno is 
       set appropriately. 

ERRORS 
       EAGAIN The uid does not match the current uid and  uid  brings  process 
              over its RLIMIT_NPROC resource limit. 
可以看到,setuid是可能发生错误的,并且在uid的进程数超过RLIMIT_NPROC极限时,发生EAGAIN错误。 


在android的源码中,setuid()定义于<android_src>/bionic/libc/unistd/setuid.c,实际上引用了一个外部符号__setuid, 
这个符号在<android_src>/bionic/libc/arch_xxx/syscalls/__setuid.S中定义,最终是一个%eax=$__NR_setuid32,%ebx=uid的int 0×80中断。 

因为只是要分析原理,我们不再鏖战于Android,转而看向Linux内核。 
在最新的kernel2.6中,setuid()位于kernel/sys.c的682行,其中,在697行,一切正常的情况下,它会调用set_user()来完成用户切换。 

set_user()实现于同一文件的587行,其中一部分代码如下: 
kernel/sys.c 
代碼:if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) && 
        new_user != INIT_USER) { 
    free_uid(new_user); 
    return -EAGAIN; 

含义很明显,当目标用户的进程数达到上限,那系统就不能再将一个进程分配给它,因而返回-EAGEIN。然后再setuid()中,直接跳过后面的代码,而返回错误。 

至此,整个漏洞的原理已经分析完毕。整理如下: 

1、在Android的shell用户下,制造大量的僵尸进程,直至达到shell用户的进程数上限RLIMIT_NPROC; 

2、kill当前系统中的adb进程,并再次占据其进程位置以保持达到上限; 

3、系统会在一段时间后重启一个adb进程,该进程最初是root用户,在完成少许初始化工作后,调用setuid()切换至shell用户; 

4、此时shell用户的进程数已经达到上限,所以setuid()失败,返回-1,并且用户更换没有完成,adb还是root权限; 

5、adb没有检查setuid()的返回值,继续后续的工作,因此产生了一个具有root权限的adb进程,可以被用于与用户的下一步交互。 

实际上,setuid在目标用户进程数达到RLIMIT_NPROC极限时返回错误,这一问题可能产生的安全隐患最早可以追溯到2000年。而在2006年,出现了真正利用这一编码问题的漏洞(CVE-2006-2607)。 

因此,这并不是一个全新的漏洞。我们可以得出几点结论: 

1、函数返回值一直是忽略的对象,因为getuid()永远不会失败,程序员可能会认为setuid()也不会失败——至少没有遇到过,因此忽略了对返回值的检查。检查一个系统函数是否调用失败是一个常识,但又是很麻烦的事,如果为了省事而忽略,问题就可能产生了。 

2、Android下的安全问题,很多并非全新的,而且个人判断将来还会有大量漏洞、恶意代码产生于传统思路,而作用于新的平台。面对这一新的平台,我们是否能抢先于攻击者做好防范准备,是一个需要我们思考和实践的问题。 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

写这篇文章前,首先要感谢 Simon_fu ,他的两篇关于 root 权限的文章对于我的工作起到了非常大的帮助,这篇文章可以说是对他的文章的一个补充。 Simon_fu 的文章可以参考如下两个网页:

Android程序的安全系统

Android应用程序获得 root权限

 

       一般来说, Android 下的应用程序可以“直接”得到的最大的权限为 system ,但是如果我们需要在程序中执行某些需要 root 权限的命令,如 ifconfig 等,就需要 root 权限了。按照 Simon 的文章中提到的,应用程序有以下两种办法临时获得 root 权限:

1)        实现一个 init 实现一个 Service ,来帮助 Android 应用程序执行 root 权限的命令。

2)        实现一个虚拟设备,这个设备帮助 Android 应用程序执行 root 权限的命令。

 

第二种办法我这里没有尝试,暂时也不会。这里讲讲我在实现第一种办法的过程和遇到的一些问题。

 

1.       将我们要执行的命令写成脚本,或者可执行程序。

下面是我的脚本 ifconfig_test.sh :

# ! /system/bin/sh

ifconfig

       注意: 脚本的第一行必须为 # ! /system/bin/sh ,否则无法执行,通过 dmesg 可以查看到信息内容为cannot execve ./ifconfig_test.sh: Exec format error

 

也可以采用 C/C++ 编写需要执行的命令或者程序,并在编译 image 的时候编译成可执行程序。

 

2.       在 init.rc 中注册 service

Android 中的 service 需要在 init.rc 中注册, Init.rc 中定义的 Service 将会被 init 进程创建,这样将可以获得root 权限。当得到相应的通知(通过属性设置)后, init 进程会启动该 service 。

本文中注册的内容如下:

service ifconfig_test /system/etc/ifconfig_test.sh

oneshot

disabled

       其中, oneshot 表示程序退出后不再重新启动, disabled 表示不在系统启动时启动。

 

注意: 这里 service name 不能超过 16 个字符。我之前的 service name 由于定义的比较长, 18 个字符,设置属性通知 service 启动后查看 dmesg 可以看到提示: init: no such service 。查看 /system/core/init/parser.c的源代码,在 parse_service->valid_name 函数中可以看到如下内容: if (strlen(name) > 16) { return 0; } ,证明 service 的名字的确不能超过 16 个字符。

 

3.       将 Android 应用程序提升为 system 权限

既然应用程序可以通过启动 service 获得 root 权限,那么岂不是很不安全。 Android 考虑到了这点,规定只有 system 权限的应用程序才能设置属性,通知 service 启动。关于提升 system 权限的文章网上已有很多,这里就不再细说,可以参考如下两篇文章:

http://blog.csdn.net/liujian885/archive/2010/03/22/5404834.aspx

http://labs.chinamobile.com/mblog/532767_73183

 

4.       在应用程序中添加属性设置代码

前面已经提到,对于 Android 来说,应用程序通知 init 启动 service 是通过设置系统属性来完成的,具体为设置 System 系统属性 “ctl.start” 为 “ifconfig_test” ,这样 Android 系统将会帮我们运行 ifconfig_test 这个service 了。

对该系统属性的设置有三种方法,分别对应三种不同的应用程序:  

1)  Java 代码

  Android 在 Java 库中提供 System.getProperty 和 System.setProperty 方法, Java 程序可以通过他们来设置和获得属性。代码如下:

SystemProperties.set("ctl.start", "ifconfig_test");

上面的代码是通知 Android 执行 ifconfig_test service ,如果需要查询当前 service 执行的状态,如是否执行完毕,可以通过如下代码查询:

ret = SystemProperties.get("init.svc. ifconfig_test ", "");

if(ret != null && ret.equals("stopped"))

  {

      return true;

  }

        2)  JNI 代码

当编写 NDK 的程序时,可以使用 property_get 和 property_set 这两个 API 来获得和设置属性。使用这两个API 必须要包含头文件 cutils/properties.h 和链接 libcutil 库。

 

3)  Shell 脚本

Android 提供了命令行 setprop 和 getprop 来设置和获取属性,他们可以在脚本中被使用。

 

由于我的程序是在 JNI 中调用脚本,脚本中又执行 ifconfig ,因此我将设置属性的部分放在了脚本中完成,代码如下:

setprop ctl.start ifconfig_test

 

#wait for the service until it stops

ret=1

while [ $ret -ne 0 ]

do

       getprop | grep "$ENABLE_MAPPER_SRV" | grep stopped

       ret=$?

done

       通过上面 4 个步骤, Android 应用程序就获得了 root 权限,更具体的说,是在执行我们需要执行的命令时临时获得了 root 权限。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

Android 下应用程序不能直接 获得 root 权限,因此如果需要修改 /sys 或 /proc 等目录下的文件时,有以下两种方法可以选择:

 

  1. 通过 service 或虚拟设备的方法将使得应用程序临时 获得 root 权限
  2. 对 /sys 或 /proc 目录下的文件修改访问权限,使得它们能够被程序访问

 

对于方法 1 ,可以参看我后续会写到的一篇文章《如何使 Android 应用程序获得 root 权限》,也可以在网上搜索别的相关文章。

 

这里讲讲方法 2 。

 

为了理解方法 2 ,首先要知道 Android 上的权限机制。 Android 会为每个安装在系统上的包( apk )配一个唯一的 linux userID ,名称为 "app_" 加一个数字,比如 app_43 。这样该应用程序的文件只对该用户可见,也就是说,应用程序只能访问相同 uid 和 gid 的文件。

另外,也可以通过 AndroidManifest.xml 修改应用程序的 uid 。如果将 android:sharedUserId 项的值改为其它 apk 的 java package name ,则该应用程序和所指定的应用程序拥有相同的 uid ,可以访问对方的文件。

通过 android:sharedUserId 还可以将应用程序提升为 system 权限。

 

有了上面的知识,这里有三种方法来修改我们所要访问的文件:

  1. 将文件改为 777 权限
  2. 将文件改为 apk 的 uid 和 gid
  3. 将文件修改为 system 的 uid ,同时将 apk 也提升为 system 权限

 

这三种方法都需要修改所要访问系统文件的权限。要更改权限,在 init.rc 文件中添加相应的命令即可。下述命令就将 /dev/cpuctl/tasks 修改为 system 的 uid 和 gid ,并且将其权限更改为 777 。

chown system system /dev/cpuctl/tasks

chmod 0777 /dev/cpuctl/tasks

 

修改 init.rc 该文件需要修改 ramdisk.img ,具体修改方法可以网上搜索,也可以参看这篇文章最后的附录。或者在源代码中直接修改 init.rc 文件然后编译即可。

 

下面依次分析一下上面提到的三种修改访问文件的方法。第一种方法是最简单的,不过安全性不好,仅仅可以作为实验使用,不推荐;第二种方法将系统文件文件的 uid 修改后,拓展性不好,并且我不能确定每次分配给apk 的 uid 是否相同。我这里试了几次,重新安装后 apk 的 uid 都是相同的,不过没有找到相关文章,所以不敢用。最后我这里采用第三种方法实现。

 

1)首先修改 init.rc

chown system system /sys/bus/usb/devices/testfile

 

这样每次启动后,系统都会执行 init.rc 文件将 testfile 的 uid 修改为 system ,这样 uid 同问 system 的程序就能够访问该文件了。

 

2) 其次将 apk 提升为 system 权限

 

将 apk 提升为 system 权限需要目标系统中的签名文件对 apk 进行签名,因此这种方法仅适用于某个 rom ,不能用于所有 rom 。

具体的提升办法网上有很多,这里不再多说,可以参考这两篇文章:

http://blog.csdn.net/liujian885/archive/2010/03/22/5404834.aspx

http://labs.chinamobile.com/mblog/532767_73183

 

至此,我们的目标就达成了。

 

附 修改 ramdisk.img 的方法

 

在网上找到了一些修改 ramdisk.img 的方法,都有点问题,我这里写一下自己的流程。由于 ramdisk.img 是首先用 cpio 打包,再用 gzip 压缩后的文件,因此在解压和压缩时都需要经历这两个步骤。

 

1)    mv ramdisk.img ramdisk.img.gz

2)    gunzip ramdisk.img.gz  #gunzip 解压得到 ramdisk.gz

3)    cpio –iI ramdisk.gz  # 用 cpio 解压上一步得到的文件

4)    修改 init.rc

5)    find. | cpio –o –H newc | gzip -9 > ramdisk.gz   #cpio 打包后, gzip 再压缩

6)    mv ramdisk.gz ramdisk.img


  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
数据来源:中经数据库 主要指标110多个(全部都是纯粹的 市辖区 指标),大致是: GDP GDP增速 第一产业增加值占GDP比重 第二产业增加值占GDP比重 第三产业增加值占GDP比重 人均GDP 社会消费品零售总额 固定资产投资(不含农户) 新设外商投资企业数_外商直接投资 实际利用外资金额(美元) 一般公共预算收入 一般公共预算支出 一般公共预算支出_教育 一般公共预算支出_科学技术 金融机构人民币各项存款余额_个人储蓄存款 金融机构人民币各项存款余额 金融机构人民币各项贷款余额 规模以上工业企业单位数 规模以上工业企业单位数_内资企业 规模以上工业企业单位数_港澳台商投资企业 规模以上工业企业单位数_外商投资企业 规模以上工业总产值 规模以上工业总产值_内资企业 规模以上工业总产值_港澳台商投资企业 规模以上工业总产值_外商投资企业 规模以上工业企业流动资产合计 规模以上工业企业固定资产合计 规模以上工业企业利润总额 规模以上工业企业应交增值税 规模以上工业企业主营业务税金及附加 户籍人口数 年均户籍人口数 户籍人口自然增长率 第一产业就业人员占全部城镇单位就业人员比重 第二产业就业人员占全部城镇单位就业人员比重 第三产业就业人员占全部城镇单位就业人员比重 城镇非私营单位就业人员数 城镇非私营单位就业人员数_第一产业 城镇非私营单位就业人员数_第二产业 城镇非私营单位就业人员数_第三产业 城镇非私营单位就业人员数_农、林、牧、渔业 城镇非私营单位就业人员数_采矿业 城镇非私营单位就业人员数_制造业 城镇非私营单位就业人员数_电力、热力、燃气及水生产和供应业 城镇非私营单位就业人员数_建筑业 城镇非私营单位就业人员数_批发和零售业 城镇非私营单位就业人员数_交通运输、仓储和邮政业 城镇非私营单位就业人员数_住宿和餐饮业 城镇非私营单位就业人员数_信息传输、软件和信息技术服务业 城镇非私营单位就业人员数_金融业 城镇非私营单位就业人员数_房地产业 城镇非私营单位就业人员数_租赁和商务服务业 城镇非私营单位就业人员数_科学研究和技术服务业 城镇非私营单位就业人员数_水利、环境和公共设施管理业 城镇非私营单位就业人员数_居民服务、修理和其他服务业 城镇非私营单位就业人员数_教育 城镇非私营单位就业人员数_卫生和社会工作 城镇非私营单位就业人员数_文化、体育和娱乐业 城镇非私营单位就业人员数_公共管理、社会保障和社会组织 城镇非私营单位在岗职工平均人数 城镇就业人员数_私营企业和个体 城镇非私营单位在岗职工工资总额 城镇非私营单位在岗职工平均工资 城镇登记失业人员数 建成区面积 建设用地面积 建设用地面积_居住用地 液化石油气供气总量 液化石油气供气总量_居民家庭 人工煤气、天然气供气总量 人工煤气、天然气供气总量_居民家庭 液化石油气用气人口 人工煤气、天然气用气人口 城市公共汽电车运营车辆数 城市出租汽车运营车辆数 城市公共汽电车客运总量 道路面积 排水管道长度 建成区绿化覆盖面积 建成区绿化覆盖率 绿地面积 公园绿地面积 维护建设资金支出 土地面积 生活用水供水量 供水总量 全社会用电量 城乡居民生活用电量 工业生产用电量 房地产开发投资 房地产开发投资_住宅 限额以上批发和零售业法人单位数 限额以上批发和零售业商品销售总额 普通中学学校数 中等职业教育学校数 普通小学学校数 普通高等学校专任教师数 普通中学专任教师数 中等职业教育专任教师数 普通小学专任教师数 普通高等学校在校生数 普通中学在校生数 中等职业教育在校生数 普通小学在校生数 电视节目综合人口覆盖率 公共图书馆总藏量_图书 医疗卫生机构数_医院和卫生院 卫生人员数_执业(助理)医师 医疗卫生机构床位数_医院和卫生院 城镇职工基本养老保险参保人数 职工基本医疗保险参保人数 失业保险参保人数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值