jni动态注册/轮询traceid/反调试学习笔记

huaweiP9 会给主动给app加一个trace进程

通过学习四哥的

Android安全防护之旅—Android应用”反调试”操作的几种方案解析

我们可以知道通过

第一、自己附加进程,先占坑,ptrace(PTRACE_TRACEME, 0, 0, 0)!

第二、签名校验不可或缺的一个选择,本地校验和服务端校验双管齐下!

第三、借助系统api判断应用调试状态和调试属性,最基础的防护!

第四、轮训检查android_server调试端口信息和进程信息,防护IDA的一种有效方式!

第五、轮训检查自身status中的TracerPid字段值,防止被其他进程附加调试的一种有效方式!

的方式来进行反调试

四哥也放出了源码地址
https://github.com/fourbrother/android_anti_debug

但是该源码在编译时报过时异常,其实就是AndroidMake方式过时了,要求用Cmake。
该篇笔记用Cmake方式进行操作学习(源码地址在文末)
本质就是把c代码实现改为c++实现。

准备工作

1.基本的jni开发知识
可以参考
android ndk开发索引
对ndk开发有一个基本了解,比如返回值类型,jni如何调用java方法等

2.so库加载的基本概念
可以参考
Android逆向新手答疑解惑篇——JNI与动态注册

JNI解析以及在Android中的实际应用
了解加载流程。

正式动工

首先当做你已经明白了so加载为什么会走jni_onLoad函数,
实在不懂就当作 重写了onCreate方法

1.自己提前附加一个进程
很简单,一行代码

ptrace(PTRACE_TRACEME, 0, 0, 0);

2.校验签名
纯java方法的

public static String getSignature() {
        Context ctx = MyApplication.getContext();
        try {
            //通过包管理器获得指定包名包含签名的包信息
            PackageInfo packageInfo = ctx.
                    getPackageManager()
                    .getPackageInfo(ctx.getPackageName(),
                            PackageManager.GET_SIGNATURES);
            // 通过返回的包信息获得签名数组
            Signature[] signatures = packageInfo.signatures;
            // 循环遍历签名数组拼接应用签名
            StringBuilder builder = new StringBuilder();
            for (Signature signature : signatures) {
                builder.append(signature.toCharsString());
            }
            // 得到应用签名
            return builder.toString();
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return "";
    }

jni的(其实就是jni调java方法)

int check_signature(JNIEnv *env) {
    //调用Java层的Utils中的获取签名的方法
    jclass javaUtilClass = env->FindClass("com/example/lahm/ctest/Utils");
    if (javaUtilClass == NULL) {
        LOGD("not find class");
        return JNI_FALSE;
    }

    LOGD("class name:%p", javaUtilClass);

    jmethodID method = (env)->GetStaticMethodID(javaUtilClass, "getSignature",
                                                "()Ljava/lang/String;");
    if (method == NULL) {
        LOGD("not find method '%s'", method);
        return JNI_FALSE;
    }
    jstring obj = (jstring) (env)->CallStaticObjectMethod(javaUtilClass, method);
    if (obj == NULL) {
       LOGD("method invoke error:%p", obj);
        return JNI_FALSE;
    }

    const char *strAry = (env)->GetStringUTFChars(obj, 0);
    int cmpVal = strcmp(strAry, app_signature);
    (env)->ReleaseStringUTFChars(obj, strAry);

    if (cmpVal == 0)
        return JNI_TRUE;
    else
        return JNI_FALSE;
}

3.检查debug状态
类似与第2点,更加简单,java方法就一行。

public static boolean checkIsDebugB() {
        return android.os.Debug.isDebuggerConnected();
    }

还可以去拿ApplicationInfo里的字段,这里不展开了。

4和5.查看调试端口&查看traceid

现在很多的加固方法,门槛就是这个,通过开启一个子线程,读取
/proc/[pid]/status文件,拿到traceid,如果不为0,则认为该app已经被调试了,此时杀死进程。

那么要点在
开启子线程,轮询文件

void create_thread_check_traceid() {
    pthread_t t_id;
    int err = pthread_create(&t_id, NULL, thread_function, NULL);
    if (err != 0) {
        LOGD("create thread fail: %s\n", strerror(err));
    }
}

pthread_create的用法我是参考的
http://blog.csdn.net/liangxanhai/article/details/7767430

轮询文件

void *thread_function(void *argv) {
    int pid = getpid();
    char file_name[20] = {'\0'};
    sprintf(file_name, "/proc/%d/status", pid);
    char linestr[256];
    int i = 0, traceid;
    FILE *fp;
    while (1) {
        i = 0;
        fp = fopen(file_name, "r");
        if (fp == NULL) {
            break;
        }
        while (!feof(fp)) {
            fgets(linestr, 256, fp);
            if (i == 5) {
                traceid = get_number_for_str(linestr);
                LOGD("traceId:%d", traceid);
                if (traceid > 0) {
                    LOGD("I was be traced...trace pid:%d", traceid);
                    exit(0);
                }
                break;
            }
            i++;
        }
        fclose(fp);
        sleep(5);
    }
    return ((void *) 0);
}

如果你已经使用了1方法,自己附加了一个进程,那就不要用轮询了traceid了,改用端口监听吧。

动态注册

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
...
}

