Error[8]: Undefined offset: 24, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述安卓加固基础(一)1.Dex字符串加密1.1前序Android应用当中,很多隐私信息都是以字符串的形式存在的。这些隐私信息是明文,对于软件来说是想当不安全的,如果我们能在打包时对Dex中的字符串加密替换,并在运行时调用解密,这样就能够避免字符串明文存在于Dex中。虽然,无法完全避免被 安卓加固基础(一)1.Dex字符串加密1.1 前序

AndroID应用当中,很多隐私信息都是以字符串的形式存在的。这些隐私信息是明文,对于软件来说是想当不安全的,如果我们能在打包时对Dex中的字符串加密替换,并在运行时调用解密,这样就能够避免字符串明文存在于Dex中。虽然,无法完全避免被破解,但是加大了逆向提取信息的难度,安全性无疑提高了很多。

1.2 字符串加密方法简介

目前市面上主要存在两种字符串加密方式:
(1)在开发阶段开发者使用加密后的字符串然后手动调用解密,这种方法工程量太大了,缺点很明显。
(2)编译后修改字节码,然后再动态植入加密后的字符串,最后让其自动调用进行解密,这里重点分析的是第二种。

1.3 强大的工具字符串加密工具StringFog
1.3.1 环境配置和运行效果

工具:

(1)AS版本3.5.0(2)StringFog配置方法:https://github.com/MegatronKing/StringFog(3)反编译工具 jeb3

代码(MainActivity.java):

