android+反调试+方案,Android反调试笔记

1)代码执行时间检测

通过取系统时间,检测关键代码执行耗时,检测单步调试,类似函数有:time,gettimeofday,clock_gettime.

也可以直接使用汇编指令RDTSC读取,但测试ARM64有兼容问题。

time_t t1, t2;

time (&t1);

/* Parts of Important Codes */

time (&t2);

if (t2 - t1 > 2) {

puts ("debugged");

}

2)检测 procfs 文件系统变化

进程的状态信息能通过 procfs 系统反馈给用户空间,调试会使进程状态发生变化:

char file [PATH_LEN];

char line [LINE_LEN];

snprintf (file, PATH_LEN-1, "/proc/%d/status", pid);

FILE *fp = fopen (file, "r");

while (fgets (line, LINE_LEN-1, fp)) {

if (strncmp (line, "TracerPid:", 10) == 0) {

if (0 != atoi (&line[10])) {

/* encrypt random .TEXT code */

}

break;

}

}

fclose (fp);

类似可以检测的接口还有:

/proc/pid/status

/proc/pid/task/pid/status

/proc/pid/stat

/proc/pid/task/pid/stat

/proc/pid/wchan

/proc/pid/task/pid/wchan

3)利用信号机制

ARM程序下断点,调试器完成两件事:

保存目标地址处指令

将目标地址处指令替换成断点指令

指令集

指令

Arm

0x01, 0x00, 0x9f, 0xef

Thumb

0x01, 0xde

Thumb2

0xf0, 0xf7, 0x00, 0xa0

当命中断点时,系统产生SIGTRAP信号,调试器收到信号后完成下面操作:

恢复断点处原指令

回退被跟踪进程的当前PC

这时当控制权回到被调试程序时,正好执行断点位置指令。这就是 ARM 平台断点的基本原理。

可以看到,断点是通过处理 SIGTRAP 信号来实现的,假如我们自己注册 SIGTRAP 的信号处理函数,并在程序中主动执行中断指令触发中断。

在中断处理函数中,NOP 掉断点指令,程序可正常执行。但在调试状态下,调试器遇到断点指令时,会去恢复原先指令,由于不是调试器下的断点,所以恢复会失败,而调试器会继续第2步操作,回退PC寄存器,程序会在此处无限循环。

4)软件断点检测

断点会替换内存中原有指令,因此通过检测内存中的断点指令,可以检测调试:

#include

#include

#include

#include

#include

#include

void checkBreakPoint ();

unsigned long getLibAddr (const char *lib);

#define LOG_TAG "ANTIDBG_DEMO"

#include

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

int main ()

{

dlopen ("./libdemo.so", RTLD_NOW);

sleep (60);

checkBreakPoint ();

return 0;

}

unsigned long getLibAddr (const char *lib)

{

puts ("Enter getLibAddr");

unsigned long addr = 0;

char lineBuf[256];

snprintf (lineBuf, 256-1, "/proc/%d/maps", getpid ());

FILE *fp = fopen (lineBuf, "r");

if (fp == NULL) {

perror ("fopen failed");

goto bail;

}

while (fgets (lineBuf, sizeof(lineBuf), fp)) {

if (strstr (lineBuf, lib)) {

char *temp = strtok (lineBuf, "-");

addr = strtoul (temp, NULL, 16);

break;

}

}

bail:

fclose(fp);

return addr;

}

void checkBreakPoint ()

{

int i, j;

unsigned int base, offset, pheader;

Elf32_Ehdr *elfhdr;

Elf32_Phdr *ph_t;

base = getLibAddr ("libdemo.so");

if (base == 0) {

LOGI ("getLibAddr failed");

return;

}

elfhdr = (Elf32_Ehdr *) base;

pheader = base + elfhdr->e_phoff;

for (i = 0; i < elfhdr->e_phnum; i++) {

ph_t = (Elf32_Phdr*)(pheader + i * sizeof(Elf32_Phdr)); // traverse program header

if ( !(ph_t->p_flags & 1) ) continue;

offset = base + ph_t->p_vaddr;

offset += sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * elfhdr->e_phnum;

char *p = (char*)offset;

for (j = 0; j < ph_t->p_memsz; j++) {

if(*p == 0x01 && *(p+1) == 0xde) {

LOGI ("Find thumb bpt %p", p);

} else if (*p == 0xf0 && *(p+1) == 0xf7 && *(p+2) == 0x00 && *(p+3) == 0xa0) {

LOGI ("Find thumb2 bpt %p", p);

} else if (*p == 0x01 && *(p+1) == 0x00 && *(p+2) == 0x9f && *(p+3) == 0xef) {

LOGI ("Find arm bpt %p", p);

}

p++;

}

}

}

5)inotify 文件系统监控

inotify 是一个内核用于通知用户态文件系统变化的机制,当文件被访问,修改,删除等时用户态可以快速感知。

1.使用 inotify_init() 初始化一个 inotify 实例并返回文件描述符,每个文件描述符都关联了一个事件队列:

int fd = inotify_init ();

2.拿到这个文件描述符后下一步就告诉内核,哪些文件发生哪些事件时你得通知我,通过函数 inotify_add_watch 实现:

int wd = inotify_add_watch (fd, path, mask);

第一个参数即 inotify_init 返回的文件描述符,path 表示关注的目标路径,可以是文件目录等。mask 表示关注的事件的掩码,如 IN_ACCESS 代表访问,IN_MODIFY 代表修改等

相应的,可以通过 inotify_rm_watch 来删除一个watch:

int ret = inotify_rm_watch (fd, wd);

这样,每当监视的文件发生变化时,内核便给 fd 关联的事件队列里面塞一个文件事件。文件事件用一个 inotify_event 结构表示,可以通过 read 来读取:

