Android trap攻防

                                                                     图/文 h_one

0x01 反编译出错

1.插入无效指令是部分逆向工具崩溃

原理:大部分逆向工具都是线性读取字节码并解析, 如dex2jar,baksmali,apktool等,当遇到无效字节码时,就会引起反编译工具解析失败。

例如:新版的dex2jar 遇到这种情况任然没法转化成jar,在新版本的baksmail和apktool已修复此问题。





010editor查看,红色框中就是加入的陷阱类,绕过方法很简单,只要将这个三个类删除,重编译即可

2.利用apk包本质上是zip/jar包进行保护

a.伪加密

在Android4.2.x前的一种保护方 式,通过APK(压缩文件)进行伪加密,其修改原理是修改连续4位字节标记为“P K 01 02”后的第5字节(ps:一般在文件末尾有多处),奇数表示加密偶数不加密,这种保护方式一般只会出现在一些cm里,因为:

1.对系统不兼容;

2.伪加密 处理后的apk市场也无法对其进行安全检测,部分市场会拒绝这类APK上传市场

b. 文件名长度操作

Android平台对文件名的长度是没有限制的,但是操作系统要求不能大于255;于是可以构建超长字符的类名达到反编译错误目的。

3.axml文件保护

目的:防止/检测二次打包

android在解析AXML文件时,是用属性的资源id,而不是资源名,当android系统遇到非法资源id时,并不会做解析,所以可以对axml文件解析,添加无用的属性。但是对于破解者来说,一般会在Java层增加log信息,然后打包apk,
此时如apktool之类的逆向工具,就会无法解析无效属性,或进入trap类(检测二次打包)

0x02 运行环境检测

环境检测主要包括

  1. ▏运行在调试状态下
  2. ▏系统代码被hook
  3. ▏以及运行环境在模拟器中

目的:为了保护关键代码被逆向分析,一般放在应用程序初始化过程中,如init_array,或jni_onload函数里进行检查代码执行。、

1.调试检测

对调试器的检测(ida,gdb,strace,
ltrace,android_server等调试工具)

a.父进程检测

b.当前运行进程检测

例如,对android_server进程检测。针对这种检测只需将android_server改名就可绕过

[objc] view
plain
 copy

  1. pid_t GetPidByName(const charchar *as_name) {
  2. DIR *pdir = NULL;
  3. struct dirent *pde = NULL;
  4. FILEFILE *pf = NULL;
  5. char buff[128];
  6. pid_t pid;
  7. char szName[128];
  8. // 遍历/proc目录下所有pid目录
  9. pdir = opendir("/proc");
  10. if (!pdir) {
  11. perror("open /proc fail.\n");
  12. return -1;
  13. }
  14. while ((pde = readdir(pdir))) {
  15. if ((pde->d_name[0] < '0') || (pde->d_name[0] > '9')) {
  16. continue;
  17. }
  18. sprintf(buff, "/proc/%s/status", pde->d_name);
  19. pf = fopen(buff, "r");
  20. if (pf) {
  21. fgets(buff, sizeof(buff), pf);
  22. fclose(pf);
  23. sscanf(buff, "%*s %s", szName);
  24. pid = atoi(pde->d_name);
  25. if (strcmp(szName, as_name) == 0) {
  26. closedir(pdir);
  27. return pid;
  28. }
  29. }
  30. }
  31. closedir(pdir);
  32. return 0;
  33. }

c.读取进程状态(/proc/pid/status)

State属性值T 表示调试状态,TracerPid 属性值正在调试此进程的pid,在非调试情况下State为S或R, TracerPid等于0

d.读取 /proc/%d/wchan

下图中第一个红色框值为非调试状态值,第二个红色框值为调试状态值:

