1. 这是看雪上的一个题目,要求显示出 it is success

https://www.kanxue.com/work-task_read-800648.htm

第三题

2. apk 安装到手机,发现闪退

3. apk拖入到jadx中,观察

  1. public class MainActivity extends AppCompatActivity {
  2. public native String stringFromJNI();
  3. static {
  4. System.loadLibrary("native-lib");
  5. }
  6. /* JADX INFO: Access modifiers changed from: protected */
  7. @Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
  8. public void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_main);
  11. TextView tv = (TextView) findViewById(R.id.sample_text);
  12. stringFromJNI();
  13. if (mystr()) {
  14. tv.setText("it is success");
  15. }
  16. }
  17. public boolean mystr() {
  18. return false;
  19. }
  20. }

4. java层平平无奇,看来答案在native层

5. so拖入到IDA中进行分析,导出表中直接就有JNI_OnLoad 和静态注册对应的函数,没有init_array段,那么猜测反调试代码应该在JNI_OnLoad中,查看JNI_OnLoad

  1. jint JNI_OnLoad(JavaVM *vm, void *reserved)
  2. {
  3. __int64 v2; // x0
  4. __int64 v3; // x0
  5. void *arg; // [xsp+18h] [xbp-38h]
  6. __int64 v6; // [xsp+20h] [xbp-30h]
  7. pthread_t newthread; // [xsp+38h] [xbp-18h] BYREF
  8. __int64 v8[2]; // [xsp+40h] [xbp-10h] BYREF
  9. v8[1] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  10. sub_109B8(vm, v8, 65540LL);
  11. v2 = sub_10184(); // 这里在检查maps 中的frida agent
  12. v3 = sub_102BC(v2); // 检查是否存在 /data/local/tmp/re.frida.server
  13. sub_10310(v3); // 检查frida监听端口27042
  14. v6 = sub_109F8(v8[0], &unk_36030);
  15. arg = (void *)sub_10A30(v8[0], v6, &unk_3604F, &unk_36055);
  16. pthread_create(&newthread, 0LL, (void *(*)(void *))sub_10448, arg);
  17. return 65540;
  18. }

查看sub_10184,可以看到在检查内存中是否存在frida字符串,frdia工作时会把frida_agent加载进进程,会有一些特征字符串,这里就在搞这个事情

  1. __int64 sub_10184()
  2. {
  3. __int64 result; // x0
  4. FILE *v1; // [xsp+10h] [xbp-470h]
  5. char v2[1024]; // [xsp+68h] [xbp-418h] BYREF
  6. __int64 v3; // [xsp+468h] [xbp-18h]
  7. v3 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  8. v1 = fopen("/proc/self/maps", "r");
  9. while ( 1 )
  10. {
  11. result = __fgets_chk(v2, 1024LL, v1, 1024LL);
  12. if ( !result )
  13. break;
  14. if ( strstr(v2, "frida") )
  15. *(int *)((char *)&dword_0 + 1) = 45465456;
  16. }
  17. _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  18. return result;
  19. }

查看sub_102BC,这里再检查/data/local/tmp/re.frida.server 目录是否存在,frida_server启动会默认创建这个目录

  1. __int64 sub_102BC()
  2. {
  3. __int64 result; // x0
  4. result = access("/data/local/tmp/re.frida.server", 0);
  5. if ( (int)result >= 0 )
  6. *(int *)((char *)&dword_0 + 1) = 45465456;
  7. return result;
  8. }

查看sub_10310,这里再查看是否有再监听0x69A2(27042)frida的默认端口

  1. // write access to const memory has been detected, the output may be wrong!
  2. __int64 sub_10310()
  3. {
  4. __int64 result; // x0
  5. FILE *v1; // [xsp+10h] [xbp-470h]
  6. char v2[1024]; // [xsp+68h] [xbp-418h] BYREF
  7. __int64 v3; // [xsp+468h] [xbp-18h]
  8. v3 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  9. v1 = fopen("/proc/net/tcp", "r");
  10. while ( 1 )
  11. {
  12. result = __fgets_chk(v2, 1024LL, v1, 1024LL);
  13. if ( !result )
  14. break;
  15. if ( strstr(v2, ":69A2") )
  16. *(int *)((char *)&dword_0 + 1) = 45465456;
  17. }
  18. _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  19. return result;
  20. }

然后创建了一个线程去执行sub_10448,查看一下,这里时检查,这里时对入参进行检查,查询JAVA层的方法是否有native话,特征就是(~*(_DWORD *)(a1 + 4) & 0x80000) == 0),推测是检查是否有hook java层的方法,有的话就干掉进程

  1. void __fastcall __noreturn sub_10448(__int64 a1)
  2. {
  3. while ( 1 )
  4. {
  5. while ( (~*(_DWORD *)(a1 + 4) & 0x80000) == 0 )
  6. ;
  7. *(int *)((char *)&dword_0 + 1) = 45465456;
  8. }
  9. }

