写在前面

这个系列本来是在公司的一个分享,内容比较多,所以就把这个PPT重新组织整理成博客,希望对大家学习有所帮助。我会先以一个”短信拦截“作为例子,抛出问题,并提出了一种基于”注入“的技术方案达到提高拦截优先级,接着再重点讲解注入的技术细节。最后,我会跟大家分享一个我业余时间开发注入框架——AIM(Android IPC Manager)。当然了,这个框架跟目前的XPosed、CydiaStructe的侧重点不太一样。

短信拦截

场景
如果某款安全支付类应用和一个未知木马安装在同一台手机,木马会截获所有的验证码短信。

问题
有什么办法可以保证安全支付类的应用可以比木马更优先截获到验证码短信呢?

解决方案
其实这类优先级抢占问题,做短信功能开发的朋友,都应该比较清楚,因为老板都关注的就是这个了,呵呵。一般的可行方案如下:
提高BroadcastReceiver的优先级;
采用动态的方式注册BroadcastReceiver;
注册时间要越早越好;
基于上述三点,我们的可以做如下优化:
监听Android的开机广播事件BOOT_COMPLETED,提早注册时间;
启动后,开启一个Service,并在里面以动态的注册BroadcastReceiver;
注册的优先级调整至最高——Integer.MAX_VALUE;
关键代码如下所示:
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
filter.setPriority(Integer.MAX_VALUE);
registerReceiver(new SmsReceiver(), filter);

问题来了
通过上面这个方案,是可以解决部分问题了,但这种方式完全没有技术含量,对处于技术第一线的黑客们根本不是问题,他们不但也采用了相同的技术,而且做得更好,在”注册时间越早越好“这一点上,他们添加几个额外的触发点,分别如下:
WIFI_STATE_CHANED
CONNECTIVITY_CHANGE
ACCESS_NETWORK_STATE
ACCESS_WIFI_STATE
这几个系统事件,都比BOOT_COMPLETED触发得要早,所以木马当然能优先截获到短信了。
基于这种对抗下,我们是否跟木马就只有打成平手呢?是否有更优的解决方案?答复是肯定的。
促进科技发展最有效的方法就是战争,在这种技术对抗中,同样也是最容易产生新的思想方案的。

基本思路
既然广播发送之后,我们有可能处于后次序。那是否有办法,可以在广播发送之前,做一下顺序调整呢?我们知道,Android的广播发送,都是由ActivityManagerService(AMS)做路由转发的,所以AMS里肯定保存了各个BroadcastReceiver的信息以及次序,我们如果有办法可以进入到AMS里头,再通过一些反射的技巧,是否就可以在每次发送广播之前,调整广播的发送顺序呢?

这个就是我要跟大家分享的技术方案——通过注入实现短信的绝对优先拦截。技术的基本思路是这样的,我们先注入到系统进程——system_process,然后截获AMS的broadcastIntent方法,加插我们的调整逻辑。

《二》里我会着重介绍注入的技术原理,然后通过一系列的DEMO讲解这个方案所涉及到的技术点。

继续
在《一》里,我把基本思路描述了一遍,接下为我们先从注入开始入手。

注入

分类
我们平时所说的代码注入,主要静态和动态两种方式
静态注入,针对是可执行文件,比如平时我们修改ELF,DEX文件等等,相关的辅助工具也很多,比如IDA、JEB、ApkTool等等;
动态注入,针对是进程,比如修改进程的寄存器、内存值等等;
动态跟静态最大的区别是,动态不需要改动源文件,但需要高权限(通常是root权限),而且所需的技术含量更高。

本质
动态注入技术,本质上就是一种调度技术。想想平时我们调试一个进程时,可以做哪些功能? 一般有下列几项:
查看变量值
修改变量值
跟踪进程跳转
查看进程调用堆栈
等等
动态注入相比于普通的调试,最大的区别就是动态注入是一个 ”自动化调试并达到加载自定义动态链接库“的过程。所谓自动化,其实就是通过代码实现,在Linux上通过Ptrace就可以完成上面所有功能,当然Ptrace功能是比较原始的,平时调试中的功能还需要很多高层逻辑封装才可以实现。
在阅读下面章节之前,强烈建议阅读一下man文档,见 这里。

目的
一般而言,我们要对一个进程进行注入,主要有以下几方面目的:
增强目标进程的功能;
修复目标进程缺陷;
劫持目标进程函数;
窃取目标进程数据;
篡改目标进程数据;


