最近一周比较忙,没时间写东西了,今天继续开始我们今天的话题:run-as命令,在上周的开发中,遇到一个问题,就是在使用run-as命令的时候出现了一个错误,不过当时因为工作进度的问题,这问题就搁浅没有解决,用了其他一个曲线救国的方式去解决的。那么咋们今天闲来说说Android中的run-as命令吧。
Android中我们知道如果设备没有root,我们想看一个应用的沙盒数据(/data/data/目录内容),在以前的方式很难办到,一般人都是选择root之后,去查看对应的数据库,xml等数据。但是如果如果我们没有root了,照样也想看这些数据,怎么办呐?其实Android中提供了一个命令,那就是:run-as,不过这个命令有一个缺陷,就是只有Debug应用才能查看,这个到后面在分析run-as源码的时候再说。这个命令的用法很简单:run-as [packagename]
其中packagename就是我们想查看的应用的包名,运行命令之后,就直接进入到指定应用的目录下:/data/data/packagename/
那么我现在稍微了解了Android中有这个命令,可以查看Debug应用的沙盒数据,那么我遇到一个什么问题呢?下面来看我运行的结果:
呵呵,点够北的,为毛这个命令会报错呢?遇到这个问题我们只有两种方式去解决:一种是网上查资料,一种是自己去看那操蛋的源码,好吧,现在我们有时间了,那就去看源码吧,关于Android中的这些命令,一般都在/system/bin和/system/xbin目录下,这些命令的源码一般都是放在源码的:Android源码目录\system\core\ 下:
那么我们就来看看run-as的源码:run-as.c:
看到这里,我们知道了,原来run-as命令运行有很多限制的:
第一个限制:运行的uid限制,运行命令的用户id只能是shell和root用户
下面我们可以验证一下,我们使用system的uid运行命令:
这里我们用于测试的设备已经root了,因为我要讲解问题,所以需要root之后操作方便。这里看到使用su可以随意设定uid,这个后面分析su源码在说他是如何做到的。这里我们将uid变成了system了,在运行run-as,发现报错了,符合预期吧。
第二限制:这个应用的安装必须合法
这里我们在仔细看看get_package_info这个函数的源码了,它位于run-as.c同目录下的package.c:
这里通过map_file函数来获取PACKAGES_LIST_FILE文件的buffer内容,我们再来看一下PACKAGES_LIST_FILE的定义:
是这个文件,我们导出这个文件,查看内容:
这个文件记录所有安装应用的信息:
包名,用户id(uid),是否为debug模式,对应的数据目录,是否是release版,组id(gid)
这里我们看到,demo.systemapi应用是debug模式的。一般正式app都是非debug模式。
这里通过读取这些信息就可以构造出一个PackageInfo了:
注意:
这里我们看到了packages.list文件是存储安装包的简略信息的,其实和他同一目录下还有一个重要文件:packages.xml,我们可以导出来看看:
看到了,这里就记录了安装应用的详细信息,还有一个重要信息就是还能够知道应用的安装来源,有些app可以做到这点,就是通过这个信息来获取到的,还有签名信息,权限等。
当然我们可以使用Android中的dumpsys packageinfo命令来查看指定应用的详细信息:
第三个限制:应用的uid必须合法
我们看到这里AID_APP的定义,在 Android源码目录\system\core\include\private\android_filesystem_config.h
这里定义了一些uid信息,看到:
AID_ROOT对应的是root用户,uid=0
AID_SYSTEM对应的是system用户,uid=100
AID_SHELL对应的是shell用户,uid=2000
从这里我们可以看到,root用户的权限》system用户的权限》shell用户的权限》第三方应用的权限
不过这里所说的权限只是对于一些情况,不是全部哦,比如一个简单的例子,Android中有些api是会做uid限制的,即时我们用反射机制也是访问失败的,那么比如有一个api他限制只能uid=1000的用户就是system用户才能调用,那么这时候,即时你是root用户也是没办法的,不过可以使用su进行降权,这个后面会通过一个例子来看。
这三个用户的id最好记住啦,算是常识了。
在往下看:
看到了,这里的AID_APP是10000,我们也知道,我们安装的应用的uid都是从10000开始的,所以uid都是大于10000的,这里所以做了一层uid的合法性判断限制。
注意:
我们一般查看应用的uid的时候,很简单,拿到最后的整数+10000就是应用的uid了,比如demo.systemapi的uid就是:
10000+140 = 10140;
这个我们可以验证的,查看上面提到的packages.list文件中的信息:
第四个限制:应用是否是debug模式
这个在之前就说过了,run-as有一个最大的限制就是查看对应应用的数据,其必须是debug模式的,所以我们在开发程序的时候,千万要保证一点,就是debug包不能外泄,不然数据就等于外泄了。
那么言归正传,源码看的差不多了,看一下问题,我们在运行run-as的时候遇到的问题是:
那么问题就出在这里:
再看get_package_info函数:
再看map_file函数:
好吧,终于找到原因了,原来是,/data/system/packages.list文件读取权限是AID_SYSTEM,我们查看一下:
好吧,问题找到了,原来是文件访问权限的问题,那么我们尝试使用su进行升级权限在查看一下:
呵呵,明显是不行的,原因很简单,run-as有限制呢,本身这个命令只能是root和shell用户才可以访问的,这里我也可以看到,shell用户也不比system用户权限低,所以要视情况而定来看这root,system,shell三个用户的权限范围。
那么我们在root用户下去尝试一下:
擦,这是可以的,因为run-as的限制权验证过去了
首先root的uid限制可以放行的,然后是debug模式可以放行,packages.list文件的访问权限是system,但是这个对于root用户来说没有限制的,因为在文件的读取权限上,root可以操作任何文件的,demo.systemapi的uid合法放行
注意:
这里运行完run-as命令之后,进入到了/data/data/demo.systemapi目录下了,看到了,变成了sh命令,#变成$了,这个其实是run-as会在启动一个shell程序,可以查看run-as的源码:
好了,到这里我们就分析了我们为什么运行run-as命令失败了,失败的原因也找到了,解决的办法也有了,但是还是需要root,这个貌似和我们开始说的有点出入,因为本身run-as命令就是在非root设备上可以查看debug模式下应用的沙盒数据的,那现在root了,还有什么意义呢,其实不是的,run-as这个命令很特殊的,我找到了一个没有root的设备,然后运行run-as命令,结果如下:
也是可以运行成功的,而且这个设备没有root。但是我们在上面一个已经root的设备运行run-as却报错误,那是为什么呢?下面我们就开始今天的正式主题,为何没有root,run-as命令还能运行成功,进入指定的应用目录下。
其实故事是这样的:我开始的时候为了讲解run-as源码和一些前提知识,我提前做了一件事,就是修改了run-as命令的权限:
我们查看run-as命令的本来权限是:-rwsr-sr-x root shell 9440 2016-04-06 17:44 run-as
然后我修改了他的权限:chmod 0755 run-as
变成:-rwxr-xr-x root shell 9440 2016-04-06 17:44 run-as
这个操作是为了我开始今日的话题的前奏,所以请大家原谅我瞒着你们干了一件坏事,不过现在大家应该明白吧
本来run-as的权限是:-rwsr-sr-x root shell 9440 2016-04-06 17:44 run-as
下面我们把run-as权限改回原来的值,然后在运行run-as:
开到了吧,运行成功了啦~
那么问题来了,为何我改了一个权限,就run-as运行报错了呢?我们看到修改的命令是:chmod 0755 run-as
修改之后的权限:-rwxr-xr-x root shell 9440 2016-04-06 17:44 run-as
和修改之前只有一个区别,就是x变成了s,那么这里是怎么个回事呢?
这个就引出了今天的话题,Linux中的setuid和setgid问题了,那Linux中的setuid和setgid问题是个什么鬼?Android中有哪些场景都用到了这个呢?下面来一一讲解
第一、首先来看一下Linux中的setuid和setgid概念
linux/unix 下的可执行文件,被设置了setuid, 当一个程序一旦设置了该标记以后, 使用该可执行程序的进程将拥有该执行文件的所有者的权限,可提升使用者的权限,普通用户可以执行改命令,使自己升级为root权限。
setuid的用法是: chmod 4755 program 或 chmod u+s program (setuid 只对文件有效)
被设置了setgid, 同理使用该程序的进程将拥有该程序所有组的权限,单独setGid的文件非常少用,通常都是即setUid又setGid,同时setuid,setgid 是为了绑定某个特殊用户及其组的特殊权限。
setgid的用法是:chmod 2755 dir 或 chmod g+s dir (setgid 只对目录有效)
同时设置setuid,setgid的用法:chmod 6755 program
被设置粘着位,设置的用法是: chmod 1777 file 或 chmod o+t file (sticky只对文件有效)
当一个目录被设置为"粘着位"(用chmod a+t),则该目录下的文件只能由
1、超级管理员删除
1、该目录的所有者删除
1、该文件的所有者删除
也就是说,即便该目录是任何人都可以写,但也只有文件的属主才可以删除文件 。
那么我们如何使用chmod命令修改文件的权限和这两个函数有什么关系呢?
我们知道修改文件的权限命令是chmod,他后面可以使用字母组合或者是数字组合。
注意:
Android中的toolbox对chmod命令做了限制,chmod只能使用数字代替字母来修改文件的权限,看下面例子:
看到了,这里使用字母组合方式来修改文件的权限会报错的:Bad mode
就是因为google自带的toolbox中chmod语法不支持+x, -x这类
第二、下面继续讲解chmod的命令的用法和参数详解
我们知道Linux中的文件权限构造是这样的:
我们可以使用ll命令查看一个文件,样式式是类似这样的:-rwxr-xr-x
下面解析一下格式所表示的意思。
这种表示方法一共有十位:
9 8 7 6 5 4 3 2 1 0
- r w x r - x r - x
第9位表示文件类型,可以为p、d、l、s、c、b和-:
p表示命名管道文件
d表示目录文件
l表示符号连接文件
-表示普通文件
s表示socket文件
c表示字符设备文件
b表示块设备文件
第8-6位、5-3位、2-0位分别表示文件所有者的权限,同组用户的权限,其他用户的权限
其形式为rwx:
r表示可读,可以读出文件的内容,对应的数字是4
w表示可写,可以修改文件的内容,对应的数字是2
x表示可执行,可运行这个程序,对应的数字是1
那么我们可以知道了,rwx的组合就是7=1+2+4
如果我们想修改一个文件的权限为所有者为rwx,同组用户的权限是r--,其他用户的权限是--x,命令如下:
chmod u=rwx,g=r,o=x file
这里看到:u代表所有者,g代表同组,o代表其他用户
我们也可以使用数字更简单:
chmod 714 file
7代表所有者:1+2+4
1代表同组用户:1
4代表其他用户:4
那么下面再来看一下如何修改文件为setuid和setgid呢?
如果一个文件被设置了SUID或SGID位,会分别表现在所有者或同组用户的权限的可执行位上。例如:
1、-rwsr-xr-x 表示SUID和所有者权限中可执行位被设置
2、-rwsr--r-- 表示SUID被设置,但所有者权限中可执行位没有被设置
3、-rwxr-sr-x 表示SGID和同组用户权限中可执行位被设置
4、-rw-r-sr-- 表示SGID被设置,但同组用户权限中可执行位没有被社
其实在UNIX的实现中,文件权限用12个二进制位表示,如果该位置上的值是
1,表示有相应的权限:
11 10 9 8 7 6 5 4 3 2 1 0
s g t r w x r w x r w x
第11位为SUID位,第10位为SGID位,第9位为sticky位,第8-0位对应于上面的三组rwx位。
11 10 9 8 7 6 5 4 3 2 1 0
上面的-rwsr-xr-x的值为: 1 0 0 1 1 1 1 0 1 1 0 1
-rw-r-sr--的值为: 0 1 0 1 1 0 1 0 0 1 0 0
所有者的s位数字对应是:4
所有组的s位数字对应是:2
粘着位的数字对应是:1
给文件加SUID和SUID的命令如下:
chmod u+s filename 设置SUID位
chmod u-s filename 去掉SUID设置
chmod g+s filename 设置SGID位
chmod g-s filename 去掉SGID设置
另外一种方法是chmod命令用八进制表示方法的设置。如果明白了前面的12位权限表示法也很简单。
如果设置文件的setuid,gid,ticky位,使用数字来表示:
chmod 7555 file
第一位的7就代表:1+2+4=7 设置了这三个位。
好了,上面说了那么多,就是为了介绍Linux中的setuid,setgid的用法和作用,以及如何使用chmod命令来操作。
下面我们就来看看Android中有哪些场景用到了setuid,setgid。
第一个场景:zygote降权处理
首先不得不说Zygote这个进程了,在之前我们说过Android中应用启动的过程都是和这个进程息息相关的,后面也会写文章详细介绍这个进程的,这里就简单说一下了,Android中每个应用启动都是来找zygote来做事的,zygote启动之后,会进入一个轮训使用socket来监听来自ActivityManagerService发来的消息,比如应用的启动,然后zygote就fork出一个进程,开始运转,那么问题来了,我们知道Linux中,父进程fork出一个子进程,默认的话,子进程会拥有和父进程一样的uid,那么我们知道zygote是root用户的。那么按照常理,所有fork出来的应用都是root用户了,看上去好危险,每个应用都有root权限了,当然这里肯定做了手脚,不然我们后面看到每个应用只有指定的权限的,可以看到这里zygote使用setuid,setgid做了降权处理。
下面来看一下源码:dalvik_system_Zygote.cpp
源码位置:Android源码\dalvik\vm\native\dalvik_system_Zygote.cpp
zygote中主要主要做处理的就是forkAndSpecializeCommon函数
往下面看:
哈哈,果然是,这里讲fork出来的进程的gid和uid设置成上层传递过来的值,从而实现降权了。
这里我们还看一个知识点,还有这个函数:enableDebugFeatures,这个是干什么的,其实看到名字都知道,就是能够让一个应用具备可调式功能,在debug的情况下。
继续往下看:
注意:
我们看到了一个重要点:gDvm.jdwpAllowed,我们知道java中的调试系统还是很复杂的,后面也会出一篇文章专门讲解调试系统,这里就不多说了,这里就说一点,Java中的调试有jdb,jdbserver,jwdp组成的,这里遵从的是C/S模式
jdb就是客户端,就是需要调试的应用
jdbserver就是服务端,就是调试代码信息处理的那一端
jwdp就是调试协议
而且每个可以调试的应用启动之后,都会有一个jwdp线程来运作这些事,我们可以使用:
ps -t 进程号
来查看进程的线程:
这里说一下的原因是为了接下来分析Android中的调试工具gdb,gdbserver做准备的。
第二场景:su工具原理
上面就讲完了setuid的一个场景zygote来对每个创建出来的应用进行降权,下面再来看一个场景:su工具
我们现在应该可以理解Android手机的root原理了吧:一个普通的进程通过执行su,从而获得一个具有root权限的进程。有了这个具有root权限的进程之后,就可以想干什么就干什么了。su所做的事情其实很简单,它再fork另外一个子进程来做真正的事情,也就是我们在执行su的时候,后面所跟的那些参数。由于su所运行在的进程的UID是root,因此由它fork出来的子进程的UID也是root。于是,子进程也可以想干什么就干什么了。不过呢,用来root手机的su还会配合另外一个称为superuser的app来使用。su在fork子进程来做真正的事情之前,会将superuser启动起来,询问用户是否允许fork一个UID是root的子进程。这样就可以对root权限进行控制,避免被恶意应用偷偷地使用。
下面来看看su的源码:
我们可以看到,这里其实很简单,就是用了setuid和setgid来设置指定的uid,如果没有指定uid默认就是root用户,uid=0。
第三、chmod修改setuid和setgid场景:run-as命令
关于上面说到的chmod命令来达到setuid等效果,在Android中也有类似的场景,首先我们前面分析的run-as命令,就是一个很好的例子,run-as命令本生的uid是root,gid是shell
如果当我们chmod 6755 run-as 设置完权限之后,他就具备了这种功效:
只要运行run-as命令的用户就会立马升级到root用户,那么我们知道root用户可以进入到任何应用的data/data/目录下面的。
现在我们也知道了,当初为什么我修改了run-as命令之后,会报错,然后在修改回来之后又可以了。
从这里我们可以看到Android中的run-as命令其实还是蛮危险的,虽然他本身有很多限制,不过追根到底还是Linux中的setuid的安全性最为值得考验,这个setuid危险性还是很大的,很多漏洞都是通过这个特性来发现的,比如著名的:MasterKey漏洞。
补充:
既然这里说到了run-as命令,我们现在知道了,他能够在非root设备上查看debug模式的应用沙盒数据,其实他还有一个重要疗效,就是为Android中的调试做基础,上面再分析zygote源码的时候提到了一句java中的调试系统jdb,其实Android中的调试系统是gdb
过gdb和gdbserver来调试APP的。具体来说,就是通过gdbserver通过ptrace附加上目标APP进程去,然后gdb再通过socket或者pipe来链接gdbserver,并且向它发出命令来对APP进程进行调试,那么需要有这些关键点:
1、第一个关键点是每一个需要调试的APK在打包的时候,都会带上一个gdbserver。因为手机上面不带有gdbserver这个工具。这个gdbserver就负责用来ptrace到要调度的APP进程去。
2、第二个关键点是ptrace的调用。一般来说,只有root权限的进程只可以调用。例如,如果我们想通过ptrace向目标进程注入一个SO,那么就需要在root过的手机上通过向su申请root权限。但是,这不是绝对的。如果一个进程与目标进程的UID是相同的,那么该进程就具有调用ptrace的权限。gdbserver在调试一个APP之前,首先要通过ptrace_attach来附加到该APP进程去。ptrace_attach在执行实际操作之后,会调用__ptrace_may_access来检查调用进程的权限,如果调用进程与目标进程具有相同的UID和GID,那么权限检查就通过。否则的话,就要求调用者进程具有执行ptrace的capability,这是通过另外一个函数ptrace_has_cap来检查的。如果是调用进程的UID是root,那么ptrace_has_cap一定会检查通过。当然,通过了上述两个权限检查之后,还要接受内核安全模块的检查,这个就不是通过UID或者Capability这一套机制来控制的了
3、第三个关键点是如何让gdbserver进程的UID与要调试的APP进程的UID一样。因为在没有root过的手机上,要想获得root权限是不可能的了,因此只能选择以目标进程相同的UID运行这个方法。这时候就可以使用run-as工具了
到这里我们知道了,Android中如果要调试一个程序,首先这个程序必须是debug模式的,也就是在AndroidManifest.xml中设置的属性,所以我们知道我们以往在使用动态方式破解apk的都是,首先都是反编译,然后修改xml中的这个debug属性,然后才能进行代码关联调试,而且我们知道动态调试so的强大工具IDA也是利用附加到目标进程中才进行调试的。原理和这里的gdb一样。后面会单独写一篇文章来介绍Android中使用动态方式来破解apk。
注意:
关于ptrace去想目标进程注入一个so的知识,这里不多说,请看我的另外一篇文章:Android中应用程序行为拦截
好了到这里,我们貌似做了这么几件事,我们稍微整理一下:
1、首先我们使用run-as命令查看应用的沙盒数据失败,通过错误信息查看run-as的源码,发现run-as命令做了很多限制
2、通过追踪发现,是/data/system/packages.list的读取权限问题导致的,不过这些问题都是我们一开始设置了run-as权限的问题印出来的。
3、通过修改run-as的权限,引出来了linux中的setuid和setgid相关知识,已经chmod命令修改文件权限的详细说明。
4、通过了解了linux中的setuid和setgid相关知识之后,我们分析了Android中使用该功能进行每个应用的降权,具体又分析了zygote源码。而且这里还分析了每个应用是否可调式的相关知识,主要是Java中的调试系统。
5、通过分析完了run-as命令之后 ,我们又扩展了一下,Android中的调试系统gdb,他其实就是基于run-as命令的,原因很简单,以为gdb调试应用的话,首先这个应用的AndroidManifest.xml中必须设置debug属性,然后要附加到目标进程中,但是对于非root设备来说如何附件目标进程呢?那么这时候就要借助run-as工具来操作了,因为run-as可以短暂的获取root权限。
那么到这里还没有结束,为什么呢?我们文章中说到了好几次关于降权的问题,这里我们在做一个例子来详细介绍Android中如何降权,这个例子,很简单,我们模拟一个系统的api,但是这个api只能system用户可以调用,代码如下:
这里做了用户uid的判断,如果不是system用户直接返回错误信息。
这里我们为了简单,也是为了向广大新老用户演示Android中如何执行jar功能,这里就在加一个入口类:
我们把工程导出jar,然后用dx命令进程转化成dex,因为不管怎么样Android中是不识别class文件的,他只是别dex文件:
dx --dex --output=输入的dex目录文件 输入的jar文件
然后在将生成的classes.dex文件塞到jar中,这个直接使用压缩软件就可以了:
然后把exec.jar导入到设备的/data目录下,这个目录随便导入,这里就为了简单导入到/data下面了:
然后把exec.jar设置到到类环境变量中,运行即可:
export CLASSPATH= jar的路径
exec /system/bin/app_process jar的目录 jar中的入口类
不过这里为了演示问题,都在root用户下做的操作:
我们看到了,运行成功,但是看到打印的log信息,权限拒绝了,因为只能system用户可以访问的,下面我们就用su进行降权到system在运行:
注意:每次切换用户的时候,一定要记得从新设置一下类变量,不然运行失败的。
呵呵,成功获取到值了。
总结一下步骤:
1》导出可执行的jar文件
2》使用dx命令将class文件转化成dex文件
3》将转化之后的dex文件塞到jar文件中,然后倒入到设备的指定目录
4》设置可执行jar文件到类环境变量中
5》执行app_process命令运行jar文件
补充:
上面我们看到了可以成功的使用app_process命令来运行一个jar文件,其实关于app_process的相关知识,在之前的一篇文章中已经说过了,他是Android系统中启动Java代码的关键。但是这里为了简单,我们没必要这么做,可以直接运行dex文件的,这里就要在介绍一个命令:dalvikvm
首先我们将上面dx之后的classes.dex文件push到/data目录下:
然后就执行dalvikvm命令:
dalvikvm -cp dex文件路径 入口类
这里同样看到权限拒绝的,我们降级切换到system用户:
运行成功啦
好了,到这里,我们就学会了如何使用app_process来运行jar文件,而且也体验到了Android中的一些api的访问限制该如何处理,主要是用过su来进行降权处理的,而su能够降权是因为,他本生提供了可以修改uid的功能,为什么可以修改,原因很简单,调用setuid和setgid函数即可,所以以后大家在手机root之后,发现还是不能调用系统的一些api的时候,可以尝试使用su来降权进行调用。
1、关于run-as命令
1)run-as命令的作用是可以查看非root设备的debug应用的沙盒数据
2)从源码分析可以得知,run-as有四个限制:
A>运行的uid限制运行命令的用户id只能是shell和root用户
B>被查看应用的安装必须合法,主要通过分析/data/system/packages.list文件来获知
C>被查看的应用必须是debug模式
D>被查看的应用的uid必须是第三方的安装应用,也就是uid必须大于10000
3)run-as的特殊权限:-rwsr-sr-x root shell 9440 2016-04-06 17:44 run-as 设置了setuid和setgid位,所以可以访问应 用沙盒数据。
4)run-as的另一个作用就是为调试gdb做了基础。调试必须要附加到目标进程中,那么对于非root设备,只能使用run-as实现
2、Android中关于Linux中的setuid和setgid的使用场景
1)第一个场景就是zygote为每个应用程序进行降权,分析了zygote源码,知道zygote需要对每个fork出来的应用做降权处理, 不然默认都是和zygote一样的权限,也就是root用户了,这样会很危险的。
2)第二场景就是su工具源码,他可以做到设置指定的uid,既可以升权,也可以降权。
3、关于chmod命令的参数详解
既可以支持字母,又可以支持数字,字母和数字有相对应的关系,而且知道Android中的chmod名 只支持数字,不支持字母 的
4、关于Android中如何运行Java代码
1)第一种方式,使用app_process命令直接执行,需要做以下工作:
A>导出可执行的jar文件
B>使用dx命令将class文件转化成dex文件
C>将转化之后的dex文件塞到jar文件中,然后倒入到设备的指定目录
D>设置可执行jar文件到类环境变量中
E>执行app_process命令运行jar文件
2)第二种方式,使用dalvikvm命令直接执行dex文件
5、我们在以后在获取root的情况下,也不能调用系统的一些api,我们可以使用降权来进行处理
6、文章中用到了一些常用工具命令
1)查看一个进程的线程情况:ps -t 进程号
2)查看一个安装应用包的信息:dumpsys packageinfo [packagename]
总算是分析完了,知识点涉及的有点多,主要是一环扣着一环,本来想只分析run-as命令的用法,结果遇到问题,去分析源码,发现错误的根源之后,去查看他的权限,引出来了Linux中的setuid和setgid问题,了解完了这个知识点之后,发现他在Android中有一些场景用到了,那就继续分析哪些场景,分析完了,之后,我们又解决一个问题,就是如何调用系统中那些有uid限制的api问题,这里通过一个例子来解析问题,引出了Android中如何执行jar文件,好吧,问题总算是结束了,但是写的还是蛮累的。知识点也是很多,希望对你有帮助,就点个大大的赞吧~~
更多内容:点击这里
PS: 关注微信公众号,最新Android技术实时推送