[转载]Android Native反调试

本文转载自: http://www.zhaoxiaodan.com/java/android/android-native%E5%8F%8D%E8%B0%83%E8%AF%95.html

思考

之前研究了下如何调试和尝试反一个别人加密的东西, 所以现在的体会就是:

其实重点不是你如何加密, 重点是如何不让别人知道你怎么加密的

因为像这种自己加密的资源运行的时候自己解密之后拿来用的程序, 我甚至根本不用关心你到底怎么加密, 加密算法是啥, 我只需要知道, 你解密完了之后, 那个资源的内存块在哪, 写个dumper就全拿到了;

加密并不能防止被破解, 只是增加破解的难度和门槛, 加密解密是一个相互博弈的过程

把资源加个密, 那么对于那些技术比较初级, 又想简单拷贝的人, 他们就拿你没办法了; 但是对于那些懂一点原理懂一点IDA的人, 并没有什么卵用

那么好, 现在我要研究一下, 怎么样再增加那么一点点门槛

之前解密的一个重要前提就是调试, 所以, 最直接想到的增加的门槛就是反调试.

测试

先建个android 工程, 并加入native支持

android-anti-debug

先打出pid和父pid出来看看

#include <jni.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <unistd.h>

#include "log.h"

void anti_debug()
{
    int pid = getpid();
    int ppid = getppid();

    LOGD("pid:%d,ppid:%d",pid,ppid);

}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    anti_debug();
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
    {
        return -1;
    }

    return JNI_VERSION_1_6;
}
06-11 06:42:24.411: D/[android-anti-debug](1451): pid:1451,ppid:192

pid 192 是:

root      192   1     492204 38400 ffffffff b756598c S zygote

在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的,这也许就是为什么要把它称为Zygote(受精卵)的原因吧

具体参见:Android系统进程Zygote启动过程的源代码分析

看下 /proc/1451/stat

[email protected]:/data/local/tmp # cat /proc/17204//status
Name:   ndroidantidebug
State:  S (sleeping)
Tgid:   17204
Pid:    17204
PPid:   2146
TracerPid:  0
Uid:    10058   10058   10058   10058
Gid:    10058   10058   10058   10058
FDSize: 256
...

用gdbserver 去attach一下看看发生什么:

[email protected]:/data/local/tmp # ps |grep blog
u0_a58    30678 2146  907884 59720 ffffffff 4005778c S com.zhaoxiaodan.blog.androidantidebug
[email protected]:/data/local/tmp # ./gdbserver --attach 127.0.0.1:1234 30678
Attached; pid = 30678
Listening on port 1234

[email protected]:/data/local/tmp # cat /proc/17204//status
Name:   ndroidantidebug
State:  t (tracing stop)
Tgid:   17204
Pid:    17204
PPid:   2146
TracerPid:  20337
Uid:    10058   10058   10058   10058
Gid:    10058   10058   10058   10058

发现TracerPid行由0 变为了 20337

ida这些调试工具其实都是使用ptrace进行的, ptrace有一个很重要的特定:

一个进程只能被一个进程调试。

所以, 最简单的办法就是在JNI_OnLoad里直接ptrace(PTRACE_TRACEME, 0, 0, 0);

方法1, 直接ptrace(PTRACE_TRACEME, 0, 0, 0);

#include <jni.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <unistd.h>

#include "log.h"

void anti_debug()
{
    ptrace(PTRACE_TRACEME, 0, 0, 0);
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    anti_debug();
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
    {
        return -1;
    }

    return JNI_VERSION_1_6;
}

然后再用gdbserver 去attach:

[email protected]:/data/local/tmp # ./gdbserver --attach 127.0.0.1:1234 31092
Cannot attach to lwp 31092: Operation not permitted (1)

Exiting

好了, 挂不上了

但是这种方法, 用反编译打开, 很容易就找到调用ptrace的地方, 不知道修改下汇编指令(比如改为nilnil), 就跳过这个调用了