[objc] view
plain
 copy

  1. static void get_process_status(pid_t pid,const char* info,charchar *outline)
  2. {
  3. FILEFILE *fp;
  4. char filename;
  5. char line = {0};
  6. snprintf( filename, sizeof(filename), "/proc/%d/status", pid );
  7. fp = fopen( filename, "r" );
  8. if ( fp != NULL )
  9. {
  10. while ( fgets( line, sizeof(line), fp ) )
  11. {
  12. if ( strstr( line, info ) )
  13. strcpy(outline,line);
  14. }
  15. fclose( fp ) ;
  16. }
  17. return ;
  18. }
  19. static int getProcessStatus(int pid)
  20. {
  21. char readline = {0};
  22. int result = STATUS_ELSE;
  23. get_process_status(pid,"State",readline);
  24. if(strstr(readline,"R"))
  25. result = STATUS_RUNNING;
  26. else if(strstr(readline,"S"))
  27. result = STATUS_SLEEPING;
  28. else if(strstr(readline,"T"))
  29. result = STATUS_TRACING;
  30. return result;
  31. }
  32. static int getTracerPid(int pid)
  33. {
  34. char readline = {0};
  35. int result = INVALID_PID;
  36. get_process_status(pid,"TracerPid",readline);
  37. charchar *pidnum = strstr(readline,":");
  38. result = atoi(pidnum + 1);
  39. return result;
  40. }
  41. static int getWchanStatus(int pid)
  42. {
  43. FILEFILE *fp= NULL;
  44. char filename;
  45. char wchaninfo = {0};
  46. int result = WCHAN_ELSE;
  47. char cmd = {0};
  48. sprintf(cmd,"cat /proc/%d/wchan",pid);
  49. LOGANTI("cmd= %s",cmd);
  50. FILEFILE *ptr;         if((ptr=popen(cmd, "r")) != NULL)
  51. {
  52. if(fgets(wchaninfo, 128, ptr) != NULL)
  53. {
  54. LOGANTI("wchaninfo= %s",wchaninfo);
  55. }
  56. }
  57. if(strncasecmp(wchaninfo,"sys_epoll\0",strlen("sys_epoll\0")) == 0)
  58. result = WCHAN_RUNNING;
  59. else if(strncasecmp(wchaninfo,"ptrace_stop\0",strlen("ptrace_stop\0")) == 0)
  60. result = WCHAN_TRACING;
  61. return result;
  62. }

e.
ptrace 自身或者fork子进程相互ptrace(梆梆加固之类的反调试)

[objc] view
plain
 copy

  1. ptrace me
  2. if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
  3. printf("DEBUGGING... Bye\n");
  4. return 1;
  5. }
  6. void anti_ptrace(void)
  7. {
  8. pid_t child;
  9. child = fork();
  10. if (child)
  11. wait(NULL);
  12. else {
  13. pid_t parent = getppid();
  14. if (ptrace(PTRACE_ATTACH, parent, 0, 0) < 0)
  15. while(1);
  16. sleep(1);
  17. ptrace(PTRACE_DETACH, parent, 0, 0);
  18. exit(0);
  19. }
  20. }

f. 防止内存dump

利用Inotify机制,对/proc/pid/mem和/proc/pid/pagemap文件进行监视。inotify
API提供了监视文件系统的事件机制,可用于监视个体文件,或者监控目录。具体原理可参考:http://man7.org/linux/man- pages/man7/inotify.7.html 

伪代码:

[objc] view
plain
 copy

  1. void __fastcall anitInotify(int flag)
  2. {
  3. MemorPagemap = flag;
  4. char *pagemap = "/proc/%d/pagemap";
  5. char *mem = "/proc/%d/mem";
  6. pagemap_addr = (charchar *)malloc(0x100u);
  7. mem_addr = (charchar *)malloc(0x100u);
  8. ret = sprintf(pagemap_addr, &pagemap, pid_);
  9. ret = sprintf(mem_addr, &mem, pid_);
  10. if ( !MemorPagemap )
  11. {
  12. ret = pthread_create(&th, 0, (void *(*)(void *)) inotity_func, mem_addr);
  13. if ( ret >= 0 )
  14. ret = pthread_detach(th);
  15. }
  16. if ( MemorPagemap == 1 )
  17. {
  18. ret = pthread_create(&newthread, 0, (void *(*)(void *)) inotity_func, pagemap_addr);
  19. if(ret > 0)
  20. ret = pthread_detach(th);
  21. }
  22. }
  23. void __fastcall __noreturn inotity_func(const char *inotity_file)
  24. {
  25. const charchar *name; // r4@1
  26. signed int fd; // r8@1
  27. bool flag; // zf@3
  28. bool ret; // nf@3
  29. ssize_t length; // r10@3
  30. ssize_t i; // r9@7
  31. fd_set readfds; // @2
  32. char event; // @1
  33. name = inotity_file;
  34. memset(buffer, 0, 0x400u);
  35. fd = inotify_init();
  36. inotify_add_watch(fd, name, 0xFFFu);
  37. while ( 1 )
  38. {
  39. do
  40. {
  41. memset(&readfds, 0, 0x80u);
  42. }
  43. while ( select(fd + 1, &readfds, 0, 0, 0) <= 0 );
  44. length = read(fd, event, 0x400u);
  45. flag = length == 0;
  46. ret = length < 0;
  47. if ( length >= 0 )
  48. {
  49. if ( !ret && !flag )
  50. {
  51. i = 0;
  52. do
  53. {
  54. inotity_kill((int)&event);
  55. i += *(_DWORD *)&event + 16;
  56. }
  57. while ( length > i );
  58. }
  59. }
  60. else
  61. {
  62. while ( *(_DWORD *)_errno() == 4 )
  63. {
  64. length = read(fd, buffer, 0x400u);
  65. flag = length == 0;
  66. ret = length < 0;
  67. if ( length >= 0 )
  68. }
  69. }
  70. }
  71. }

