Android下反调试与反反调试

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

反调试检测

反调试检测的几种方式。

1. TrackerId

首先,通过 IDA Pro 的调试器附加到当前 app 进程
image.png
关于IDA Pro调试android app的详细教程可以参考这篇文章【使用IDA Pro动态调试Android APP】

使用 top 命令查看进程状态

top | grep com.cyrus.example17305 u0_a137      10 -10 4.8G 104M  42M t  0.0   1.8   0:02.02 com.cyrus.example

在输出中,S 表示进程状态,17305 是 PID。

通过head /proc/[pid]/status 可以查看详细的进程状态。

head -n 6 /proc/17305/statusName:   m.cyrus.example
State:  S (sleeping)
Tgid:   17305
Pid:    17305
PPid:   728
TracerPid:      16208

TracerPid: 16208 说明当前的进程正在被进程 16208 调试或跟踪,否则没有被调试值应该为0。

2. stat

这时我们断点调试 app
image.png

再通过 head /proc/[pid]/status 可以查看详细的进程状态,包括是否被调试等信息。

head -n 6 /proc/17305/statusName:   m.cyrus.example
State:  t (tracing stop)
Tgid:   17305
Pid:    17305
PPid:   728
TracerPid:      16208

在输出中,t (tracing stop) 表示 app 停止(被调试或其他暂停)

3. wchan

使用 cat 命令查看 /proc/[pid]/wchan 文件,该文件显示进程当前正在等待的内核函数。

cat /proc/17305/wchanptrace_stop

在输出中,ptrace_stop 表示进程 17305 当前正在被调试器暂停,等待调试器发出的命令。

4. android app 反调试检测

在 Android app 中实现反调试检测

package com.cyrus.example.antidebugimport android.os.Bundle
import android.os.Debug
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.cyrus.example.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
import java.io.IOException
import java.net.InetSocketAddress
import java.net.Socketclass AntiDebugActivity : AppCompatActivity() {private val TAG = "AntiDebug"private lateinit var debugInfoTextView: TextViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_anti_debug)// 绑定 TextViewdebugInfoTextView = findViewById(R.id.debugInfoTextView)// 使用协程来执行调试检测CoroutineScope(Dispatchers.Main).launch {val debugInfo = checkDebugInfo()// 将调试信息显示到 TextViewdebugInfoTextView.text = debugInfo// 打印调试信息到日志Log.d(TAG, debugInfo)}}// 检查所有的调试信息private suspend fun checkDebugInfo(): String {val debugInfoBuilder = StringBuilder()val debuggerConnected = isDebuggerConnected()val waitingForDebugger = isWaitingForDebugger()// 获取TrackerId(TracerPid)val tracerPid = hasTracerPid()// 从 /proc/self/stat 获取调试状态val debugStatus = getProcStatStatus()// 获取wchan trace标识val wchanStatus = getWchanStatus()// 检测 JDWP 端口时使用协程的 IO 线程val jdwpDetected = withContext(Dispatchers.IO) {detectJDWP()}debugInfoBuilder.append("Debugging Information:\n")debugInfoBuilder.append("Debugger Connected: ").append(debuggerConnected).append("\n")debugInfoBuilder.append("Waiting for Debugger: ").append(waitingForDebugger).append("\n")debugInfoBuilder.append("JDWP Port (Debugger Attached): ").append(jdwpDetected).append("\n")debugInfoBuilder.append("TracerPid: ").append(tracerPid).append("\n")debugInfoBuilder.append("状态: ").append(debugStatus).append("\n")debugInfoBuilder.append("Wchan 状态: ").append(wchanStatus).append("\n")if (debuggerConnected || waitingForDebugger || tracerPid != 0 || jdwpDetected|| debugStatus == "停止(可能是被调试状态)" || wchanStatus.contains("trace")) {debugInfoBuilder.append("\nApp is being debugged!\n")} else {debugInfoBuilder.append("\nApp is not being debugged.\n")}return debugInfoBuilder.toString()}// 方法 1: 使用 Debug.isDebuggerConnected()private fun isDebuggerConnected(): Boolean {return Debug.isDebuggerConnected()}// 方法 2: 检查 Debug.waitingForDebugger()private fun isWaitingForDebugger(): Boolean {return Debug.waitingForDebugger()}// 方法 3: 返回 TracerPid 的值private fun hasTracerPid(): Int {try {BufferedReader(FileReader("/proc/self/status")).use { reader ->var line: String?while (reader.readLine().also { line = it } != null) {if (line!!.startsWith("TracerPid:")) {return line!!.split(":")[1].trim().toInt()}}}} catch (e: IOException) {e.printStackTrace()}return 0 // 如果没有找到 TracerPid,返回 0 表示没有被调试}// 方法 4: 检测调试端口(JDWP),在后台线程中运行private fun detectJDWP(): Boolean {return try {Socket().use { socket ->socket.connect(InetSocketAddress("127.0.0.1", 8700), 1000)}true} catch (e: IOException) {// 没有调试器连接false}}// 从 /proc/self/wchan 获取进程的等待状态private fun getWchanStatus(): String {try {// 读取 /proc/self/wchan 文件val wchanFile = File("/proc/self/wchan")if (wchanFile.exists()) {return wchanFile.readText().trim()}} catch (e: Exception) {e.printStackTrace()}return "无法获取 Wchan 状态"}// 解析 /proc/self/stat 获取进程状态private fun getProcStatStatus(): String {try {// 读取 /proc/self/stat 文件val statFile = File("/proc/self/stat")val statContent = statFile.readText()// /proc/self/stat 的内容格式是以空格分隔的字段// 第3个字段是进程状态val statFields = statContent.split(" ")if (statFields.size > 2) {val processState = statFields[2] // 进程状态字段return when (processState) {"R" -> "运行中""S" -> "睡眠中""D" -> "不可中断睡眠中""T" -> "停止(可能是被调试状态)""Z" -> "僵尸进程"else -> "未知状态: $processState"}}} catch (e: Exception) {e.printStackTrace()}return "无法获取调试状态"}}

当调试器附加到 app
image.png

源码地址:https://github.com/CYRUS-STUDIO/AndroidExample

修改Android内核,绕过反调试

1. TrackerId

编辑 fs/proc/array.c

修改tpid(TrackerId)固定返回0
image.png

2. stat

编辑 fs/proc/array.c

直接把状态标识T(stopped)和t(tracing stop),修改为S(sleeping)

static const char * const task_state_array[] = {"R (running)",    /*   0 */"S (sleeping)",       /*   1 */"D (disk sleep)",  /*   2 */"T (stopped)",    /*   4 */"t (tracing stop)",    /*   8 */"X (dead)",       /*  16 */"Z (zombie)",     /*  32 */
};

修改后

static const char * const task_state_array[] = {"R (running)",    /*   0 */"S (sleeping)",       /*   1 */"D (disk sleep)",  /*   2 */"S (sleeping)",       /*   4 */"S (sleeping)",    /*   8 */"X (dead)",       /*  16 */"Z (zombie)",     /*  32 */
};

3. wchan

编辑 fs/proc/base.c

修改 proc_pid_wchan 函数,去掉 trace 检测标识

static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,struct pid *pid, struct task_struct *task)
{unsigned long wchan;char symname[KSYM_NAME_LEN];wchan = get_wchan(task);if (wchan && ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)&& !lookup_symbol_name(wchan, symname))seq_printf(m, "%s", symname);elseseq_putc(m, '0');return 0;
}

修改后

static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,struct pid *pid, struct task_struct *task)
{unsigned long wchan;char symname[KSYM_NAME_LEN];wchan = get_wchan(task);if (wchan && ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)&& !lookup_symbol_name(wchan, symname))// 判断 symname 中是否包含 "trace"if (strstr(symname, "trace")) {// sys_epoll_wait 是内核中实现 epoll_wait 系统调用的具体函数。// 这个表示进程正在等待 ep_poll 函数(通常与 I/O 事件相关)。seq_printf(m, "%s", "sys_epoll_wait");} else {seq_printf(m, "%s", symname);}elseseq_putc(m, '0');return 0;
}

修改 ro.debugble 使全局可调试

编辑 device/{vendor}/{device}/common_prop.mk

找到 ro.debuggable 这一行。如果没有这一行,你可以手动添加

# Debug
PRODUCT_PROPERTY_OVERRIDES += \ro.debuggable=1 \

修改完成后,你就可以调试设备上所有 app 了。

编译和刷新系统

关于如何编译和刷新系统可以参考这篇文章【使用 release key 对 LineageOS 进行编译和签名】

测试

打开 IDA Pro 的调试器附加到当前 app 进程并 Pause process 。

通过命令行读取进程 TracerPid、State 和 wchan 信息检测是否修改成功。

adb shell# 查看进程信息
top | grep com.cyrus.example6780 root         20   0  32M 1.4M 1.0M S  0.0   0.0   0:00.00 grep com.cyrus.example5256 u0_a137      10 -10 4.9G 105M  43M S  0.0   1.8   0:02.69 com.cyrus.example# 查看进程状态
head -n 6 /proc/5256/status
Name:   m.cyrus.example
State:  S (sleeping)
Tgid:   5256
Pid:    5256
PPid:   738
TracerPid:      0# 查看wchan状态   
wayne:/ # cat /proc/5256/wchan
sys_epoll_waitwayne:/ # cat /proc/5256/wchan
SyS_epoll_wait

通过APP读取 TracerPid、State 和 wchan 信息,检测是否修改成功。
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/144069.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

CS61C 2020计算机组成原理Lecture 04

1. C Memory Layout 1.1 Where Do the Variables Go? 1.2 The Stack 1.2.1 Stack misuse example gpt4解释: 在C语言中,从函数返回指向本地变量的指针是不安全的,原因在于局部变量(本地变量)的生命周期。当一个函数被…

全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类

全网最适合入门的面向对象编程教程:50 Python 函数方法与接口-接口和抽象基类 摘要: 在 Python 中,接口和抽象基类(Abstract Base Classes, ABCs)都用于定义类的结构和强制子类实现特定的方法,Python 没有…

5.10 创建内核目录

首先是 创建内核的目录。 修改 cmake 然后是 创建 cmake , kernel 目录下的init 目录, init 目录下的start.s 文件,再init目录下 创建init.c init.h 文件。 然后是修改cmake 然后是 start.S 的编写 然后是 创建 init.c init.h 文件。 然后是 修改 img…

如何在WordPress中添加事件Schema(分步指南)

如果你正在举办一个在线活动,那么你可能正在寻找通过网络宣传的方法。此时,模式标记可以帮助你在搜索引擎结果中提高活动的可见性。 活动模式将帮助谷歌和其他搜索引擎更好地理解你的活动详情,使它们能够在活动列表、丰富摘要和谷歌知识面板…

通过markdown表格批量生成格式化的word教学单元设计表格

素材: 模板: 代码: import pandas as pd from python_docx_replace import docx_replace,docx_get_keys from docx import Document from docxcompose.composer import Composerdef parse_markdown_tables(file_path):with open(file_path,…

华为昇腾智算中心-智算中心测试方案与标准

本方案是企业内训课程《华为昇腾智算中心深度技术研修》的一部分授课课件的样例。方案内容中详细阐述了华为昇腾环境下智算中心的测试方案和标准,以确保硬件和软件系统在实际部署和运行中的高效性和稳定性。主要内容包括集群硬件清单、节点拓扑配置以及环境配置。硬…

MySQL数据库迁移与备份实录

这里写目录标题 事情起因的概述查看磁盘空间使用情况为了进一步的明确宕机原因,查看MySQL日志信息进一步排查 如何针对磁盘空间不足进行挂载区域的修改以及数据的迁移与备份分析与梳理如何修改MySQL数据卷的挂载位置停止MySQL服务备份 MySQL 配置文件迁移 MySQL 数据…

info 命令:查看命令手册

一、命令简介 在 Linux 系统中,可以使用 man​ 查看普通的帮助手册。还可以使用 info​ 命令阅读 Info 格式的文档。 ​info​ 文档的特点:大量使用超链接,通过方向键将光标移动到链接的文字,按下回车键,就可以切换到…

新发布的OpenAI o1生成式AI模型在强化学习方面迈出了重要的一步

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

ROS第五梯:ROS+VSCode+C++单步调试

解决问题:在ROS项目中进行断点调试。 第一步:创建一个ROS项目或者打开一个现有的ROS项目。 第二步:修改c_cpp_properties.json 增加一段命令: "compileCommands": "${workspaceFolder}/build/compile_commands.json"第三…

[Python]案例驱动最佳入门:Python数据可视化在气候研究中的应用

在全球气候问题日益受到关注的今天,气温变化成为了科学家、政府、公众讨论的热门话题。然而,全球气温究竟是如何变化的?我们能通过数据洞察到哪些趋势?本文将通过真实模拟的气温数据,结合Python数据分析和可视化技术&a…

【诉讼流程-健身房-违约-私教课-诉讼书提交流程-民事诉讼-自我学习-铺平通往法律的阶梯-讲解(3)】

【诉讼流程-健身房-违约-私教课-诉讼书提交流程-民事诉讼-自我学习-铺平通往法律的阶梯-讲解(3)】 1、前言说明2、流程说明3、现场提交(线下)4、网上提交1-起诉书样例2-起诉书编写(1)原告信息:&…

如何将MySQL卸载干净(win11)

相信点进来的你肯定是遇到了这个问题,那就是在安装MySQL的时候操作错误,最后结果不是自己想要的。卸载重新安装又发现安装不了。其实最主要的原因就是没有将MySQL卸载干净,那么如何把MySQL卸载干净?下面本篇文章就来给大家一步步介…

sensitive-word 敏感词 v0.20.0 数字全部匹配,而不是部分匹配

敏感词系列 sensitive-word-admin 敏感词控台 v1.2.0 版本开源 sensitive-word-admin v1.3.0 发布 如何支持分布式部署? 01-开源敏感词工具入门使用 02-如何实现一个敏感词工具?违禁词实现思路梳理 03-敏感词之 StopWord 停止词优化与特殊符号 04-…

Matlab进行频率切片小波变换

Matlab进行频率切片小波变换(FSWT)源代码,将一维信号生成时频图。 输入信号可以是任何一维信号,心电信号、脑电信号、地震波形、电流电压数据等。 相比连续小波变换(CWT),频率切片小波变换(Frequency Slice Wavelet Transform,FSWT)是一种更具…

计算机毕业设计hadoop+spark知网文献论文推荐系统 知识图谱 知网爬虫 知网数据分析 知网大数据 知网可视化 预测系统 大数据毕业设计 机器学习

《HadoopSpark知网文献论文推荐系统》开题报告 一、研究背景及意义 随着互联网技术的迅猛发展和大数据时代的到来,学术文献的数量呈爆炸式增长,用户面临着严重的信息过载问题。如何高效地从海量文献中筛选出用户感兴趣的论文,成为当前学术界…

涛思数据库安装和卸载

安装 cd opt/taos/TDengine-server-2.4.0.5 sudo ./install.sh 启动taos​ 安装后,请使用 systemctl 命令来启动 TDengine 的服务进程 systemctl start taosd检查服务是否正常工作: systemctl status taosd 升级 3.0 版在之前版本的基础上&#x…

Parasoft助力Joby Aviation符合DO-178B标准

Joby Aviation,这家成立于2009年的美国高科技企业,以其对电动垂直起降(eVTOL)技术的深刻洞察与不懈追求,正引领着全球空中出行领域的革新。作为该领域的先驱者,Joby Aviation专注于研发并商业化运营其革命性…

蓝桥杯嵌入式客观题合集

十四届模拟赛二客观题 解析:STM32微控制器的I/O端口寄存器必须按32位字被访问 解析:微分电路能将三角波转换为方波;积分电路能将方波转换为三角波 解析:放大电路的本质是能量的控制与转换 解析:具有n个节点&#xff0c…

Ansible——Playbook基本功能???

文章目录 一、Ansible Playbook介绍1、Playbook的简单组成1)“play”2)“task”3)“playbook” 2、Playbook与ad-hoc简单对比区别联系 3、YAML文件语法:---以及多个---??使用 include 指令 1. 基本结构2. 数…