Android和Linux的关系

Thu 01 October 2015

Android和Linux的关系

引导

GRUB vs Bootloader

Linux主流引导程序是grub,以grub2为例,如果GRUB是安装在MBR上,BIOS加载MBR执行GRUB,GRUB寻找/boot分区, 按grub.cfg的内容显示系统列表。 grub2引入了模块的概念,原因是:识别设备,文件系统靠all in one的程序已经搞不定了。grub2加载Linux内核和initrd系统,操作系统真正开始执行。

Android没有硬盘,没有GRUB,CPU加电后执行flash上的Bootloader,Bootloader加载Linux内核。过程和GRUB类似,不同的是Bootloader位于flash的起始位置,kernel是接在kernel后面的一个固定位置。每个SoC厂商的bootloader一般是不一样的,而GRUB是通用的。

文件系统

ext4目前都很受欢迎

system分区, data分区都在flash(NAND)上:

shell@android:/ $ ls -al /dev/block/platform/omap/omap_hsmmc.0/by-name
lrwxrwxrwx root     root              2015-09-04 21:30 boot -> /dev/block/mmcblk0p7
lrwxrwxrwx root     root              2015-09-04 21:30 cache -> /dev/block/mmcblk0p11
lrwxrwxrwx root     root              2015-09-04 21:30 dgs -> /dev/block/mmcblk0p6
lrwxrwxrwx root     root              2015-09-04 21:30 efs -> /dev/block/mmcblk0p3
lrwxrwxrwx root     root              2015-09-04 21:30 metadata -> /dev/block/mmcblk0p13
lrwxrwxrwx root     root              2015-09-04 21:30 misc -> /dev/block/mmcblk0p5
lrwxrwxrwx root     root              2015-09-04 21:30 param -> /dev/block/mmcblk0p4
lrwxrwxrwx root     root              2015-09-04 21:30 radio -> /dev/block/mmcblk0p9
lrwxrwxrwx root     root              2015-09-04 21:30 recovery -> /dev/block/mmcblk0p8
lrwxrwxrwx root     root              2015-09-04 21:30 sbl -> /dev/block/mmcblk0p2
lrwxrwxrwx root     root              2015-09-04 21:30 system -> /dev/block/mmcblk0p10
lrwxrwxrwx root     root              2015-09-04 21:30 userdata -> /dev/block/mmcblk0p12
lrwxrwxrwx root     root              2015-09-04 21:30 xloader -> /dev/block/mmcblk0p1

上面mmcblk开头的设备是NAND支持eMMC控制器,早期是没有这个东西的。早期这些分区一般用Yaffs文件系统(针对flash设计的),现在基本上是ext4(和Linux一样了)

Linux上更自由了,想用什么文件系统用什么,只要支持Linux的文件权限管理,由于有grub2,连boot分区都没文件系统限制了。

Android在Linux的基础上引入的新东西

Android不是在纯Linux内核上的应用层扩展,也包含很多Linux内核模块(驱动)

Ashmem:

匿名共享内存驱动,为进程间通讯服务。可以认为是POSIX共享内存的升级版本,带gc的。设备名:/dev/ashmem

Binder:

基于OpenBinder的进程间通讯驱动加上层系统,从aidl编译出的内容,更容易理解binder的rpc细节。设备名:/dev/binder

Log

日志也是驱动实现的

shell@android:/ $ ls -al /dev/log/
crw-rw-rw- root     log       10,  42 2015-09-04 21:30 events
crw-rw-rw- root     log       10,  43 2015-09-04 21:30 main
crw-rw-rw- root     log       10,  41 2015-09-04 21:30 radio
crw-rw-rw- root     log       10,  40 2015-09-04 21:30 system

系统实现了4个类型的日志, 其中main是供普通APP使用的

LowMemoryKiller:

内存不足时根据oom_adj杀进程,oom_adj越大的越优先被Kill,每个进程都有不是APP专有的。

1号进程(init)的oom_adj:

shell@android:/ $ cat /proc/1/oom_adj
-16

init杀不得,一kill整个系统都关机了,所以是最小值

Bionic

这不是驱动,但是应用层的基础,用于替代Linux上层glibc的实现,出于体积小,占用内存小等目的。地位等同于kernel32.dll,在其他嵌入式系统上,还有ulibc。

Dalvik

下面介绍

mksh

Android没有使用Linux上首选的bash,而是用了一个mksh的shell,之前用ash

Android的虚拟机

Dalvik是专门给Android定制的虚拟机,而Linux上跑的是传统JVM(如:openjdk)

针对低内存环境设计,执行dex字节码(和普通JVM不一样),是一个寄存器式虚拟机。dex的设计也是为了小内存。

为了让系统内置的dex更快的加载执行,还有一个odex格式。

每个APP进程都有一个单独的虚拟机实例。都是Zygote fork出去的, 相同的部分物理内存只有1份。这是和Linux上不一样的。

Android的安全机制