g.
对read做hook

因为一般的内存dump都会调用到read函数,所以对read做内存hook,检测read数据是否在自己需要保护的空间来阻止dump

h. 设置单步调试陷阱

[objc] view
plain
 copy

  1. int handler()
  2. {
  3. return bsd_signal(5, 0);
  4. }
  5. int set_SIGTRAP()
  6. {
  7. int result;
  8. bsd_signal(5, (int)handler);
  9. result = raise(5);
  10. return result;
  11. }

2. 模拟器检测

用户层行为和数据检测,模拟器特有属性值,以及模拟器体系结构特征

  1. ▏电池状态和电流,模拟器默认电话号码检测,检测设备IDS 是不是“000000000000000”, 检测imsid是不是“310260000000000“,手机运营商等
  2. ▏API Demo,Dev tool一般模拟器上才有的应用,检测安装应用,短信箱,通信录,相册等
  3. ▏读取/system/build.prop文件
  4. ▏调用__system_property_get或反射调用Systemproperty.get获取系统属性值
  5. ▏通过执行shell命令检测模拟器,如getprop
  6. ▏检查模拟器特有文件如 /dev/socket/qemud","/dev/qemu_pipe","/sysrtem/bin/qemud",/dev/qemu_pipe,/dev/qemu_trace等
  7. ▏模拟器cpu信息值差异,如hardware,Revision等
  8. ▏系统属性值等(android.os.build)
  9. ▏基于Qemu二进制翻译技术(ps:真机具有真正的物理CPU,在执行一段指令的时候只能一条一条的去执行指令(编译器没有对指令进行优化的前提下)。模拟器没有真正的物理CPU,所以,他在执行一段指令的时候,这段指令已经被人为的优化掉)http://www.dexlabs.org/blog/btdetect
  10. ▏通过观察低级别的缓存行为。检测方法: 默认情况下,Android模拟器提供了Android SDK是基于QEMU,仿真器不具备分裂缓存。而在真实设备上存在两个不同的缓存,一个用于数据访问,一个用于指令。https://bluebox.com/technical/android-emulator-detection-by-observing-low-level-caching-behavior/

0x03防app运行环境被hook(*)

hook代码肯定是在app自身模块加载之前运行的,那么在app的maps表里会首先加载hook框架的dex,我们只要对此dex做简单的校验,就会检测到app被注入了。

编码思路

遍历maps表,查看子串是否存在“apk@classes.dex”的字符串,若存在--获取该模块的startAddr和endAddr, 然后检验此odex的头部是否为真正的dex文件。

0x4抗静态分析(ida F5 以及执行流程图)

1.arm 指令插花

2.通过栈修改程序调用过程

STDFD保存寄存器值到栈上,LDMFD将栈上数据赋值到寄存器中,这个过程修改了函数返回的地址

0x05如何绕过app环境检测

1.调试过程中修改代码

hook检测点(path检测点的函数代码,函数返回值修改等),修改源码(改变字段属性主要针对模拟器仿真,修改函数返回值,例如绕过签名校验等)

eg:

ida patch 线程退出函数: patch 地址48D9668A处的函数调用

