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 <stdlib.h>
#include <stdio.h>
#include <elf.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
void checkBreakPoint ();
unsigned long getLibAddr (const char *lib);
#define LOG_TAG "ANTIDBG_DEMO"
#include <android/log.h>
#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反调试笔记的更多相关文章
- 浅谈android反调试之轮询TracePid(解决方案是特色)
参考文章: 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_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 ...
随机推荐
- Jsop的原理
Jsop的原理:利用script不存在跨域的问题,动态创建script标签,把需要请求的数据源地址赋值给其src属性,并且指定一个回调函数,从而接受到我们想要的数据
- ffmpeg+libmp3lame库源码安装教程(CentOS)
lame--libmp3lame的安装包,支持MP3编码:yasm--NASM的重写,用于编译ffmpeg. 1.下载 ffmpeg下载链接:http://ffmpeg.org/download.ht ...
- Tomcat和JDK版本的对应关系
当我们在谈论Tomcat与JDK版本的对应关系的时候,我们实际上在讨论两个问题. 第一个是,我们想安装了某个版本的Tomcat(比如Tomcat7),需要安装哪个版本的JDK,才能把Tomcat运行起 ...
- 查验身份证 (15 分) 一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下: 首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};然后将计算的和对11取模得到值Z;最后按照以下关系对应Z值与校验码M的值:
// test4.cpp : 此文件包含 "main" 函数.程序执行将在此处开始并结束.// #include "pch.h"#include <ios ...
- python-time,datetime
1.time模块:time.time() # 返回当前时间的时间戳(1970纪元后经过的浮点秒数)time.localtime([ sec ]) # 接收时间戳(默认为当前时间),返回struct_t ...
- matlab plot line settings
- ueeditor 百度编译器使用onchange效果
<script id="editor" type="text/plain" style="width:100%;height:200px;&qu ...
- LY.JAVA面向对象编程.内部类
2018-07-18 10:14:48 /* 内部类概述: 把类定义在其他类的内部,这个类就被称为内部类. 举例:在类A中定义了一个类B,类B就是内部类. 内部的访问特点: A:内部类可以直接访问外部 ...
- Python自然语言处理---信息提取
1.数据 目前的数据总体上分为结构化和非结构化的数据.结构化的数据是指实体和关系的规范和可预测的组织.大部分的需要处理的数据都属于非结构化的数据. 2.信息提取 简言之就是从文本中获取信息意义的方法. ...
- Unity运行错误代码处理
1.Unity在运行时出现如图错误,但不影响运行效果展示. 2.错误原因:代码不规范. 3.检查代码,查看变量是否定义正确.