基于Linux的安全规则,进行安全增强

  • Linux的基础安全规则(用户机制,文件权限机制)

  • 虚拟UID的隔离机制。Linux的UID是基于用户的,Android的UID是面向APP的,安装的时候分配。这样APP间的权限就互相隔离了;APP间可以要求申请相同的UID

  • SELinux,从Linux阵营搬过来的安全增强组件,比较复杂,针对性增强

Android APP进程的创建过程

Linux进程的创建:fork后,子进程执行exec。在Android非APP也是这样的。

  1. ActivityManagerService发起创建APP进程。干活的是Zygote进程,从socket收到请求执行forkAndSpecialize

  2. Zygote本身已经创建了虚拟机,预加载了framework的类,Zygote把自己fork了一份,fork出来的子进程也就不需要再创建虚拟机了。

  3. 子进程最终才执行ActivityThread中的入口,相当于普通Java程序的main了,到这一步我们的APP已经是可以调试的了。

root环境下的一种hook APP的方法,这体现了APP的创建过程:

so注入到Zygote进程空间,这个so加载后hook Zygote的forkAndSpecialize
(细节: 通过符号dvm_dalvik_system_Zygote找到Zygote对象,通过虚表找到forkAndSpecialize方法)

每个APP进程创建的时候我们都预先知道了,但这个时候还不知道是什么APP

在执行完`forkAndSpecialize`后子进程中, 我们再dlopen加载另一个so,这是真正干活的so

第二个so hook class "android/os/Process"setArgV0这个方法,这个方法会告诉我们APP的信息

PS:用什么hook?C和Java通杀的substrate,老本行在iOS上。

普通Java程序的启动

在Linux运行Java程序需要有jre环境。Android上没有一个标准的JRE

但有的时候我们想在Android启动一个普通的Java进程,不是Android APP。

系统没有java -jar给我们使用,但系统提供了app_process给我们使用, 系统内置命令am就是这样启动的。

root是怎么回事

获得root权限, id为0的用户, 最高权限

Linux下面的/etc/passwd直接说明这一点:

root@ubuntu:~# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash

Android没有/etc/passwd这套东西了

获取root有很多种方式,在没有漏洞的情况下(笑话),从bootloader引导进一个自制的Recovery就得到root权限了,放一个su到system分区去。这种方式在早期比较多。

根据漏洞获得su权限,remount system分区放进su。

免root,就是给APP自己获得root权限,但不放su不为其他APP服务。

某APP要获得系统最高权限的Dialog,就是su弹出来的。su也可以不弹直接给你权限(模拟器默认系统)。在Linux上su是要你输入密码的。

root权限和system权限一样吗?不一样。root最大。有system权限只能搞定部分Android服务,还有部分和硬件相关的服务Android用其他专有用户去运行的。

adb shell下shell和root的区别:

shell@android:/ $ id
uid=2000(shell) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)
shell@android:/ # type su
su is /system/xbin/su
shell@android:/ $ su
WARNING: generic atexit() called from legacy shared library
shell@android:/ # id
uid=0(root) gid=0(root)

各进程的用户区别:

shell@android:/ # ps
root      1     0     364    228   c015fa58 0000e800 S /init
system    398   129   648444 106776 ffffffff 401ddfe8 S system_server
root      129   1     458344 23444 ffffffff 401de10c S zygote
u0_a68    29500 129   509940 28880 ffffffff 401deee4 S com.youku.phone
u0_a68    29533 129   490044 27656 ffffffff 401deee4 S com.youku.phone:download
u0_a67    29775 129   743864 45868 ffffffff 401deee4 S com.tencent.mm
wifi      473   1     2748   956   c015fa58 4018510c S /system/bin/wpa_supplicant

总结:Android努力的让不同功能的进程用不同的用户去执行,来达到文件系统和进程空间隔离的目的。但有root权限后这些规则就失效了,注入任何进程,hook任何你想hook的东西,杀掉任何你想杀掉的进程,可以直接写flash。

Android上没有但Linux上有的安全机制

sudo,让指定用户获得su权限,或执行某些特点的高权限命令

iptable,防火墙用户层接口(有也没用,普通APP没权限执行)

Android混乱的根源

向设备安装APP的能力

这是Android一切混乱的根源,什么APP都可以装,没有审核机制。iOS的APP是有签名的,很难未经审核就大规模分发。连调试也需要有开发者的证书,且只能在有限的设备上调试。

后台执行能力

Android现在没有Service都不好意思叫APP。iOS的后台被限制为几种不同的场景。一般的APP,系统给机会你执行指定的函数,你单次消耗的时间越多得到的机会就越少。使用其他伎俩常驻的,在人工审核面前过不了。

iOS提供了相对靠谱的后台推送机制。大部分APP不需要想法去常驻后台。

其他

  • 用busybox可以补上Android在应用层缺失的很多Linux命令(需root权限), PS:用Linux kernel + busybox可以构成一个迷你的Linux系统

  • Linux上的tcpdump在Android一样可以抓包

  • 虽然没有提供iptable程序,但内核是支持的防火墙功能的

Category: Android Tagged: Android Linux

Comments