a.在数据窗口定位到机器码位置

b.F2 编辑机器码
00 00 0A EF (movs R0, R0)

c.F2保存修改

2.fopen函数相关的检测

由于/proc/pid/status,/proc/pid/wchan,/proc/pid/mem等都是针对文件状态的检测,入口点函数一般都为fopen,
我们可以事先拦截fopen,查看app是否有这方面的防护。

以某某app为例:如下图此app fopen了这些文件,我们就能猜测这是对调试检测。

绕过方法:在指定目录下(/data/local/tmp)新建一文件alimolisec,
hook fopen函数检测到文件名子串有/proc/self时,就重定位打开alimolisec文件

[objc] view
plain
 copy

  1. FILEFILE * MyOpen( const charchar * filename, const charchar * mode ) {
  2. FILEFILE *file = NULL;
  3. if (strstr(filename, "/proc/self")){
  4. LOGI("fileName:%s", filename);
  5. file = oldFopen("/data/local/tmp/hone", mode );
  6. }else
  7. file = oldFopen( filename, mode );
  8. return file;
  9. }

0x06 CM中的那些壳子

加固手法:


1.不替换源classes.dex,也没有做任何加密的处理,对classes.dex中的Activity,service, receiver等的oncreate,onReceive加密替换,壳首先拿到执行权,在自身so里完成对源dex还原。

2.不替换源classes.dex,也没有做任何加密的处理,
修改原Dex的Class_Data,将MyContentProvider,Application,Activity,service类的入口函数 onCreate方法标记为native方法,但是原始字节码仍然未加密保存在dex文件中。

3.对classes.dex整包加密,使用壳加载器内存解密classes.dex并替换原始成源classes.dex

4.对classes.dex整包加密,并将原dex拆分成两部分,在内存中分两块区域存储(阿里移动安全比赛加固)。

脱壳手法:

1.对部分整包加密的可以通过运行时内存dump,部分通过拦截dvmDexFileOpenPartial函数即可获取完整dex。(dex连续)

2.对于修改了dex的Class_data和classes.dex做了拆分的,可以通过找到dex对应的pDvmDex结构重建dex(dex不连续或不完整的)

0x07 android平台常用的hook框架

▏cydia substrate

原理:框架注入zygote进程,采用inline hook(
修改目标函数前N字节,跳转到自定义函数入口,备份目标函数前N个字节,跳转回目标函数)

▏Xposed

原理:替换app_process,将需要hook的java函数替换成JNI函数,所有需要HOOK的函数首先由xposedCallHandler处理,xposedCallHandler负责调用注册的beforeHookMethod和afterHookedMethod函数

▏adbi

原理:利用ptrace()函数attach到一个进程上,然后在其调用序列中插入一个调用dlopen()函数的步骤,将一个事先预先备好的.so文件加载到要hook的进程内存中,最终由这个加载的.so文件在初始化函数中hook指定的函数。

参考文档

http://man7.org/linux/man-pages/man7/inotify.7.html

http://www.dexlabs.org/blog/btdetect

https://bluebox.com/technical/android-emulator-detection-by-observing-low-level-caching-behavior/

https://github.com/crmulliner/adbi

http://www.cydiasubstrate.com/

转载地址:http://blog.csdn.net/zihao2012/article/details/44238273#0-tsina-1-93113-397232819ff9a47a7b7e80a40613cfe1