3. 再看Java_com_r0ysue_test1_MainActivity_stringFromJNI

  1. __int64 __fastcall Java_com_r0ysue_test1_MainActivity_stringFromJNI(JNIEnv *env)
  2. {
  3. __int64 v2; // [xsp+0h] [xbp-560h]
  4. __int64 v3; // [xsp+8h] [xbp-558h]
  5. const char *v4; // [xsp+28h] [xbp-538h]
  6. const char *v5; // [xsp+40h] [xbp-520h]
  7. const char *nptr; // [xsp+58h] [xbp-508h]
  8. FILE *v7; // [xsp+98h] [xbp-4C8h]
  9. int v8; // [xsp+A4h] [xbp-4BCh]
  10. unsigned __int64 v9; // [xsp+B0h] [xbp-4B0h]
  11. unsigned int pthread_create_sym; // [xsp+CCh] [xbp-494h]
  12. char v12[24]; // [xsp+130h] [xbp-430h] BYREF
  13. char v13[1024]; // [xsp+148h] [xbp-418h] BYREF
  14. __int64 v14; // [xsp+548h] [xbp-18h]
  15. v14 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  16. sub_FFF4(v12, "Hello from C++");
  17. pthread_create_sym = sub_F690("/system/lib64/libc.so", "pthread_create");
  18. __android_log_print(6, "r0ysue", "%x", pthread_create_sym);
  19. v8 = 1;
  20. v7 = fopen("/proc/self/maps", "r");
  21. while ( __fgets_chk(v13, 1024LL, v7, 1024LL) )
  22. {
  23. if ( strstr(v13, "libc.so") )
  24. {
  25. if ( v8 == 2 )
  26. {
  27. nptr = strtok(v13, "-");
  28. v9 = strtoul(nptr, 0LL, 16);
  29. v5 = strtok(0LL, " ");
  30. strtoul(v5, 0LL, 16);
  31. }
  32. else
  33. {
  34. strtok(v13, "-");
  35. v4 = strtok(0LL, " ");
  36. strtoul(v4, 0LL, 16);
  37. }
  38. ++v8;
  39. }
  40. }
  41. sub_1004C(pthread_create_sym, v9);
  42. v3 = sub_100EC(v12);
  43. v2 = sub_100B4(env, v3);
  44. sub_10110(v12);
  45. _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  46. return v2;
  47. }

重点是sub_1004C,其中0xD61F020058000050LL 是一段frida hook native的特征码,这里应该是在检查是否有hook pthread_create 函数

  1. const char *__fastcall sub_1004C(int a1, __int64 a2)
  2. {
  3. if ( *(_QWORD *)(a2 + a1) == 0xD61F020058000050LL )
  4. *(int *)((char *)&dword_0 + 1) = 45465456;
  5. return "Hello from C++";
  6. }

inlinehook 检查内容参考博客

https://www.52pojie.cn/thread-1530251-1-1.html

