精华内容
下载资源
问答
  • 我们通过精心组织组件扫描的方式,来装配不同的子模块,形成一个可运行的应用; 在载入某个子模块后,我们发现应用虽然正常启动,但尝试登陆的时候,出现一个很异常的异常,部分stacktrace如下: ...

    1.场景复现:

    一个登陆异常引出的问题。

    我们通过精心组织组件扫描的方式,来装配不同的子模块,形成一个可运行的应用;

    在载入某个子模块后,我们发现应用虽然正常启动,但尝试登陆的时候,出现一个很异常的异常,部分stacktrace如下:
    
    
    processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface org.springframework.mobile.device.Device] with root cause
    
    java.lang.NoSuchMethodException: org.springframework.mobile.device.Device.<init>()
    	at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_171]
    	at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_171]
    	at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:209) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    	at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:84) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    	at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:132) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    	at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    	

    异常栈部分调用关系如下:

     

     

    如果移除子模块,应用启动后,同一个入口会触发的正常调用栈如下:

     

     

     

    换句话说,异常情况和正常情况,走了不同的处理链。

     

    1.我们初步怀疑是包冲突,但验证后排除掉这个问题;

    2.然后继续通过调试代码,分析发现加入子模块后,启动的

    HandlerMethodArgumentResolver 实例有27个;

    而不加入子模块,启动的

    HandlerMethodArgumentResolver 实例有33个,多了几个resolver,其中包括DeviceHandlerMethodArgumentResolver,看起来找到好的线索了;

     

    3.根据线索,怀疑是配置问题,首先怀疑的是安全方面的配置问题;但一直测试分析没有得到结果;

    4.后续,用排除法,发现此模块中去掉一个定义的配置类,问题就可以得到解决,这个配置如下::

    Configuration
    public class WebMvcConfig extends WebMvcConfigurationSupport {

    ...

    }

     

     

    2.经过所有线索的梳理,这个问题产生的内在机制比较明确了:

     

    HandlerMethodArgumentResolver

    源码注释:
    Strategy interface for resolving method parameters into argument values in the context of a given request.

    简单可以理解为:解析方法参数的策略接口;通俗的理解为,(controller的)参数解析(构造)转换按这个接口来实现;

     

    WebMvcConfigurationSupport

    源码注释:

    This is the main class providing the configuration behind the MVC Java config.

    其中有一个方法:

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {

    简而言之,DeviceHandlerMethodArgumentResolver 实例是靠

    WebMvcConfigurationSupport 提供的机制(模板函数),加载进容器处理链的;

     

    DelegatingWebMvcConfiguration

     

    实际上,spring 依靠 

    DelegatingWebMvcConfiguration 自动检测并代理实现所有的自定义配置
    /**
    * A subclass of {@code WebMvcConfigurationSupport} that detects and delegates
    * to all beans of type {@link WebMvcConfigurer} allowing them to customize the
    * configuration provided by {@code WebMvcConfigurationSupport}. This is the
    * class actually imported by {@link EnableWebMvc @EnableWebMvc}.
    *
    * @author Rossen Stoyanchev
    * @since 3.1
    */
    @Configuration
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
    

     

    WebMvcAutoConfiguration

    DelegatingWebMvcConfiguration 的实例创建,是通过下面这个类
    WebMvcAutoConfiguration 导入,但是有个条件:当容器内不存在 WebMvcConfigurationSupport 的实例时
    @Configuration
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
    ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {
    
    
    
    

    所以,当我们自己子模块的代码,有这个自定义配置时

    @Configuration
    public class WebMvcConfig extends WebMvcConfigurationSupport {

    系统默认的webmvc 配置行为将会被覆盖

     

    转载于:https://www.cnblogs.com/leo_chow/p/10065773.html

    展开全文
  • 今天继续开始我们今天的话题:run-as命令,在上周的开发中,遇到一个问题,就是在使用run-as命令的时候出现了一个错误,不过当时因为工作进度的问题,这问题就搁浅没有解决,用了其他一个曲线救国的方式去解决的。...

    一、前言

    最近一周比较忙,没时间写东西了,今天继续开始我们今天的话题: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:

    开到了吧,运行成功了啦~

     

    三、引出Linux中的setuid和getuid概念

    那么问题来了,为何我改了一个权限,就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 设置了这三个位。

     

    四、Android中关于setuid和setgid的使用场景

    好了,上面说了那么多,就是为了介绍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权限。

     

    六、如何调用系统受uid限制的api

    那么到这里还没有结束,为什么呢?我们文章中说到了好几次关于降权的问题,这里我们在做一个例子来详细介绍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文件,好吧,问题总算是结束了,但是写的还是蛮累的。知识点也是很多,希望对你有帮助,就点个大大的赞吧~~

     

    《Android应用安全防护和逆向分析》

    点击立即购买:京东  天猫  

    更多内容:点击这里

    关注微信公众号,最新技术干货实时推送

    展开全文
  • 今天继续开始我们今天的话题:run-as命令,在上周的开发中,遇到一个问题,就是在使用run-as命令的时候出现了一个错误,不过当时因为工作进度的问题,这问题就搁浅没有解决,用了其他一个曲线救国的方式

    转载自:http://blog.csdn.net/jiangwei0910410003/article/details/51405631

    一、前言

    最近一周比较忙,没时间写东西了,今天继续开始我们今天的话题: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:


    开到了吧,运行成功了啦~


    三、引出Linux中的setuid和getuid概念

    那么问题来了,为何我改了一个权限,就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 设置了这三个位。


    四、Android中关于setuid和setgid的使用场景

    好了,上面说了那么多,就是为了介绍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权限。


    六、如何调用系统受uid限制的api

    那么到这里还没有结束,为什么呢?我们文章中说到了好几次关于降权的问题,这里我们在做一个例子来详细介绍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文件


    展开全文
  • Hadoop的话题系列

    2017-11-15 23:05:00
    大家好,今天就市面上比较火热云计算技术中最杰出开源软件hadoop方面的话题,分享以下宅鸟在工作和学习中对hadoop认识理解开发运维方面理解。 首先由大家带着以下几个问题引出以后博文. 1、什么是云计算? 2...

    大家好,今天就市面上比较火热的云计算技术中最杰出的开源软件hadoop方面的话题,分享以下宅鸟在工作和学习中对hadoop的认识理解开发运维方面的理解。

    首先由大家带着以下几个问题引出以后博文.

    1、什么是云计算?

    2、云计算在行业内存在方式?

    3、hadoop为什么云计算方面的杰出代表?

    4、怎样揭开hadoop神秘的面纱?

    5、hadoop能给企业带来哪些好处?


    如果大家对这些比较清楚,如果不太清楚的朋友可以在网上搜到很多这方面的介绍。

    宅鸟希望给大家分享一些干货

    6、hadoop集搭建

    7、CDH4 搭建hadoop集

    8、通过ganglia对整个hadoop集的监控

    9、hadoop运维过程中需要注意哪些方面

    ...


    本片作为hadoop系列的开篇索引,索引还会陆续更新


    本文转自birdinroom 51CTO博客,原文链接:http://blog.51cto.com/birdinroom/1343104,如需转载请自行联系原作者


    展开全文
  • 今天继续开始我们今天的话题:run-as命令,在上周的开发中,遇到一个问题,就是在使用run-as命令的时候出现了一个错误,不过当时因为工作进度的问题,这问题就搁浅没有解决,用了其他一个曲线救国的方式去解决的。...
  • 今天继续开始我们今天的话题:run-as命令,在上周的开发中,遇到一个问题,就是在使用run-as命令的时候出现了一个错误,不过当时因为工作进度的问题,这问题就搁浅没有解决,用了其他一个曲线救国的方式去解决的。...
  • 【第16天】“提问法”的话题展开法

    千次阅读 2012-09-29 08:22:30
     通过一些简单提问就可以“撬开”对方嘴巴,引出的话题,例如:  甲:您在什么地方工作?  乙:在南京一家单位工作,现在来这里出差。  甲:南京不错啊,好山好水,还有很多美食。
  • 今天跟同事讨论一个跟Watchdog喂狗任务有关问题,却引出另一个话题(过程比较零乱,暂时略过):在嵌入式系统里,reboot(这里指reboot说都是正常情况敲入命令,比如linux shell下输入reboot, vxworks shell...
  • 内容提要 • 如何引出该话题 ...引出话题 • 谈到HashMap和线程不安全对象时可以引出; • 为什么要引出该话题?包含值钱要素太多; • 回答数据结构hash表、红黑树等相关问题时可以引出; • 谈到并发和vola..
  • 总裁演说思维方式应用:口才训练方法关键词联想训练法 关键词联想就是由一个关键词引发联想...例如关键词“吃饭”可以引出如下话题: 1、吃了吗? 2、早上吃什么啊! 3、晚上准备吃什么啊! 4、你喜欢吃什么啊! 5
  • 讲师将针对0到3年左右的java开发,从Java集合,异常处理机制,设计模式,虚拟机调优和数据库方面,讲述每个点的值钱技能,以及在面试时引出相关值钱话题的方式
  • 今年发改委首次明确新基建范围,其中如何加快传统基础设施向"新基建"融合基础设施转变,这一话题被提上日程。 借此机会,引出隧道综合监控系统,为国内新基建发展出一份薄力。 ——图扑(hightopo) 随着我国...
  • 什么是java中CAS

    2018-07-30 17:08:55
    问题一:java中的CAS是什么?问题二:为什么要使用CAS?问题三:CAS使用中需要注意什么... 这里以提问的方式引出话题,下面带大家慢慢了解CAS。1.CAS的含义CAS是compare and swap的缩写,即我们所说的比较交换。cas是...
  • 讲师将针对0到3年左右的java开发,从Java集合,异常处理机制,设计模式,虚拟机调优和数据库方面,讲述每个点的值钱技能,以及在面试时引出相关值钱话题的方式。...
  • 1、聊一聊 " I was alone,but not lonely"! 今天的文章话题引出来自bug技术交流群,主要是想把这种协议解析和设计的方式分享给...
  • 前言:​ 在使用Java语言进行开发过程中,涉及到局部变量与方法内部作用域的数据交换与解析的过程,而这一过程,一般都是通过方法形参的方式进行java对象的传递。这里引出一个问题,当对象作为参数传递时,究竟传的...
  • 今天的文章话题引出来自bug技术交流群,主要是想把这种协议解析和设计的方式分享给大家!2、正文部分1话题引出bug技术交流群一个小哥贴出了如下图片 :问到了共联体类型强制转化的问题,当时bug菌点开看到这几行代码...
  • 第三章开篇以马克·波林的“生存实验室”为案例引出#为机器人赋予生命#的话题。生存实验室是由一群极具理想的人组合而成的,他们以各种奇怪的方式制造出各种奇怪的机器人,并努力使他们处于失控状态。看似很疯狂,但...
  • 近日来,我在倒腾360手机卫士时候,发现,你无论是把他数据清空,还是把它强行停止以后,甚至是把它卸载以后,它service都没有被Android系统干掉,...这就引出了,我们今天的话题,说一说service重启方式...
  • 与人沟通技术

    2006-11-28 13:25:00
    如果我有一个新的项目需要和老板沟通,我会怎么和他沟通呢?前提条件是:对了所存在的问题...2、提出存在的问题,(可以通过引导的方式来提出,比如说:现在我们存在这样的一个问题,如果有一个解决方法多好啊,然后是,
  • 人际交往中谈话技巧

    千次阅读 2005-12-09 19:53:00
    换言之,要意识到自己责任不仅是把自己思想表达清楚,还应考虑怎样谈才能使对方产生兴趣,易于理解,并根据对方各种反馈信息来调整自己讲话内容和方式。为此,要注意以下四个方面问题: 一是选择话题。 ...

空空如也

空空如也

1 2 3
收藏数 59
精华内容 23
关键字:

引出话题的方式