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

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

第三题

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

3. apk拖入到jadx中,观察

public class MainActivity extends AppCompatActivity {
public native String stringFromJNI(); static {
System.loadLibrary("native-lib");
} /* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.sample_text);
stringFromJNI();
if (mystr()) {
tv.setText("it is success");
}
} public boolean mystr() {
return false;
}
}

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

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

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

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

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

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

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

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

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

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

void __fastcall __noreturn sub_10448(__int64 a1)
{
while ( 1 )
{
while ( (~*(_DWORD *)(a1 + 4) & 0x80000) == 0 )
;
*(int *)((char *)&dword_0 + 1) = 45465456;
}
}

3. 再看Java_com_r0ysue_test1_MainActivity_stringFromJNI

__int64 __fastcall Java_com_r0ysue_test1_MainActivity_stringFromJNI(JNIEnv *env)
{
__int64 v2; // [xsp+0h] [xbp-560h]
__int64 v3; // [xsp+8h] [xbp-558h]
const char *v4; // [xsp+28h] [xbp-538h]
const char *v5; // [xsp+40h] [xbp-520h]
const char *nptr; // [xsp+58h] [xbp-508h]
FILE *v7; // [xsp+98h] [xbp-4C8h]
int v8; // [xsp+A4h] [xbp-4BCh]
unsigned __int64 v9; // [xsp+B0h] [xbp-4B0h]
unsigned int pthread_create_sym; // [xsp+CCh] [xbp-494h]
char v12[24]; // [xsp+130h] [xbp-430h] BYREF
char v13[1024]; // [xsp+148h] [xbp-418h] BYREF
__int64 v14; // [xsp+548h] [xbp-18h] v14 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
sub_FFF4(v12, "Hello from C++");
pthread_create_sym = sub_F690("/system/lib64/libc.so", "pthread_create");
__android_log_print(6, "r0ysue", "%x", pthread_create_sym);
v8 = 1;
v7 = fopen("/proc/self/maps", "r");
while ( __fgets_chk(v13, 1024LL, v7, 1024LL) )
{
if ( strstr(v13, "libc.so") )
{
if ( v8 == 2 )
{
nptr = strtok(v13, "-");
v9 = strtoul(nptr, 0LL, 16);
v5 = strtok(0LL, " ");
strtoul(v5, 0LL, 16);
}
else
{
strtok(v13, "-");
v4 = strtok(0LL, " ");
strtoul(v4, 0LL, 16);
}
++v8;
}
}
sub_1004C(pthread_create_sym, v9);
v3 = sub_100EC(v12);
v2 = sub_100B4(env, v3);
sub_10110(v12);
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
return v2;
}

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

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

inlinehook 检查内容参考博客

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

由此可编写frida脚本

function hook_dlopen(soName) {
Interceptor.attach(Module.findExportByName(null, "dlopen"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if (path.indexOf(soName) >= 0) {
this.is_can_hook = true;
}
}
},
onLeave: function (retval) {
if (this.is_can_hook) {
console.log("hook start...");
hook_func()
}
}
}
); Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if (path.indexOf(soName) >= 0) {
this.is_can_hook = true;
}
}
},
onLeave: function (retval) {
if (this.is_can_hook) {
console.log("hook start...");
hook_func()
}
}
}
);
} function hook_func() {
//把启动线程检查的地方nop掉
var lib_handler = Process.findModuleByName("libnative-lib.so")
//0x10984 pthread create 那一行
var dst_addr = new NativePointer(lib_handler.base.add(0x10984))
Memory.patchCode(dst_addr, 4, function (code) {
var cw = new Arm64Writer(code, { pc: dst_addr });
cw.putNop()
cw.flush();
}); var strStr = Module.findExportByName("libc.so", "strstr");
console.log("[*] strstr addr: " + strStr);
if (strStr) {
Interceptor.attach(strStr, {
onEnter: function(args) {
//console.log("[*] strstr hooked")
var arg0= ptr(args[0]).readCString();
var arg1= ptr(args[1]).readCString();
if(arg1.indexOf(":69A2")>=0){
console.log("[*]onEnter strstr hooked "+arg0+","+arg1+")");
this.flag_69A2=true
} if(arg1.indexOf("frida")>=0){
console.log("[*]onEnter strstr hooked "+arg0+","+arg1+")");
this.flag_frida=true
} },
onLeave: function(retval) {
if(this.flag_69A2){
console.log("[*]onLeave flag_69A2 hooked "+retval);
retval.replace(0x0);
this.flag_69A2 = false
} if(this.flag_frida){
console.log("[*]onLeave flag_frida hooked "+retval);
retval.replace(0x0);
this.flag_frida = false
}
}
});
} var access = Module.findExportByName("libc.so", "access");
console.log("[*] access addr: " + access); if (access) {
Interceptor.attach(access, {
onEnter: function(args) {
//console.log("[*] access hooked")
var arg0= ptr(args[0]).readCString();
var arg1= args[1];
if(arg0.indexOf("re.frida.server")>=0){
console.log("[*]onEnter re.frida.server hooked"+arg0+","+arg1+")");
this.flag_reFrida=true
} },
onLeave: function(retval) { if(this.flag_reFrida){
console.log("[*]onLeave re.frida.server access hooked"+retval);
retval.replace(-1);
this.flag_reFrida = false
}
}
});
} } function main() {
hook_dlopen("libnative-lib.so") Java.perform(function () {
var cls = Java.use("com.r0ysue.test1.MainActivity")
cls.mystr.implementation = function () {
console.log("call MainActivity mystr")
return true
}
})
} // 不能延时启动,否则线程已经起来了
setImmediate(main)

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

日志
......
[*]onEnter strstr hooked 6: 0F01A8C0:BD0B 2B1D467C:1B5C 01 00000000:00000000 00:00000000 00000000 10112 0 2123256 1 0000000000000000 24 4 30 10 -1
,:69A2)
[*]onLeave flag_69A2 hooked 0x0
[*]onEnter strstr hooked 7: 0100007F:B167 0100007F:69A2 01 00000000:00000000 00:00000000 00000000 2000 0 2125983 1 0000000000000000 21 4 30 10 16
,:69A2)
[*]onLeave flag_69A2 hooked 0x7fca407404
[*]onEnter strstr hooked 8: 0100007F:C105 0100007F:69A2 06 00000000:00000000 03:000013C9 00000000 0 0 0 3 0000000000000000
,:69A2)
[*]onLeave flag_69A2 hooked 0x7fca407404
[*]onEnter strstr hooked 9: 0100007F:69A2 0100007F:B167 01 00000000:00000000 00:00000000 00000000 0 0 2124098 1 0000000000000000 21 4 31 10 16
,:69A2)
[*]onLeave flag_69A2 hooked 0x7fca4073f6
[*]onEnter strstr hooked 10: 0100007F:A63D 0100007F:9933 01 00000000:000004A0 00:00000000 00000000 2000 0 2123640 2 0000000000000000 21 4 0 10 16
,:69A2)
[*]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. [转帖]ASH、AWR、ADDM区别联系

    ==================================================================================================== ...

  2. 【转帖】关于网卡特性TSO、UFO、GSO、LRO、GRO

    https://www.cnblogs.com/larrypeng/p/12496810.html 我们来看下关于网卡特性的解释,不过记住GSO和GRO两个特性就好. TSO(TCP Segmenta ...

  3. 京东ES支持ZSTD压缩算法上线了:高性能,低成本 | 京东云技术团队

    ​ 1 前言 在<ElasticSearch降本增效常见的方法>一文中曾提到过zstd压缩算法[1],一步一个脚印我们终于在京东ES上线支持了zstd:我觉得促使目标完成主要以下几点原因: ...

  4. js解析上传APK文件的详细信息

    前端解析APK版本信息 需要安装这个包,可以使用cnpm或者npm npm 安装 app-info-parser ( 命令:npm install app-info-parser) APKInfo为i ...

  5. 初试高云FPGA

    前言 之前一直眼馋Sipeed的Tang系列,正好遇到有工程需要高速控制并行总线,就买了NANO 9K和Primer 20K试试水 买回来先拆的贵的20k,结果发现Sipeed设计师有奇怪的脑回路: ...

  6. docker的架构及工作原理(详解)

    目录 一.docker架构图 二.Client 客户端 三.Host 主机(docker引擎) 四.Image 镜像 五.Container 容器 六.镜像分层 可写的容器层 七.Volume 数据卷 ...

  7. OpenIM Open Source Instant Messaging Project Docker Compose Deployment Guide

    The deployment of OpenIM involves multiple components and supports various methods including source ...

  8. 物联网浏览器(IoTBrowser)-电子秤模块及二次驱动开发

    本章介绍电子秤模块的示例功能以及二次开发称重驱动的代码,二次开发以顶尖OS2型号驱动为示例,实现方式与物联网浏览器(IoTBrowser)-顶尖OS2电子秤协议实现类似,不同的是电子秤只需要采集重量不 ...

  9. 金融时间序列预测方法合集:CNN、LSTM、随机森林、ARMA预测股票价格(适用于时序问题)、相似度计算、各类评判指标绘图(数学建模科研适用)

    金融时间序列预测方法合集:CNN.LSTM.随机森林.ARMA预测股票价格(适用于时序问题).相似度计算.各类评判指标绘图(数学建模科研适用) 1.使用CNN模型预测未来一天的股价涨跌-CNN(卷积神 ...

  10. Jupyter Notebook 下 import 第三方库,显示 no module xxx 【本质是环境没有切换过来】

    1.最简单情况下 切换环境即可 首先激活环境: ​ activate env  # 激活你的环境名称 jupyter notebook ​ 之后去运行代码即可,如果还不行请看下面: 2.遇到Jupyt ...