过程
如上图所示,进程A注入到进程B后,通过修改寄存器和内存,让进程B加载自定义的动态库a,当a被加载后,a会尝试加载其他模块,比如加载dex文件等等,具体的注入过程如下:
ATTATCH,指定目标进程,开始调试;
GETREGS,获取目标进程的寄存器,保存现场;
SETREGS,修改PC等相关寄存器,使其指向mmap;
POPETEXT,把so path写入mmap申请的地址空间;
SETRESG,修改PC等相关寄存器,使其指向dlopen;
SETREGS,恢复现场;
DETACH,解除调试,使其恢复;
上述是一个简化的过程,整个注入的代码,我已经上传到github,地址https://github.com/boyliang/Poison
当so被dlopen加载到目标进程后,我们需要让so中的逻辑被执行,比较复杂的做法是同样使用ptrace修改寄存器的办法,让目标进程调用dlsym找到我们函数的地址。而比较简单的做法有两种,如下
使用gcc的预编译指令__attribute__ ((__constructor__)),作用是让so被加载后,函数被自动执行;
__attribute__ ((__constructor__))
void Main() {
 LOGI(">>>>>>>>>>>>>I am in, I am a bad boy 1!!!!<<<<<<<<<<<<<<");
 
 void* handle = dlopen("libinso.so", RTLD_NOW);
 void (*setA_func)(int) = (void (*)(int))dlsym(handle, "setA");
 
 if (setA_func) {
   setA_func(999);
 }
}
使用c++全局对象初始化,其构造函数会被自动执行;
void Main();
 
static void* _main(void*){
    Main();
    return NULL;
}
 
class EntryClass {
public:
 
    EntryClass() {
        pthread_t tid;
        pthread_create(&tid, NULL, _main, NULL);
        pthread_detach(tid);
    }
 
} boy;

示例一
下面示例一个通过ptrace注入的示例,涉及到两部分代码,一部分是目标进程代码记作host,另一部分是被我们注入的so代码记作libmyso.so

Host代码
包含三个源文件,分别是demo1.c,inso.h, inso.c
/*
 * inso.h
 *
 *  Created on: 2014年6月24日
 *      Author: boyliang
 */
 
 
__attribute__ ((visibility ("default"))) void setA(int i);
 
__attribute__ ((visibility ("default"))) int getA();
/*
 * inso.c
 *
 *  Created on: 2014年6月24日
 *      Author: boyliang
 */
 
#include <stdio.h>
#include "inso.h"
 
static int gA = 1;
 
void setA(int i){
    gA = i;
}
 
int getA(){
    return gA;
}
/*
 * demo1.c
 *
 *  Created on: 2014年6月24日
 *      Author: boyliang
 */
 
#include <stdio.h>
#include <unistd.h>
 
#include "inso.h"
#include "log.h"
 
int main(){
 
    LOGI("DEMO1 start.");
 
    while(1){
        LOGI("%d", getA());
        setA(getA() + 1);
        sleep(2);
    }
 
    return 0;
}
libmyso.so代码
/*
 * myso.c
 *
 *  Created on: 2014年6月24日
 *      Author: boyliang
 */
 
#include <stdio.h>
#include <stddef.h>
#include <dlfcn.h>
#include <pthread.h>
#include <stddef.h>
 
#include  "log.h"
 
__attribute__ ((__constructor__))
void Main() {
    LOGI(">>>>>>>>>>>>>I am in, I am a bad boy 1!!!!<<<<<<<<<<<<<<");
 
    void* handle = dlopen("libinso.so", RTLD_NOW);
    void (*setA_func)(int) = (void (*)(int))dlsym(handle, "setA");
 
    if (setA_func) {
        setA_func(999);
    }
}
调用
注入程序,我将其命名为poison,使用方法是poison <so_path> <target_pit>。下面是示例的输出显示:
I/TTT     (  594): DEMO1 start.
I/TTT     (  594): 1
I/TTT     (  594): 2
I/TTT     (  594): 3
I/TTT     (  594): 4
I/TTT     (  594): 5
I/TTT     (  594): 6
I/TTT     (  594): 7
I/TTT     (  594): >>>>>>>>>>>>>I am in, I am a bad boy 1!!!!<<<<<<<<<<<<<<
I/TTT     (  594): 999
I/TTT     (  594): 1000
I/TTT     (  594): 1001
当执行./poison /data/local/tmp/libmyso.so 594后,输出中马上出现了特定字符串,并且打印的数据一下子变成了999,证明我们注入成功了。
示例代码
上述示例所涉及到代码,我都放发布到github上了,大家如果想研究代码,可以到https://github.com/boyliang/injection_by_ptrace
在 《三》,我会再介绍一种Android上特有的注入技术,敬请期待。

