Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
195 views
in Technique[技术] by (71.8m points)

android - Check .apk-signature in C/native Code

I have developed an Android application which also contains a native part written in C (which does not depend on the app).

The application itself is useless if the shared library does not do its work.

I would like to let the native-part (a shared library) only do its work, if there exists an unmodified version of the application (.apk) it has been shipped with.

The best method for me would be this way:

  1. Application get installed
  2. Shared Library checks signature/hash of the application/.apk
  3. Only does its work when the signature matches a known one

In this way, I would like to protect my application from modification and piracy.

Are there any tips for doing this? I just found posts with checking own signature in java, but that's jokeless if one could de- & recompile the app.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As I have been asked to publish some code how I'm now checking the CRC-code of my Java-application from within C, here are some snippets.

I cannot post a complete working solution as it's spread over multiple lines due to performance-reasons, but I hope this is most complete and working:

In your MyApplication.java:

public class MyApplication extends Application {
    private static Context context;

    public static Context getAppContext() {
        return MyApplication.context;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        MyApplication.context = getApplicationContext();
    }
}

Android.mk:

LOCAL_CFLAGS += -O3 -DDEBUG_MODE=0 -DCLASSES_CRC=2331492378

Inside your C-code:

#define LOG_TAG "Your Log Tag"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

#if DEBUG_MODE
#define LOGDH(...) LOGD(__VA_ARGS__)
#define LOGIH(...) LOGI(__VA_ARGS__)
#define LOGEH(...) LOGE(__VA_ARGS__)
#else
#define LOGDH(...) //
#define LOGIH(...) //
#define LOGEH(...) //
#endif

int isSecure = -1;

jclass MyApplication;
jclass Context;
jclass ApplicationInfo;


jclass ZipFile;
jclass ZipEntry;
jclass CheckedInputStream;
jclass Adler32;
jclass Checksum;

jmethodID MyApplication_getAppContextMethodId;
jmethodID Context_getApplicationInfoMethodId;

jmethodID ZipFile_ConstructorMethodId;
jmethodID CheckedInputStream_ConstructorMethodId;
jmethodID Adler32_ConstructorMethodId;

jmethodID ZipFile_getEntryMethodId;
jmethodID ZipFile_getInputStreamMethodId;
jmethodID CheckedInputStream_readMethodId;
jmethodID CheckedInputStream_getChecksumMethodId;
jmethodID Checksum_getValueMethodId;

jfieldID ApplicationInfo_flagsFieldId;
jfieldID ApplicationInfo_FLAG_DEBUGGABLEFieldId;
jfieldID ApplicationInfo_sourceDirFieldId;


static long getClassesCRC(JNIEnv *env) {
    jobject appContextInstance = (*env)->CallStaticObjectMethod(env,
            MyApplication, MyApplication_getAppContextMethodId);
    if (!appContextInstance) {
        LOGEH("Unable to get instance of AppContext");
        return false;
    }

    jobject applicationInfoInstance = (*env)->CallObjectMethod(env,
            appContextInstance, Context_getApplicationInfoMethodId);
    if (!appContextInstance) {
        LOGEH("Unable to get instance of ApplicationInfo");
        return false;
    }

    jobject zipFileInstance = (*env)->NewObject(env, ZipFile,
            ZipFile_ConstructorMethodId,
            (*env)->GetObjectField(env, applicationInfoInstance,
                    ApplicationInfo_sourceDirFieldId));
    if (!zipFileInstance) {
        LOGEH("Unable to get instance of ZipFile");
        return -1;
    }

    jstring classesDexString = (*env)->NewStringUTF(env, "classes.dex");
    jobject zipEntryInstance = (*env)->CallObjectMethod(env, zipFileInstance,
            ZipFile_getEntryMethodId, classesDexString);
    if (!zipFileInstance) {
        LOGEH("Unable to get instance of ZipEntry");
        return -1;
    }
    (*env)->DeleteLocalRef(env, classesDexString);

    jobject adler32Instance = (*env)->NewObject(env, Adler32,
            Adler32_ConstructorMethodId);
    if (!adler32Instance) {
        LOGEH("Unable to get instance of Adler32");
        return -1;
    }

    jobject inputStreamInstance = (*env)->CallObjectMethod(env, zipFileInstance,
            ZipFile_getInputStreamMethodId, zipEntryInstance);
    if (!inputStreamInstance) {
        LOGEH("Unable to get instance of InputStream");
        return -1;
    }

    jobject checkedInputStreamInstance = (*env)->NewObject(env,
            CheckedInputStream, CheckedInputStream_ConstructorMethodId,
            inputStreamInstance, adler32Instance);
    if (!checkedInputStreamInstance) {
        LOGEH("Unable to get instance of CheckedInputStream");
        return -1;
    }

    int bufferSize = 128;
    jbyteArray bufferBytes = (*env)->NewByteArray(env, bufferSize);

    while ((*env)->CallIntMethod(env, checkedInputStreamInstance,
            CheckedInputStream_readMethodId, bufferBytes) > 0) {
    }
    (*env)->DeleteLocalRef(env, bufferBytes);

    jobject checksumInstance = (*env)->CallObjectMethod(env,
            checkedInputStreamInstance, CheckedInputStream_getChecksumMethodId);
    if (!checksumInstance) {
        LOGEH("Unable to get instance of CheckSum");
        return -1;
    }

    return (*env)->CallLongMethod(env, checksumInstance,
            Checksum_getValueMethodId);
}

