【Android逆向】反调试绕过(nop 绕过)
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
脚本原理
- hook strstr 方法,对传入的字符串进行检查,凡是和frida相关的都重置返回值绕过
- hook access ,不检查目的目录
- 把启动线程检查的地方nop掉
- hook java层方法
结果:成功显示it is success
【Android逆向】反调试绕过(nop 绕过)的更多相关文章
- 编译Android内核 For nexus 5 以及绕过Android的反调试
本文博客链接:http://blog.csdn.net/qq1084283172/article/details/54880488 前面的博客中已经记录了Nexus 5手机的Android 4.4.4 ...
- 《Android逆向反编译代码注入》 - 逆向安全入门必看视频教程
适合人群: Android开发人员.逆向反编译开发人员.以及对Android逆向安全感兴趣的朋友. 视频地址: 51CTO学院:https://edu.51cto.com/course/24485 ...
- 解决Android加固多进程ptrace反调试的思路整理
本文博客链接:http://blog.csdn.net/qq1084283172/article/details/53613481 一.Android多进程反调试的原理代码 当ptrace附加目标进程 ...
- Windows 32位-调试与反调试
1.加载调试符号链接文件并放入d:/symbols目录下. 0:000> .sympath srv*d:\symbols*http://msdl.microsoft.com/download/s ...
- C/C++ 程序反调试的方法
C/C++ 要实现程序反调试有多种方法,BeingDebugged,NtGlobalFlag,ProcessHeap,CheckRemoteDebuggerPresent,STARTUPINFO,Is ...
- 修改Android手机内核,绕过反调试
本文博客链接:http://blog.csdn.net/qq1084283172/article/details/57086486 0x1.手机设备环境 Model number: Nexus 5 O ...
- 手动绕过百度加固Debug.isDebuggerConnected反调试的方法
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78237571 1.调用Debug.isDebuggerConnected函数这种反 ...
- 一种绕过PTRACE反调试的办法
Linux 系统gdb等调试器,都是通过ptrace系统调用实现.Android加固中,ptrace自身防止调试器附加是一种常用的反调试手段. 调试时一般需要手工在ptrace处下断点,通过修改ptr ...
- angr进阶(6)绕过反调试
angr绕过反调试,一个是通过之前的方式,使用从特定位置开始测试的方法,还有一种通过hook进行反调试的方法. 其原理就在于angr能够符号化表示函数tumctf2016_zwiebe p.hook_ ...
- Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码)
Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码) 来源 https://blog.csdn.net/jiangwei0910410003/article/details/51 ...
随机推荐
- [转帖]我们为什么放弃 MongoDB 和 MySQL,选择 TiDB
https://zhuanlan.zhihu.com/p/164706527 写在前面的话 技术选型是由技术方向和业务场景 trade-off 决定的,脱离业务场景来说技术选型是没有任何意义的,所以本 ...
- [转帖]PostgreSQL(三) 内存参数优化和原理(work_mem)内存表 pgfincore插件使用方法
1.常用内存参数 1.1 shared_buffers shared_buffers是PostgreSQL用于共享缓冲区的内存,是由8kb大小的块所形成的数组.PostgreSQL在进行更新.查询等操 ...
- [转帖]DD硬盘性能相关因素
https://www.jianshu.com/p/a15d7a65c876 本文简单介绍下DD测试硬盘性能时,各个因素的影响 首先列出测试结果 image.png oflag分析--/home ...
- 【转帖】浅析经典JVM垃圾收集器-Serial/ParNew/Parallel Scavenge/Serial Old/Parallel Old/CMS/G1
https://zhuanlan.zhihu.com/p/481256418 在讲述垃圾收集器之前,我们得先知道JVM中常见的垃圾收集算法有什么,具体请参考我的这篇博文.如果说收集算法是内存回收的方法 ...
- AI-WEB-1.0靶机
AI-WEB-1.0靶机 情报收集 扫描靶机,打开网站提示 Not even Google search my contents! dirb http://192.168.218.139 扫描网站 进 ...
- 源码学习之Spring容器创建原理
1 前言 众所周知,Spring可以帮我们管理我们需要的bean.在我们需要用到这些bean的时候,可以很方便的获取到它,然后进行一系列的操作.比如,我们定义一个bean MyTestBean pub ...
- devDependencies和dependencies有何区别
01==>devDependencies用于本地环境开发时候所需要的依赖包. 上线后就不在需要了: npm i webpack-cli -D --save-dev等价为-D 意思是安装到开发环境 ...
- 常见的for循环优化方式
?> 前言 经常使用一些循环,进行耗时计算的操作,特别是 for 循环,它是一种重复计算的操作,如果处理不好,耗时就比较大,如果处理书写得当,将大大提高效率,下面总结几条 for 循环的常见优化 ...
- Linux的守护进程 [补档-2023-08-10]
12-1守护进程 12-1-1介绍 Daemom是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或者事件.这些进 程一般不直接和用户交互,不受用户的登录,注销等影响.没 ...
- React 的学习笔记一 (未完结)
一.React 是什么 React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库.使用 React 可以将一些简短.独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作 ...