Android反调试检测

java层保护

1 代码混淆
apk应用在被逆向分析时,java层代码就像被扒光一样,而native代码分析难度大,但是需要扎实的c/c++基础。这个时候大家就可以考虑一下代码混淆技术稍作保护(毕竟看着好多abc也挺烦的)。Android开发中提供了Proguard这一工具来进行代码混淆。
这里只做简单介绍,Proguard是一个开源项目,他能够对Java类中的代码进行压缩(Shrink),优化(Optimize),混淆(Obfuscate),预检(Preveirfy)。

  1. 压缩(Shrink):在压缩处理这一步中,用于检测和删除没有使用的类,字段,方法和属性。
  2. 优化(Optimize):在优化处理这一步中,对字节码进行优化,并且移除无用指令。
  3. 混淆(Obfuscate):在混淆处理这一步中,使用a,b,c等无意义的名称,对类,字段和方法进行重命名。
  4. 预检(Preveirfy):在预检这一步中,主要是在Java平台上对处理后的代码进行预检。

Proguard在Android Studio中都已经内置,每次新创建一个工程时,都会自动在gradle中被添加。

2 调试器检测
利用Java层API来判断Android程序是否是处于被调试下。进行动态调试的时候,需要进行jdb连接:jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700,当接连成功之后,android.os.Debug.isDebuggerConnected() 这个方法就会ture。这就跟系统的android.os.Debug.isDebuggerConnected()方法有关。解决也很简单,使用反编译工具方便程序,然后搜索isDebuggerConnected() 关键字, 然后Nop 掉改函数的调用即可。
还有就是判断当前应用属性:ApplicationInfo.FLAG_DEBUGGABLE。在反编译后AndroidMainfiest,xml 中添加:android:debuggable=“true”。

native层

1 ptrace检测
每个进程同时刻只能被1个调试进程ptrace,再次p自己会失败。所以可以主动ptrace自己,根据返回值判断自己是否被调试了;或者多进程ptrace。

// 单线程ptrace
void ptraceCheck()
{
// ptrace如果被调试返回值为-1,如果正常运行,返回值为0
int iRet=ptrace(PTRACE_TRACEME, 0, 0, 0);
if(-1 == iRet)
{
LOGA("ptrace失败,进程正在被调试\n");
return;
}
else
{
LOGB("ptrace的返回值为:%d\n",iRet);
return;
}
}

2 进程名检测
远程调试要在手机中运行android_server gdbserver gdb等进程,遍历进程,查找固定的进程名,如果能找到就说明调试器在运行。
有的时候不使用apk附加调试的方法进行逆向,而是写一个.out可执行文件直接加载so进行调试,这样程序的父进程名和正常启动apk的父进程名是不一样的。正常启动的apk程序父进程是zygote。如果不是,则证明为调试状态。
还有就是自身进程名,正常进程名一般是apk的com.xxx这样的格式。
3 apk线程检测
正常apk进程一般会有十几个线程在运行(比如会有jdwp线程),自己写可执行文件加载so一般只有一个线程,可以根据这个差异来进行调试环境检测(咱们没用过,咱啥也不知道)。
4 安卓系统调试检测函数
这个跟上面有点重复。分析android自带调试检测函数isDebuggerConnected()在native的实现, 尝试在native使用。

(1)dalvik模式下:
找到进程中libdvm.so中的dvmDbgIsDebuggerConnected()函数,
调用他就能得知程序是否被调试。 dlopen(/system/lib/libdvm.so)
dlsym(_Z25dvmDbgIsDebuggerConnectedv)
(2)art模式下:
art模式下,结果存放在libart.so中的全局变量gDebuggerActive中,
符号名为_ZN3art3Dbg15gDebuggerActiveE。
但是貌似新版本android不允许使用非ndk原生库,dlopen(libart.so)会失败。 所以无法用dalvik那样的方法了。

5 轮询检测法
轮询TracePid
在这里插入图片描述
这个主要是参考了https://www.cnblogs.com/jiaoxiake/p/6790803.html,可以进去了解下。

6 断点检测
如果函数被下软件断点,则断点地址会被改写为bkpt指令, 可以在函数体中搜索bkpt指令来检测软件断点。

// IDA 6.8 断点扫描
// 参数1:函数首地址 参数2:函数size
typedef uint8_t u8;
typedef uint32_t u32;
void checkbkpt(u8* addr,u32 size)
{
// 结果
u32 uRet=0;
// 断点指令
// u8 armBkpt[4]={0xf0,0x01,0xf0,0xe7};
// u8 thumbBkpt[2]={0x10,0xde};
u8 armBkpt[4]={0};
armBkpt[0]=0xf0;
armBkpt[1]=0x01;
armBkpt[2]=0xf0;
armBkpt[3]=0xe7;
u8 thumbBkpt[2]={0};
thumbBkpt[0]=0x10;
thumbBkpt[1]=0xde;
// 判断模式
int mode=(u32)addr%2;
if(1==mode) {
LOGA("checkbkpt:(thumb mode)该地址为thumb模式\n");
u8* start=(u8*)((u32)addr-1);
u8* end=(u8*)((u32)start+size);
// 遍历对比
while(1)
{
if(start >= end) {
uRet=0;
LOGA("checkbkpt:(no find bkpt)没有发现断点.\n");
break;
}
if( 0==memcmp(start,thumbBkpt,2) ) {
uRet=1;
LOGA("checkbkpt:(find it)发现断点.\n");
break;
}
start=start+2;
}//while
}//if
else
{
LOGA("checkbkpt:(arm mode)该地址为arm模式\n");
u8* start=(u8*)addr;
u8* end=(u8*)((u32)start+size);
// 遍历对比
while(1)
 {
if (start >= end) {
uRet = 0;
LOGA("checkbkpt:(no find)没有发现断点.\n");
break;
}
if (0 == memcmp(start,armBkpt , 4)){
uRet = 1;
LOGA("checkbkpt:(find it)发现断点.\n");
break;
}
start = start + 4;
}//while
}//else
return;
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值