├── Android 调试工具 ├── Android ART 运行时(上).md ├── Android ART 运行时(下).md ├── Android Hook 技术防范漫谈.md ├── Android Hook 框架(Cydia篇).md ├── Android Hook 框架(XPosed篇).md ├── Android Hook(上).md ├── Android Hook(下).md ├── Android Inline Hook 的指令修复详解.md ├── Android Java 层的anti-Hook 技巧.md ├── Android Native Hook 工具实践.md ├── Android Native Hook 技术线路概述.md ├── Android 应用程序通用脱壳方法研究.md ├── Ida Pro.md ├── Smali Instrumentation.md ├── 初识JEBAPI.md └── 常见app加固厂商脱壳方法.md ├── Android安全概述 ├── Android Linux 内核层安全.md ├── Android 安全概述.md ├── Android 安全的其他话题.md ├── Android 应用层安全.md ├── Android 本地用户空间层安全.md └── Android 框架层安全.md ├── Android应用安全 ├── Android Activity Security.md ├── Android Application Security.md ├── Android Broadcast Security.md ├── Android Content Provider Security.md ├── Android Logcat Security.md ├── Android Service Security.md └── OWASP Mobile top 10_2014.md ├── Android开发基础 ├── Android Handler消息传递机制.md ├── Android NDK 开发基础.md ├── Android 基于回调的事件处理机制.md ├── Android 基于监听的事件处理机制.md ├── Android 工程相关文件说明.md ├── Android 源码编译.md ├── AndroidManifest.xml.md └── Android开发基础知识.md ├── Android系统安全 ├── Android 中堆unlink 利用学习.md └── Android 系统漏洞挖掘.md ├── Android逆向基础 ├── ARM 寄存器简介.md ├── ARM 汇编伪指令简介.md ├── ARM 汇编指令简介.md ├── apk 反编译基础.md └── smali 语法.md ├── Java语言基础 ├── Java Basic.md ├── Java synthetic.md └── Java 反射机制到Android 的注解.md ├── LICENSE ├── README.md └── pictures ├── 1-1.jpg ├── 1-2.jpg ├── 2-1.jpg ├── 3-1.jpg ├── 4-1.jpg ├── 5-1.jpg ├── AndroidARM_3-1_FP.png ├── adnroidactivity.gif ├── android111.jpg ├── android_anti_hooking1.png ├── android_anti_hooking10.png ├── android_anti_hooking11.png ├── android_anti_hooking12.png ├── android_anti_hooking13.png ├── android_anti_hooking14.png ├── android_anti_hooking2.png ├── android_anti_hooking3.png ├── android_anti_hooking4.png ├── android_anti_hooking5.png ├── android_anti_hooking6.png ├── android_anti_hooking7.png ├── android_anti_hooking8.png ├── android_anti_hooking9.png ├── android_native_hook1.jpg ├── android_native_hook15.jpg ├── android_native_hook16.jpg ├── android_native_hook17.png ├── android_native_hook18.png ├── android_native_hook19.png ├── android_native_hook2.jpg ├── android_native_hook20.png ├── android_native_hook21.png ├── android_native_hook22.png ├── android_native_hook3.jpg ├── android_native_hook4.jpg ├── android_native_hook5.jpg ├── android_native_hook6.jpg ├── android_native_hook7.jpg ├── android_native_hook8.jpg ├── android_native_hook9.jpg ├── androidac1.jpg ├── androidac10.jpg ├── androidac11.jpg ├── androidac2.jpg ├── androidac3.jpg ├── androidac4.jpg ├── androidac5.jpg ├── androidac6.jpg ├── androidac7.jpg ├── androidac8.jpg ├── androidac9.jpg ├── androidart1.jpg ├── androidart10.jpg ├── androidart11.jpg ├── androidart12.jpg ├── androidart13.jpg ├── androidart14.jpg ├── androidart15.jpg ├── androidart16.jpg ├── androidart17.jpg ├── androidart18.jpg ├── androidart19.jpg ├── androidart2.jpg ├── androidart20.jpg ├── androidart21.jpg ├── androidart22.jpg ├── androidart3.jpg ├── androidart4.jpg ├── androidart5.jpg ├── androidart6.jpg ├── androidart7.jpg ├── androidart8.jpg ├── androidart9.jpg ├── androidbasic.gif ├── androidbr1.jpg ├── androidbr2.jpg ├── androidbr3.jpg ├── androidbr4.jpg ├── androidbr5.jpg ├── androidbr6.jpg ├── androidcat1.jpg ├── androidcat10.jpg ├── androidcat11.png ├── androidcat2.jpg ├── androidcat3.jpg ├── androidcat4.jpg ├── androidcat5.jpg ├── androidcat6.jpg ├── androidcat7.jpg ├── androidcat8.jpg ├── androidcat9.jpg ├── androidcydia1.jpg ├── androidcydia2.jpg ├── androidcydia3.jpg ├── androidcydia4.jpg ├── androiddump1.jpg ├── androiddump10.jpg ├── androiddump11.jpg ├── androiddump12.jpg ├── androiddump13.jpg ├── androiddump14.jpg ├── androiddump15.jpg ├── androiddump16.jpg ├── androiddump2.jpg ├── androiddump3.jpg ├── androiddump4.jpg ├── androiddump5.jpg ├── androiddump6.jpg ├── androiddump7.jpg ├── androiddump8.jpg ├── androiddump9.jpg ├── androidhook1.jpg ├── androidhook10.jpg ├── androidhook2.jpg ├── androidhook3.jpg ├── androidhook4.jpg ├── androidhook5.jpg ├── androidhook6.jpg ├── androidhook7.jpg ├── androidhook8.jpg ├── androidhook9.jpg ├── androidida1.jpg ├── androidida10.jpg ├── androidida11.jpg ├── androidida111.jpg ├── androidida12.jpg ├── androidida13.jpg ├── androidida14.jpg ├── androidida15.jpg ├── androidida16.jpg ├── androidida17.jpg ├── androidida18.jpg ├── androidida19.jpg ├── androidida2.jpg ├── androidida20.jpg ├── androidida21.jpg ├── androidida22.jpg ├── androidida23.jpg ├── androidida24.jpg ├── androidida25.jpg ├── androidida26.jpg ├── androidida27.jpg ├── androidida28.jpg ├── androidida29.jpg ├── androidida3.jpg ├── androidida30.jpg ├── androidida31.jpg ├── androidida32.jpg ├── androidida33.jpg ├── androidida34.jpg ├── androidida35.jpg ├── androidida36.jpg ├── androidida37.jpg ├── androidida38.jpg ├── androidida39.jpg ├── androidida4.jpg ├── androidida40.jpg ├── androidida41.jpg ├── androidida42.jpg ├── androidida43.jpg ├── androidida44.jpg ├── androidida45.jpg ├── androidida451.jpg ├── androidida46.jpg ├── androidida47.jpg ├── androidida48.jpg ├── androidida481.jpg ├── androidida482.jpg ├── androidida49.jpg ├── androidida491.jpg ├── androidida492.jpg ├── androidida5.jpg ├── androidida50.jpg ├── androidida51.jpg ├── androidida52.jpg ├── androidida53.jpg ├── androidida54.jpg ├── androidida55.jpg ├── androidida56.jpg ├── androidida57.jpg ├── androidida58.jpg ├── androidida59.jpg ├── androidida6.jpg ├── androidida7.jpg ├── androidida8.jpg ├── androidida9.jpg ├── androidjeb1.jpg ├── androidjeb10.jpg ├── androidjeb11.jpg ├── androidjeb12.jpg ├── androidjeb13.jpg ├── androidjeb14.jpg ├── androidjeb15.jpg ├── androidjeb2.jpg ├── androidjeb3.jpg ├── androidjeb4.jpg ├── androidjeb5.jpg ├── androidjeb6.jpg ├── androidjeb7.jpg ├── androidjeb8.jpg ├── androidjeb9.jpg ├── androidpro1.jpg ├── androidpro2.jpg ├── androidpro3.jpg ├── androidpro4.jpg ├── androidser1.jpg ├── androidser2.jpg ├── androidser3.jpg ├── androidser4.jpg ├── androidshell1.png ├── androidshell10.png ├── androidshell11.png ├── androidshell12.png ├── androidshell13.png ├── androidshell14.png ├── androidshell15.png ├── androidshell16.png ├── androidshell17.png ├── androidshell18.png ├── androidshell19.png ├── androidshell2.png ├── androidshell20.png ├── androidshell21.png ├── androidshell22.png ├── androidshell23.png ├── androidshell24.png ├── androidshell25.png ├── androidshell26.png ├── androidshell27.png ├── androidshell28.png ├── androidshell29.png ├── androidshell3.png ├── androidshell30.png ├── androidshell31.png ├── androidshell32.png ├── androidshell33.png ├── androidshell34.png ├── androidshell35.png ├── androidshell36.png ├── androidshell37.png ├── androidshell38.png ├── androidshell39.png ├── androidshell4.png ├── androidshell40.png ├── androidshell41.png ├── androidshell42.png ├── androidshell43.png ├── androidshell5.png ├── androidshell6.png ├── androidshell7.png ├── androidshell8.png ├── androidshell9.png ├── androidtiaoshi1.jpg ├── androidtiaoshi2.jpg ├── androidtiaoshi3.jpg ├── apktool1.png ├── apktool2.png ├── appaudi.png ├── arm9_whypc8.jpg ├── arm_cheatsheetv1.png ├── armcondition.png ├── armldmstm.png ├── becall1.JPG ├── call1.JPG ├── callback1.jpg ├── callback2.jpg ├── callback3.jpg ├── callback4.jpg ├── event1.jpg ├── event2.jpg ├── event3.jpg ├── handle1.jpg ├── handle2.jpg ├── handle3.jpg ├── heapunlink1.jpg ├── heapunlink2.jpg ├── heapunlink3.png ├── heapunlink4.png ├── heapunlink5.png ├── heapunlink6.png ├── heapunlink7.png ├── heapunlink8.png ├── hellojni.png ├── howtoapk.png ├── jniflow.png ├── pidcat.png ├── project1.jpg ├── project2.jpg ├── project3.jpg ├── project4.jpg ├── project5.jpg ├── run_as.png ├── shared_prefs.png ├── shellaccess.png ├── stringproper.png ├── weixinzhifu.jpg └── zhifubao.jpg /Android 调试工具/Android ART 运行时(下).md: -------------------------------------------------------------------------------- 1 | 原文by zyq0879 2 | 3 | ## 0x01 前言 4 | 5 | 之前对Android的两个运行时的源码做了一些研究,又加上如火如荼的Android加固服务的兴起,便产生了打造一个用于脱壳的运行时,于是便有了DexHunter的诞生(源码:https://github.com/zyq8709/DexHunter/ )。今天,我就通过这篇小文聊聊我的一些简单的思路,供大家参考和讨论。 6 | 7 | ## 0x02 相关机制 8 | 9 | 首先,先来看一看Android运行时的一些相关机制,看看我们来怎么搞。 10 | 11 | 首当其冲,要脱壳少不了研究一下Dex文件的格式,这一点Android的官方文档写的已经很清晰了,我这里就简单再提一下。整个结构便如图1所示: 12 | 13 | 14 | ![](../pictures/androidart15.jpg) 15 | 图1 Dex文件结构 16 | 17 | 其实就是分区段存储不同的内容,在头部里有指向各个区段起始的偏移值。当然我们最关心的就是class_defs和data这两个段了。 18 | 19 | class_defs包含了所有的类,用class_def_item来描述。图2是对class_def_item展开的一个示意图: 20 | 21 | 22 | ![](../pictures/androidart16.jpg) 23 | 图2 class_def_item结构 24 | 25 | 每个class_def_item指向一个class_data_item,每个class_data_item 包含了一个class的数据,每个方法用encoded_method结构来描述,它又指向了一个code_item,这个里面就保存着一个方法的所有指令。 26 | 27 | 对于ART下,安装后的dex文件会被编译为oat文件,这个oat文件其实是一个ELF文件,图3是它的一个结构: 28 | 29 | 30 | ![](../pictures/androidart17.jpg) 31 | 图3 OAT文件结构 32 | 33 | 其中可以看到oatdata指向的部分包含了原有的Dex文件,这个是我们的目标。当然oatexec指向了编译后的ARM指令,但是对于我们暂时来说没有什么卵用。 34 | 35 | ## 0x03 四个时机 36 | 37 | 为了脱壳,我们要建立一个概念,就是“时机”。对于非虚拟机壳,从内存中转储是一个最为有效和统用的技巧,那么就必须要找到一个时机,保证内存中的数据是完全正确的。 38 | 39 | 在Android中呢,便有这么四个时机: 40 | 41 | 打开Dex文件 42 | 43 | 就是把APK中的dex文件提取并做cache,那么最终打开的其实是odex或oat文件; 44 | 45 | 加载Class 46 | 47 | 运行时读取存储在Dex中的每个class,并用来填充一个生成的Class对象,其中包含了class的所有成员,这样一个class才能被使用;图4表示了ART和DVM下的Class对象的结构 48 | 49 | ![](../pictures/androidart18.jpg) 50 | ![](../pictures/androidart19.jpg) 51 | 52 | 图4 Class的结构 53 | 54 | 初始化Class 55 | 56 | 如果一个class有static块,那么这个部分就会编译为类的初始化器,具体看说就是方法,在class真正需要被使用的时候就会执行它,当然,壳就可以利用它来做许多事情; 57 | 58 | 调用具体的方法 59 | 60 | 不用多说,就是根据生成的Class对象查找到具体的代码指令并执行了。 61 | 62 | ## 0x04 两种加载 63 | 64 | 好,那我们怎么做呢?很简单,我们就从类的加载开始。 65 | 66 | 总的来说,有两种可以加载类的方法,一个是显示加载,主要用于反射,就是通过调用Class.forName()或ClassLoader.loadClass()方法来主动加载一个类;另一个是隐式加载,主要是通过创建第一个class的实例或在类产生前访问静态成员时发生。这些操作的背后在运行时中是有相应的函数来真正完成的。 67 | 68 | 在ART中: 69 | 70 | 显式加载: 71 | 72 | ClassLoader.loadClass 对应DexFile_defineClassNative 73 | 74 | Class.forName 对应Class_classForName 75 | 76 | 隐式加载: 77 | 78 | 对应artAllocObjectFromCode 79 | 80 | 图5表述了这个关系: 81 | ![](../pictures/androidart20.jpg) 82 | 83 | 图5 ART中的实现 84 | 85 | 在DVM中: 86 | 87 | 显式加载: 88 | 89 | ClassLoader.loadClass对应Dalvik_dalvik_system_DexFile_defineClassNative 90 | 91 | Class.forName对应Dalvik_java_lang_Class_classForName 92 | 93 | 隐式加载: 94 | 95 | 对应dvmResolveClass 96 | 97 | 图6是DVM中的实现表示: 98 | ![](../pictures/androidart21.jpg) 99 | 100 | 图6 DVM中的实现 101 | 102 | ## 0x05 开始修改 103 | 104 | 很清晰看到,我们找到了关键点,在ART中是DefineClass,DVM中是Dalvik_dalvik_system_DexFile_defineClassNative,我们就从这里动手,主要的修改就发生在这里。简单地说就是主动地一次性加载并初始化所有的类。 105 | 106 | 这样做是隐含了几条原则的: 107 | 108 | 当类被加载时,dex中对应的部分必须有效; 109 | 类初始化的时候,dex中的内容包括生成的Class对象是可以被修改的; 110 | 只有在执行一个方法时,才要求code_item是有效的。 111 | 图7就是DexHunter的一个工作流程: 112 | 113 | 114 | ![](../pictures/androidart22.jpg) 115 | 图7 DexHunter原理 116 | 117 | 下面就分这几个步骤来说: 118 | 119 | (1) 定位内存 120 | 121 | 对于之前提到的入口函数,都有一个参数表示在操作的文件。 122 | 123 | ART中,这个参数是DexFile对象,其中有一个location_成员,是一个字符串,可以简单的理解为此文件的路径。那么DVM中是DexOrJar,相对的字符串成员是fileName。这下我们就好整了,只要我们指定了目标字符串,我们就可以从可能使用的众多dex文件中找出我们想要的那个,而且方便的是,通过这两个对象,我们还能很容易找到操作的文件在内存中的起始地址和长度。 124 | 125 | (2) 主动加载并初始化 126 | 127 | 这个就是遍历dex文件中class_defs区段里每一个class_def_item,并逐一加载和初始化,在ART里我们使用FindClass函数来加载类,EnsureInitialized进行初始化;在DVM中用dvmDefineClass加载,dvmIsClassInitialized 和dvmInitClass来初始化。 128 | 129 | (3) 转储并自动修复 130 | 131 | 最后就是真正抓取dex了。把dex分为三部分: 132 | 133 | Part 1: class_defs之前的内容 134 | Part 2: class_defs段 135 | Part 3: class_defs后边的部分 136 | 我们把Part 1存在part1文件里,Part 3存在data文件中,Part 2先不要急。 137 | 138 | 现在我们要解析class_defs的东东了。不整代码了,用文字简单来说,就是模仿Android的过程,我们把每个class_data_item解码为内存中的对象(有LEB128编码),便于我们的修复。 139 | 140 | 下边就要进行一些判断看需不需要修复: 141 | 142 | 看class_def_item中的 class_data_off是不是在之前拿到的dex文件的内存范围内,如果跑出去了,就需要把这个类的class_data_item给放到dex尾部去,修改class_def_item并保存。 143 | 144 | 比较解析出来的accessflag、codeoff和运行时生成的方法对象的accessflag、codeoff,如果不一致,以运行时中的为准,并修改保存。 145 | 146 | 同样,检查code_item_off是否出界了,一旦出界,把code_item收回来,继续向尾部添加,并修改class_def_item的相关内容重新保存。 147 | 148 | 当然了,所谓放到尾部,只是先保证偏移值从尾部开始的,真正的内容先存在extra文件了。被修改过的class_defs段,就保存在classdef文件中了。 149 | 150 | 然后我们把四个文件重新拼起来,就得到原始的dex或odex了。 151 | 152 | ## 0x06 有趣的现象 153 | 154 | 最后聊一下我们看到的一些有趣的现象。 155 | 156 | 360基本上是把原始的dex加密存在了一个so中,加载之前解密。 157 | 158 | 阿里把一些class_data_item和code_item拆出去了,打开dex时会修复之间的关系。同时一些annotation_off是无效的的来防止静态解析。 159 | 160 | 百度是把一些class_data_item拆走了,与阿里很像,同时它还会抹去dex文件的头部;它也会选择个别方法重新包装,达到调用前还原,调用后抹去的效果。我们可以通过对DoInvoke (ART)和dvmMterp_invokeMethod (DVM)监控来获取到相关代码。 161 | 162 | 梆梆和爱加密与360的做法很像,梆梆把一堆read,write, mmap等libc函数hook了,防止读取相关dex的区域,爱加密的字符串会变,但是只是文件名变目录不变。 163 | 164 | 腾讯针对于被保护的类或方法造了一个假的class_data_item,不包含被保护的内容。真正的class_data_item会在运行的时候释放并连接上去,但是code_item却始终存在于dex文件里,它用无效数据填充annotation_off和debug_info_off来实现干扰反编译。 165 | 166 | ## 0x07 参考 167 | 168 | https://source.android.com/devices/tech/dalvik/dex-format.html 169 | /libcore/libart/src/main/java/java/lang/ClassLoader.java 170 | /libcore/libdvm/src/main/java/java/lang/ClassLoader.java 171 | /libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java 172 | /libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java 173 | https://github.com/anestisb/oatdump_plus#dalvik-opcode-changes-in-art -------------------------------------------------------------------------------- /Android 调试工具/Android Native Hook 技术线路概述.md: -------------------------------------------------------------------------------- 1 | 原文 by [gtoad blog](https://gtoad.github.io/2018/07/05/Android-Native-Hook/) 2 | 3 | ## 前言 4 | 在目前的安卓APP测试中对于Native Hook的需求越来越大,越来越多的APP开始逐渐使用NDK来开发核心或者敏感代码逻辑。 个人认为原因如下: 5 | 6 | 1. 安全的考虑。各大APP越来越注重安全性,NDK所编译出来的so库逆向难度明显高于java代码产生的dex文件。越是敏感的加密算法与数据就越是需要用NDK进行开发。 7 | 2. 性能的追求。NDK对于一些高性能的功能需求是java层无法比拟的。 8 | 3. 手游的兴起。虚幻4,Unity等引擎开发的手游中都有大量包含游戏逻辑的so库。 9 | 10 | 因此,本人调查了一下Android Native Hook工具目前的现状:尽管Java层的Hook工具多种多样,但是Native Hook的工具缺寥寥无几。(文末说明1) 主要有两大路线: 11 | 12 | 1. PLT Hook 13 | 2. Inline Hook 14 | 15 | 这两种技术路线本人都实践了一下,下面来对比总结。 16 | 17 | ## PLT Hook 18 | 先来介绍一下Android PLT Hook的基本原理。Linux在执行动态链接的ELF的时候,为了优化性能使用了一个叫延时绑定的策略。相关资料有很多,这边简述一下:这个策略是为了解决原本静态编译时要把各种系统API的具体实现代码都编译进当前ELF文件里导致文件巨大臃肿的问题。所以当在动态链接的ELF程序里调用共享库的函数时,第一次调用时先去查找PLT表中相应的项目,而PLT表中再跳跃到GOT表中希望得到该函数的实际地址,但这时GOT表中指向的是PLT中那条跳跃指令下面的代码,最终会执行_dl_runtime_resolve()并执行目标函数。第二次调用时也是PLT跳转到GOT表,但是GOT中对应项目已经在第一次_dl_runtime_resolve()中被修改为函数实际地址,因此第二次及以后的调用直接就去执行目标函数,不用再去执行_dl_runtime_resolve()了。因此,PLT Hook通过直接修改GOT表,使得在调用该共享库的函数时跳转到的是用户自定义的Hook功能代码。 19 | 20 | 了解PLT Hook的原理后,可以进一步分析出这种技术的特点: 21 | 22 | 1. 由于修改的是GOT表中的数据,因此修改后,所有对该函数进行调用的地方就都会被Hook到。这个效果的影响范围是该PLT和GOT所处的整个so库。因此,当目标so库中多行被执行代码都调用了该PLT项所对应的函数,那它们都会去执行Hook功能。 23 | 2. PLT与GOT表中仅仅包含本ELF需要调用的共享库函数项目,因此不在PLT表中的函数无法Hook到。 24 | 那么这些特点会导致什么呢? 25 | 26 | 1. 可以大量Hook那些系统API,但是难以精准Hook住某次函数调用。这比较适用于开发者对于自家APP性能监控的需求。比如Hook住malloc使其输出参数,这样就能大量统计评估该APP对于内存的需求。但是对于一些对Hook对象有一定精准度要求的需求来说很不利,比如说是安全测试或者逆向分析的工作需求,这些工作中往往需要对于目标so中的某些关键点有准确的观察。 27 | 2. 对于一些so内部自定义的函数无法Hook到。因为这些函数不在PLT表和GOT表里。这个缺点对于不少软件分析者来说可能是无法忍受的。因为许多关键或核心的代码逻辑往往都是自定义的。例如NDK中实现的一些加密工作,即使使用了共享库中的加密函数,但秘钥的保存管理等依然需要进一步分析,而这些工作对于自定义函数甚至是某行汇编代码的监控能力要求是远远超出PLT Hook所能提供的范围。 28 | 3. 在回调原函数方面,PLT Hook在hook目标函数时,如果需要回调原来的函数,那就在Hook后的功能函数中直接调用目标函数即可。可能有点绕,详细解释一下:假设对目标函数malloc()的调用在1.so中,用户用PLT Hook技术开发的HookMalloc()功能函数在2.so中。(因为通常情况下目标函数与用户的自定义Hook功能函数不在一个ELF文件里)当1.so中调用malloc()时会去1.so的PLT表中查询,结果是执行流程进入了2.so中的HookMalloc()中。如果这时候HookMalloc中希望调用原目标函数malloc(),那就直接调用malloc()就好了。因为这里的malloc会去2.so中的PLT表中查询,不受1.so中那个被修改过的PLT表的影响。 29 | 30 | ## 典型的PLT Hook工具推荐 31 | 本技术路线的典型代表是爱奇艺开源的[xHook](https://github.com/iqiyi/xHook)工具库。xhook 是一个针对 Android 平台 ELF (可执行文件和动态库) 的 PLT (Procedure Linkage Table) hook 库。从维护频率和项目标志设计来看这是一款产品级的开源工具。 32 | 33 | 通过学习其源码与使用后可以发现,这个工具库主要是用于开发者开发时把该项目集成进自己的APP,然后使用这个工具库来帮助开发者监控APK运行时那些他们关心的性能数据。比如通过hook malloc来监控内存分配等。由于这个库是被开发者集成进了APP中,所以它对于这个app的监控是不需要Root权限的。 34 | 35 | 我个人认为这个工具对于PLT Hook技术的解读与定位非常好!在我上文分析的三点中不难看出,PLT Hook技术应用的方向就应该是对自家开发的APP的性能监控。用该技术进行Hook的最大优势就在于其对于目标API可以进行批量Hook,使得开发者可以节省下大量的原本需要在APP中各个功能点上插入相关日志输出的工作。PLT Hook技术是偏向于开发者的利器的定位非常明确。对于非官方的软件分析者似乎并不适合。 36 | 37 | ## Inline Hook 38 | 本技术路线的基本原理是在代码段中插入跳转指令,从而把程序执行流程引向用户需要的功能代码中去,以此达到Hook的效果,如下图所示: 39 | ![](../pictures/android_native_hook15.jpg) 40 | 41 | 42 | 这张图是一张arm下最基本的hook流程,如果客官对于Inline Hook有着进一步的学习需求的话,请看一下本人的另一篇文章《[Android Native Hook工具实践](https://github.com/JnuSimba/AndroidSecNotes/blob/master/Android%20%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7/Android%20Native%20Hook%20%E5%B7%A5%E5%85%B7%E5%AE%9E%E8%B7%B5.md)》吧。从上图中可以看出主要有如下几个步骤: 43 | 44 | 1. 在想要Hook的目标代码处备份下面的几条指令,然后插入跳转指令,把程序流程转移到一个stub段上去。 45 | 2. 在stub代码段上先把所有寄存器的状态保存好,并调用用户自定义的Hook功能函数,然后把所有寄存器的状态恢复并跳转到备份代码处。 46 | 3. 在备份代码处把当初备份的那几条指令都执行一下,然后跳转到当初备份代码位置的下面接着执行程序。 47 | 由此可以看出使用Inline Hook有如下的Hook效果特点: 48 | 49 | 1. 完全不受函数是否在PLT表中的限制,直接在目标so中的任意代码位置都可进行Hook。这个Hook精准度是汇编指令级的。这对于逆向分析人员和安全测试人员来说是个非常好的特性! 50 | 2. 可以介入任意函数的操作。由于汇编指令级的Hook精度,以及不受PLT表的限制,Inline Hook技术可以去函数执行中的任意代码行间进行Hook功能操作,从而读取或修改任意寄存器,使得函数的操作流程完全可以被控制。 51 | 3. 对Hook功能函数的限制较小。由于在第二步调用Hook功能函数前已经把所有之前的寄存器状态都进行保存了,因此此时的Hook功能函数几乎就是个独立的函数,它无需受限于原本目标函数的参数形式,完全都由自己说了算。并且执行完后也完全是一个正常的函数退出形式释放栈空间。 52 | 4. 对于PLT Hook的强制批量Hook的特性,Native Hook要灵活许多。当想要进行批量Hook一些系统API时也可以直接去找内存里对应的如libc.so这些库,对它们中的API进行Hook,这样的话,所有对这个API的调用也就都被批量Hook了。 53 | 54 | ## 技术对比 55 | 根据以上的分析,我们发现这两种技术在原理和适用场景上的差别是相当大的。因此有必要进行一下对比,给那些有Native Hook需求的童鞋一些参考。 56 | ![](../pictures/android_native_hook16.jpg) 57 | 58 | ## 总结 59 | 从上面的分析中不难看出,这两种技术各有特点。PLT Hook技术就好比自行车,容易得到,操作简便,但是功能极为有限;Inline Hook技术就像汽车,造价昂贵,操作复杂,但是几乎可以应对各种需求。 因此对于正在寻找Native Hook工具的同学们需要仔细预估一下自己的Native Hook需求,如果只对于系统调用有参数或者性能上的监控需求,那可以考虑采用PLT Hook技术路线。一般适合APP的官方员工。 而如果是希望应对各种各样APP自己独有的NDK函数或者代码段的话,目前只能选择Inline Hook。适合APP逆向人员,软件分析人员,CTF Android逆向解题等。 60 | 61 | ##文末说明 62 | 关于文中的一些解释与需求可能与别的同学的理解有偏差,这很正常,因为大家对Native Hook的需求不同。此处补充解释一下: 63 | 64 | 关于目前公开的Android Native Hook工具寥寥无几这一点我补充解释一下:唯一一个公开且接近于Java Hook的Xposed那样好用的工具可能就只是Cydia Substrate了。但是该项目已经好几年没更新,并且只支持到安卓5.0以前。还有一个不错的Native Hook工具是Frida,但是它的运行原理涉及调试,因此遇到反调试会相当棘手。由于本人反调试遇到的情况较多,所以Frida不怎么用。 -------------------------------------------------------------------------------- /Android 调试工具/常见app加固厂商脱壳方法.md: -------------------------------------------------------------------------------- 1 | 原文 by [mottoin](http://www.mottoin.com/89035.html) 2 | 3 | ### Apk文件结构 4 | ![](../pictures/androidshell1.png) 5 | ### Dex文件结构 6 | ![](../pictures/androidshell2.png) 7 | ## 壳史 8 | 9 | ### 第一代壳 Dex加密 10 | 11 | 1. Dex字符串加密 12 | 2. 资源加密 13 | 3. 对抗反编译 14 | 4. 反调试 15 | 5. 自定义DexClassLoader 16 | 17 | ### 第二代壳 Dex抽取与So加固 18 | 19 | 1. 对抗第一代壳常见的脱壳法 20 | 2. Dex Method代码抽取到外部(通常企业版) 21 | 3. Dex动态加载 22 | 4. So加 23 | 24 | ### 第三代壳 Dex动态解密与So混淆 25 | 26 | 1. Dex Method代码动态解密** 27 | 2. So代码膨胀混淆 28 | 3. 对抗之前出现的所有脱壳法 29 | 30 | ### 第四代壳 arm vmp(未来) 31 | 32 | vmp 33 | 34 | ## 壳的识别 35 | 36 | ### 1.用加固厂商特征: 37 | 38 | * 娜迦: libchaosvmp.so , libddog.solibfdog.so 39 | * 爱加密:libexec.so, libexecmain.so 40 | * 梆梆: libsecexe.so, libsecmain.so , libDexHelper.so 41 | * 360:libprotectClass.so, libjiagu.so 42 | * 通付盾:libegis.so 43 | * 网秦:libnqshield.so *百度:libbaiduprotect.so 44 | 45 | ### 2.基于特征的识别代码 46 | ![](../pictures/androidshell3.png) 47 | 48 | 49 | ## 第一代壳 50 | 51 | 1. 内存Dump法 52 | 2. 文件监视法 53 | 3. Hook法 54 | 4. 定制系统 55 | 5. 动态调试法 56 | 57 | ### 内存Dump法 58 | 59 | 内存中寻找dex.035或者dex.036 60 | /proc/xxx/maps中查找后,手动Dump 61 | ![](../pictures/androidshell4.png) 62 | 63 | android-unpacker https://github.com/strazzere/android-unpacker 64 | ![](../pictures/androidshell5.png) 65 | 66 | drizzleDumper https://github.com/DrizzleRisk/drizzleDumper 67 | 升级版的android-unpacker,read和lseek64代替pread,匹配dex代替匹配odex 68 | ![](../pictures/androidshell6.png) 69 | 70 | ![](../pictures/androidshell7.png) 71 | ### 文件监视法 72 | 73 | Dex优化生成odex 74 | inotifywait-for-Android https://github.com/mkttanabe/inotifywait-for-Android 75 | 监视文件变化 76 | ![](../pictures/androidshell8.png) 77 | 78 | notifywait-for-Android https://github.com/mkttanabe/inotifywait-for-Android 79 | 监视DexOpt输出 80 | 81 | ![](../pictures/androidshell9.png) 82 | 83 | ![](../pictures/androidshell10.png) 84 | ### Hook法 85 | 86 | Hook dvmDexFileOpenPartial 87 | http://androidxref.com/4.4_r1/xref/dalvik/vm/DvmDex.cpp 88 | ![](../pictures/androidshell11.png) 89 | ![](../pictures/androidshell12.png) 90 | 91 | 92 | ### 定制系统 93 | 94 | 修改安卓源码并刷机 95 | ![](../pictures/androidshell13.png) 96 | 97 | DumpApk https://github.com/CvvT/DumpApk 98 | 只针对部分壳 99 | ![](../pictures/androidshell14.png) 100 | 101 | ### 动态调试法 102 | 103 | IDA Pro 104 | ![](../pictures/androidshell15.png) 105 | ![](../pictures/androidshell16.png) 106 | ![](../pictures/androidshell17.png) 107 | 108 | ### gdb gcore法 109 | ``` 110 | .gdbserver :1234 –attach pid 111 | .gdb 112 | (gdb) target remote :1234 113 | (gdb) gcore 114 | ``` 115 | coredump文件中搜索“dex.035” 116 | ![](../pictures/androidshell18.png) 117 | 118 | 119 | ## 第二代壳 120 | 121 | 1. 内存重组法 122 | 2. Hook法 123 | 3. 动态调试 124 | 4. 定制系统 125 | 5. 静态脱壳机 126 | 127 | ### 内存重组法 128 | 129 | #### Dex篇 130 | 131 | ZjDroid http://bbs.pediy.com/showthread.php?t=190494 132 | 133 | 对付一切内存中完整的dex,包括壳与动态加载的jar 134 | 135 | ![](../pictures/androidshell19.png) 136 | ![](../pictures/androidshell20.png) 137 | 138 | 139 | 140 | #### so篇 141 | 142 | elfrebuild 143 | 144 | ![](../pictures/androidshell21.png) 145 | ![](../pictures/androidshell22.png) 146 | 147 | 148 | 构造soinfo,然后对其进行重建 149 | ![](../pictures/androidshell23.png) 150 | ![](../pictures/androidshell24.png) 151 | 152 | 153 | 154 | ### Hook法 155 | 156 | 针对无代码抽取且Hook dvmDexFileOpenPartial失败 157 | 158 | Hook dexFileParse 159 | 160 | http://androidxref.com/4.4_r1/xref/dalvik/vm/DvmDex.cpp 161 | ![](../pictures/androidshell25.png) 162 | 163 | 164 | https://github.com/WooyunDota/DumpDex 165 | ![](../pictures/androidshell26.png) 166 | 167 | 168 | 针对无代码抽取且Hook dexFileParse失败 169 | 170 | Hook memcmp 171 | 172 | http://androidxref.com/4.4_r1/xref/dalvik/vm/DvmDex.cpp 173 | 174 | ![](../pictures/androidshell27.png) 175 | ![](../pictures/androidshell28.png) 176 | 177 | 178 | 179 | ### 定制系统 180 | 181 | 修改安卓源码并刷机-针对无抽取代码 182 | 183 | https://github.com/bunnyblue/DexExtractor 184 | ![](../pictures/androidshell29.png) 185 | 186 | 187 | Hook dexfileParse 188 | ![](../pictures/androidshell30.png) 189 | 190 | ![](../pictures/androidshell31.png) 191 | 192 | 193 | DexHunter-最强大的二代壳脱壳工具 194 | 195 | https://github.com/zyq8709/DexHunter 196 | 197 | DexHunter的工作流程: 198 | 199 | ![](../pictures/androidshell32.png) 200 | 201 | DexHunter的工作原理: 202 | 203 | ![](../pictures/androidshell33.png) 204 | 205 | 绕过三进程反调试 206 | 207 | http://bbs.pediy.com/showthread.php?p=1439627 208 | ![](../pictures/androidshell34.png) 209 | 210 | ![](../pictures/androidshell35.png) 211 | 212 | 213 | 修改系统源码后: 214 | 215 | ![](../pictures/androidshell36.png) 216 | 217 | http://www.cnblogs.com/lvcha/p/3903669.html 218 | 219 | ![](../pictures/androidshell37.png) 220 | 221 | ls /proc/345/task 222 | ![](../pictures/androidshell38.png) 223 | 224 | ./gdbserver :1234 --attach346 225 | ... 226 | (gdb) gcore 227 | gcore防Dump解决方案: 228 | 229 | http://bbs.pediy.com/showthread.php?t=198995 230 | 231 | 断点mmap调试,针对Hook dexFileParse无效 232 | 233 | 原理: dexopt优化时, dvmContinueOptimization()->mmap() 234 | 235 | ![](../pictures/androidshell39.png) 236 | 237 | ### 静态脱壳机 238 | 239 | 分析壳so逻辑并还原加密算法 240 | 241 | http://www.cnblogs.com/2014asm/p/4924342.html 242 | 243 | ![](../pictures/androidshell40.png) 244 | 245 | 自定义linker脱so壳 246 | 247 | https://github.com/devilogic/udog 248 | 249 | main() -> dump_file() 250 | ![](../pictures/androidshell41.png) 251 | 252 | ## 第三代壳 253 | 254 | 1. dex2oat法 255 | 2. 定制系统 256 | 3. dex2oat法 257 | 258 | ART模式下,dex2oat生成oat时,内存中的DEX是完整的 259 | 260 | http://bbs.pediy.com/showthread.php?t=210532 261 | ![](../pictures/androidshell42.png) 262 | 263 | 264 | ### 定制系统 265 | 266 | Hook Dalvik_dalvik_system_DexFile_defineClassNative 267 | 268 | 枚举所有DexClassDef,对所有的class,调用dvmDefineClass进行强制加载 269 | ![](../pictures/androidshell43.png) 270 | 271 | 272 | ## 第N代壳 273 | 274 | ### so + vmp 275 | ### 动态调试 + 人肉还原 -------------------------------------------------------------------------------- /Android安全概述/Android Linux 内核层安全.md: -------------------------------------------------------------------------------- 1 | # 第二章 Android Linux 内核层安全 2 | 3 | > 来源:[Yury Zhauniarovich | Publications](http://www.zhauniarovich.com/pubs.html) 4 | 5 | > 译者:[飞龙](https://github.com/) 6 | 7 | > 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 8 | 9 | 作为最广为人知的开源项目之一,Linux 已经被证明是一个安全,可信和稳定的软件,全世界数千人对它进行研究,攻击和打补丁。 不出所料,Linux 内核是 Android 操作系统的基础[3]。 Android 不仅依赖于 Linux 的进程,内存和文件系统管理,它也是 Android 安全架构中最重要的组件之一。 在 Android 中,Linux 内核负责配置应用沙盒,以及规范一些权限。 10 | 11 | ## 2.1 应用沙盒 12 | 13 | 让我们考虑一个 Android 应用安装的过程。 Android 应用以 Android 软件包(`.apk`)文件的形式分发。 一个包由 Dalvik 可执行文件,资源,本地库和清单文件组成,并由开发者签名来签名。 有三个主要媒介可以在 Android 操作系统的设备上安装软件包: 14 | 15 | + Google Play 16 | + 软件包安装程序 17 | + adb install 工具 18 | 19 | Google Play 是一个特殊的应用,它为用户提供查找由第三方开发人员上传到市场的应用,以及安装该应用的功能。虽然它也是第三方应用,但 Google Play 应用(因为使用与操作系统相同的签名进行签名)可访问 Android 的受保护组件,而其他第三方应用则缺少这些组件。如果用户从其他来源安装应用,则通常隐式使用软件包安装程序。此系统应用提供了用于启动软件包安装过程的界面。由 Android 提供的`adb install`工具主要由第三方应用开发人员使用。虽然前两个媒介需要用户在安装过程中同意权限列表,但后者会安静地安装应用。这就是它主要用于开发工具的原因,旨在将应用安装在设备上进行测试。该过程如图 2.1 的上半部分所示。此图显示了 Android 安全体系结构的更详细的概述。我们将在本文中参考它来解释这个操作系统的特性。 20 | 21 | 在 Linux 内核层配置应用沙箱的过程如下。 在安装过程中,每个包都会被分配一个唯一的用户标识符(UID)和组标识符(GID),在设备的应用生命周期内不会更改。 因此,在 Android 中每个应用都有一个相应的 Linux 用户。 用户名遵循格式`app_x`,并且该用户的 UID 等于`Process.FIRST_APPLICATION_UID + x`,其中`Process.FIRST_APPLICATION_UID`常量对应于`10000`。例如,在图 2.1 中,`ex1.apk`包在安装期间获得了用户名`app 1`,UID 等于 `10001`。 22 | 23 | ![](../pictures/2-1.jpg) 24 | 25 | 图 2.1:Android 安全架构 26 | 27 | 在 Linux 中,内存中的所有文件都受 Linux 自定义访问控制(DAC)的约束。访问权限由文件的创建者或所有者为三种用户类型设置:文件的所有者,与所有者在同一组中的用户和所有其他用户。对于每种类型的用户,分配读,写和执行(`r-w-x`)权限的元组。因此,因为每个应用都有自己的 UID 和 GID,Linux 内核强制应用在自己的隔离地址空间内执行。除此之外,应用唯一的 UID 和 GID 由 Linux 内核使用,以实现不同应用之间的设备资源(内存,CPU 等)的公平分离。安装过程中的每个应用也会获得自己的主目录,例如`/data/data/package_name`,其中`package_name`是 Android 软件包的名称,例如`com.ex.ex1`,在 Android 中,这个文件夹是内部存储目录,其中应用将私有数据放在里面。分配给此目录的 Linux 权限只允许“所有者"应用写入并读取此目录。有一些例外应该提到。使用相同证书签名的应用能够在彼此之间共享数据,可以拥有相同的 UID 或甚至可以在相同的进程中运行。 28 | 29 | 这些架构决策在 Linux 内核层上建立了高效的应用沙箱。 这种类型的沙箱很简单,并基于 Linux 可选访问控制模型(DAC)的验证。 幸运的是,因为沙盒在 Linux 内核层上执行,本地代码和操作系统应用也受到本章[3]中所描述的这些约束的约束。 30 | 31 | ## 2.2 Linux 内核层上的权限约束 32 | 33 | 通过将 Linux 用户和组所有者分配给实现此功能的组件,可以限制对某些系统功能的访问。 这种类型的限制可以应用于系统资源,如文件,驱动程序和套接字。 Android 使用文件系统权限和特定的内核补丁(称为 Paranoid Networking)[13]来限制低级系统功能的访问,如网络套接字,摄像机设备,外部存储器,日志读取能力等。 34 | 35 | 使用文件系统权限访问文件和设备驱动程序,可以限制进程对设备某些功能的访问。例如,这种技术被应用于限制应用对设备相机的访问。 `/dev/cam`设备驱动程序的权限设置为`0660`,属于`root`所有者和摄像机所有者组。这意味着只有以`root`身份运行或包含在摄像机组中的进程才能读取和写入此设备驱动程序。因此,仅包括在相机组中的应用程序可以与相机交互。权限标签和相应组之间的映射在文件框架`/base/data/etc/platform.xml`中定义,摘录如清单 2.1 所示。因此,在安装过程中,如果应用程序已请求访问摄像机功能,并且用户已批准该应用程序,则还会为此应用程序分配一个摄像机 Linux 组 GID(请参阅清单 2.1 中的第 8 行和第 9 行)。因此,此应用程序可以从`/dev/cam`设备驱动程序读取信息。 36 | 37 | ```xml 38 | 1 ... 39 | 2 40 | 3 ... 41 | 4 42 | 5 43 | 6 44 | 7 45 | 8 46 | 9 47 | 10 48 | 11 49 | 12 50 | 13 51 | 14 52 | 15 ... 53 | 16 54 | ``` 55 | 56 | 代码 2.1:权限标签和 Linux 组之间的映射 57 | 58 | Android 中有一些地方可以用于设置文件、驱动和 Unix 套接字的文件系统权限:`init`程序,`init.rc`配置文件,`ueventd.rc`配置文件和系统 ROM 文件系统配置文件。 它们在第 3 章中会详细讨论。 59 | 60 | 在传统的 Linux 发行版中,允许所有进程启动网络连接。 同时,对于移动操作系统,必须控制对网络功能的访问。 为了在 Android 中实现此控制,需要添加特殊的内核补丁,将网络设施的访问限制于属于特定 Linux 组或具有特定 Linux 功能的进程。 这些针对 Android 的 Linux 内核补丁已经获得了 Paranoid 网络的名称。 例如,对于负责网络通信的`AF_INET`套接字地址族,此检查在`kernel/net/ipv4/af_inet.c`文件中执行(参见清单 2.2 中的代码片段)。 Linux 组和 Paranoid 网络的权限标签之间的映射也在`platform.xml`文件中设置(例如,参见清单 2.1 中的第 4 行)。 61 | 62 | ```c 63 | 1 ... 64 | 2 #ifdef CONFIG_ANDROID_PARANOID_NETWORK 65 | 3 #include 66 | 4 67 | 5 static inline int current_has_network ( void ) 68 | 6 { 69 | 7 return in_egroup_p (AID_INET) || capable (CAP_NET_RAW) ; 70 | 8 } 71 | 9 #else 72 | 10 static inline int current_has_network ( void ) 73 | 11 { 74 | 12 return 1; 75 | 13 } 76 | 14 #endif 77 | 15 ... 78 | 16 79 | 17 /* 80 | 18 * Create an inet socket . 81 | 19 */ 82 | 20 83 | 21 static int inet create ( struct net *net , struct socket *sock , int protocol , 84 | 22 int kern ) 85 | 23 { 86 | 24 ... 87 | 25 if (!current_has_network() ) 88 | 26 return −EACCES; 89 | 27 ... 90 | 28 } 91 | ``` 92 | 93 | 代码 2.2:Paranoid 网络补丁 94 | 95 | 类似的 Paranoid 网络补丁也适用于限制访问 IPv6 和蓝牙[19]。 96 | 97 | 这些检查中使用的常量在内核中硬编码,并在`kernel/include/linux/android_aid.h`文件中规定(参见清单 2.3)。 98 | 99 | ```c 100 | 1 ... 101 | 2 #ifndef LINUX_ANDROID_AID_H 102 | 3 #define LINUX_ANDROID_AID_H 103 | 4 104 | 5 /* AIDs that the kernel treats differently */ 105 | 6 #define AID_OBSOLETE_000 3001 /* was NET_BT_ADMIN */ 106 | 7 #define AID_OBSOLETE_001 3002 /* was NET_BT */ 107 | 8 #define AID_INET 3003 108 | 9 #define AID_NET_RAW 3004 109 | 10 #define AID_NET_ADMIN 3005 110 | 11 #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ 111 | 12 #define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ 112 | 13 113 | 14 #endif 114 | ``` 115 | 116 | 代码 2.3:硬编码在 Linux 内核中的 Android ID 常量 117 | 118 | 因此,在 Linux 内核层,通过检查应用程序是否包含在特殊预定义的组中来实现 Android 权限。 只有此组的成员才能访问受保护的功能。 在应用程序安装期间,如果用户已同意所请求的权限,则该应用程序包括在相应的 Linux 组中,因此获得对受保护功能的访问。 -------------------------------------------------------------------------------- /Android安全概述/Android 安全概述.md: -------------------------------------------------------------------------------- 1 | # 第一章 Android 2 | 3 | > 来源:[Yury Zhauniarovich | Publications](http://www.zhauniarovich.com/pubs.html) 4 | 5 | > 译者:[飞龙](https://github.com/) 6 | 7 | > 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 8 | 9 | Android 安全架构的理解不仅帮助我了解 Android 的工作原理,而且为我开启了如何构建移动操作系统和 Linux 的眼界。 本章从安全角度讲解 Android 架构的基础知识。 在第 1.1 节中,我们会描述 Android 的主要层级,而第 1.2 节给出了在此操作系统中实现的安全机制的高级概述。 10 | 11 | ## 1.1 Android 技术栈 12 | 13 | Android 是一个用于各种移动设备的软件栈,以及由 Google 领导的相应开源项目[9]。 Android 由四个层组成:Linux 内核,本地用户空间,应用程序框架和应用程序层。 有时本地用户空间和应用程序框架层被合并到一个层中,称为 Android 中间件层。 图 1.1 表示 Android 软件栈的层级。 粗略地说,在这个图中,绿色块对应在 C/C++ 中开发的组件,而蓝色对应在 Java 中实现的组件。 Google 在 Apache 2.0 许可证下分发了大部分 Android 代码。 此规则最值得注意的例外是 Linux 内核中的更改,这些更改在 GNU GPL V2 许可证下。 14 | 15 | ![](../pictures/1-1.jpg) 16 | 17 | 图 1.1:Android 软件栈 18 | 19 | Linux 内核层。在 2005 年被 Google 认识之前,Android 是 Android Inc. 公司的初创产品。创业公司的特点之一是,他们倾向于最大限度地重复利用已经存在的组件,以减少其产品的时间和成本。 Android 公司选择 Linux 内核作为他们新平台的核心。在 Android 中,Linux 内核负责进程,内存,通信,文件系统管理等。虽然 Android 主要依赖于“vanilla" Linux 内核功能,但是已经做出了系统操作所需的几个自定义更改。其中 Binder(一个驱动程序,提供对 Android 中的自定义 RPC / IPC 机制的支持),Ashmem(替代标准的 Linux 共享内存功能),Wakelocks(一种防止系统进入睡眠的机制)是最值得注意的更改[19]。虽然这些变化被证明在移动操作系统中非常有用,但它们仍然在 Linux 内核的主要分支之外。 20 | 21 | 22 | 本地用户空间层。通过本地用户空间,我们可了解在 Dalvik 虚拟机之外运行的所有用户空间组件,并且不属于 Linux Kernel 层。这个层的第一个组件是硬件抽象层(HAL),它与 Linux 内核和本地用户空间层之间实际上是模糊的。在 Linux 中,硬件驱动程序嵌入到内核中或作为模块动态加载。虽然 Android 是建立在 Linux 内核之上,它利用了一种非常不同的方法来支持新的硬件。相反,对于每种类型的硬件,Android 定义了一个 API,它由上层使用并用于与这种类型的硬件交互。硬件供应商必须提供一个软件模块,负责实现在 Android 中为这种特定类型的硬件定义的API。因此,此解决方案不再允许 Android 将所有可能的驱动程序嵌入内核,并禁用动态模块加载内核机制。提供此功能的组件在 Android 中称为硬件抽象层。此外,这样的架构解决方案允许硬件供应商选择许可证,在其下分发它们的驱动程序[18,19]。 23 | 24 | 内核通过启动一个名为 init 的用户空间进程来完成其启动。 此过程负责启动 Android 中的所有其他进程和服务,以及在操作系统中执行一些操作。 例如,如果关键服务在 Android 中停止应答,init 进程可以重新启动它。 该进程根据`init.rc`配置文件执行操作。 工具箱包括基本的二进制文件,在 Android [19]中提供`shell`工具的功能。 25 | 26 | 27 | Android 还依赖于一些关键的守护进程。 它在系统启动时启动,并在系统工作时保持它们运行。 例如,`rild`(无线接口层守护进程,负责基带处理器和其他系统之间的通信),`servicemanager`(一个守护进程,它包含在 Android 中运行的所有 Binder 服务的索引),`adbd`(Android Debug Bridge 守护进程,作为主机和目标设备之间的连接管理器)等。 28 | 29 | 本地用户空间中最后一个组件是本地库。 有两种类型的本地库:来自外部项目的本地库,以及在 Android 自身中开发的本地库。 这些库被动态加载并为 Android 进程提供各种功能[19]。 30 | 31 | 应用程序框架层。 Dalvik 是 Android 的基于寄存器的虚拟机。它允许操作系统执行使用 Java 语言编写的 Android 应用程序。在构建过程中,Java 类被编译成由 Dalvik VM 解释的`.dex`文件。 Dalvik VM 特别设计为在受限环境中运行。此外,Dalvik VM 提供了与系统其余部分交互的功能,包括本地二进制和库。为了加速进程初始化过程,Android 利用了一个名为 Zygote 的特定组件。这是一个将所有核心库链接起来的特殊“预热"过程。当新应用程序即将运行时,Android 会从 Zygote 分配一个新进程,并根据已启动的应用程序的规范设置该进程的参数。该解决方案允许操作系统不将链接库复制到新进程中,从而加快应用程序启动操作。在 Android 中使用的 Java 核心库,是从 Apache Harmony 项目借用的。 32 | 33 | 系统服务是 Android 的最重要的部分之一。 Android 提供了许多系统服务,它们提供了基本的移动操作系统功能,供 Android 应用开发人员在其应用中使用。 例如,`PackageManagerService`负责管理(安装,更新,删除等)操作系统中的 Android 包。 使用 JNI 接口系统服务可以与本地用户空间层的守护进程,工具箱二进制文件和本地库进行交互。 公共 API 到系统服务都是通过 Android 框架库提供的。 应用程序开发人员使用此 API 与系统服务进行交互。 34 | 35 | Android 应用程序层。 Android 应用程序是在 Android 上运行的软件应用程序,并为用户提供大多数功能。 Stock Android 操作系统附带了一些称为系统应用程序的内置应用程序。 这些是作为 AOSP 构建过程的一部分编译的应用程序。 此外,用户可以从许多应用市场安装用户应用,来扩展基本功能并向操作系统引入新的功能。 36 | 37 | ## 1.2 Android 一般安全说明 38 | 39 | Android 的核心安全原则是,对手应用程序不应该损害操作系统资源,用户和其他应用程序。 为了促使这个原则的执行,Android 是一个分层操作系统,利用了所有级别提供的安全机制。 专注于安全性,Android 结合了两个层级的组件:Linux 内核层和应用程序框架层(参见图 1.2)。 40 | 41 | 在 Linux 内核层级,每个应用程序都在特殊的应用程序沙箱中运行。 内核通过使用标准 Linux 设施(进程分离,以及通过网络套接字和文件系统的任意访问控制)来强制隔离应用程序和操作系统组件。 这种隔离的实现是,为每个应用程序分配单独的 Unix 用户(UID)和组(GID)标识符。 这种架构决策强制在单独的 Linux 进程中运行每个应用程序。 因此,由于在 Linux 中实现的进程隔离,在默认情况下,应用程序不能相互干扰,并且对操作系统提供的设施具有有限的访问。 因此,应用程序沙盒确保应用程序不能耗尽操作系统资源,并且不能与其他应用程序交互[3]。 42 | 43 | ![](../pictures/1-2.jpg) 44 | 45 | 图 1.2:Android 内核实施中的两个层级 46 | 47 | Linux 内核层提供的强制机制,有效地使用沙箱,将应用程序与其他应用程序和系统组件隔离。 同时,需要有效的通信协议来允许开发人员重用应用组件并与操作系统单元交互。 该协议称为进程间通信(IPC),因为它能够促进不同进程之间的交互。 在 Android 中,此协议在 Android 中间件层实现(在 Linux 内核层上发布的特殊驱动程序)。 此层级的安全性由 IPC 引用监控器提供。 引用监控器调解进程之间的所有通信,并控制应用程序如何访问系统的组件和其他应用程序。 在 Android 中,IPC 引用监控器遵循强制访问控制(MAC)访问控制类型。 48 | 49 | 默认情况下,所有 Android 应用都在低特权应用程序沙箱中运行。 因此,应用程序只能访问一组有限的系统功能。 Android 操作系统控制应用程序对系统资源的访问,这可能会对用户体验造成不利影响[3]。 该控制以不同的形式实现,其中一些在以下章节中详细描述。 还有一部分受保护的系统功能(例如,摄像头,电话或 GPS 功能),其访问权限应该提供给第三方应用程序。 然而,这种访问应以受控的方式提供。 在 Android 中,这种控制使用权限来实现。 基本上,每个提供受保护系统资源的访问的敏感 API 都被分配有一个权限(Permission)- 它是唯一的安全标签。 此外,受保护特性还可能包括其他应用的组件。 50 | 51 | 为了使用受保护的功能,应用程序的开发者必须在文件`AndroidManifest.xml`中请求相应的权限。 在安装应用程序期间,Android 操作系统将解析此文件,并向用户提供此文件中声明的权限列表。 应用程序的安装根据“全有或全无"原则进行,这意味着仅当接受所有权限时才安装应用程序。 否则,将不会安装应用程序。 权限仅在安装时授予,以后无法修改。 作为权限的示例,我们考虑需要监控 SMS 传入消息的应用程序。 在这种情况下,`AndroidManifest.xml`文件必须在``标签中包含以下声明:`android.permission.RECEIVE SMS`。 52 | 53 | 应用程序尝试使用某个功能,并且该功能尚未在 Android 清单文件中声明,通常会产生安全性异常。 在下面几节中我们会讲解权限实现机制的细节。 -------------------------------------------------------------------------------- /Android安全概述/Android 安全的其他话题.md: -------------------------------------------------------------------------------- 1 | # 第六章 Android 安全的其它话题 2 | 3 | > 来源:[Yury Zhauniarovich | Publications](http://www.zhauniarovich.com/pubs.html) 4 | 5 | > 译者:[飞龙](https://github.com/) 6 | 7 | > 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 8 | 9 | 在本章中,我们会涉及到与 Android 安全相关的其他主题,这些主题不直接属于已经涉及的任何主题。 10 | 11 | ## 6.1 Android 签名过程 12 | 13 | Android 应用程序以 Android 应用包文件(`.apk`文件)的形式分发到设备上。 由于这个平台的程序主要是用 Java 编写的,所以这种格式与 Java 包的格式 -- `jar`(Java Archive)有很多共同点,它用于将代码,资源和元数据(来自可选的`META-INF`目录 )文件使用 zip 归档算法转换成一个文件。 `META-INF`目录存储软件包和扩展配置数据,包括安全性,版本控制,扩展和服务[5]。 基本上,在 Android 的情况中,`apkbuilder`工具将构建的项目文件压缩到一起[1],使用标准的 Java 工具`jarsigner`对这个归档文件签名[6]。 在应用程序签名过程中,`jarsigner`创建`META-INF`目录,在 Android 中通常包含以下文件:清单文件(`MANIFEST.MF`),签名文件(扩展名为`.SF`)和签名块文件(`.RSA`或`.DSA`) 。 14 | 15 | 清单文件(`MANIFEST.MF`)由主属性部分和每个条目属性组成,每个包含在未签名的`apk`中文件拥有一个条目。 这些每个条目中的属性存储文件名称信息,以及使用 base64 格式编码的文件内容摘要。 在 Android 上,SHA1 算法用于计算摘要。 清单 6.1 中提供了清单文件的摘录。 16 | 17 | ``` 18 | 1 Manifest−Version : 1.0 19 | 2 Created−By: 1.6.0 41 (Sun Microsystems Inc. ) 20 | 3 21 | 4 Name: res/layout/main . xml 22 | 5 SHA1−Digest : NJ1YLN3mBEKTPibVXbFO8eRCAr8= 23 | 6 24 | 7 Name: AndroidManifest . xml 25 | 8 SHA1−Digest : wBoSXxhOQ2LR/pJY7Bczu1sWLy4= 26 | ``` 27 | 28 | 代码 6.1:清单文件的摘录 29 | 30 | 包含被签名数据的签名文件(`.SF`)的内容类似于`MANIFEST.MF`的内容。 这个文件的一个例子如清单 6.2 所示。 主要部分包含清单文件的主要属性的摘要(`SHA1-Digest-Manifest-Main-Attributes`)和内容摘要(`SHA1-Digest-Manifest`)。 每个条目包含清单文件中的条目的摘要以及相应的文件名。 31 | 32 | ``` 33 | 1 Signature−Version : 1.0 34 | 2 SHA1−Digest−Manifest−Main−Attributes : nl/DtR972nRpjey6ocvNKvmjvw8= 35 | 3 Created−By: 1.6.0 41 (Sun Microsystems Inc. ) 36 | 4 SHA1−Digest−Manifest : Ej5guqx3DYaOLOm3Kh89ddgEJW4= 37 | 5 38 | 6 Name: res/layout/main.xml 39 | 7 SHA1−Digest : Z871jZHrhRKHDaGf2K4p4fKgztk= 40 | 8 41 | 9 Name: AndroidManifest.xml 42 | 10 SHA1−Digest : hQtlGk+tKFLSXufjNaTwd9qd4Cw= 43 | 11 ... 44 | ``` 45 | 46 | 代码 6.2:签名文件的摘录 47 | 48 | 最后一部分是签名块文件(`.DSA`或`.RSA`)。 这个二进制文件包含签名文件的签名版本; 它与相应的`.SF`文件具有相同的名称。 根据所使用的算法(RSA 或 DSA),它有不同的扩展名。 49 | 50 | 相同的apk文件有可能签署几个不同的证书。 在这种情况下,在`META-INF`目录中将有几个`.SF`和`.DSA`或`.RSA`文件(它们的数量将等于应用程序签名的次数)。 51 | 52 | ### 6.1.1 Android 中的应用签名检查 53 | 54 | 大多数 Android 应用程序都使用开发人员签名的证书(注意 Android 的“证书”和“签名”可以互换使用)。 此证书用于确保原始应用程序的代码及其更新来自同一位置,并在同一开发人员的应用程序之间建立信任关系。 为了执行这个检查,Android 只是比较证书的二进制表示,它用于签署一个应用程序及其更新(第一种情况)和协作应用程序(第二种情况)。 55 | 56 | 这种对证书的检查通过`PackageManagerService`中的方法`int compareSignatures(Signature[] s1,Signature[] s2)`来实现,代码如清单 6.3 所示。在上一节中,我们注意到在 Android 中,可以使用多个不同的证书签署相同的应用程序。这解释了为什么该方法使用两个签名数组作为参数。尽管该方法在 Android 安全规定中占有重要地位,但其行为强烈依赖于平台的版本。在较新版本中(从 Android 2.2 开始),此方法比较两个`Signature`数组,如果两个数组不等于`null`,并且如果所有`s2`签名都包含在`s1`中,则返回`SIGNATURE MATCH`值,否则为`SIGNATURE_NOT_MATCH`。在版本 2.2 之前,此方法检查数组`s1`是否包含在`s2`中。这种行为允许系统安装升级,即使它们已经使用原始应用程序的证书子集签名[2]。 57 | 58 | 在几种情况下,需要同一开发人员的应用程序之间的信任关系。 第一种情况与`signature`和`signatureOrSystem`的权限相关。 要使用受这些权限保护的功能,声明权限和请求它的包必须使用同一组证书签名。 第二种情况与 Android 运行具有相同 UID 或甚至在相同 Linux 进程中运行不同应用程序的能力有关。 在这种情况下,请求此类行为的应用程序必须使用相同的签名进行签名。 59 | 60 | ```java 61 | 1 static int compareSignatures ( Signature[] s1 , Signature[] s2 ) { 62 | 2 if ( s1 == null ) { 63 | 3 return s2 == null 64 | 4 ? PackageManager.SIGNATURE_NEITHER_SIGNED 65 | 5 : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; 66 | 6 } 67 | 7 if ( s2 == null ) { 68 | 8 return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; 69 | 9 } 70 | 10 HashSet set1 = new HashSet() ; 71 | 11 for ( Signature sig : s1 ) { 72 | 12 set1.add( sig ) ; 73 | 13 } 74 | 14 HashSet set2 = new HashSet() ; 75 | 15 for ( Signature sig : s2 ) { 76 | 16 set2.add( sig ) ; 77 | 17 } 78 | 18 // Make sure s2 contains all signatures in s1 . 79 | 19 if ( set1.equals ( set2 ) ) { 80 | 20 return PackageManager.SIGNATURE_MATCH; 81 | 21 } 82 | 22 return PackageManager.SIGNATURE_NO_MATCH; 83 | 23 } 84 | ``` 85 | 86 | 代码 6.3:`PackageManagerService`中的`compareSignatures`方法 -------------------------------------------------------------------------------- /Android安全概述/Android 应用层安全.md: -------------------------------------------------------------------------------- 1 | # 第五章 Android 应用层安全 2 | 3 | > 来源:[Yury Zhauniarovich | Publications](http://www.zhauniarovich.com/pubs.html) 4 | 5 | > 译者:[飞龙](https://github.com/) 6 | 7 | > 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 8 | 9 | 10 | 虽然在这一节中我们描述了应用层的安全性,但是实际的安全实施通常出现在到目前为止描述的底层。 但是,在介绍应用层之后,我们更容易解释 Android 的一些安全功能。 11 | 12 | ## 5.1 应用组件 13 | 14 | Android 应用以 Android 软件包(`.apk`)文件的形式分发。 一个包由 Dalvik 可执行文件,资源文件,清单文件和本地库组成,并由应用的开发人员使用自签名证书签名。 每个 Android 应用由四个组件类型的几个组件组成:活动(Activity),服务(Service),广播接收器(Boardcast Reciver)和内容供应器(Content Provider)。 将应用分离为组件有助于应用的一部分在应用之间重用。 15 | 16 | 活动。 活动是用户界面的元素之一。 一般来说,一个活动通常代表一个界面。 17 | 18 | 服务。 服务是 Android 中的后台工作装置。 服务可以无限期运行。 最知名的服务示例是在后台播放音乐的媒体播放器,即使用户离开已启动此服务的活动。 19 | 20 | 广播接收器。 广播接收器是应用的组件,它接收广播消息并根据所获得的消息启动工作流。 21 | 22 | 内容供应器。 内容供应器是为应用提供存储和检索数据的能力的组件。 它还可以与另一应用共享一组数据。 23 | 24 | 因此,Android 应用由不同的组件组成,没有中央入口点,不像 Java 程序和`main`方法那样。 由于没有入口点,所有组件(广播接收器除外,它也可以动态定义)需要由应用的开发人员在`AndroidManifest.xml`文件中声明。 分离成组件使得我们可以在其它应用中使用组件。 例如,在清单 5.1 中,显示了一个应用的`AndroidManifest.xml`文件的示例。 此应用包含第 21 行中声明的一个`Activity`。其他应用可能会调用此活动,将此组件的功能集成到其应用中。 25 | 26 | ```xml 27 | 1 28 | 2 34 | 8 35 | 9 36 | 10 37 | 11 41 | 15 42 | 16 43 | 17 44 | 18 47 | 21 50 | 24 51 | 25 52 | 26 53 | 27 54 | 28 55 | 29 56 | 30 57 | 31 58 | 32 59 | 33 60 | 34 61 | ``` 62 | 63 | 代码 5.1:`AndroidManifest.xml`文件示例 64 | 65 | Android 提供了各种方式来调用应用的组件。 我们可以通过使用方法`startActivity`和`startActivityForResult`启动新的活动。 服务通过`startService`方法启动。 在这种情况下,被调用的服务调用其方法`onStart`。 当开发人员要在组件和服务之间建立连接时,它调用`bindService`方法,并在被调用的服务中调用`onBind`方法。 当应用或系统组件使用`sendBroadcast`,`sendOrderedBroadcast`和`sendStickyBroadcast`方法发送特殊消息时,将启动广播接收器。 66 | 67 | 内容供应器由来自内容解析器的请求调用。所有其他组件类型通过`Intent`(意图)激活。 意图是 Android 中基于`Binder`框架的特殊通信手段。意图被传递给执行组件调用的方法。被调用的组件可以被两种不同类型的意图调用。为了显示这些类型的差异,让我们考虑一个例子。例如,用户想要在应用中选择图片。应用的开发人员可以使用显式意图或隐式意图来调用选择图片的组件。对于第一种意图类型,开发人员可以在他的应用的组件中实现挑选功能,并使用带有组件名称数据字段的显式意图调用此组件。当然,开发人员可以调用其他应用的组件,但是在这种情况下,他必须确保该应用安装在系统中。一般来说,从开发人员的角度来看,一个应用中的组件或不同应用的组件之间的交互不存在差异。对于第二种意图类型,开发人员将选择适当组件的权利转移给操作系统。 `intent`对象在其`Action`,`Data`和`Category`字段中包含一些信息。根据这个信息,使用意图过滤器,操作系统选择可以处理意图的适当组件。意图过滤器定义了组件可以处理的意图的“模板"。当然,相同的应用可以定义一个意图过滤器,它将处理来自其他组件的意图。 68 | 69 | ## 5.2 应用层的权限 70 | 71 | 权限不仅用于保护对系统资源的访问。 第三方应用的开发人员还可以使用自定义权限来保护对其应用的组件的访问。 自定义权限声明的示例如清单 5.1 中第 11 行所示。自定义权限的声明类似于系统权限之一。 72 | 73 | 为了说明自定义权限的用法,请参考图 5.1。由 3 个组件组成的应用 2 希望保护对其中两个的访问:C1 和 C2。为了实现这个目标,应用 2 的开发者必须声明两个权限标签`p1`,`p2`,并相应地将它们分配给受保护的组件。如果应用 1 的开发者想要访问应用 2 的组件 C1 ,则他必须定义他的应用需要权限`p1`。在这种情况下,应用 1 就可以使用应用 2 的组件 C1。如果应用没有指定所需的权限,则禁止访问受此权限保护的组件(参见图 5.1 中组件 C2 的情况)。回头看看我们在代码 5.1 中的`AndroidManifest.xml`文件的例子,活动`TestActivity`被权限`com.testpackage.permission.mypermission`保护,它在同一个应用清单文件中声明。如果另一个应用想要使用`TestActivity`提供的功能,它必须请求使用此权限,类似于第 16 行中的操作。 74 | 75 | ![](../pictures/5-1.jpg) 76 | 77 | 图 5.1:保护第三方应用组件的权限实施 78 | 79 | `ActivityManagerService`负责调用应用的组件。 为了保证应用组件的安全性,在用于调用组件的框架方法(例如,5.1 节中描述的`startActivity`)中,放置特殊的钩子。 这些钩子检查应用是否有权调用组件。 这些检查以`PackageManagerServer`类的`CheckUidPermission`方法结束(参见清单 4.6)。 因此,发生在 Android 框架层的实际的权限实施,可以看做 Android 操作系统的受信任部分。 因此,应用不能绕过检查。 有关如何调用组件和权限检查的更多信息,请参见[permissions](https://developer.android.com/guide/topics/security/permissions.html)。 -------------------------------------------------------------------------------- /Android应用安全/Android Application Security.md: -------------------------------------------------------------------------------- 1 | 最近想扩展学习下 Android 应用安全,找到一份[入门指引](https://manifestsecurity.com/android-application-security/) ,大概走了一遍,有一些注意的点且记下。 2 | 3 | 1. 建议下载的 Appie 版本为 [2.0](https://sourceforge.net/projects/appiefiles/files/Appie2.zip/download),因为作者写这些文章时用的是2.0 版本,亲试使用3.1版本时 goat droid 等 app 的 db 都是损坏的。如果在 cmd 内 goatdroid 执行不了,可以找到 jar 文件并 java -jar xx.jar 启动它 4 | 5 | 2. 在 drozer 启动时出现找不到java 的 错误,可以在用户家目录如 C:\Users\s1mba 下建立 .drozer_config 文件,内容如下: 6 | ``` 7 | [executables] 8 | java = D:\Java\jdk1.8.0_91\bin\java.exe 9 | [executables] 10 | javac = D:\Java\jdk1.8.0_91\bin\javac.exe 11 | ``` 12 | cd drozer 所在目录(如D:\Appie\Appie\vendor\drozer)再执行 drozer console connect, 否则执行命令 list 可能提示没有module,对某个module 使用时 run app.package.info –help 13 | 14 | 3. 使用 virtualbox 启动 genymotion avd 时,设置 network 为 adaptor1为host-only(允许全部访问),在全局config 建立一个nat 网络,将network adaptor2 设置为nat;若宿主机还需要使用代理才能访问网络,则在 avd wifi 中也需要长按设置下代理(或者为 burpsuite) 15 | 16 | 4. 在使用 adb 安装一些 apk 到 avd 时提示 arm_abi 冲突,需要安装下 [genymotion-arm-translation_v1.1.zip](http://download.csdn.net/detail/wjr2012/9017005),下载后将其拖动到 avd 界面安装即可 17 | 18 | 5. 在登录 goatdroid、herd financial 等 app 时需要设置下server ip port 即 宿主机的 ip,port 默认是 9888。如果不知道用户名密码则在 goatdroid service 界面找下 db 所在,查询下已有用户名和密码,一般有个默认用户 goatdroid : goatdroid 19 | 20 | 6. 第12章中说可以绕过登录页面直接启动 intent-filter 出来的主页,但貌似用户主页是在 activities.Home,此 activity 外部调用不了 21 | 22 | 7. 安装 Genymotion 时最好用打包 virtualbox 的版本,settings 设置下代理网络(如果需要),设置下sdk 地址(即 appie2 目录下 23 | 某位置,如D:\Appie2\Appie\bin\adt\sdk) 24 | 25 | 8. cmd 中 adb devices 启动时如果报错端口占用,可能是还有另外一份 sdk(比如 android studio)并开启了 adb。 26 | 27 | 9. 单独使用 SDK Manager 时需要设置下代理。使用 android studio 时设置 auto detect proxy 让其找到 pac 文件即可,但编译时需 28 | 要设置下 Gradle 的代理,在 gradle.properties 文件中配置 29 | ``` 30 | systemProp.https.proxyHost=proxy.example.com 31 | systemProp.http.proxyHost=proxy.example.com 32 | systemProp.https.proxyPort=8080 33 | systemProp.http.proxyPort=8080 34 | ``` 35 | 10. 测试android 应用安全常用工具 36 | adb (adb devices | adb shell | adb install | adb uninstall | adb push | adb pull | adb forward | adb shell am[activityManager] | adb shell pm[packageManager]) 37 | drozer (模拟一个app 的方式与其他app 交互,adb forward tcp:31415 tcp:31415 ) 38 | That means for these tasks we won’t be needing a rooted device, and neither drozer need rooted device to run. All the attacks we will do from drozer console will be originated from drozer app to testing application on your device. So it is like attacking your Banking application installed on your phone from a malicious application also installed on the same device. 39 | apktool(反编译apk 成 smali 文件等) 40 | dex2jar (将apk 文件反编译成 jar 文件,即class 文件集合) 41 | jdgui(把jar 文件反编译成 java 源文件) 42 | 43 | 11. android:debuggable 44 | Look for **android:debuggable** value in the AndroidManifest.xml file. 45 | In order to figure out which PID belong to our application, type **adb jdwp** before running the application you wanted to test. 46 | Now with the help of **run-as** binary we can execute commands as com.mwr.example.sieve application 47 | ![](../pictures/run_as.png) 48 | **Note: Above is the shell access of my personal phone which is not rooted.** 49 | Now you can extract the data or run an arbitary code using application permission like shown below. 50 | ![](../pictures/shellaccess.png) 51 | 52 | 12. android:allowBackup 53 | allowBackup 风险位置:AndroidMannifest.xml 文件 android:allowBackup 属性 54 | allowBackup 风险触发前提条件:未将 AndroidMannifest.xml 文件中的 android:allowBackup 属性值设为 false 55 | allowBackup 风险原理:当 allowBackup 标志值为 true 时,即可通过 adb backup 和 adb restore 来备份和恢复应用程序数据 56 | 57 | 13. 开发者后门 58 | There are sometimes when developer put a backdoor to a particular application. He/She puts that because he doesn’t want somebody else to access that sensitive piece of Information and sometimes that backdoor is for debugging purposes. 59 | 通过反编译成 java 源代码,查看某些 activity 也许可以发现一些登录的后门。 60 | -------------------------------------------------------------------------------- /Android应用安全/Android Logcat Security.md: -------------------------------------------------------------------------------- 1 | 原文 by 瘦蛟舞 2 | ## 0x00 科普 3 | development version :开发版,正在开发内测的版本,会有许多调试日志。 4 | release version : 发行版,签名后开发给用户的正式版本,日志量较少。 5 | android.util.Log:提供了五种输出日志的方法 6 | Log.e(), Log.w(), Log.i(), Log.d(), Log.v() 7 | ERROR, WARN, INFO, DEBUG, VERBOSE 8 | android.permission.READ_LOGS:app 读取日志权限,android 4.1 之前版本通过申请 READ_LOGS 权限就可以读取其他应用的 log 了。但是谷歌发现这样存在安全风险,于是 android 4.1 以及之后版本,即使申请了 READ_LOGS 权限也无法读取其他应用的日志信息了。4.1版本中 Logcat 的签名变为 “signature|system|development” 了,这意味着只有系统签名的app或者root 权限的app 才能使用该权限。普通用户可以通过ADB 查看所有日志。 9 | 10 | ## 0x01 测试 11 | 12 | 测试方法是非常简单的,可以使用sdk 中的小工具monitor 或者ADT 中集成的 logcat 来查看日志,将工具目录加入环境变量用起来比较方便。当然如果你想更有bigger 也可以使用 adb logcat。android 整体日志信息量是非常大的,想要高效一些就必须使用filter 来过滤一些无关信息,filter 是支持正则的,可以做一些关键字匹配比如 password、token、email 等。本来准备想做个小工具自动化收集,但是觉得这东西略鸡肋没太大必要,故本文的重点也是在如何安全地使用logcat 方面。 13 | ![](../pictures/androidcat1.jpg) 14 | ![](../pictures/androidcat2.jpg) 15 | 当然也可以自己写个app 在直接在手机上抓取logcat,不过前面提到因为android系统原因如果手机是android4.1 或者之后版本,即使在manifest.xml 中加入了如下申请也是无法读取到其他应用的 log的。 16 | `` 17 | ![](../pictures/androidcat3.jpg) 18 | 19 | root 权限可以随便看logcat,所以“logcat 信息泄露”漏洞因谷歌在4.1上的动作变得很鸡肋了。 20 | ![](../pictures/androidcat4.jpg) 21 | 22 | ## 0x02 smali注入logcat 23 | 将敏感数据在加密前打印出来就是利用静态 smali 注入插入了logcat 方法。 使用APK 反编译后用smali 注入非常方便,但要注意随意添加寄存器可能破坏本身逻辑,新手建议不添加寄存器直接使用已有的寄存器。 24 | `invoke-static {v0, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I` 25 | ![](../pictures/androidcat5.jpg) 26 | ![](../pictures/androidcat6.jpg) 27 | 28 | ## 0x03 建议 29 | 有些人认为任何log 都不应该在发行版本打印。但是为了app 的错误采集,异常反馈,必要的日志还是要被输出的,只要遵循安全编码规范就可以将风险控制在最小范围。 30 | Log.e()/w()/i():建议打印操作日志 31 | Log.d()/v():建议打印开发日志 32 | 1、敏感信息不应用 Log.e()/w()/i(), System.out/err 打印。 33 | 2、如果需要打印一些敏感信息建议使用 Log.d()/v()。(前提:release版本将被自动去除) 34 | ``` java 35 | @Override 36 | public void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_proguard); 39 | // *** POINT 1 *** Sensitive information must not be output by Log.e()/w()/i(), System.out/err. 40 | Log.e(LOG_TAG, "Not sensitive information (ERROR)"); 41 | Log.w(LOG_TAG, "Not sensitive information (WARN)"); 42 | Log.i(LOG_TAG, "Not sensitive information (INFO)"); 43 | // *** POINT 2 *** Sensitive information should be output by Log.d()/v() in case of need. 44 | // *** POINT 3 *** The return value of Log.d()/v()should not be used (with the purpose of substitution or comparison). 45 | Log.d(LOG_TAG, "sensitive information (DEBUG)"); 46 | Log.v(LOG_TAG, "sensitive information (VERBOSE)"); 47 | } 48 | ``` 49 | 3、Log.d()/v()的返回值不应被使用。(仅做开发调试观测) 50 | ``` java 51 | Examination code which Log.v() that is specifeied to be deleted is not deketed 52 | int i = android.util.Log.v("tag", "message"); 53 | System.out.println(String.format("Log.v() returned %d. ", i)); //Use the returned value of Log.v() for examination 54 | ``` 55 | 4、release版apk 实现自动删除Log.d()/v()等代码。 56 | eclipse中配置ProGuard 57 | ![](../pictures/androidcat7.jpg) 58 | 开发版所有log 都打印出来了。 59 | ![](../pictures/androidcat8.jpg) 60 | 发行版ProGuard 移除了d/v 的log 61 | ![](../pictures/androidcat9.jpg) 62 | 反编译后查看确实被remove了 63 | ![](../pictures/androidcat10.jpg) 64 | 65 | 5、公开的APK 文件应该是release 版而不是development 版。 66 | ## 0x04 native code 67 | android.util.Log 的构造函数是私有的,并不会被实例化,只是提供了静态的属性和方法。 68 | 而android.util.Log 的各种Log 记录方法的实现都依赖于native 的实现 println_native(),Log.v()/Log.d()/Log.i()/Log.w()/Log.e() 最终都是调用了 println_native()。 69 | ``` java 70 | Log.e(String tag, String msg) 71 | public static int v(String tag, String msg) { 72 | return println_native(LOG_ID_MAIN, VERBOSE, tag, msg); 73 | } 74 | ``` 75 | ``` c 76 | println_native(LOG_ID_MAIN, VERBOSE, tag, msg) 77 | /* 78 | * In class android.util.Log: 79 | * public static native int println_native(int buffer, int priority, String tag, String msg) 80 | */ 81 | static jint android_util_Log_println_native(JNIEnv* env, jobject clazz, 82 | jint bufID, jint priority, jstring tagObj, jstring msgObj) 83 | { 84 | const char* tag = NULL; 85 | const char* msg = NULL; 86 | 87 | if (msgObj == NULL) { 88 | jniThrowNullPointerException(env, "println needs a message"); 89 | return -1; 90 | } 91 | 92 | if (bufID < 0 || bufID >= LOG_ID_MAX) { 93 | jniThrowNullPointerException(env, "bad bufID"); 94 | return -1; 95 | } 96 | 97 | if (tagObj != NULL) 98 | tag = env->GetStringUTFChars(tagObj, NULL); 99 | msg = env->GetStringUTFChars(msgObj, NULL); 100 | 101 | int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg); 102 | 103 | if (tag != NULL) 104 | env->ReleaseStringUTFChars(tagObj, tag); 105 | env->ReleaseStringUTFChars(msgObj, msg); 106 | 107 | return res; 108 | } 109 | ``` 110 | 其中__android_log_buf_write() 又调用了write_to_log 函数指针。 111 | ``` c 112 | static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) 113 | { 114 | #ifdef HAVE_PTHREADS 115 | pthread_mutex_lock(&log_init_lock); 116 | #endif 117 | 118 | if (write_to_log == __write_to_log_init) { 119 | log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); 120 | log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); 121 | log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); 122 | log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); 123 | 124 | write_to_log = __write_to_log_kernel; 125 | 126 | if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || 127 | log_fds[LOG_ID_EVENTS] < 0) { 128 | log_close(log_fds[LOG_ID_MAIN]); 129 | log_close(log_fds[LOG_ID_RADIO]); 130 | log_close(log_fds[LOG_ID_EVENTS]); 131 | log_fds[LOG_ID_MAIN] = -1; 132 | log_fds[LOG_ID_RADIO] = -1; 133 | log_fds[LOG_ID_EVENTS] = -1; 134 | write_to_log = __write_to_log_null; 135 | } 136 | 137 | if (log_fds[LOG_ID_SYSTEM] < 0) { 138 | log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; 139 | } 140 | } 141 | 142 | #ifdef HAVE_PTHREADS 143 | pthread_mutex_unlock(&log_init_lock); 144 | #endif 145 | 146 | return write_to_log(log_id, vec, nr); 147 | } 148 | ``` 149 | 总的来说println_native() 的操作就是打开设备文件然后写入数据。 150 | 151 | ## 0x05 其他注意 152 | 1、使用Log.d()/v() 打印异常对象。(如SQLiteException 可能导致sql注入的问题) 153 | 2、使用android.util.Log 类的方法输出日志,不推荐使用 System.out/err 154 | 3、使用 BuildConfig.DEBUG ADT的版本不低于21 155 | public final static boolean DEBUG = true; 156 | 157 | 在release 版本中会被自动设置为false 158 | `if (BuildConfig.DEBUG) android.util.Log.d(TAG, "Log output information");` 159 | 160 | 4、启动Activity 的时候,ActivityManager会输出intent 的信息如下: 161 | 162 | * 目标包名 163 | * 目标类名 164 | * intent.setData(URL)的URL 165 | ![](../pictures/androidcat11.png) 166 | 167 | 5、即使不用System.out/err 程序也有可能输出相关信息,如使用 `Exception.printStackTrace() ` 168 | 6、ProGuard 不能移除如下log:("result:" + value). 169 | `Log.d(TAG, "result:" + value); ` 170 | 171 | 当遇到此类情况应该使用BulidConfig(注意ADT版本) 172 | `if (BuildConfig.DEBUG) Log.d(TAG, "result:" + value);` 173 | 174 | 7、不应将日志输出到sdscard 中,这样会让日志变得全局可读 175 | 176 | ## 0x06 日志工具类 177 | ``` java 178 | import android.util.Log; 179 | 180 | /** 181 | * Log统一管理类 182 | * 183 | * 184 | * 185 | */ 186 | public class L 187 | { 188 | 189 | private L() 190 | { 191 | /* cannot be instantiated */ 192 | throw new UnsupportedOperationException("cannot be instantiated"); 193 | } 194 | 195 | public static boolean isDebug = true;// 是否需要打印bug,可以在application的onCreate函数里面初始化 196 | private static final String TAG = "way"; 197 | // 下面四个是默认tag的函数 198 | public static void i(String msg) 199 | { 200 | if (isDebug) 201 | Log.i(TAG, msg); 202 | } 203 | 204 | public static void d(String msg) 205 | { 206 | if (isDebug) 207 | Log.d(TAG, msg); 208 | } 209 | 210 | public static void e(String msg) 211 | { 212 | if (isDebug) 213 | Log.e(TAG, msg); 214 | } 215 | 216 | public static void v(String msg) 217 | { 218 | if (isDebug) 219 | Log.v(TAG, msg); 220 | } 221 | // 下面是传入自定义tag的函数 222 | public static void i(String tag, String msg) 223 | { 224 | if (isDebug) 225 | Log.i(tag, msg); 226 | } 227 | 228 | public static void d(String tag, String msg) 229 | { 230 | if (isDebug) 231 | Log.i(tag, msg); 232 | } 233 | 234 | public static void e(String tag, String msg) 235 | { 236 | if (isDebug) 237 | Log.i(tag, msg); 238 | } 239 | 240 | public static void v(String tag, String msg) 241 | { 242 | if (isDebug) 243 | Log.i(tag, msg); 244 | } 245 | } 246 | ``` 247 | ## 0x07 参考 248 | http://www.jssec.org/dl/android_securecoding_en.pdf 249 | http://source.android.com/source/code-style.html#log-sparingly 250 | http://developer.android.com/intl/zh-cn/reference/android/util/Log.html 251 | http://developer.android.com/intl/zh-cn/tools/debugging/debugging-log.html 252 | http://developer.android.com/intl/zh-cn/tools/help/proguard.html 253 | https://www.securecoding.cert.org/confluence/display/java/DRD04-J.+Do+not+log+sensitive+information 254 | https://android.googlesource.com/platform/frameworks/base.git/+/android-4.2.2_r1/core/jni/android_util_Log.cpp 255 | 256 | -------------------------------------------------------------------------------- /Android应用安全/Android Service Security.md: -------------------------------------------------------------------------------- 1 | 原文 by 瘦蛟舞 2 | ## 0x00 科普 3 | 一个Service 是没有界面且能长时间运行于后台的应用组件.其它应用的组件可以启动一个服务运行于后台,即使用户切换到另一个应用也会继续运行.另外,一个组件可以绑定到一个service 来进行交互,即使这个交互是进程间通讯也没问题.例如,一个service可能处理网络通信,播放音乐,执行文件I/O,或与一个内容提供者交互,所有这些都在后台进行. 4 | 1. Service不是一个单独的进程,它和它的应用程序在同一个进程中 5 | 2. Service不是一个线程,这样就意味着我们应该避免在Service中进行耗时操作 6 | 3. Android给我们提供了解决上述问题的替代品IntentService; IntentService是继承于Service并处理异步请求的一个类, 7 | 在IntentService中有一个工作线程来处理耗时操作,请求的Intent记录会加入队列 8 | 4. 客户端通过startService(Intent) 来启动IntentService,我们并不需要手动地去控制IntentService,当任务执行完后,IntentService会自动停止; 可以启动IntentService多次,每个耗时操作会以工作队列的方式在IntentService 的onHandleIntent 回调方法中执行,并且每次只会执行一个工作线程,执行完一再到二 9 | 10 | ## 0x01 知识要点 11 | ### 生命周期 12 | ![](../pictures/androidser1.jpg) 13 | 14 | 左图是startService() 创建service,右图是bindService() 创建service。 startService 与bindService 都可以启动Service,那么它们之间有什么区别呢?它们两者的区别就是使Service 的周期改变。 15 | 16 | 由startService 启动的Service必须要有stopService 来结束 Service,不调用stopService 则会造成Activity 结束了而Service 还运行着。bindService 启动的Service 可以由unbindService 来结束,也可以在Activity 结束之后(onDestroy) 自动结束。 17 | 18 | ### 关键方法 19 | 20 | * onStartCommand() 系统在其它组件比如activity 通过调用startService() 请求service启动时调用这个方法.一旦这个方法执行,service就启动并且在后台长期运行.如果你实现了它,你需要负责 21 | 在service 完成任务时停止它,通过调用stopSelf() 或 stopService().(如果你只想提供绑定,你不需实现此方法). 22 | 23 | * OnBind() 当组件调用bindService()想要绑定到service 时(比如想要执行进程间通讯)系统调用此方法.在你的实现中,你必须提供一个返回一个IBinder 来以使客户端能够使用它与service 通讯,你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null. 24 | 25 | * OnCreate() 系统在service第一次创建时执行此方法,来执行只运行一次的初始化工作(在调用它方法如onStartCommand()或onBind()之前).如果service已经运行,这个方法不会被调用. 26 | 27 | * OnDestroy() 系统在service不再被使用并要销毁时调用此方法.你的service应在此方法中释放资源,比如线程,已注册的侦听器,接收器等等.这是service收到的最后一个调用. 28 | 29 | * public abstract boolean bindService (Intent service, ServiceConnection conn, int flags) 30 | BindService中使用bindService()方法来绑定服务,调用者和绑定者绑在一起,调用者一旦(all)退出服务也就终止了. 31 | 32 | * startService() 33 | startService()方法会立即返回然后Android 系统调用service的onStartCommand() 方法.但是如果service 尚没有运行,系统会先调用onCreate(),然后调用onStartCommand(). 34 | 35 | * protected abstract void onHandleIntent (Intent intent) 36 | 调用工作线程处理请求 37 | 38 | * public boolean onUnbind (Intent intent) 39 | 当所有client均从service发布的接口断开的时候被调用。默认实现不执行任何操作,并返回false。 40 | 41 | 42 | ### extends 43 | 44 | 1. Service 45 | 这是所有service的基类.当你派生这个类时,在service中创建一个新的线程来做所有的工作是十分重要的.因为这个service会默认使用你的应用的主线程(UI线程),这将拉低你的应用中所有运行的activity 的性能 46 | 47 | 2. IntentService 48 | 这是一个Service的子类,使用一个工作线程来处理所有的启动请求,一次处理一个.这是你不需你的service 同时处理多个请求时的最好选择.你所有要做的就是实现onHandleIntent(),这个方法接收每次启动请求发来的intent,于是你可以做后台的工作. 49 | 50 | 51 | ### 表现形式 52 | 53 | 1. Started 54 | 一个service 在某个应用组件(比如一个activity)调用startService() 时就处于"started"状态(注意,可能已经启动了).一旦运行后,service可以在后台无限期地运行,即使启动它的组件销毁了.通常地,一个startedservice执行一个单一的操作并且不会返回给调用者结果.例如,它可能通过网络下载或上传一个文件.当操作完成后,service自己就停止了 55 | 56 | 2. Bound 57 | 一个service在某个应用组件调用bindService()时就处于"bound"状态.一个boundservice提供一个client-server接口以使组件可以与service交互,发送请求,获取结果,甚至通过进程间通讯进行交叉进行这些交互.一个boundservice仅在有其它应用的组件绑定它时运行.多个应用组件可以同时绑定到一个service,但是当所有的自由竞争组件不再绑定时,service就销毁了. 58 | 59 | 60 | ### Bound Service 61 | 当创建一个提供绑定的service时,你必须提供一个客户端用来与service交互的IBinder.有三种方式你可以定义这个接口: 62 | 63 | 1. 从类Binder派生 64 | 如果你的service是你自己应用的私有物,并且与客户端运行于同一个进程中(一般都这样),你应该通过从类Binder派生来创建你的接口并且从onBind()返回一它的实例.客户端接收这个Binder然后使用它来直接操作所实现的Binder甚至Service的公共接口. 65 | 66 | 2. 当你的service仅仅是一个后台工作并且仅服务于自己的应用时,这是最好的选择.唯一使你不能以这种方式创建你的接口的理由就是你的service被其它应用使用或者是跨进程的. 67 | 68 | 69 | 3. 使用一个Messenger 70 | 如果你需要你的接口跨进程工作,你可以为service 创建一个带有Messager 的接口.在此方式下,service 定义一个Handler 来负责不同类型的Message对象.这个Handler是Messenger可以与客户端共享一个IBinder 的基础,它允许客户端使用Message 对象发送命令给service.客户端可以定义一个自己的Messenger 以使service 可以回发消息. 71 | 这是执行IPC的最简单的方法,因为Messenger 把所有的请求都放在队列中依次送入一个线程中,所以你不必把你的service 设计为线程安全的 72 | 73 | 4. 使用AIDL 74 | AIDL(Android 接口定义语言)执行把对象分解为操作系统能够理解并能跨进程封送的基本体以执行IPC的所有的工作.上面所讲的使用一个Messenger,实际上就是基于AIDL的.就像上面提到的,Messenger在一个线程中创建一个容纳所有客户端请求的队列,使用service一个时刻只接收一个请求.然而,如果你想要你的service同时处理多个请求,那么你可以直接使用AIDL.在此情况下,你的service必须是多线程安全的. 75 | 要直接使用AIDL,你必须创建一个.aidl文件,它定义了程序的接口.Android SDK 工具使用这个文件来生成一个实现接口和处理IPC的抽象类,之后你在你的service内派生它. 76 | 77 | 注:大多数应用不应使用AIDL来处理一个绑定的service,因为它可能要求有多线程能力并且导致实现变得更加复杂.同样的,AIDL也不适合于大多数应用,并且本文档不会讨论如何在你的service中使用它.如果你确定你需要直接使用AIDL,请看AIDL的文档. 78 | 79 | 80 | 81 | ### 注意 82 | 如果你打算只在本应用内使用自己的service,那么你不需指定任何intent 过滤器.不使用intent 过滤器,你必须使用一个明确指定service 的类名的intent 来启动你的service. 83 | 另外,你也可以通过包含android:exported属性,并指定其值为“false”来保证你的service是私有的.即使你的service使用了intent过滤器,也会起作用. 84 | 当一个service被启动后,它的生命期就不再依赖于启动它的组件并且可以独立运行于后台,即使启动它的组件死翘翘了.所以,service应该工作完成后调用stopSelf() 自己停止掉,或者其它组件也可以调用stopService() 停止service. 如果service没有提供绑定功能,传给startService() 的intent是应用组件与service之间唯一的通讯方式.然而,如果你希望service回发一个结果,那么启动这个service 的客户端可以创建一个用于广播(使用getBroadcast())的PendingIntent 然后放在intent 中传给service,service然后就可以使用广播来回送结果. 85 | 86 | ## 0x02 安全建议 87 | ### service分类 88 | ![](../pictures/androidser2.jpg) 89 | 90 | 私有service:不能被其他应用调用,相对安全 91 | 公开service:可以被任意应用调用 92 | 合作service:只能被信任合作公司的应用调用 93 | 内部service:只能被内部应用调用 94 | 95 | ### intent-filter与exported组合建议 96 | ![](../pictures/androidser3.jpg) 97 | 98 | 总结: 99 | exported属性明确定义 100 | 私有service不定义intent-filter并且设置exported为false 101 | 公开的service设置exported为true,intent-filter可以定义或者不定义 102 | 内部/合作service设置exported为true,intent-filter不定义 103 | 104 | 105 | ### rule book 106 | 107 | 1. 只被应用本身使用的service应设置为私有 108 | 109 | 2. service接收到的数据需要谨慎处理 110 | 111 | 3. 内部service 需使用签名级别的protectionLevel 来判断是否是内部应用调用 112 | 113 | 4. 不应在service创建(onCreate方法被调用)的时候决定是否提供服务,应在 onStartCommand/onBind/onHandleIntent 等方法被调用的时候做判断. 114 | 115 | 5. 当service 有返回数据的时候,应判断数据接收app是否有信息泄露的风险 116 | 117 | 6. 有明确的服务需调用时使用显式意图 118 | 119 | 7. 尽量不发送敏感信息 120 | 121 | 8. 合作service 需对合作公司的app 签名做效验 122 | 123 | 124 | ## 0x03 测试方法 125 | 1. service不像broadcast receicer,它只能静态注册,通过反编译查看配置文件Androidmanifest.xml即可确定service,若有导出的service则进行下一步 126 | 127 | 2. 方法查看service类,重点关注 onCreate/onStarCommand/onHandleIntent 方法 128 | 129 | 3. 检索所有类中 startService/bindService 方法及其传递的数据 130 | 131 | 4. 根据业务情况编写测试poc或者直接使用adb命令测试 132 | 133 | 134 | ## 0x04 案例 135 | ### 案例1:权限提升 136 | 乐phone手机出厂默认包含一个名为jp.aplix.midp.tools的应用包。本应用以system权限运行,并向其他应用提供ApkInstaller服务,用来进行对Apk文件的安装和删除。通过向ApkInstaller服务传递构造好的参数,没有声明任何权限的应用即可达到安装和删除任意Package的行为,对系统安全性产生极大影响。 137 | 138 | ``` java 139 | Intent in = new Intent(); 140 | 141 | in.setComponent(new ComponentName("jp.aplix.midp.tools","jp.aplix.midp.tools.ApkInstaller")); 142 | 143 | in.putExtra("action", "deleteApk"); 144 | 145 | in.putExtra("pkgName", "xxxxx"); 146 | 147 | startService(in); 148 | ``` 149 | 150 | ### 案例2:services 劫持 151 | 攻击原理:隐式启动services,当存在同名services,先安装应用的services优先级高 152 | 攻击模型 153 | ![](../pictures/androidser4.jpg) 154 | 155 | ### 案例3:拒绝服务 156 | (java.lang.NullPointerException 空指针异常) 157 | 现在除了空指针异常crash外还多出了一类crash:intent 传入对象的时候,转化出现异常. 158 | Serializable: 159 | ``` 160 | Intent i = getIntent(); 161 | if(i.getAction().equals("serializable_action")) 162 | { 163 | i.getSerializableExtra("serializable_key");//未做异常判断 164 | } 165 | Parcelable: 166 | this.b =(RouterConfig) this.getIntent().getParcelableExtra("filed_router_config");//引发转型异常崩溃 167 | ``` 168 | POC内传入畸形数据即可引发crash,修复很简单捕获异常即可. 169 | 170 | ## 0x05 参考 171 | 172 | http://developer.android.com/reference/android/app/Service.html 173 | http://developer.android.com/guide/components/services.html 174 | 175 | -------------------------------------------------------------------------------- /Android开发基础/Android Handler消息传递机制.md: -------------------------------------------------------------------------------- 1 | Android为了线程安全,并不允许我们在UI线程(主线程)外操作UI;很多时候我们做界面刷新都需要通过Handler来通知UI组件更新。除了用Handler完成界面更新外,还可以使用runOnUiThread()来更新,甚至使用AsyncTask、更高级的事务总线。当然,这里我们只讲解Handler,什么是Handler,执行流程,相关方法,子线程与主线程中使用Handler的区别等。 2 | 3 | ## 1.Handler类的引入: 4 | ![](../pictures/handle1.jpg) 5 | 6 | 7 | ## 2.Handler的执行流程图: 8 | ![](../pictures/handle2.jpg) 9 | 10 | 11 | 流程图解析: 相关名词 12 | > UI线程 :就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue; 13 | > Handler :作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象 14 | > Message :Handler接收与处理的消息对象 15 | > MessageQueue :消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue; 16 | > Looper :每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理。 17 | 18 | 简单点说: 19 | > 当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理。 20 | 21 | ## 3.Handler的相关方法: 22 | ``` 23 | void handleMessage (Message msg):处理消息的方法,通常是用于被重写 24 | sendEmptyMessage (int what):发送空消息 25 | sendEmptyMessageDelayed (int what,long delayMillis):指定延时多少毫秒后发送空信息 26 | sendMessage (Message msg):立即发送信息 27 | sendMessageDelayed (Message msg):指定延时多少毫秒后发送信息 28 | final boolean hasMessage (int what):检查消息队列中是否包含what属性为指定值的消息 29 | 如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息 30 | ``` 31 | 32 | ## 4.Handler的使用示例: 33 | 34 | ### 1)Handler写在主线程中 35 | 36 | 在主线程中,因为系统已经初始化了一个Looper对象,所以我们直接创建Handler对象,就可以进行信息的发送与处理了。 37 | 代码示例:简单的一个定时切换图片的程序,通过Timer定时器,定时修改ImageView显示的内容,从而形成帧动画 38 | 运行效果图: 39 | ![](../pictures/handle3.jpg) 40 | 41 | 实现代码: 42 | ``` xml 43 | 50 | 51 | 57 | 58 | 59 | ``` 60 | MainActivity.java: 61 | ``` java 62 | public class MainActivity extends Activity { 63 | 64 | //定义切换的图片的数组id 65 | int imgids[] = new int[]{ 66 | R.drawable.s_1, R.drawable.s_2,R.drawable.s_3, 67 | R.drawable.s_4,R.drawable.s_5,R.drawable.s_6, 68 | R.drawable.s_7,R.drawable.s_8 69 | }; 70 | int imgstart = 0; 71 | 72 | final Handler myHandler = new Handler() 73 | { 74 | @Override 75 | //重写handleMessage方法,根据msg中what的值判断是否执行后续操作 76 | public void handleMessage(Message msg) { 77 | if(msg.what == 0x123) 78 | { 79 | imgchange.setImageResource(imgids[imgstart++ % 8]); 80 | } 81 | } 82 | }; 83 | 84 | @Override 85 | protected void onCreate(Bundle savedInstanceState) { 86 | super.onCreate(savedInstanceState); 87 | setContentView(R.layout.activity_main); 88 | final ImageView imgchange = (ImageView) findViewById(R.id.imgchange); 89 | 90 | //使用定时器,每隔200毫秒让handler发送一个空信息 91 | new Timer().schedule(new TimerTask() { 92 | @Override 93 | public void run() { 94 | myHandler.sendEmptyMessage(0x123); 95 | 96 | } 97 | }, 0,200); 98 | } 99 | 100 | } 101 | ``` 102 | ### 2)Handler写在子线程中 103 | 104 | 如果是Handler写在子线程中的话,我们就需要自己创建一个Looper对象了。 105 | 创建的流程如下: 106 | 1] 直接调用Looper.prepare()方法即可为当前线程创建Looper对象,而它的构造器会创建配套的MessageQueue; 107 | 2] 创建Handler对象,重写handleMessage( )方法就可以处理来自于其他线程的信息了; 108 | 3] 调用Looper.loop()方法启动Looper 109 | 使用示例: 输入一个数,计算后通过Toast输出在这个范围内的所有质数 110 | 实现代码:main.xml: 111 | ``` xml 112 | 117 | 123 |