Hacking Android apps

    In this article, we will briefly talk about how you can protect your program from hacking without integrating a standard solution from Google and provide an example of working code. Interesting? We ask for a cut!

    What is the weakness of Google Application Licensing?

    The fact is that this mechanism is well known to developers and its hacking is not difficult. All you need to do is download apktool, find the LicenseChecker class and tweak the checkAccess method slightly.

    How to realize your own protection?

    Obviously, any defense can be broken. This method is not a silver bullet, but it has the right to life. To verify the uniqueness of the application, it makes sense to check the certificate with which this application was signed. Certificate information can be read from PackageInfo:

    PackageInfo info =getPackageManager().getPackageInfo(getPackageName(), 0);
    Signature[] signatures = info.signatures;

    The zero element of the array will contain the necessary information about the key with which the application was signed. Save the key itself, you will need it very soon.
    It makes sense that doing such a check in Java does not make sense, since a similar technique using apktool will bury your protection in a few minutes. Therefore, this check should be transferred to the active level.

    const char* rsa = "PUT_YOUR_RSA_KEY_HERE";
    jint verifyCertificate(JNIEnv *env, jobject obj, jobject cnt) {
    	jclass cls = env->GetObjectClass(cnt);
    	jmethodID mid = env->GetMethodID(cls, "getPackageManager",
    	jmethodID pnid = env->GetMethodID(cls, "getPackageName",
    	if (mid == 0 || pnid == 0) {
    		return ERROR;
    	jobject pacMan_o = env->CallObjectMethod(cnt, mid);
    	jclass pacMan = env->GetObjectClass(pacMan_o);
    	jstring packName = (jstring) env->CallObjectMethod(cnt, pnid);
    	/*flags = PackageManager.GET_SIGNATURES*/
    	int flags = 0x40;
    	mid = env->GetMethodID(pacMan, "getPackageInfo",
    	if (mid == 0) {
    		return ERROR;
    	jobject pack_inf_o = (jobject) env->CallObjectMethod(pacMan_o, mid,
    			packName, flags);
    	jclass packinf = env->GetObjectClass(pack_inf_o);
    	jfieldID fid;
    	fid = env->GetFieldID(packinf, "signatures",
    	jobjectArray signatures = (jobjectArray) env->GetObjectField(pack_inf_o,
    	jobject signature0 = env->GetObjectArrayElement(signatures, 0);
    	mid = env->GetMethodID(env->GetObjectClass(signature0), "toByteArray",
    	jbyteArray cert = (jbyteArray) env->CallObjectMethod(signature0, mid);
    	if (cert == 0) {
    		return ERROR;
    	jclass BAIS = env->FindClass("java/io/ByteArrayInputStream");
    	if (BAIS == 0) {
    		return ERROR;
    	mid = env->GetMethodID(BAIS, "", "([B)V");
    	if (mid == 0) {
    		return ERROR;
    	jobject input = env->NewObject(BAIS, mid, cert);
    	jclass CF = env->FindClass("java/security/cert/CertificateFactory");
    	mid = env->GetStaticMethodID(CF, "getInstance",
    	jstring X509 = env->NewStringUTF("X509");
    	jobject cf = env->CallStaticObjectMethod(CF, mid, X509);
    	if (cf == 0) {
    		return ERROR;
    	mid = env->GetMethodID(CF, "generateCertificate",
    	if (mid == 0) {
    		return ERROR;
    	jobject c = env->CallObjectMethod(cf, mid, input);
    	if (c == 0) {
    		return ERROR;
    	jclass X509Cert = env->FindClass("java/security/cert/X509Certificate");
    	mid = env->GetMethodID(X509Cert, "getPublicKey",
    	jobject pk = env->CallObjectMethod(c, mid);
    	if (pk == 0) {
    		return ERROR;
    	mid = env->GetMethodID(env->GetObjectClass(pk), "toString",
    	if (mid == 0) {
    		return ERROR;
    	jstring all = (jstring) env->CallObjectMethod(pk, mid);
    	const char * all_char = env->GetStringUTFChars(all, NULL);
    	char * out = NULL;
    	if (all_char != NULL) {
    		char * startString = strstr(all_char, "modulus:");
    		char * end = strstr(all_char, "public exponent");
    		bool isJB = false;
    		if (startString == NULL) {
    			startString = strstr(all_char, "modulus=");
    			end = strstr(all_char, ",publicExponent");
    			isJB = true;
    		if (startString != NULL && end != NULL) {
    			int len;
    			if (isJB) {
    				startString += strlen("modulus=");
    				len = end - startString;
    			} else {
    				startString += strlen("modulus:");
    				len = end - startString - 5; /* -5 for new lines*/
    			out = new char[len + 2];
    			strncpy(out, startString, len);
    			out[len] = '\0';
    	env->ReleaseStringUTFChars(all, all_char);
    	char * is_found = strstr(out, rsa);
    	// при отладке сертификат не проверяем
    	if (IS_DEBUG) {
    		return is_found != NULL ? 0 : 1;
    	} else {
    		return is_found != NULL ? 1 :0;

    What to do next?

    It is necessary to transfer some of its functionality to the same library and check the certificate for authenticity before returning the value. And do not forget about the fantasy, which is necessary in order to confuse a potential hacker. Suppose you determine that this copy of the application is not genuine. There is no point in explicitly talking about this, it’s much more fun to add a random element to the program’s actions. For example, if you have a game, you can add strength to your opponents or make the player more vulnerable. You can also add random drops, for example, in 10% of cases (a fair remark from the zabayevskiy habrayuzer : random drops will ruin the karma of your program.). It all depends on you.

    Here is a simple example of a possible implementation:

    First, write a class that makes a library call to retrieve some data.

    public class YourClass {
    	static {
    public native int getSomeValue();
    public native void init(Context ctx);

    The init method is needed in order not to invoke certificate authentication every time.

    Implementation of native methods:

    jint isCertCorrect = 0;
    JNIEXPORT void JNICALL Java_com_your_package_YourClass_init(JNIEnv *env, jobject obj, jobject ctx) {
    	isCertCorrect = verifyCertificate(env, obj, ctx);
    JNIEXPORT jint JNICALL Java_com_your_package_YourClass_getSomeValue(JNIEnv *env, jobject obj) {
    if (isCertCorrect ) {
    // всё по плану
    } else {
    // включаем фантазию тут

    This protection option can also be hacked by disassembling, but for this you need a completely different level of knowledge and much more time than in the case of the implementation of protection at the Java level. If you have certain means, it makes sense to purchase an obfuscator for C code, in which case hacking will be far from a trivial task.

    Application protection in the presence of a server part.

    If part of the application logic is implemented on the server, it makes sense to issue certificate authentication to the server. Alternatively, you can use the following algorithm of actions:

    1. The server generates private and public RSA keys;
    2. The client sends a request to the server and receives a public key;
    3. On the client, we implement a native library with a function of the form String getInfo (String publicKey); the function should read the certificate, add some random variable to it, then encrypt the received string using the public RSA key;
    4. The client makes a new request to the server, sending the received string. The server decodes, separates the certificate from a random variable and checks its authenticity;
    5. Depending on the results of the scan, the server responds to all of the following client requests.

    We hope this mechanism will help Habr's readers to increase earnings from their Android applications.

    Also popular now: