ANDROID调试检测技术汇编

ANDROID调试检测技术汇编

1 调试器调试端口
2 调试器进程名
3 父进程名检测
4 自身进程名检测
5 apk线程检测
6 apk进程fd文件检测
7 安卓系统自带调试检测函数
8 ptrace检测
9 函数hash值检测
10 断点指令检测
11 系统源码修改检测
12 单步调试陷阱
13 利用IDA先截获信号特性的检测
14 利用IDA解析缺陷反调试
15 五种代码执行时间检测
16 三种种进程信息结构检测
17 Inotify事件监控dump

1. IDA调试端口检测

原理:
调试器远程调试时,会占用一些固定的端口号。
做法:
读取/proc/net/tcp,查找IDA远程调试所用的23946端口,若发现说明进程正在被IDA调试。
(也可以运行netstat ­apn结果中搜索23946端口)

void CheckPort23946ByTcp()
{
   
FILE* pfile=NULL;
char buf[0x1000]={
   0};
// 执行命令
char* strCatTcp= "cat /proc/net/tcp |grep :5D8A";
//char* strNetstat="netstat |grep :23946";
pfile=popen(strCatTcp,"r");
if(NULL==pfile)
{
   
LOGA("CheckPort23946ByTcp popen打开命令失败!\n");
return;
}
// 获取结果
while(fgets(buf,sizeof(buf),pfile))
{
   
// 执行到这里,判定为调试状态
LOGA("执行cat /proc/net/tcp |grep :5D8A的结果:\n");
LOGB("%s",buf);
}//while
pclose(pfile);
}

2. 调试器进程名检测

原理:
远程调试要在手机中运行android_server gdbserver gdb等进程。
做法:
遍历进程,查找固定的进程名,找到说明调试器在运行。
void SearchObjProcess()
{
   
FILE* pfile=NULL;
char buf[0x1000]={
   0};
// 执行命令
//pfile=popen("ps | awk '{print $9}'","r"); // 部分不支持awk命令
pfile=popen("ps","r");
if(NULL==pfile)
{
   
LOGA("SearchObjProcess popen打开命令失败!\n");
return;
}
// 获取结果
LOGA("popen方案:\n");
while(fgets(buf,sizeof(buf),pfile))
{
   
// 打印进程
LOGB("遍历进程:%s\n",buf);
// 查找子串
char* strA=NULL,strB=NULL,strC=NULL,strD=NULL;
strA=strstr(buf,"android_server");
strB=strstr(buf,"gdbserver");
strC=strstr(buf,"gdb");
strD=strstr(buf,"fuwu");
if(strA || strB ||strC || strD)
{
   
// 执行到这里,判定为调试状态
LOGB("发现目标进程:%s\n",buf);
}//if
}//while
pclose(pfile);
}

3 父进程名检测

原理:
有的时候不使用apk附加调试的方法进行逆向,而是写一个.out可执行文件直接加载so进行
调试,这样程序的父进程名和正常启动apk的父进程名是不一样的。
测试发现:
(1)正常启动的apk程序:父进程是zygote
(2)调试启动的apk程序:在AS中用LLDB调试发现父进程还是zygote
(3)附加调试的apk程序:父进程是zygote
(4)vs远程调试 用可执行文件加载so:父进程名为gdbserver
结论:父进程名非zygote的,判定为调试状态。
做法:

读取/proc/pid/cmdline,查看内容是否为zygote
void CheckParents()
{
   
///
// 设置buf
char strPpidCmdline[0x100]={
   0};
snprintf(strPpidCmdline, sizeof(strPpidCmdline), "/proc/%d/cmdl
ine", getppid());
// 打开文件
int file=open(strPpidCmdline,O_RDONLY);
if(file<0)
{
   
LOGA("CheckParents open错误!\n");
return;
}
// 文件内容读入内存
memset(strPpidCmdline,0,sizeof(strPpidCmdline));
ssize_t ret=read(file,strPpidCmdline,sizeof(strPpidCmdline));
if(-1==ret)
{
   
LOGA("CheckParents read错误!\n");
return;
}
// 没找到返回0
char sRet=strstr(strPpidCmdline,"zygote");
if(NULL==sRet)
{
   
// 执行到这里,判定为调试状态
LOGA("父进程cmdline没有zygote子串!\n");
return;
}
int i=0;
return;
}

4 自身进程名检测

原理:
和上条一样,也是写个.out加载so来脱壳的场景,
正常进程名一般是apk的com.xxx这样的格式。
代码:

5 apk线程检测

原理:
同样.out加载so来脱壳的场景,
正常apk进程一般会有十几个线程在运行(比如会有jdwp线程),
自己写可执行文件加载so一般只有一个线程,
可以根据这个差异来进行调试环境检测。

void CheckTaskCount()
{
   
char buf[0x100]={
   0};
char* str="/proc/%d/task";
snprintf(buf,sizeof(buf),str,getpid());
// 打开目录:
DIR* pdir = opendir(buf);
if (!pdir)
{
   
perror("CheckTaskCount open() fail.\n");
return;
}
// 查看目录下文件个数:
struct dirent* pde=NULL;
int Count=0;
while ((pde = readdir(pdir)))
{
   
// 字符过滤
if ((pde->d_name[0] <= '9') && (pde->d_name[0] >= '0'))
{
   
++Count;
LOGB("%d 线程名称:%s\n",Count,pde->d_name);
}
}
LOGB("线程个数为:%d",Count);
if(1>=Count)
{
   
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kerve

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值