由此可编写frida脚本

  1. function hook_dlopen(soName) {
  2. Interceptor.attach(Module.findExportByName(null, "dlopen"),
  3. {
  4. onEnter: function (args) {
  5. var pathptr = args[0];
  6. if (pathptr !== undefined && pathptr != null) {
  7. var path = ptr(pathptr).readCString();
  8. if (path.indexOf(soName) >= 0) {
  9. this.is_can_hook = true;
  10. }
  11. }
  12. },
  13. onLeave: function (retval) {
  14. if (this.is_can_hook) {
  15. console.log("hook start...");
  16. hook_func()
  17. }
  18. }
  19. }
  20. );
  21. Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
  22. {
  23. onEnter: function (args) {
  24. var pathptr = args[0];
  25. if (pathptr !== undefined && pathptr != null) {
  26. var path = ptr(pathptr).readCString();
  27. if (path.indexOf(soName) >= 0) {
  28. this.is_can_hook = true;
  29. }
  30. }
  31. },
  32. onLeave: function (retval) {
  33. if (this.is_can_hook) {
  34. console.log("hook start...");
  35. hook_func()
  36. }
  37. }
  38. }
  39. );
  40. }
  41. function hook_func() {
  42. //把启动线程检查的地方nop掉
  43. var lib_handler = Process.findModuleByName("libnative-lib.so")
  44. //0x10984 pthread create 那一行
  45. var dst_addr = new NativePointer(lib_handler.base.add(0x10984))
  46. Memory.patchCode(dst_addr, 4, function (code) {
  47. var cw = new Arm64Writer(code, { pc: dst_addr });
  48. cw.putNop()
  49. cw.flush();
  50. });
  51. var strStr = Module.findExportByName("libc.so", "strstr");
  52. console.log("[*] strstr addr: " + strStr);
  53. if (strStr) {
  54. Interceptor.attach(strStr, {
  55. onEnter: function(args) {
  56. //console.log("[*] strstr hooked")
  57. var arg0= ptr(args[0]).readCString();
  58. var arg1= ptr(args[1]).readCString();
  59. if(arg1.indexOf(":69A2")>=0){
  60. console.log("[*]onEnter strstr hooked "+arg0+","+arg1+")");
  61. this.flag_69A2=true
  62. }
  63. if(arg1.indexOf("frida")>=0){
  64. console.log("[*]onEnter strstr hooked "+arg0+","+arg1+")");
  65. this.flag_frida=true
  66. }
  67. },
  68. onLeave: function(retval) {
  69. if(this.flag_69A2){
  70. console.log("[*]onLeave flag_69A2 hooked "+retval);
  71. retval.replace(0x0);
  72. this.flag_69A2 = false
  73. }
  74. if(this.flag_frida){
  75. console.log("[*]onLeave flag_frida hooked "+retval);
  76. retval.replace(0x0);
  77. this.flag_frida = false
  78. }
  79. }
  80. });
  81. }
  82. var access = Module.findExportByName("libc.so", "access");
  83. console.log("[*] access addr: " + access);
  84. if (access) {
  85. Interceptor.attach(access, {
  86. onEnter: function(args) {
  87. //console.log("[*] access hooked")
  88. var arg0= ptr(args[0]).readCString();
  89. var arg1= args[1];
  90. if(arg0.indexOf("re.frida.server")>=0){
  91. console.log("[*]onEnter re.frida.server hooked"+arg0+","+arg1+")");
  92. this.flag_reFrida=true
  93. }
  94. },
  95. onLeave: function(retval) {
  96. if(this.flag_reFrida){
  97. console.log("[*]onLeave re.frida.server access hooked"+retval);
  98. retval.replace(-1);
  99. this.flag_reFrida = false
  100. }
  101. }
  102. });
  103. }
  104. }
  105. function main() {
  106. hook_dlopen("libnative-lib.so")
  107. Java.perform(function () {
  108. var cls = Java.use("com.r0ysue.test1.MainActivity")
  109. cls.mystr.implementation = function () {
  110. console.log("call MainActivity mystr")
  111. return true
  112. }
  113. })
  114. }
  115. // 不能延时启动,否则线程已经起来了
  116. setImmediate(main)

frida -U -f com.r0ysue.test1 -l lesson12.js --no-pause

日志
  1. ......
  2. [*]onEnter strstr hooked 6: 0F01A8C0:BD0B 2B1D467C:1B5C 01 00000000:00000000 00:00000000 00000000 10112 0 2123256 1 0000000000000000 24 4 30 10 -1
  3. ,:69A2)
  4. [*]onLeave flag_69A2 hooked 0x0
  5. [*]onEnter strstr hooked 7: 0100007F:B167 0100007F:69A2 01 00000000:00000000 00:00000000 00000000 2000 0 2125983 1 0000000000000000 21 4 30 10 16
  6. ,:69A2)
  7. [*]onLeave flag_69A2 hooked 0x7fca407404
  8. [*]onEnter strstr hooked 8: 0100007F:C105 0100007F:69A2 06 00000000:00000000 03:000013C9 00000000 0 0 0 3 0000000000000000
  9. ,:69A2)
  10. [*]onLeave flag_69A2 hooked 0x7fca407404
  11. [*]onEnter strstr hooked 9: 0100007F:69A2 0100007F:B167 01 00000000:00000000 00:00000000 00000000 0 0 2124098 1 0000000000000000 21 4 31 10 16
  12. ,:69A2)
  13. [*]onLeave flag_69A2 hooked 0x7fca4073f6
  14. [*]onEnter strstr hooked 10: 0100007F:A63D 0100007F:9933 01 00000000:000004A0 00:00000000 00000000 2000 0 2123640 2 0000000000000000 21 4 0 10 16
  15. ,:69A2)
  16. [*]onLeave flag_69A2 hooked 0x0

脚本原理

  1. hook strstr 方法,对传入的字符串进行检查,凡是和frida相关的都重置返回值绕过
  2. hook access ,不检查目的目录
  3. 把启动线程检查的地方nop掉
  4. hook java层方法