package com.example.testdex;import androIDx.appcompat.app.AppCompatActivity;import androID.os.Bundle;import androID.Widget.TextVIEw;public class MainActivity extends AppCompatActivity {    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate (savedInstanceState);        setContentVIEw (R.layout.activity_main);        String str ="Stringfog";               //这是加密字符串    }}

jeb3反编译的效果(MainActivity.java):

package com.example.testdex;import androID.os.Bundle;import androIDx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {    public MainActivity() {        super();    }    protected voID onCreate(Bundle arg2) {        super.onCreate(arg2);        this.setContentVIEw(0x7F09001C);        StringFog.decrypt("GxEeBQFHMQAV");    //显然"StringFog"被加密了    }}
1.3.2 StringFog加密原理分析

首先看加密的方法:StringFog采用的是base64+xor(异或)算法

import java.util.Base64;public class StringFog {	private static byte[] xor(byte[] data, String key) {     //异或算法	    int len = data.length;	    int lenKey = key.length();	    int i = 0;	    int j = 0;	    while (i < len) {	        if (j >= lenKey) {	            j = 0;	        }	        data[i] = (byte) (data[i] ^ key.charat(j));	        i++;	        j++;	    }	    return data;	}	public static String encode(String data, String key) {	    return new String(Base64.getEncoder().encode(xor(data.getBytes(), key)));	    //调用base64加密包	}	public static String decode(String data, String key) {	    return new String(xor(Base64.getDecoder().decode(data), key));	    //调用base64解密包	}

按照安卓程序加密的字符串测试:

	public static voID main(String[] args) {		String test = encode("Stringfog","Hello World");		System.out.println(test);	}

输出结果为:


显然跟jeb3里面看到的一样。

ps:Base64 packge 下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi 下载后导入commons-codec-1.14.jar包

1.3.2 StringFog加密原理分析
StringFog实际上是 *** 作了class文件,编译class文件的字节码文件,发现如果都是字符串常量的话,指令都为LDC****************************************************************************[root@iZbp1dubkpj5g938jakcouZ ~]# javac Hello.java[root@iZbp1dubkpj5g938jakcouZ ~]# javap -c HelloCompiled from "Hello.java"public class Hello {  public Hello();    Code:       0: aload_0       1: invokespecial #1                  // Method java/lang/Object."<init>":()V       4: return  public static voID main(java.lang.String[]);    Code:       0: ldc           #2                  // String This is a String!       2: astore_1       3: return}

那么我们可以借助asm库拦截方法中的每条LDC指令,然后插入该指令即可

总结:字符串加密方法能够较为有效的阻止他人通过字符串搜素定位代码,但是不能防止Hook,总而言之,这只是一个比较普通的混淆方法

2.资源加密

java代码将png图片加密:

package cn.zzh;import java.io.fileinputStream;import java.io.fileOutputStream;import java.io.IOException;import java.io.inputStream;import java.io.OutputStream;public class pngEncrypt {	public static voID main(String[] args){	    //调用加密方法	    KMD.encrypt("f://1.png");	}    //加密后,会在原图片的路径下生成加密后的图片    public static voID encrypt(String filePath){        byte[] tempbytes = new byte[5000];        try {            inputStream in = new fileinputStream(filePath);            OutputStream out = new fileOutputStream(filePath.subSequence(0, filePath.lastIndexOf("."))+"2.png");            while (in.read(tempbytes) != -1) {                byte a = tempbytes[0];                tempbytes[0] = tempbytes[1]; //将第一个字符和第二个字符交换                tempbytes[1] = a;                out.write(tempbytes);//写文件            }        } catch (IOException e) {            e.printstacktrace();        }    }}

使用winhex查看发现:

变成了:

然后我们只需要在AS中将编写解密代码即可:

下面是一个简单的测试demo:

(1)建立asserts文件,将加密的12.png文件放进去(2)代码编写:package com.example.testdex;import androIDx.appcompat.app.AppCompatActivity;import androID.content.Context;import androID.content.res.AssetManager;import androID.graphics.Bitmap;import androID.graphics.BitmapFactory;import androID.os.Bundle;import androID.util.Log;import androID.Widget.TextVIEw;import androID.Widget.Toast;import java.io.IOException;import java.io.inputStream;public class MainActivity extends AppCompatActivity {    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate (savedInstanceState);        setContentVIEw (R.layout.activity_main);        Bitmap bitmap= getimageFromAssets(this,"12.png");//图片资源加密        if(bitmap != null) {            //imageVIEw.setimage(ImageSource.bitmap(bitmap));            Toast.makeText (this, "图片解密成功", Toast.LENGTH_SHORT).show ();        } else {           // Log.i(TAG,"图片为空");            Toast.makeText (this, "图片解密失败", Toast.LENGTH_SHORT).show ();        }    }    public Bitmap getimageFromAssets(Context context, String filename) {        Bitmap image = null;        AssetManager am = context.getResources().getAssets();        try {            inputStream is = am.open(filename);            byte[] buffer = new byte[1500000];//记得要足够大            is.read(buffer);            for(int i=0; i<buffer.length; i+= 5000){//和加密相同                byte temp = buffer[i];                buffer[i] = buffer[i+1];                buffer[i+1] = temp;            }            image = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);            if (is!=null){                is.close();            }        } catch (IOException e) {            e.printstacktrace();        }        return image;    }}

测试结果:

@H_502_131@

此时,我们打开apk查看assert文件时依然无法看到有用的文件

ps:同时我们还可以进行资源路径混淆,详情见:https://github.com/shwenzhang/AndResGuard/blob/master/README.zh-cn.md

3.对抗反编译3.1类名混淆

现在的反编译工具都太先进了,很多纯粹的对抗反编译技术都不在适应了,
但是我们可以将类名进行混淆:

gradle版本在3.4以下时我们使用proguard-rules.pro进行混淆,达到3.4以上时我们使用R8穿插一些proguard规则进行混淆
下面重点研究的是3.4以上版本的情况:

官方文档:https://developer.androID.com/studio/build/shrink-code?hl=zh-cn

通用教程:https://www.jianshu.com/p/65027e18c2fe

混淆规则(如下):

############################################### 对于一些基本指令的添加############################################### 代码混淆压缩比,在0~7之间,默认为5,一般不做修改-optimizationpasses 5# 混合时不使用大小写混合,混合后的类名为小写-dontusemixedcaseclassnames# 指定不去忽略非公共库的类-dontskipnonpubliclibraryclasses# 这句话能够使我们的项目混淆后产生映射文件# 包含有类名->混淆后类名的映射关系-verbose# 指定不去忽略非公共库的类成员-dontskipnonpubliclibraryclassmembers# 不做预校验,preverify是proguard的四个步骤之一,AndroID不需要preverify,去掉这一步能够加快混淆速度。-dontpreverify# 保留Annotation不混淆-keepattributes *Annotation*,InnerClasses# 避免混淆泛型-keepattributes Signature# 抛出异常时保留代码行号-keepattributes Sourcefile,lineNumbertable# 指定混淆是采用的算法,后面的参数是一个过滤器# 这个过滤器是谷歌推荐的算法,一般不做更改-optimizations !code/simplification/cast,!fIEld/*,!class/merging/*############################################### AndroID开发中一些需要保留的公共部分############################################### 保留我们使用的四大组件,自定义的Application等等这些类不被混淆# 因为这些子类都有可能被外部调用-keep public class * extends androID.app.Activity-keep public class * extends androID.app.Appliction-keep public class * extends androID.app.Service-keep public class * extends androID.content.broadcastReceiver-keep public class * extends androID.content.ContentProvIDer-keep public class * extends androID.app.backup.BackupAgentHelper-keep public class * extends androID.preference.Preference-keep public class * extends androID.vIEw.VIEw-keep public class com.androID.vending.licensing.IlicensingService# 保留support下的所有类及其内部类-keep class androID.support.** {*;}# 保留继承的-keep public class * extends androID.support.v4.**-keep public class * extends androID.support.v7.**-keep public class * extends androID.support.annotation.**# 保留R下面的资源-keep class **.R$* {*;}# 保留本地native方法不被混淆-keepclasseswithmembernames class * {    native <methods>;}# 保留在Activity中的方法参数是vIEw的方法,# 这样以来我们在layout中写的onClick就不会被影响-keepclassmembers class * extends androID.app.Activity{    public voID *(androID.vIEw.VIEw);}# 保留枚举类不被混淆-keepclassmembers enum * {    public static **[] values();    public static ** valueOf(java.lang.String);}# 保留我们自定义控件(继承自VIEw)不被混淆-keep public class * extends androID.vIEw.VIEw{    *** get*();    voID set*(***);    public <init>(androID.content.Context);    public <init>(androID.content.Context, androID.util.AttributeSet);    public <init>(androID.content.Context, androID.util.AttributeSet, int);}# 保留Parcelable序列化类不被混淆-keep class * implements androID.os.Parcelable {    public static final androID.os.Parcelable$Creator *;}# 保留Serializable序列化的类不被混淆-keepclassmembers class * implements java.io.Serializable {    static final long serialVersionUID;    private static final java.io.ObjectStreamFIEld[] serialPersistentFIElds;    !static !transIEnt <fIElds>;    !private <fIElds>;    !private <methods>;    private voID writeObject(java.io.ObjectOutputStream);    private voID readobject(java.io.ObjectinputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆-keepclassmembers class * {    voID *(**On*Event);    voID *(**On*Listener);}# webVIEw处理,项目中没有使用到webVIEw忽略即可-keepclassmembers class fqcn.of.JavaScript.interface.for.webvIEw {    public *;}-keepclassmembers class * extends androID.webkit.webVIEwClIEnt {    public voID *(androID.webkit.WebVIEw, java.lang.String, androID.graphics.Bitmap);    public boolean *(androID.webkit.WebVIEw, java.lang.String);}-keepclassmembers class * extends androID.webkit.webVIEwClIEnt {    public voID *(androID.webkit.webVIEw, jav.lang.String);}
3.2 so层混淆

这个之前已经写过了:
文档:函数混淆(JNI_Onload).note
链接:http://note.youdao.com/noteshare?ID=f6add1ff6a6c4b78aac4a9e27fe390ed&sub=04FF4E2B1F504587A06C69F39C4DF22C

3.3 签名验证
3.3.1 在MainActivity.java中进行签名验证
package com.example.signatureverify;import androIDx.appcompat.app.AppCompatActivity;import androID.content.Context;import androID.content.pm.PackageInfo;import androID.content.pm.PackageManager;import androID.content.pm.Signature;import androID.os.Bundle;import androID.Widget.Toast;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Locale;public class MainActivity extends AppCompatActivity {    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate (savedInstanceState);        setContentVIEw (R.layout.activity_main);        Context context =getApplicationContext ();        Toast.makeText (context, "我是正版", Toast.LENGTH_SHORT).show ();        String cert_sha1="59F8A6B86A367F0586F1A15DDDB63D75263C5D62"; // 通过调试提前获取apk的sha1签名        boolean is_org_app = false;        try {            is_org_app = isOrgApp(context,cert_sha1);        } catch (PackageManager.nameNotFoundException e) {            e.printstacktrace ();        } catch (NoSuchAlgorithmException e) {            e.printstacktrace ();        }        if(!is_org_app){            androID.os.Process.killProcess ((androID.os.Process.myPID ()));            //如果签名不一致,说明程序被修改了,直接退出        }    }//比较签名    private boolean isOrgApp(Context context, String cert_sha1) throws PackageManager.nameNotFoundException, NoSuchAlgorithmException {        String current_sha1=getAppSha1(context);        current_sha1=current_sha1.replace (":","");        return cert_sha1.equals (current_sha1);    }//生成sha1的签名    private String getAppSha1(Context context) throws PackageManager.nameNotFoundException, NoSuchAlgorithmException {        PackageInfo info=context.getPackageManager ().getPackageInfo (context.getPackagename (),PackageManager.GET_SIGNATURES);        byte[] cert =info.signatures[0].toByteArray ();        MessageDigest md =MessageDigest.getInstance ("SHA1");        byte[] publicKey=md.digest (cert);        StringBuffer hexString =new StringBuffer ();        for(int i=0;i<publicKey.length;i++){            String appendString=Integer.toHexString (0xFF&publicKey[i]).toupperCase(Locale.US);            if(appendString.length ()==1){                hexString.append("0");            }            hexString.append(appendString);//签名的格式是11:22,所以需要加上":"            hexString.append (":");        }        String result=hexString.toString ();        return result.substring (0,result.length ()-1);    }}

完成程序后,使用AndroIDKiller进行修改时,将"我是正版"字符串修改成"我是盗版",在运行程序时,程序会直接闪退

3.3.2 在So层中进行签名验证

MainActivity:

package com.example.jnisignatureverify;import androIDx.appcompat.app.AppCompatActivity;import androID.content.Context;import androID.content.pm.PackageInfo;import androID.content.pm.PackageManager;import androID.os.Bundle;import androID.vIEw.VIEw;import androID.Widget.button;import androID.Widget.TextVIEw;import androID.Widget.Toast;import java.security.MessageDigest;import java.util.Locale;public class MainActivity extends AppCompatActivity {    // Used to load the 'native-lib' library on application startup.    static {        System.loadlibrary("native-lib");    }    protected TextVIEw appSignaturesTv;    protected TextVIEw jniSignaturesTv;    protected button checkBtn;    protected button tokenBtn;    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        super.setContentVIEw(R.layout.activity_main);        initVIEw();        appSignaturesTv.setText(getSha1Value(MainActivity.this));        jniSignaturesTv.setText(getSignaturesSha1(MainActivity.this));    }    private VIEw.OnClickListener clickListener = new VIEw.OnClickListener(){        @OverrIDe        public voID onClick(VIEw v) {            boolean result = checkSha1(MainActivity.this);            if(result){                Toast.makeText(getApplicationContext(),"验证通过",Toast.LENGTH_LONG).show();            }else{                Toast.makeText(getApplicationContext(),"验证不通过,请检查valID.cpp文件配置的sha1值",Toast.LENGTH_LONG).show();            }        }    };    private VIEw.OnClickListener tokenClickListener = new VIEw.OnClickListener(){        @OverrIDe        public voID onClick(VIEw v) {            String result = getToken(MainActivity.this,"12345");            Toast.makeText(getApplicationContext(),result,Toast.LENGTH_LONG).show();        }    };    private voID initVIEw() {        appSignaturesTv = (TextVIEw) findVIEwByID(R.ID.app_signatures_tv);        jniSignaturesTv = (TextVIEw) findVIEwByID(R.ID.jni_signatures_tv);        checkBtn = (button) findVIEwByID(R.ID.check_btn);        tokenBtn = (button) findVIEwByID(R.ID.token_btn);        checkBtn.setonClickListener(clickListener);        tokenBtn.setonClickListener(tokenClickListener);    }    /**     * A native method that is implemented by the 'native-lib' native library,     * which is packaged with this application.     */    public native String getSignaturesSha1(Context context);    public native boolean checkSha1(Context context);    public native String getToken(Context context,String userID);//获取apk当前的签名    public String getSha1Value(Context context) {        try {            PackageInfo info = context.getPackageManager().getPackageInfo(                    context.getPackagename(), PackageManager.GET_SIGNATURES);            byte[] cert = info.signatures[0].toByteArray();            MessageDigest md = MessageDigest.getInstance("SHA1");            byte[] publicKey = md.digest(cert);            StringBuffer hexString = new StringBuffer();            for (int i = 0; i < publicKey.length; i++) {                String appendString = Integer.toHexString(0xFF & publicKey[i])                        .toupperCase(Locale.US);                if (appendString.length() == 1)                    hexString.append("0");                hexString.append(appendString);            }            String result = hexString.toString();            return result.substring(0, result.length());        } catch (Exception e) {            e.printstacktrace();        }        return null;    }}

native-lib.cpp:

#include <stdio.h>#include <stdlib.h>#include <jni.h>#include <androID/log.h>#include <cstring>#define TAG    "jni-log"#define LOGD(...)  __androID_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)//签名信息const char *app_sha1="59F8A6B86A367F0586F1A15DDDB63D75263C5D62";const char hexcode[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};char* getSha1(jnienv *env, jobject context_object){    //上下文对象    jclass context_class = env->GetobjectClass(context_object);    //反射获取PackageManager    jmethodID methodID = env->getmethodID(context_class, "getPackageManager", "()LandroID/content/pm/PackageManager;");    jobject package_manager = env->CallObjectMethod(context_object, methodID);    if (package_manager == NulL) {        //LOGD("package_manager is NulL!!!");        return NulL;    }    //反射获取包名    methodID = env->getmethodID(context_class, "getPackagename", "()Ljava/lang/String;");    Jstring package_name = (Jstring)env->CallObjectMethod(context_object, methodID);    if (package_name == NulL) {        //LOGD("package_name is NulL!!!");        return NulL;    }    env->DeleteLocalRef(context_class);    //获取PackageInfo对象    jclass pack_manager_class = env->GetobjectClass(package_manager);    methodID = env->getmethodID(pack_manager_class, "getPackageInfo", "(Ljava/lang/String;I)LandroID/content/pm/PackageInfo;");    env->DeleteLocalRef(pack_manager_class);    jobject package_info = env->CallObjectMethod(package_manager, methodID, package_name, 0x40);    if (package_info == NulL) {        LOGD("getPackageInfo() is NulL!!!");        return NulL;    }    env->DeleteLocalRef(package_manager);    //获取签名信息    jclass package_info_class = env->GetobjectClass(package_info);    jfIEldID fIEldID = env->GetFIEldID(package_info_class, "signatures", "[LandroID/content/pm/Signature;");    env->DeleteLocalRef(package_info_class);    jobjectArray signature_object_array = (jobjectArray)env->GetobjectFIEld(package_info, fIEldID);    if (signature_object_array == NulL) {        LOGD("signature is NulL!!!");        return NulL;    }    jobject signature_object = env->GetobjectArrayElement(signature_object_array, 0);    env->DeleteLocalRef(package_info);    //签名信息转换成sha1值    jclass signature_class = env->GetobjectClass(signature_object);    methodID = env->getmethodID(signature_class, "toByteArray", "()[B");    env->DeleteLocalRef(signature_class);    jbyteArray signature_byte = (jbyteArray) env->CallObjectMethod(signature_object, methodID);    jclass byte_array_input_class=env->FindClass("java/io/ByteArrayinputStream");    methodID=env->getmethodID(byte_array_input_class,"<init>","([B)V");    jobject byte_array_input=env->NewObject(byte_array_input_class,methodID,signature_byte);    jclass certificate_factory_class=env->FindClass("java/security/cert/CertificateFactory");    methodID=env->GetStaticmethodID(certificate_factory_class,"getInstance","(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");    Jstring x_509_Jstring=env->NewStringUTF("X.509");    jobject cert_factory=env->CallStaticObjectMethod(certificate_factory_class,methodID,x_509_Jstring);    methodID=env->getmethodID(certificate_factory_class,"generateCertificate",("(Ljava/io/inputStream;)Ljava/security/cert/Certificate;"));    jobject x509_cert=env->CallObjectMethod(cert_factory,methodID,byte_array_input);    env->DeleteLocalRef(certificate_factory_class);    jclass x509_cert_class=env->GetobjectClass(x509_cert);    methodID=env->getmethodID(x509_cert_class,"getEncoded","()[B");    jbyteArray cert_byte=(jbyteArray)env->CallObjectMethod(x509_cert,methodID);    env->DeleteLocalRef(x509_cert_class);    jclass message_digest_class=env->FindClass("java/security/MessageDigest");    methodID=env->GetStaticmethodID(message_digest_class,"getInstance","(Ljava/lang/String;)Ljava/security/MessageDigest;");    Jstring sha1_Jstring=env->NewStringUTF("SHA1");    jobject sha1_digest=env->CallStaticObjectMethod(message_digest_class,methodID,sha1_Jstring);    methodID=env->getmethodID(message_digest_class,"digest","([B)[B");    jbyteArray sha1_byte=(jbyteArray)env->CallObjectMethod(sha1_digest,methodID,cert_byte);    env->DeleteLocalRef(message_digest_class);    //转换成char    Jsize array_size=env->GetArrayLength(sha1_byte);    jbyte* sha1 =env->GetByteArrayElements(sha1_byte,NulL);    char *hex_sha=new char[array_size*2+1];    for (int i = 0; i <array_size ; ++i) {        hex_sha[2*i]=hexcode[((unsigned char)sha1[i])/16];        hex_sha[2*i+1]=hexcode[((unsigned char)sha1[i])%16];    }    hex_sha[array_size*2]='<?xml version="1.0" enCoding="utf-8"?><linearLayout    xmlns:androID="http://schemas.androID.com/apk/res/androID"    xmlns:app="http://schemas.androID.com/apk/res-auto"    xmlns:tools="http://schemas.androID.com/tools"    androID:layout_margin="16dp"    androID:layout_wIDth="match_parent"    androID:layout_height="match_parent"    androID:orIEntation="vertical"    tools:context="com.example.jnisignatureverify.MainActivity">    <linearLayout        androID:layout_wIDth="match_parent"        androID:orIEntation="vertical"        androID:layout_height="wrap_content">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="APP签名信息:"/>        <TextVIEw            androID:ID="@+ID/app_signatures_tv"            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"/>    </linearLayout>    <linearLayout        androID:layout_margintop="16dp"        androID:orIEntation="vertical"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="jni配置的签名信息:"/>        <TextVIEw            androID:ID="@+ID/jni_signatures_tv"            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"/>    </linearLayout>    <button        androID:ID="@+ID/check_btn"        androID:layout_margintop="16dp"        androID:text="验证签名是否正确"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"/>    <button        androID:ID="@+ID/token_btn"        androID:layout_margintop="16dp"        androID:text="获取token"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"/></linearLayout>';    LOGD("hex_sha %s ",hex_sha);    return hex_sha;}jboolean checkValIDity(jnienv *env,char *sha1){    //比较签名    if (strcmp(sha1,app_sha1)==0)    {        LOGD("验证成功");        return true;    }    LOGD("验证失败");    return false;}extern "C"JNIEXPORT Jstring JNICALLJava_com_example_jnisignatureverify_MainActivity_getSignaturesSha1(jnienv *env, jobject thiz,                                                                   jobject context) {    // Todo: implement getSignaturesSha1()    return env->NewStringUTF(app_sha1);}extern "C"JNIEXPORT jboolean JNICALLJava_com_example_jnisignatureverify_MainActivity_checkSha1(jnienv *env, jobject thiz,                                                           jobject contextObject) {    // Todo: implement checkSha1()    char *sha1 = getSha1(env,contextObject);    jboolean result = checkValIDity(env,sha1);    return result;}extern "C"JNIEXPORT Jstring JNICALLJava_com_example_jnisignatureverify_MainActivity_getToken(jnienv *env, jobject thiz,                                                          jobject contextObject, Jstring user_ID) {    // Todo: implement getToken()    char *sha1 = getSha1(env,contextObject);    jboolean result = checkValIDity(env,sha1);    if(result){        return env->NewStringUTF("获取Token成功");    }else{        return env->NewStringUTF("获取失败,请检查native-lib.cpp文件配置的sha1值");    }}

activity_main.xml:

[+++]
点赞收藏分享文章举报

李政辉发布了3 篇原创文章 · 获赞 3 · 访问量 64私信 关注 总结

以上是内存溢出为你收集整理的安卓加固基础全部内容,希望文章能够帮你解决安卓加固基础所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
安卓加固基础_app_内存溢出

安卓加固基础

安卓加固基础,第1张

概述安卓加固基础(一)1.Dex字符串加密1.1前序Android应用当中,很多隐私信息都是以字符串的形式存在的。这些隐私信息是明文,对于软件来说是想当不安全的,如果我们能在打包时对Dex中的字符串加密替换,并在运行时调用解密,这样就能够避免字符串明文存在于Dex中。虽然,无法完全避免被 安卓加固基础(一)1.Dex字符串加密1.1 前序

AndroID应用当中,很多隐私信息都是以字符串的形式存在的。这些隐私信息是明文,对于软件来说是想当不安全的,如果我们能在打包时对Dex中的字符串加密替换,并在运行时调用解密,这样就能够避免字符串明文存在于Dex中。虽然,无法完全避免被破解,但是加大了逆向提取信息的难度,安全性无疑提高了很多。

1.2 字符串加密方法简介

目前市面上主要存在两种字符串加密方式:
(1)在开发阶段开发者使用加密后的字符串然后手动调用解密,这种方法工程量太大了,缺点很明显。
(2)编译后修改字节码,然后再动态植入加密后的字符串,最后让其自动调用进行解密,这里重点分析的是第二种。

1.3 强大的工具字符串加密工具StringFog
1.3.1 环境配置和运行效果

工具:

(1)AS版本3.5.0(2)StringFog配置方法:https://github.com/MegatronKing/StringFog(3)反编译工具 jeb3

代码(MainActivity.java):

package com.example.testdex;import androIDx.appcompat.app.AppCompatActivity;import androID.os.Bundle;import androID.Widget.TextVIEw;public class MainActivity extends AppCompatActivity {    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate (savedInstanceState);        setContentVIEw (R.layout.activity_main);        String str ="Stringfog";               //这是加密字符串    }}

jeb3反编译的效果(MainActivity.java):

package com.example.testdex;import androID.os.Bundle;import androIDx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {    public MainActivity() {        super();    }    protected voID onCreate(Bundle arg2) {        super.onCreate(arg2);        this.setContentVIEw(0x7F09001C);        StringFog.decrypt("GxEeBQFHMQAV");    //显然"StringFog"被加密了    }}
1.3.2 StringFog加密原理分析

首先看加密的方法:StringFog采用的是base64+xor(异或)算法

import java.util.Base64;public class StringFog {	private static byte[] xor(byte[] data, String key) {     //异或算法	    int len = data.length;	    int lenKey = key.length();	    int i = 0;	    int j = 0;	    while (i < len) {	        if (j >= lenKey) {	            j = 0;	        }	        data[i] = (byte) (data[i] ^ key.charat(j));	        i++;	        j++;	    }	    return data;	}	public static String encode(String data, String key) {	    return new String(Base64.getEncoder().encode(xor(data.getBytes(), key)));	    //调用base64加密包	}	public static String decode(String data, String key) {	    return new String(xor(Base64.getDecoder().decode(data), key));	    //调用base64解密包	}

按照安卓程序加密的字符串测试:

	public static voID main(String[] args) {		String test = encode("Stringfog","Hello World");		System.out.println(test);	}

输出结果为:


显然跟jeb3里面看到的一样。

ps:Base64 packge 下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi 下载后导入commons-codec-1.14.jar包

1.3.2 StringFog加密原理分析
StringFog实际上是 *** 作了class文件,编译class文件的字节码文件,发现如果都是字符串常量的话,指令都为LDC****************************************************************************[root@iZbp1dubkpj5g938jakcouZ ~]# javac Hello.java[root@iZbp1dubkpj5g938jakcouZ ~]# javap -c HelloCompiled from "Hello.java"public class Hello {  public Hello();    Code:       0: aload_0       1: invokespecial #1                  // Method java/lang/Object."<init>":()V       4: return  public static voID main(java.lang.String[]);    Code:       0: ldc           #2                  // String This is a String!       2: astore_1       3: return}

那么我们可以借助asm库拦截方法中的每条LDC指令,然后插入该指令即可

总结:字符串加密方法能够较为有效的阻止他人通过字符串搜素定位代码,但是不能防止Hook,总而言之,这只是一个比较普通的混淆方法

2.资源加密

java代码将png图片加密:

package cn.zzh;import java.io.fileinputStream;import java.io.fileOutputStream;import java.io.IOException;import java.io.inputStream;import java.io.OutputStream;public class pngEncrypt {	public static voID main(String[] args){	    //调用加密方法	    KMD.encrypt("f://1.png");	}    //加密后,会在原图片的路径下生成加密后的图片    public static voID encrypt(String filePath){        byte[] tempbytes = new byte[5000];        try {            inputStream in = new fileinputStream(filePath);            OutputStream out = new fileOutputStream(filePath.subSequence(0, filePath.lastIndexOf("."))+"2.png");            while (in.read(tempbytes) != -1) {                byte a = tempbytes[0];                tempbytes[0] = tempbytes[1]; //将第一个字符和第二个字符交换                tempbytes[1] = a;                out.write(tempbytes);//写文件            }        } catch (IOException e) {            e.printstacktrace();        }    }}

使用winhex查看发现:

变成了:

然后我们只需要在AS中将编写解密代码即可:

下面是一个简单的测试demo:

(1)建立asserts文件,将加密的12.png文件放进去(2)代码编写:package com.example.testdex;import androIDx.appcompat.app.AppCompatActivity;import androID.content.Context;import androID.content.res.AssetManager;import androID.graphics.Bitmap;import androID.graphics.BitmapFactory;import androID.os.Bundle;import androID.util.Log;import androID.Widget.TextVIEw;import androID.Widget.Toast;import java.io.IOException;import java.io.inputStream;public class MainActivity extends AppCompatActivity {    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate (savedInstanceState);        setContentVIEw (R.layout.activity_main);        Bitmap bitmap= getimageFromAssets(this,"12.png");//图片资源加密        if(bitmap != null) {            //imageVIEw.setimage(ImageSource.bitmap(bitmap));            Toast.makeText (this, "图片解密成功", Toast.LENGTH_SHORT).show ();        } else {           // Log.i(TAG,"图片为空");            Toast.makeText (this, "图片解密失败", Toast.LENGTH_SHORT).show ();        }    }    public Bitmap getimageFromAssets(Context context, String filename) {        Bitmap image = null;        AssetManager am = context.getResources().getAssets();        try {            inputStream is = am.open(filename);            byte[] buffer = new byte[1500000];//记得要足够大            is.read(buffer);            for(int i=0; i<buffer.length; i+= 5000){//和加密相同                byte temp = buffer[i];                buffer[i] = buffer[i+1];                buffer[i+1] = temp;            }            image = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);            if (is!=null){                is.close();            }        } catch (IOException e) {            e.printstacktrace();        }        return image;    }}

测试结果:

@H_502_131@

此时,我们打开apk查看assert文件时依然无法看到有用的文件

ps:同时我们还可以进行资源路径混淆,详情见:https://github.com/shwenzhang/AndResGuard/blob/master/README.zh-cn.md

3.对抗反编译3.1类名混淆

现在的反编译工具都太先进了,很多纯粹的对抗反编译技术都不在适应了,
但是我们可以将类名进行混淆:

gradle版本在3.4以下时我们使用proguard-rules.pro进行混淆,达到3.4以上时我们使用R8穿插一些proguard规则进行混淆
下面重点研究的是3.4以上版本的情况:

官方文档:https://developer.androID.com/studio/build/shrink-code?hl=zh-cn

通用教程:https://www.jianshu.com/p/65027e18c2fe

混淆规则(如下):

############################################### 对于一些基本指令的添加############################################### 代码混淆压缩比,在0~7之间,默认为5,一般不做修改-optimizationpasses 5# 混合时不使用大小写混合,混合后的类名为小写-dontusemixedcaseclassnames# 指定不去忽略非公共库的类-dontskipnonpubliclibraryclasses# 这句话能够使我们的项目混淆后产生映射文件# 包含有类名->混淆后类名的映射关系-verbose# 指定不去忽略非公共库的类成员-dontskipnonpubliclibraryclassmembers# 不做预校验,preverify是proguard的四个步骤之一,AndroID不需要preverify,去掉这一步能够加快混淆速度。-dontpreverify# 保留Annotation不混淆-keepattributes *Annotation*,InnerClasses# 避免混淆泛型-keepattributes Signature# 抛出异常时保留代码行号-keepattributes Sourcefile,lineNumbertable# 指定混淆是采用的算法,后面的参数是一个过滤器# 这个过滤器是谷歌推荐的算法,一般不做更改-optimizations !code/simplification/cast,!fIEld/*,!class/merging/*############################################### AndroID开发中一些需要保留的公共部分############################################### 保留我们使用的四大组件,自定义的Application等等这些类不被混淆# 因为这些子类都有可能被外部调用-keep public class * extends androID.app.Activity-keep public class * extends androID.app.Appliction-keep public class * extends androID.app.Service-keep public class * extends androID.content.broadcastReceiver-keep public class * extends androID.content.ContentProvIDer-keep public class * extends androID.app.backup.BackupAgentHelper-keep public class * extends androID.preference.Preference-keep public class * extends androID.vIEw.VIEw-keep public class com.androID.vending.licensing.IlicensingService# 保留support下的所有类及其内部类-keep class androID.support.** {*;}# 保留继承的-keep public class * extends androID.support.v4.**-keep public class * extends androID.support.v7.**-keep public class * extends androID.support.annotation.**# 保留R下面的资源-keep class **.R$* {*;}# 保留本地native方法不被混淆-keepclasseswithmembernames class * {    native <methods>;}# 保留在Activity中的方法参数是vIEw的方法,# 这样以来我们在layout中写的onClick就不会被影响-keepclassmembers class * extends androID.app.Activity{    public voID *(androID.vIEw.VIEw);}# 保留枚举类不被混淆-keepclassmembers enum * {    public static **[] values();    public static ** valueOf(java.lang.String);}# 保留我们自定义控件(继承自VIEw)不被混淆-keep public class * extends androID.vIEw.VIEw{    *** get*();    voID set*(***);    public <init>(androID.content.Context);    public <init>(androID.content.Context, androID.util.AttributeSet);    public <init>(androID.content.Context, androID.util.AttributeSet, int);}# 保留Parcelable序列化类不被混淆-keep class * implements androID.os.Parcelable {    public static final androID.os.Parcelable$Creator *;}# 保留Serializable序列化的类不被混淆-keepclassmembers class * implements java.io.Serializable {    static final long serialVersionUID;    private static final java.io.ObjectStreamFIEld[] serialPersistentFIElds;    !static !transIEnt <fIElds>;    !private <fIElds>;    !private <methods>;    private voID writeObject(java.io.ObjectOutputStream);    private voID readobject(java.io.ObjectinputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆-keepclassmembers class * {    voID *(**On*Event);    voID *(**On*Listener);}# webVIEw处理,项目中没有使用到webVIEw忽略即可-keepclassmembers class fqcn.of.JavaScript.interface.for.webvIEw {    public *;}-keepclassmembers class * extends androID.webkit.webVIEwClIEnt {    public voID *(androID.webkit.WebVIEw, java.lang.String, androID.graphics.Bitmap);    public boolean *(androID.webkit.WebVIEw, java.lang.String);}-keepclassmembers class * extends androID.webkit.webVIEwClIEnt {    public voID *(androID.webkit.webVIEw, jav.lang.String);}
3.2 so层混淆

这个之前已经写过了:
文档:函数混淆(JNI_Onload).note
链接:http://note.youdao.com/noteshare?ID=f6add1ff6a6c4b78aac4a9e27fe390ed&sub=04FF4E2B1F504587A06C69F39C4DF22C

3.3 签名验证
3.3.1 在MainActivity.java中进行签名验证
package com.example.signatureverify;import androIDx.appcompat.app.AppCompatActivity;import androID.content.Context;import androID.content.pm.PackageInfo;import androID.content.pm.PackageManager;import androID.content.pm.Signature;import androID.os.Bundle;import androID.Widget.Toast;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Locale;public class MainActivity extends AppCompatActivity {    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate (savedInstanceState);        setContentVIEw (R.layout.activity_main);        Context context =getApplicationContext ();        Toast.makeText (context, "我是正版", Toast.LENGTH_SHORT).show ();        String cert_sha1="59F8A6B86A367F0586F1A15DDDB63D75263C5D62"; // 通过调试提前获取apk的sha1签名        boolean is_org_app = false;        try {            is_org_app = isOrgApp(context,cert_sha1);        } catch (PackageManager.nameNotFoundException e) {            e.printstacktrace ();        } catch (NoSuchAlgorithmException e) {            e.printstacktrace ();        }        if(!is_org_app){            androID.os.Process.killProcess ((androID.os.Process.myPID ()));            //如果签名不一致,说明程序被修改了,直接退出        }    }//比较签名    private boolean isOrgApp(Context context, String cert_sha1) throws PackageManager.nameNotFoundException, NoSuchAlgorithmException {        String current_sha1=getAppSha1(context);        current_sha1=current_sha1.replace (":","");        return cert_sha1.equals (current_sha1);    }//生成sha1的签名    private String getAppSha1(Context context) throws PackageManager.nameNotFoundException, NoSuchAlgorithmException {        PackageInfo info=context.getPackageManager ().getPackageInfo (context.getPackagename (),PackageManager.GET_SIGNATURES);        byte[] cert =info.signatures[0].toByteArray ();        MessageDigest md =MessageDigest.getInstance ("SHA1");        byte[] publicKey=md.digest (cert);        StringBuffer hexString =new StringBuffer ();        for(int i=0;i<publicKey.length;i++){            String appendString=Integer.toHexString (0xFF&publicKey[i]).toupperCase(Locale.US);            if(appendString.length ()==1){                hexString.append("0");            }            hexString.append(appendString);//签名的格式是11:22,所以需要加上":"            hexString.append (":");        }        String result=hexString.toString ();        return result.substring (0,result.length ()-1);    }}

完成程序后,使用AndroIDKiller进行修改时,将"我是正版"字符串修改成"我是盗版",在运行程序时,程序会直接闪退

3.3.2 在So层中进行签名验证

MainActivity:

package com.example.jnisignatureverify;import androIDx.appcompat.app.AppCompatActivity;import androID.content.Context;import androID.content.pm.PackageInfo;import androID.content.pm.PackageManager;import androID.os.Bundle;import androID.vIEw.VIEw;import androID.Widget.button;import androID.Widget.TextVIEw;import androID.Widget.Toast;import java.security.MessageDigest;import java.util.Locale;public class MainActivity extends AppCompatActivity {    // Used to load the 'native-lib' library on application startup.    static {        System.loadlibrary("native-lib");    }    protected TextVIEw appSignaturesTv;    protected TextVIEw jniSignaturesTv;    protected button checkBtn;    protected button tokenBtn;    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        super.setContentVIEw(R.layout.activity_main);        initVIEw();        appSignaturesTv.setText(getSha1Value(MainActivity.this));        jniSignaturesTv.setText(getSignaturesSha1(MainActivity.this));    }    private VIEw.OnClickListener clickListener = new VIEw.OnClickListener(){        @OverrIDe        public voID onClick(VIEw v) {            boolean result = checkSha1(MainActivity.this);            if(result){                Toast.makeText(getApplicationContext(),"验证通过",Toast.LENGTH_LONG).show();            }else{                Toast.makeText(getApplicationContext(),"验证不通过,请检查valID.cpp文件配置的sha1值",Toast.LENGTH_LONG).show();            }        }    };    private VIEw.OnClickListener tokenClickListener = new VIEw.OnClickListener(){        @OverrIDe        public voID onClick(VIEw v) {            String result = getToken(MainActivity.this,"12345");            Toast.makeText(getApplicationContext(),result,Toast.LENGTH_LONG).show();        }    };    private voID initVIEw() {        appSignaturesTv = (TextVIEw) findVIEwByID(R.ID.app_signatures_tv);        jniSignaturesTv = (TextVIEw) findVIEwByID(R.ID.jni_signatures_tv);        checkBtn = (button) findVIEwByID(R.ID.check_btn);        tokenBtn = (button) findVIEwByID(R.ID.token_btn);        checkBtn.setonClickListener(clickListener);        tokenBtn.setonClickListener(tokenClickListener);    }    /**     * A native method that is implemented by the 'native-lib' native library,     * which is packaged with this application.     */    public native String getSignaturesSha1(Context context);    public native boolean checkSha1(Context context);    public native String getToken(Context context,String userID);//获取apk当前的签名    public String getSha1Value(Context context) {        try {            PackageInfo info = context.getPackageManager().getPackageInfo(                    context.getPackagename(), PackageManager.GET_SIGNATURES);            byte[] cert = info.signatures[0].toByteArray();            MessageDigest md = MessageDigest.getInstance("SHA1");            byte[] publicKey = md.digest(cert);            StringBuffer hexString = new StringBuffer();            for (int i = 0; i < publicKey.length; i++) {                String appendString = Integer.toHexString(0xFF & publicKey[i])                        .toupperCase(Locale.US);                if (appendString.length() == 1)                    hexString.append("0");                hexString.append(appendString);            }            String result = hexString.toString();            return result.substring(0, result.length());        } catch (Exception e) {            e.printstacktrace();        }        return null;    }}

native-lib.cpp:

#include <stdio.h>#include <stdlib.h>#include <jni.h>#include <androID/log.h>#include <cstring>#define TAG    "jni-log"#define LOGD(...)  __androID_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)//签名信息const char *app_sha1="59F8A6B86A367F0586F1A15DDDB63D75263C5D62";const char hexcode[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};char* getSha1(jnienv *env, jobject context_object){    //上下文对象    jclass context_class = env->GetobjectClass(context_object);    //反射获取PackageManager    jmethodID methodID = env->getmethodID(context_class, "getPackageManager", "()LandroID/content/pm/PackageManager;");    jobject package_manager = env->CallObjectMethod(context_object, methodID);    if (package_manager == NulL) {        //LOGD("package_manager is NulL!!!");        return NulL;    }    //反射获取包名    methodID = env->getmethodID(context_class, "getPackagename", "()Ljava/lang/String;");    Jstring package_name = (Jstring)env->CallObjectMethod(context_object, methodID);    if (package_name == NulL) {        //LOGD("package_name is NulL!!!");        return NulL;    }    env->DeleteLocalRef(context_class);    //获取PackageInfo对象    jclass pack_manager_class = env->GetobjectClass(package_manager);    methodID = env->getmethodID(pack_manager_class, "getPackageInfo", "(Ljava/lang/String;I)LandroID/content/pm/PackageInfo;");    env->DeleteLocalRef(pack_manager_class);    jobject package_info = env->CallObjectMethod(package_manager, methodID, package_name, 0x40);    if (package_info == NulL) {        LOGD("getPackageInfo() is NulL!!!");        return NulL;    }    env->DeleteLocalRef(package_manager);    //获取签名信息    jclass package_info_class = env->GetobjectClass(package_info);    jfIEldID fIEldID = env->GetFIEldID(package_info_class, "signatures", "[LandroID/content/pm/Signature;");    env->DeleteLocalRef(package_info_class);    jobjectArray signature_object_array = (jobjectArray)env->GetobjectFIEld(package_info, fIEldID);    if (signature_object_array == NulL) {        LOGD("signature is NulL!!!");        return NulL;    }    jobject signature_object = env->GetobjectArrayElement(signature_object_array, 0);    env->DeleteLocalRef(package_info);    //签名信息转换成sha1值    jclass signature_class = env->GetobjectClass(signature_object);    methodID = env->getmethodID(signature_class, "toByteArray", "()[B");    env->DeleteLocalRef(signature_class);    jbyteArray signature_byte = (jbyteArray) env->CallObjectMethod(signature_object, methodID);    jclass byte_array_input_class=env->FindClass("java/io/ByteArrayinputStream");    methodID=env->getmethodID(byte_array_input_class,"<init>","([B)V");    jobject byte_array_input=env->NewObject(byte_array_input_class,methodID,signature_byte);    jclass certificate_factory_class=env->FindClass("java/security/cert/CertificateFactory");    methodID=env->GetStaticmethodID(certificate_factory_class,"getInstance","(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");    Jstring x_509_Jstring=env->NewStringUTF("X.509");    jobject cert_factory=env->CallStaticObjectMethod(certificate_factory_class,methodID,x_509_Jstring);    methodID=env->getmethodID(certificate_factory_class,"generateCertificate",("(Ljava/io/inputStream;)Ljava/security/cert/Certificate;"));    jobject x509_cert=env->CallObjectMethod(cert_factory,methodID,byte_array_input);    env->DeleteLocalRef(certificate_factory_class);    jclass x509_cert_class=env->GetobjectClass(x509_cert);    methodID=env->getmethodID(x509_cert_class,"getEncoded","()[B");    jbyteArray cert_byte=(jbyteArray)env->CallObjectMethod(x509_cert,methodID);    env->DeleteLocalRef(x509_cert_class);    jclass message_digest_class=env->FindClass("java/security/MessageDigest");    methodID=env->GetStaticmethodID(message_digest_class,"getInstance","(Ljava/lang/String;)Ljava/security/MessageDigest;");    Jstring sha1_Jstring=env->NewStringUTF("SHA1");    jobject sha1_digest=env->CallStaticObjectMethod(message_digest_class,methodID,sha1_Jstring);    methodID=env->getmethodID(message_digest_class,"digest","([B)[B");    jbyteArray sha1_byte=(jbyteArray)env->CallObjectMethod(sha1_digest,methodID,cert_byte);    env->DeleteLocalRef(message_digest_class);    //转换成char    Jsize array_size=env->GetArrayLength(sha1_byte);    jbyte* sha1 =env->GetByteArrayElements(sha1_byte,NulL);    char *hex_sha=new char[array_size*2+1];    for (int i = 0; i <array_size ; ++i) {        hex_sha[2*i]=hexcode[((unsigned char)sha1[i])/16];        hex_sha[2*i+1]=hexcode[((unsigned char)sha1[i])%16];    }    hex_sha[array_size*2]='<?xml version="1.0" enCoding="utf-8"?><linearLayout    xmlns:androID="http://schemas.androID.com/apk/res/androID"    xmlns:app="http://schemas.androID.com/apk/res-auto"    xmlns:tools="http://schemas.androID.com/tools"    androID:layout_margin="16dp"    androID:layout_wIDth="match_parent"    androID:layout_height="match_parent"    androID:orIEntation="vertical"    tools:context="com.example.jnisignatureverify.MainActivity">    <linearLayout        androID:layout_wIDth="match_parent"        androID:orIEntation="vertical"        androID:layout_height="wrap_content">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="APP签名信息:"/>        <TextVIEw            androID:ID="@+ID/app_signatures_tv"            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"/>    </linearLayout>    <linearLayout        androID:layout_margintop="16dp"        androID:orIEntation="vertical"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="jni配置的签名信息:"/>        <TextVIEw            androID:ID="@+ID/jni_signatures_tv"            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"/>    </linearLayout>    <button        androID:ID="@+ID/check_btn"        androID:layout_margintop="16dp"        androID:text="验证签名是否正确"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"/>    <button        androID:ID="@+ID/token_btn"        androID:layout_margintop="16dp"        androID:text="获取token"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"/></linearLayout>';    LOGD("hex_sha %s ",hex_sha);    return hex_sha;}jboolean checkValIDity(jnienv *env,char *sha1){    //比较签名    if (strcmp(sha1,app_sha1)==0)    {        LOGD("验证成功");        return true;    }    LOGD("验证失败");    return false;}extern "C"JNIEXPORT Jstring JNICALLJava_com_example_jnisignatureverify_MainActivity_getSignaturesSha1(jnienv *env, jobject thiz,                                                                   jobject context) {    // Todo: implement getSignaturesSha1()    return env->NewStringUTF(app_sha1);}extern "C"JNIEXPORT jboolean JNICALLJava_com_example_jnisignatureverify_MainActivity_checkSha1(jnienv *env, jobject thiz,                                                           jobject contextObject) {    // Todo: implement checkSha1()    char *sha1 = getSha1(env,contextObject);    jboolean result = checkValIDity(env,sha1);    return result;}extern "C"JNIEXPORT Jstring JNICALLJava_com_example_jnisignatureverify_MainActivity_getToken(jnienv *env, jobject thiz,                                                          jobject contextObject, Jstring user_ID) {    // Todo: implement getToken()    char *sha1 = getSha1(env,contextObject);    jboolean result = checkValIDity(env,sha1);    if(result){        return env->NewStringUTF("获取Token成功");    }else{        return env->NewStringUTF("获取失败,请检查native-lib.cpp文件配置的sha1值");    }}

activity_main.xml:

点赞收藏分享文章举报

李政辉发布了3 篇原创文章 · 获赞 3 · 访问量 64私信 关注 总结

以上是内存溢出为你收集整理的安卓加固基础全部内容,希望文章能够帮你解决安卓加固基础所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1065977.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存