进击的Android注入术相关推荐

  1. Android注入要点记录

    虽然之前注入过android很多次,但所做的事情不过是在别人的框架下做些修改,调调bug,完全没有彻底消化和掌握注入的知识和技术.所以我决定写这一篇博文,总结android注入的实现要点. andro ...

  2. 进击的Android Hook 注入术《五》

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 继续 BinderProxy 原理 获取AMS引用 获取JavaBBinder 替换mObject对象 示例四 最后 继续 在A ...

  3. 进击的Android Hook 注入术《四》

    目录(?)[-] 继续 注入之后 示例三 输出 最后 继续 在前<一>.<二>.<三>里已经把注入的技术介绍完了,这章开始说注入之后需要做的事情.如果对注入技术已经 ...

  4. 进击的Android Hook 注入术《三》

    目录(?)[-] 继续 Component Injection 原理 示例二 comdemohost comdemoinject 绕过ClassLoader双亲委托 输出 最后 继续 在<二&g ...

  5. 进击的Android Hook 注入术《二》

    目录(?)[-] 继续 注入 分类 本质 目的 过程 示例一 Host代码 libmysoso代码 调用 示例代码 继续 在<一>里,我把基本思路描述了一遍,接下为我们先从注入开始入手. ...

  6. 进击的Android Hook 注入术《一》

    目录(?)[-] 写在前面 短信拦截 场景 问题 解决方案 问题来了 基本思路 写在前面 这个系列本来是在公司的一个分享,内容比较多,所以就把这个PPT重新组织整理成博客,希望对大家学习有所帮助.我会 ...

  7. android注入 定位,[原创]修改源码实现全局(无需root)注入躲开注入检测

    看这篇文章需要的技能 1.会编译android源码(如果你不愿意编译源码,还有另外一种办法,下面我会提供) 2.会使用substrate或者xposed 以上2个网上资料很多我就不啰嗦了 一.市面上目 ...

  8. Android 注入 看雪

    2019独角兽企业重金招聘Python工程师标准>>> https://bbs.pediy.com/thread-141355.htm [原创]发个Android平台上的注入代码 2 ...

  9. 进击的Android之manifests

    (一)AndroidManifests.xml配置文件介绍 (1)AndroidManifests.xml是什么? 1)AndroidManifests.xml是干什么的?AndroidManifes ...

最新文章

  1. Mysql中的递归层次查询(父子查询,无限极查询)
  2. 浏览器tab关闭事件_Python--使用Pyqt5实现简易浏览器(最新版本测试过)
  3. 【学习笔记】SAP 成本对象控制
  4. c语言socket调用图灵API,socket通信(四):socket实现机器人在线聊天
  5. Mysql @RN :方式ROW_NUMBER的实现方法
  6. linux网络编程——webserver服务器编写
  7. .htaccess更改目录下的默认主页
  8. 你们一直吐槽的12306,你知道他们的的架构有多牛X吗
  9. Android NDK: 如何编译JNI为指定的指令集(armeabi/arm64)
  10. python音乐播放器图片_Python音乐播放器
  11. mysql 判断语句_mysql条件判断语句讲解
  12. 物联网产业链全景图(附另13大电子行业全景图,必收藏)
  13. GLES3.0中文API-glMemoryBarrier
  14. amend用法 git 信息_详解git commit --amend 用法
  15. Can't open /dev/sdb1 exclusively. Mounted filesystem?
  16. TSP_旅行商问题 - 遗传算法(四)
  17. 把阿拉伯数字全部转换为大写(1,2,3.... = 一、二、三、)
  18. 搭建exchange邮件服务器一定要ad域么?_域渗透神器-AD Explorer使用指南
  19. 全国计算机等级考试shi,全国计算机等级考试等级设置
  20. 包(package)与模块的导入--from与import

热门文章

  1. IDEA 2020报“java:程序包XXXX不存在”或“java:找不到符号”,在idea中使用Tomcat部署项目报找不到包,Maven compile能过,idea build失败问题
  2. java语言的编译器_JAVA语言编译器是一个CASE工具。()。
  3. python:脑残服务员和脑残和顾客上线了
  4. 2018互联网大会,科幻大片照进现实!
  5. 2.4G无线小模块CI24R1超低成本
  6. tablepc是什么平板电脑_平板电脑是什么
  7. Android 计算控件尺寸(转)
  8. Video Input with OpenCV and similarity measurement(使用opencv测量两个视频的相似度)
  9. IE浏览器提示网站还原错误
  10. ubuntu16.04 rtl8821ce无线网卡wifi频繁掉线问题解决