方法2, 暗桩

根据上面说的/proc/$pid/statusTracerPid行显示调试程序的pid的原理, 可以写一个方法检查下这个值, 如果!=0就退出程序

检查函数如下:

void be_attached_check()
{
    try
    {
        const int bufsize = 1024;
        char filename[bufsize];
        char line[bufsize];
        int pid = getpid();
        sprintf(filename, "/proc/%d/status", pid);
        FILE* fd = fopen(filename, "r");
        if (fd != nullptr)
        {
            while (fgets(line, bufsize, fd))
            {
                if (strncmp(line, "TracerPid", 9) == 0)
                {
                    int statue = atoi(&line[10]);
                    LOGD("%s", line);
                    if (statue != 0)
                    {
                        LOGD("be attached !! kill %d", pid);
                        fclose(fd);
                        int ret = kill(pid, SIGKILL);
                    }
                    break;
                }
            }
            fclose(fd);
        } else
        {
            LOGD("open %s fail...", filename);
        }
    } catch (...)
    {

    }

}

可以把这个函数搞成一个宏, 然后写个程序随机的把这个宏插入到源码的各个地方, 随着代码的不断执行, 会遇到各个这样的检查点

其实也没什么卵用, 只不过桩子多了, 你拔起来就麻烦点咯

下面这个只是用线程模拟检查的过程:

#include "android-anti-debug.h"
#include <string>
#include <sys/ptrace.h>
#include <unistd.h>
#include <stdlib.h>
#include <chrono>
#include <thread>
#include "log.h"

void be_attached_check()
{
    try
    {
        const int bufsize = 1024;
        char filename[bufsize];
        char line[bufsize];
        int pid = getpid();
        sprintf(filename, "/proc/%d/status", pid);
        FILE* fd = fopen(filename, "r");
        if (fd != nullptr)
        {
            while (fgets(line, bufsize, fd))
            {
                if (strncmp(line, "TracerPid", 9) == 0)
                {
                    int statue = atoi(&line[10]);
                    LOGD("%s", line);
                    if (statue != 0)
                    {
                        LOGD("be attached !! kill %d", pid);
                        fclose(fd);
                        int ret = kill(pid, SIGKILL);
                    }
                    break;
                }
            }
            fclose(fd);
        } else
        {
            LOGD("open %s fail...", filename);
        }
    } catch (...)
    {

    }

}

//检查线程, 每秒检查一下
void thread_task(int n)
{
    while (true)
    {
        LOGD("start be_attached_check...");
        be_attached_check();
        std::this_thread::sleep_for(std::chrono::seconds(n));
    }
}

void anti_debug()
{
//  ptrace(PTRACE_TRACEME, 0, 0, 0);
    auto checkThread = std::thread(thread_task, 1);
    checkThread.detach();
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    anti_debug();
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
    {
        return -1;
    }

    return JNI_VERSION_1_6;
}
时间: 2024-08-23 21:28:43

[转载]Android Native反调试的相关文章

[转载]Android开发常用调试技术记录

ANDROID 调试技术: 1)Ps 指令 ls –l /proc/27/ cat /proc/27/cmdline       #cmdline文件表示了这个进程所在的命令行. cat /proc/27/status      #status 文件表明了获知这个进程的相关信息. #stat包含更多信息,但较难读. cat /proc/27/task      #task 文件表明了进程信息. 2)vmstat 查看虚拟内存统计信息 3)top 统计CPU的消耗情况 4)dumpstat  du

修改android内核反调试标志