Android trap攻防思路整理的更多相关文章

  1. 阿里聚安全攻防挑战赛第三题Android PwnMe解题思路

    阿里聚安全攻防挑战赛第三题Android PwnMe解题思路 大家在聚安全挑战赛正式赛第三题中,遇到android app 远程控制的题目.我们今天带你一探究竟,如何攻破这道题目. 一.题目 购物应用 ...

  2. Angular2发布思路(整理官网Deployment页面)

    本文是按着ng2官网的高级内容“Deployment”的思路整理得出的,原文虽然在angular2的中文站下挂着,截止目前却还是英文版未翻译,笔者就在这里结合自己的理解给出原文的一点点整理.这是原文地 ...

  3. android精品开源项目整理

    转载地址:http://www.eoeandroid.com/thread-311366-1-1.html 前言:无论你是android的初学者,还有是Android开发了好多年的高手,可能都会有很多 ...

  4. GTP+SDI工程播出部分思路整理(3)

    GTP+SDI工程播出部分思路整理(3) 1.本文的目的主要分析video_out_to_sdi模块中输入信号 tx_usrclk, rst, tx_mode, tx_level_b的使用 Tx_us ...

  5. GTP+SDI工程播出部分思路整理(2)

    GTP+SDI工程播出部分思路整理(2) 以同样的方法来分析tx_video_a_c_in信号: SDI核中tx_video_a_c_in信号连接情况如下所示 .tx_video_a_c_in     ...

  6. GTP+SDI工程播出部分思路整理

    GTP+SDI工程播出部分思路整理 1.video_out_to_sdi模块 关于video_out_to_sdi模块的输出信号: tx_video_a_y[9:0] 这是要输入SDI IP核内的 t ...

  7. Android开发——Fragment知识整理(二)

    0.  前言 Android开发中的Fragment的应用非常广泛,在Android开发--Fragment知识整理(一)中简单介绍了关于Fragment的生命周期,常用API,回退栈的应用等知识.这 ...

  8. Android开发——Fragment知识整理(一)

    0.  前言 Fragment,顾名思义是片段的意思,可以把Fragment当成Activity的一个组成部分,甚至Activity的界面可以完全有不同的Fragment组成.Fragment需要被嵌 ...

  9. 一些JavaSE学习过程中的思路整理(主观性强,持续更新中...)

    目录 一些JavaSE学习过程中的思路整理(主观性强,持续更新中...) Java书写规范 IDEA的一些常用快捷键 Java类中作为成员变量的类 Java源文件中只能有一个public类 Java中 ...

随机推荐

  1. 全局解决Vue跳转相同路由导致报错的问题

    大家使用Vue做开发的时候应该都遇到过这个问题,就是某个页面下调用this.$router.push(path),而path指向的页面和当前页面是同一页面时,就会发生报错,vue-router会提示你 ...

  2. 操作系统---IO权限管理和敏感指令

    简化版 使用IOPL设置一个特权级的用户程序对所有端口的访问权限,使用I/O位图对一个特权级的用户程序设置个性化的端口访问权限(能访问部分端口.不能访问另外的端口). 用户程序的CPL<IOPL ...

  3. HDOJ-2896(AC自动机+文本串中出现了哪几个模板串)

    病毒侵袭 HDOJ-2896 主要使用AC自动机解决,其次在query函数中改变一下,用来记录每个模板串出现的次数,还有insert函数中记录模板串的编号 需要注意最好使用结构体,而且不能一次性使用m ...

  4. SQL-MYSQL的时间格式转换(持续补充)

    ======================SQLSERVER===================================== SELECT CONVERT(varchar(100), GE ...

  5. XUPT-D

    /*     泰泰学长又来玩数字了,泰泰学长想让你帮他求1-n的和,但是这次的求和可不是简单的1+2+...+n. 这次的求和是这样的,如果加到一个数字是2的指数倍,那就不加,反而减掉这个数.    ...

  6. 微软跨平台UI框架MAUI真的要来啦

    .NET 6 preview已经上线,是时候为在BUILD 2020上宣布的新.NET Multi-platform App UI(MAUI)做准备了.对于客户端应用程序开发人员来说,这一年.NET有 ...

  7. Raft共识算法详解

    Raft共识算法 一.背景 拜占庭将军问题是分布式领域最复杂.最严格的容错模型.但在日常工作中使用的分布式系统面对的问题不会那么复杂,更多的是计算机故障挂掉了,或者网络通信问题而没法传递信息,这种情况 ...

  8. 使用dcmtk库读取.dcm文件并获取信息+使用OpenCV显示图像

    借助VS2013和OpenCV的绘图功能,在工程DICOMReader.sln中实现了对单张.dcm图像的读取与显示,以下是详细步骤. 前期准备工作 编译器:VS2013 库:dcmtk-3.6.0( ...

  9. C语言可变参函数分析

    code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); ...

  10. Ubuntu18.04美化(Mac OS主题) 美化小白专用

    本文主要针对第一次接触Ubuntu美化的童鞋们,有些啰嗦的地方大神勿喷 先上效果图 首先安装神器 gnome-tweak-tool 开启一个终端,输入 sudo apt install gnome-t ...