jni_onLoad的方法只给了JavaVM环境
需要先获得jni环境

if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
    LOGD("ERROR: GetEnv failed\n");
        return JNI_ERR;
    }

然后再调用RegisterNatives注册,传的参数
clazz 就是native函数所在的类,可提前调用env->FindClass获取,methods是一个数组,其中包含注册信息,nMethods是数量。


JNINativeMethod methods[] = {
            {"isEquals", "(Ljava/lang/String;)Z", (void *) isEquals},
    };
    jclass clazz = env->FindClass("com/example/lahm/ctest/MyApplication");
    if (clazz == NULL) {
        LOGD("Native registration unable to find class '%s'", className);
        return JNI_ERR;
    }
    int methodsLength;
    //建立方法隐射关系
    //取得方法长度
    methodsLength = sizeof(methods) / sizeof(methods[0]);
    if (env->RegisterNatives(clazz, methods, methodsLength) != 0) {
        LOGD("RegisterNatives failed for '%s'", className);
        return JNI_ERR;
    }

完成动态注册,记得要在申明该native方法的类里先写上方法名,
cpp文件里写上同名方法(就不是自动生成的jni方法名),不然找不到。

liblog的调用

因为四哥的代码写了很多的log,所以他写了个宏

#include <android/log.h>

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ceshi", __VA_ARGS__)

但是这样写,无法通过编译
需要在CmakeLists文件里链接上去(这个是官方指导)

find_library( # 设置外部引用库.这个库支持你在c/c++中打印log,具体请见  android/log.h
             log-lib

             # 外部引用库的名称
             log )

target_link_libraries( # 指定被链接的库.
             ctest

             # 链接log-lib到ctest
             ${log-lib} )

#将不同的源文件编译成多份so包
add_library( ccheck SHARED src/main/cpp/ccheck.cpp )
target_link_libraries(ccheck ${log-lib})

至此,完成学习。
完整代码地址
https://github.com/lamster2018/learnNDK

至于以上方法的反制方式
四哥也写了
Android逆向之旅—应用的”反调试”方案解析(附加修改IDA调试端口和修改内核信息

另附上
看雪出品-企业壳反调试及hook检测分析

Android so加固
https://mp.weixin.qq.com/s/BhGxnJrRnrYAEWJ7zDZKmQ

----后续更--
还有一种类似检查签名的方法--检查dex的完整性防二次打包

Android代码保护(签名校验、classes.dex文件完整性校验)

private void checkCRC() {
        String orginalCrc = getString(R.string.str_code);
        ZipFile zf;
        try {
            zf = new ZipFile(getApplicationContext().getPackageCodePath());
            ZipEntry ze = zf.getEntry("classes.dex");
            String strCrc = String.valueOf(ze.getCrc());
            String MD5Crc = MD5Util.GetMD5Code(strCrc);
            Log.e("checkcrc", MD5Crc);
            if (!orginalCrc.equals(MD5Crc)) {
                //ActivityManagerUtil.getScreenManager().removeAllActivity();
                Process.killProcess(Process.myPid());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

文中也提到

"DEX文件打包在APK文件中,针对APK代码的篡改攻击就是针对该文件,比如使用apktool工具反编译文件,修改smali代码。以下就是通过检查classes.dex文件的CRC32值来判断文件是否被篡改。
其中R.string.str_code是存放在本地的crc加密后的值,string.xml文件是不在dex文件中的,所以修改string.xml文件是不会造成crc值的变化(亲测),其实也可以把这个值存放在后台,通过请求网络来获取。"

本质和检查签名是一样的,不过是提供另一种思路。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容