第一处 msm/fs/proc/base.c文件 273行 proc_pid_wchan 函数修改为(主要对285行进行了修改) static int proc_pid_wchan(struct task_struct *task, char *buffer) { unsigned long wchan; char symname[KSYM_NAME_LEN]; wchan = get_wchan(task); if (lookup_symbol_name(wchan, symname) < 0

Android程序反破解技术

0x1 对抗反编译可以大概分为以下几点: 1.对抗反编译工具,例如apktool.dex2jar等 2.对抗静态编译,分为代码混淆技术.NDK保护.apk加壳保护. 3.对抗动态调试,分为检测调试器.检测模拟器. 4.防止重编译,分为检查签名,校验保护. 更多相关内容请参考<Android软件安全与逆向分析>. 0x02 对抗反编译工具,例如dex2jar,通常是在分析dex2jar源码后,来找到漏洞,在java源码中加入会触发dex2jar异常的代码,从而使dex2jar不能正常使用. de

[转载] Android逃逸技术汇编

本文转载自: http://blogs.360.cn/360mobile/2016/10/24/android_escape/ 摘    要 传统逃逸技术涉及网络攻防和病毒分析两大领域,网络攻防领域涉及的逃逸技术主要为网络入侵逃逸技术,病毒分析领域涉及到的逃逸技术主要包括针对静态分析.动态分析的木马逃逸技术. 本文介绍的Android木马逃逸技术研究了针对用户感知.杀软查杀.沙箱动态养殖和人工分析的各种逃逸技术. 大多数Android木马的作恶途径是长期留存用户终端,通过持续性作恶获取收益. 为

[转载]Android应用方法隐藏及反调试技术浅析

本文转载自: http://drops.wooyun.org/tips/9471 0x00 前言 Android应用的加固和对抗不断升级,单纯的静态加固效果已无法满足需求,所以出现了隐藏方法加固,运行时动态恢复和反调试等方法来对抗,本文通过实例来分析有哪些对抗和反调试手段. 0x01 对抗反编译 首先使用apktool进行反编译,发现该应用使用的加固方式会让apktool卡死,通过调试apktool源码(如何调试apktool可参见前文<Android应用资源文件格式解析与保护对抗研究>),发

Android Native一处反调试

之前转载了一篇文章介绍了两种反调试方式,分别是ptrace自身和查看TracerPid信息,文章地址: http://www.cnblogs.com/dacainiao/p/5124151.html 这一处反调试是在调试某加固时遇到的,原理是查看当前所有的tcp连接,如果有<00000000:5D8A>就退出,也就是本地连接的23946端口(IDA的默认监听端口). 当然,解决办法可以Path掉该处的反调试或者修改IDA的客户端把默认的监听端口改成其他的. 反调试的方法还有很多,只是分析的样本

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

0x1.手机设备环境 Model number: Nexus 5 OS Version: Android 4.4.4 KTU84P Kernel Version: 3.4.0-gd59db4e 0x2.Android内核提取 查找Android设备的boot分区文件.高通芯片的设备可以通过下面的命令进行查找. cd /home/androidcode/AndroidDevlop/modifyNexus5Boot adb shell ls -al /dev/block/platform/msm_s

浅谈Android反调试 之 PTRACE_TRACEME

反调试原理: 关于Ptrace:  http://www.cnblogs.com/tangr206/articles/3094358.html ptrace函数 原型为: #include <sys/ptrace.h>long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);ptrace有四个参数:  1). enum __ptrace_request request:指示了ptrace要执行的命

Android逆向之旅---应用的&quot;反调试&quot;方案解析(附加修改IDA调试端口和修改内核信息)

一.前言 在前一篇文章中详细介绍了Android现阶段可以采用的几种反调试方案策略,我们在破解逆向应用的时候,一般现在第一步都回去解决反调试,不然后续步骤无法进行,当然如果你是静态分析的话获取就没必要了.但是有时候必须要借助动态调试方可破解,就需要进行操作了.现阶段反调试策略主要包括以下几种方式: 第一.自己附加进程,先占坑,ptrace(PTRACE_TRACEME, 0, 0, 0)!第二.签名校验不可或缺的一个选择,本地校验和服务端校验双管齐下!第三.借助系统api判断应用调试状态和调试属