struct inotify_event {

__s32 wd; /* watch descriptor */

__u32 mask; /* watch mask */

__u32 cookie; /* cookie to synchronize two events */

__u32 len; /* length (including nulls) of name */

char name[0]; /* stub for possible name */

};

size_t len = read (fd, buf, LEN);

通过监视 /proc/pid/maps 文件的打开事件,可防针对 360 加固的 dump 脱壳

文件变化与事件触发非必然联系,例如 /proc/pid/status 中的 TracerPid 值在被调试时是变化的,但其变化没有事件发生,原因未知可能与 inofity 的内核实现有关

6)ptrace()

ptrace() 是 Linux 的一个系统调用,也是 Linux 下 gdb 等调试器实现的基础。它提供了 Linux 下一个进程跟踪另一个进程寄存器、内存等的能力。

由于 ptrace() 到一个线程后,任何信号都将导致线程STOP 并将控制权交由调用者 ,因此如果利用每个线程只能有一个ptrace跟踪 来防止附加的话,需要处理好这个关系:

while (waitpid (g_childPid, &stat, 0) ) {

if (WIFEXITED (stat) || WIFSIGNALED(stat)) {

XXX_DEBUG_LOG ("waitpid : child died\n");

exit (11);

}

ptrace (PTRACE_CONT, g_childPid, NULL, NULL);

}

7)多进程的反调试实现

写了份 demo : GITHUB

浅谈android反调试之轮询TracePid&lpar;解决方案是特色&rpar;

参考文章: 1.  http://bbs.pediy.com/thread-207538.htm 2.  http://www.wjdiankong.cn/android 需求: 常见的Android ...

浅谈android反调试之 转发端口

反调试方案: 我们最通常使用的动态工具是IDA, IDA的动态调试端口默认为23946,我们可以通过/pro/net/tcp 查看android 系统所有TCP Socket 启动android_se ...

浅谈android反调试之 API判断

反调试:利用Java层API来判断Android程序是否是处于被调试下. 1.直接调用系统的android.os.Debug.isDebuggerConnected()方法 我们进行动态调试的时候,其 ...

浅谈Android反调试 之 PTRACE&lowbar;TRACEME

反调试原理: 关于Ptrace:  http://www.cnblogs.com/tangr206/articles/3094358.html ptrace函数 原型为:     #include & ...

浅谈android反调试之 签名校验

反调试原理 很多时候,我们都需要进行修改修改应用程序的指令,然后重打包运行,重新打包就需要充签名. 利用签名的变化我们用于反调试.反调试实现代码如下: 为了更加隐藏,比较函数可能在SO层进行实现,如下 ...

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

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

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

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

Android反编译调试源码

Android反编译调试源码 1. 反编译得到源码 直接在windows 命令行下输入命令java -jar apktool_2.0.0.jar d -d 小米运动_1.4.641_1058.apk ...

华为手机内核代码的编译及刷入教程【通过魔改华为P9 Android Kernel 对抗反调试机制】

0x00  写在前面 攻防对立.程序调试与反调试之间的对抗是一个永恒的主题.在安卓逆向工程实践中,通过修改和编译安卓内核源码来对抗反调试是一种常见的方法.但网上关于此类的资料比较少,且都是基于AOSP ...

随机推荐

iHover – 30&plus; 纯 CSS 实现的超炫的图片悬停特效

iHover 是一个令人印象深刻的图片悬停效果集合,完全基于 CSS3 实现,无依赖,能够搭配 Bootstrap 3 很好地工作.基于 SCSS 技术构建,便于修改变量.有模块化的代码,无需包含整个 ...

R语言 常见模型

转自 雪晴网 [R]如何确定最适合数据集的机器学习算法 抽查(Spot checking)机器学习算法是指如何找出最适合于给定数据集的算法模型.本文中我将介绍八个常用于抽查的机器学习算法,文中还包括各 ...

百度地图 获取矩形point

http://developer.baidu.com/map/jsdemo.htm#f0_7  鼠标绘制点线面

使用Groovy进行依赖注入

为什么选择Groovy? 传统的依赖注入是XML,对我而言,可读性太差,太不美观,强烈地想换一个方式进行依赖注入,Groovy作为XML的替代方案,在Spring4之后被引入,是基于JVM的一门方言, ...

poj 2762 Going from u to v or from v to u&quest; &lpar;推断它是否是一个薄弱环节图&rpar;

意甲冠军:给定一个有向图有m单向边缘.免费推断是否两点起来(a可以b要么b可以a或最多彼此),该请求 弱联通重量. 算法: 缩点求强连通分量.然后又一次建图.推断新图是否是一条单链,即不能分叉,假设分 ...

SQL查询中连接--学习

一.开头说 不出意外,还是先说下SQL中所有的联接类型: 内连接.外连接(左连接.右连接 .全连接).交叉连接 然后接下来就是依次学习下各种连接的使用效果 二.各种连接秀 首先准备两张表   学生表: ...

MySQL高级知识(五)——索引分析

前言:前面已经学习了explain(执行计划)的相关知识,这里利用explain对索引进行优化分析. 0.准备 首先创建三张表:tb_emp(职工表).tb_dept(部门表)和tb_desc(描述表 ...

Maven SSH三大框架整合的加载流程

此课程中讲 SSH三大框架整合的加载流程,还可以,初步接触的朋友可以听一听. < \day02视频\ ...

BZOJ4127Abs——树链剖分&plus;线段树

题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...

java动手动脑2

仔细阅读示例: EnumTest.java,运行它,分析运行结果? 你能得到什么结论?你掌握了枚举类型的基本用法了吗? 运行结果: 第一个false是判断s和t是否引用同一个对象,第二个false是判 ...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值