结果:成功显示it is success

【Android逆向】反调试绕过(nop 绕过)的更多相关文章

  1. 编译Android内核 For nexus 5 以及绕过Android的反调试

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/54880488 前面的博客中已经记录了Nexus 5手机的Android 4.4.4 ...

  2. 《Android逆向反编译代码注入》 - 逆向安全入门必看视频教程

      适合人群: Android开发人员.逆向反编译开发人员.以及对Android逆向安全感兴趣的朋友. 视频地址: 51CTO学院:https://edu.51cto.com/course/24485 ...

  3. 解决Android加固多进程ptrace反调试的思路整理

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/53613481 一.Android多进程反调试的原理代码 当ptrace附加目标进程 ...

  4. Windows 32位-调试与反调试

    1.加载调试符号链接文件并放入d:/symbols目录下. 0:000> .sympath srv*d:\symbols*http://msdl.microsoft.com/download/s ...

  5. C/C++ 程序反调试的方法

    C/C++ 要实现程序反调试有多种方法,BeingDebugged,NtGlobalFlag,ProcessHeap,CheckRemoteDebuggerPresent,STARTUPINFO,Is ...

  6. 修改Android手机内核,绕过反调试

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/57086486 0x1.手机设备环境 Model number: Nexus 5 O ...

  7. 手动绕过百度加固Debug.isDebuggerConnected反调试的方法

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78237571 1.调用Debug.isDebuggerConnected函数这种反 ...

  8. 一种绕过PTRACE反调试的办法

    Linux 系统gdb等调试器,都是通过ptrace系统调用实现.Android加固中,ptrace自身防止调试器附加是一种常用的反调试手段. 调试时一般需要手工在ptrace处下断点,通过修改ptr ...

  9. angr进阶(6)绕过反调试

    angr绕过反调试,一个是通过之前的方式,使用从特定位置开始测试的方法,还有一种通过hook进行反调试的方法. 其原理就在于angr能够符号化表示函数tumctf2016_zwiebe p.hook_ ...

  10. Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码)

    Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码) 来源 https://blog.csdn.net/jiangwei0910410003/article/details/51 ...

随机推荐

  1. Jmeter学习之八_测试kafka

    Jmeter学习之八_测试kafka 背景 最近在持续学习. 昨天学习了grafana展示Jmeter测试数据库的结果 今天想着能够测试一下kafka验证一下kafka的吞吐量等信息 说干就干的. 遇 ...

  2. [转帖]AHCI到NVMe,SSD的关键科技革命

      https://baijiahao.baidu.com/s?id=1718020841628703656&wfr=spider&for=pc HDD和早期SSD大部分使用SATA接 ...

  3. 【转帖】基于paramiko的二次封装

    https://www.jianshu.com/p/944674f44b24 paramiko 是 Python 中的一个用来连接远程主机的第三方工具,通过使用 paramiko 可以用来代替以 ss ...

  4. [转帖]使用SkyWalking监控nginx (以openresty为例)

    https://www.cnblogs.com/hahaha111122222/p/15829737.html 安装使用SkyWalking先看这篇文章,地址:https://www.cnblogs. ...

  5. 自己想的一些判断存储长度的sql

    create table zhaobsh (t1 date ,t2 TIMESTAMP) insert into zhaobsh values (CURRENT_DATE,CURRENT_TIMEST ...

  6. PG数据库异步流复制

    PG数据库异步流复制 背景说明 最近想进行一个数据库高可用课题的研究. 因为之前某种原因,这次选择的是PG数据库. 为了简单起见, 暂时采用PG异步流复制的场景. 这次仅是为了测试, 不考虑高可用绿色 ...

  7. 同步存储读取vuex中store中的值

    main.js import store from "./store"; Vue.prototype.$store = store; 在 store中的index.js中 impo ...

  8. 【JS 逆向百例】浏览器插件 Hook 实战,亚航加密参数分析

    关注微信公众号:K哥爬虫,QQ交流群:808574309,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途 ...

  9. Walrus 0.5发布:重构交互流程,打造开箱即用的部署体验

    开源应用管理平台 Walrus 0.5 已于近日正式发布! Walrus 0.4 引入了全新应用模型,极大程度减少了重复的配置工作,并为研发团队屏蔽了云原生及基础设施的复杂度.Walrus 0.5 在 ...

  10. 6.9 Windows驱动开发:内核枚举进线程ObCall回调

    在笔者上一篇文章<内核枚举Registry注册表回调>中我们通过特征码定位实现了对注册表回调的枚举,本篇文章LyShark将教大家如何枚举系统中的ProcessObCall进程回调以及Th ...