static bool isDebuggable(JNIEnv *env) {
    jobject appContextInstance = (*env)->CallStaticObjectMethod(env,
            MyApplication, Application_getAppContextMethodId);
    if (!appContextInstance) {
        LOGEH("Unable to get instance of AppContext");
        return false;
    }

    jobject applicationInfoInstance = (*env)->CallObjectMethod(env,
            appContextInstance, Context_getApplicationInfoMethodId);
    if (!appContextInstance) {
        LOGEH("Unable to get instance of ApplicationInfo");
        return false;
    }

    int FLAG_DEBUGGABLE = (*env)->GetStaticIntField(env, ApplicationInfo,
            ApplicationInfo_FLAG_DEBUGGABLEFieldId);
    int flags = (*env)->GetIntField(env, applicationInfoInstance,
            ApplicationInfo_flagsFieldId);

    return (0 != (flags &= FLAG_DEBUGGABLE));
}

static bool isSecureEnvironment(JNIEnv *env) {

    //isSecure = true; // TODO remove this

    if (isSecure == -1) {
        isSecure = true;

        if (isDebuggable(env)) {
            // someone used the app in debug-mode
#if DEBUG_MODE != 1
            // TODO report

#endif
            LOGEH("App IS DEBUGGABLE!");
            isSecure = false;
        } else {

            // check CRC
            long classesCRC = getClassesCRC(env);
            if (classesCRC != (long) CLASSES_CRC) {
#if DEBUG_MODE != 1
                // TODO report
#endif
                LOGEH("CRC-CHECK FAILED: %lu", classesCRC);

                isSecure = false;
            }
        }
    }
    return isSecure;
}


static bool initJavaClasses(JNIEnv * env) {
    jclass local = (*env)->FindClass(env, "eu/my/MyApplication");
    MyApplication = (*env)->NewGlobalRef(env, local);
    if (!MyApplication) {
    LOGEH("Unable to find the MyApplication class");
    return false;
    }
    local = (*env)->FindClass(env, "android/content/Context");
    Context = (*env)->NewGlobalRef(env, local);
    (*env)->DeleteLocalRef(env, local);
    if (!Context) {
    LOGEH("Unable to find the Context class");
    return false;
    }
    local = (*env)->FindClass(env, "android/content/pm/ApplicationInfo");
    ApplicationInfo = (*env)->NewGlobalRef(env, local);
    (*env)->DeleteLocalRef(env, local);
    if (!ApplicationInfo) {
    LOGEH("Unable to find the ApplicationInfo class");
    return false;
    }

    local = (*env)->FindClass(env, "java/util/zip/ZipFile");
    ZipFile = (*env)->NewGlobalRef(env, local);
    (*env)->DeleteLocalRef(env, local);
    if (!ZipFile) {
    LOGEH("Unable to find the ZipFile class");
    return false;
    }

    local = (*env)->FindClass(env, "java/util/zip/ZipEntry");
    ZipEntry = (*env)->NewGlobalRef(env, local);
    (*env)->DeleteLocalRef(env, local);
    if (!ZipEntry) {
    LOGEH("Unable to find the ZipEntry class");
    return false;
    }

    local = (*env)->FindClass(env, "java/util/zip/CheckedInputStream");
    CheckedInputStream = (*env)->NewGlobalRef(env, local);
    (*env)->DeleteLocalRef(env, local);
    if (!CheckedInputStream) {
    LOGEH("Unable to find the CheckedInputStream class");
    return false;
    }

    local = (*env)->FindClass(env, "java/util/zip/Adler32");
    Adler32 = (*env)->NewGlobalRef(env, local);
    (*env)->DeleteLocalRef(env, local);
    if (!Adler32) {
    LOGEH("Unable to find the Adler32 class");
    return false;
    }

    local = (*env)->FindClass(env, "java/util/zip/Checksum");
    Checksum = (*env)->NewGlobalRef(env, local);
    (*env)->DeleteLocalRef(env, local);
    if (!Checksum) {
    LOGEH("Unable to find the Checksum class");
    return false;
    }

    return true;
}

static bool initJavaMethods(JNIEnv * env) {
    MyApplication_getAppContextMethodId = (*env)->GetStaticMethodID(env,
    MyApplication, "getAppContext", "()Landroid/content/Context;");
    if (!MyApplication_getAppContextMethodId) {
    LOGEH("Unable to find the getAppContext method");
    return false;
    }
    Context_getApplicationInfoMethodId = (*env)->GetMethodID(env, Context,
    "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
    if (!Context_getApplicationInfoMethodId) {
    LOGEH("Unable to find the getApplicationInfo method");
    return false;
    }

    ZipFile_ConstructorMethodId = (*env)->GetMethodID(env, ZipFile, "<init>",
    "(Ljava/lang/String;)V");
    if (!ZipFile_ConstructorMethodId) {
    LOGEH("Unable to find the constructor method");
    return false;
    }

    CheckedInputStream_ConstructorMethodId = (*env)->GetMethodID(env,
    CheckedInputStream, "<init>",
    "(Ljava/io/InputStream;Ljava/util/zip/Checksum;)V");
    if (!CheckedInputStream_ConstructorMethodId) {
    LOGEH("Unable to find the constructor method");
    return false;
    }

    Adler32_ConstructorMethodId = (*env)->GetMethodID(env, Adler32, "<init>",
    "()V");
    if (!Adler32_ConstructorMethodId) {
    LOGEH("Unable to find the constructor method");
    return false;
    }

    ZipFile_getEntryMethodId = (*env)->GetMethodID(env, ZipFile, "getEntry",
    "(Ljava/lang/String;)Ljava/util/zip/ZipEntry;");
    if (!ZipFile_getEntryMethodId) {
    LOGEH("Unable to find the getEntry method");
    return false;
    }

    ZipFile_getInputStreamMethodId = (*env)->GetMethodID(env, ZipFile,
    "getInputStream", "(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;");
    if (!ZipFile_getInputStreamMethodId) {
    LOGEH("Unable to find the getInputStream method");
    return false;
    }

    CheckedInputStream_readMethodId = (*env)->GetMethodID(env, CheckedInputStream,
    "read", "([B)I");
    if (!CheckedInputStream_readMethodId) {
    LOGEH("Unable to find the read method");
    return false;
    }

    CheckedInputStream_getChecksumMethodId = (*env)->GetMethodID(env,
    CheckedInputStream, "getChecksum", "()Ljava/util/zip/Checksum;");
    if (!CheckedInputStream_getChecksumMethodId) {
    LOGEH("Unable to find the getChecksum method");
    return false;
    }

    Checksum_getValueMethodId = (*env)->GetMethodID(env, Checksum, "getValue",
    "()J");
    if (!Checksum_getValueMethodId) {
    LOGEH("Unable to find the getValue method");
    return false;
    }

    return true;
}

static bool initJavaFields(JNIEnv * env) {
    ApplicationInfo_flagsFieldId = (*env)->GetFieldID(env, ApplicationInfo, "flags",
    "I");

    if (!ApplicationInfo_flagsFieldId) {
    LOGEH("Unable to find the flags field");
    return false;
    }

    ApplicationInfo_FLAG_DEBUGGABLEFieldId = (*env)->GetStaticFieldID(env,
    ApplicationInfo, "FLAG_DEBUGGABLE", "I");
    if (!ApplicationInfo_FLAG_DEBUGGABLEFieldId) {
    LOGEH("Unable to get static field FLAG_DEBUGGABLE");
    return false;
    }

    ApplicationInfo_sourceDirFieldId = (*env)->GetFieldID(env, ApplicationInfo,
    "sourceDir", "Ljava/lang/String;");
  

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...