android安全问题——浅谈

来源:互联网 发布:usb网络通道 编辑:程序博客网 时间:2024/06/10 04:00

最近在研究安卓安全问题,在网上找了很多资料,杂乱,也不知道是不是我能力问题,反正没有找到类似一个稍微来说全一点,而且又可以直接拿来参考的资料(个人比较懒,喜欢直接拿来用,见谅)。以下是我单方面的见解,如有不正确的地方,希望大家多多指教,相互交流,相互学习,共同进步奋斗

正传:安卓安全问题,总体分为三点:代码安全,资源安全,代码加固。

一,首先说下代码安全,主要是java代码安全,现在主要用到的技术为代码混淆(据说没什么卵用),本着有总比没有好的原则,还是起到一定作用的,起码扰乱了对方的思维,消耗对方的时间去理解代码,后面还会讲到加壳技术(不能正确的反编译出java代码)。案例如下:


-optimizationpasses 5  #指定代码的压缩级别 0 - 7  
-dontusemixedcaseclassnames  #是否使用大小写混合  
-dontskipnonpubliclibraryclasses  #如果应用程序引入的有jar包,并且想混淆jar包里面的class  
-dontpreverify  #混淆时是否做预校验(可去掉加快混淆速度) 
-ignorewarnings #这1句是屏蔽警告,脚本中把这行注释去掉 
-verbose #混淆时是否记录日志(混淆后生产映射文件 map 类名 -> 转化后类名的映射  
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*  #淆采用的算法  
  
#如果加入jar包,必须在此写一个-libraryjars libs/XX.jar
-libraryjars libs/BaiduLBS_Android.jar
-libraryjars libs/fastjson-1.1.37.jar


-dontwarn  #dontwarn去掉警告
-dontskipnonpubliclibraryclassmembers


-keep public class * extends Android.app.Activity  #所有activity的子类不要去混淆  
-keep public class * extends android.app.Application  
-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 com.android.vending.licensing.ILicensingService #指定具体类不要去混淆
-keep class com.shixu.faceapp.model.** { *; }
  
-keepclasseswithmembernames class * {  
    native <methods>;  #保持 native 的方法不去混淆  
}  
  
-keepclasseswithmembers class * {  
    public <init>(android.content.Context, android.util.AttributeSet);  #保持自定义控件类不被混淆,指定格式的构造方法不去混淆  
}  
  
-keepclasseswithmembers class * {  
    public <init>(android.content.Context, android.util.AttributeSet, int);  
}  
  
-keepclassmembers class * extends android.app.Activity {   
    public void *(android.view.View); #保持指定规则的方法不被混淆(Android layout 布局文件中为控件配置的onClick方法不能混淆)  
}  
  
-keep public class * extends android.view.View {  #保持自定义控件指定规则的方法不被混淆  
    public <init>(android.content.Context);  
    public <init>(android.content.Context, android.util.AttributeSet);  
    public <init>(android.content.Context, android.util.AttributeSet, int);  
    public void set*(...);  
}  
  
-keepclassmembers enum * {  #保持枚举 enum 不被混淆  
    public static **[] values();  
    public static ** valueOf(java.lang.String);  
}  
  
-keep class * implements android.os.Parcelable {  #保持 Parcelable 不被混淆(aidl文件不能去混淆)  
    public static final android.os.Parcelable$Creator *;  


-keepnames class * implements java.io.Serializable #需要序列化和反序列化的类不能被混淆(注:Java反射用到的类也不能被混淆)  
  
-keepclassmembers class * implements java.io.Serializable { #保护实现接口Serializable的类中,指定规则的类成员不被混淆  
    static final long serialVersionUID;  
    private static final java.io.ObjectStreamField[] serialPersistentFields;  
    !static !transient <fields>;  
    private void writeObject(java.io.ObjectOutputStream);  
    private void readObject(java.io.ObjectInputStream);  
    java.lang.Object writeReplace();  
    java.lang.Object readResolve();  
}  
  
-keepattributes Signature  #过滤泛型(不写可能会出现类型转换错误,一般情况把这个加上就是了)  
  
-keepattributes *Annotation*  #假如项目中有用到注解,应加入这行配置  
  
-keep class **.R$* { *; }  #保持R文件不被混淆,否则,你的反射是获取不到资源id的  
  
-keep class **.Webview2JsInterface { *; }  #保护WebView对HTML页面的API不被混淆  
-keepclassmembers class * extends android.webkit.WebViewClient {  #如果你的项目中用到了webview的复杂操作 ,最好加入  
     public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap);  
     public boolean *(android.webkit.WebView,java.lang.String);  
}  
-keepclassmembers class * extends android.webkit.WebChromeClient {  #如果你的项目中用到了webview的复杂操作 ,最好加入  
     public void *(android.webkit.WebView,java.lang.String);  
}  
#对WebView的简单说明下:经过实战检验,做腾讯QQ登录,如果引用他们提供的jar,若不加防止WebChromeClient混淆的代码,oauth认证无法回调,反编译基代码后可看到他们有用到WebChromeClient,加入此代码即可。  
  
-keepclassmembernames class com.cgv.cn.movie.common.bean.** { *; }  #转换JSON的JavaBean,类成员名称保护,使其不被混淆


注:

1.可将以上代码拷贝到项目的proguard-project.txt文件中,并且在project.properties将proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt前面的#去掉即可使用。

2.如果用到fastjson或者Gjson,代码混淆会出现找不到对象的问题,在这里需要不混淆model实体类处理,如以上代码:-keep class com.shixu.faceapp.model.** { *; }

3.以上代码不一定满足所有人的需求,网上很多相关方面的代码,可以自行拷贝 

4.如果代码不正确签名打包会报错,这里不要急,查看报错原因,慢慢调试,如果实在不行,换代码再试奋斗


二,java代码加壳技术

加壳技术原理如图所示:


图片也是我从网上找的,原理不多说,看图,如果想详细了解原理(我具体也不是很懂,再次不自我补课了,后期会深入研究学习),可以去百度一下,网上一大坨,我只说我在这里怎么用。案例如下:

/*
 * 文 件 名:  AdnroidShell.java
 * 版    权:  Anhui Shixu Intelligent Technology CO.,LTD. Copyright YYYY-YYYY,  All rights reserved
 * 描    述:  <描述>
 * 修 改 人:  gaoqiang
 * 修改时间:  2016-9-14
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.Adler32;


/**
 * 
 * apk加壳技术运用
 * 
 * @author gaoqiang
 * @version [版本号, 2016-9-14]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class AdnroidShell
{
    /**
     * @param args
     */
    public static void main(String[] args)
    {
        // TODO Auto-generated method stub
        try
        {
            File payloadSrcFile = new File("force/**.apk"); // 需要加壳的程序
            System.out.println("apk size:" + payloadSrcFile.length());
            File unShellDexFile = new File("force/**.dex"); // 解客dex
            byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));// 以二进制形式读出apk,并进行加密处理//对源Apk进行加密操作
            byte[] unShellDexArray = readFileBytes(unShellDexFile);// 以二进制形式读出dex
            int payloadLen = payloadArray.length;
            int unShellDexLen = unShellDexArray.length;
            int totalLen = payloadLen + unShellDexLen + 4;// 多出4字节是存放长度的。
            byte[] newdex = new byte[totalLen]; // 申请了新的长度
            // 添加解壳代码
            System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);// 先拷贝dex内容
            // 添加加密后的解壳数据
            System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen);// 再在dex内容后面拷贝apk的内容
            // 添加解壳数据长度
            System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen - 4, 4);// 最后4为长度
            // 修改DEX file size文件头
            fixFileSizeHeader(newdex);
            // 修改DEX SHA1 文件头
            fixSHA1Header(newdex);
            // 修改DEX CheckSum文件头
            fixCheckSumHeader(newdex);
            String str = "force/classes.dex";
            File file = new File(str);
            if (!file.exists())
            {
                file.createNewFile();
            }
            FileOutputStream localFileOutputStream = new FileOutputStream(str);
            localFileOutputStream.write(newdex);
            localFileOutputStream.flush();
            localFileOutputStream.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
    // 直接返回数据,读者可以添加自己加密方法
    private static byte[] encrpt(byte[] srcdata)
    {
        for (int i = 0; i < srcdata.length; i++)
        {
            srcdata[i] = (byte)(0xFF ^ srcdata[i]);
        }
        return srcdata;
    }
    
    /**
     * 修改dex头,CheckSum 校验码
     * 
     * @param dexBytes
     */
    private static void fixCheckSumHeader(byte[] dexBytes)
    {
        Adler32 adler = new Adler32();
        adler.update(dexBytes, 12, dexBytes.length - 12);// 从12到文件末尾计算校验码
        long value = adler.getValue();
        int va = (int)value;
        byte[] newcs = intToByte(va);
        // 高位在前,低位在前掉个个
        byte[] recs = new byte[4];
        for (int i = 0; i < 4; i++)
        {
            recs[i] = newcs[newcs.length - 1 - i];
            System.out.println(Integer.toHexString(newcs[i]));
        }
        System.arraycopy(recs, 0, dexBytes, 8, 4);// 效验码赋值(8-11)
        System.out.println(Long.toHexString(value));
        System.out.println();
    }
    
    /**
     * int 转byte[]
     * 
     * @param number
     * @return
     */
    public static byte[] intToByte(int number)
    {
        byte[] b = new byte[4];
        for (int i = 3; i >= 0; i--)
        {
            b[i] = (byte)(number % 256);
            number >>= 8;
        }
        return b;
    }
    
    /**
     * 修改dex头 sha1值
     * 
     * @param dexBytes
     * @throws NoSuchAlgorithmException
     */
    private static void fixSHA1Header(byte[] dexBytes)
        throws NoSuchAlgorithmException
    {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(dexBytes, 32, dexBytes.length - 32);// 从32为到结束计算sha--1
        byte[] newdt = md.digest();
        System.arraycopy(newdt, 0, dexBytes, 12, 20);// 修改sha-1值(12-31)
        // 输出sha-1值,可有可无
        String hexstr = "";
        for (int i = 0; i < newdt.length; i++)
        {
            hexstr += Integer.toString((newdt[i] & 0xff) + 0x100, 16).substring(1);
        }
        System.out.println(hexstr);
    }
    
    /**
     * 修改dex头 file_size值
     * 
     * @param dexBytes
     */
    private static void fixFileSizeHeader(byte[] dexBytes)
    {
        // 新文件长度
        byte[] newfs = intToByte(dexBytes.length);
        System.out.println(Integer.toHexString(dexBytes.length));
        byte[] refs = new byte[4];
        // 高位在前,低位在前掉个个
        for (int i = 0; i < 4; i++)
        {
            refs[i] = newfs[newfs.length - 1 - i];
            System.out.println(Integer.toHexString(newfs[i]));
        }
        System.arraycopy(refs, 0, dexBytes, 32, 4);// 修改(32-35)
    }
    
    /**
     * 以二进制读出文件内容
     * 
     * @param file
     * @return
     * @throws IOException
     */
    private static byte[] readFileBytes(File file)
        throws IOException
    {
        byte[] arrayOfByte = new byte[1024];
        ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
        FileInputStream fis = new FileInputStream(file);
        while (true)
        {
            int i = fis.read(arrayOfByte);
            if (i != -1)
            {
                localByteArrayOutputStream.write(arrayOfByte, 0, i);
            }
            else
            {
                return localByteArrayOutputStream.toByteArray();
            }
        }
    }
}


操作步骤:

1.将以上代码拷贝到java项目中,并且新建force文件夹(和src文件夹同级别);

2.运行你自己的app项目,将bin里面的apk和classes.dex(必须改名例如和apk名称相同)拷贝到java项目中的force文件夹中,并且把java项目中的force/**.apk和force/**.dex名称换成你自己的名称并且运行java代码,F5刷新下foce文件夹会得到新的classes.dex;

3.将classes.dex文件拷贝回app项目中的bin文件下,然后打包签名即可。

备注:

1.加壳技术我也是在网上找的,我比较笨,他说的我没怎么看懂(https://my.oschina.net/u/2323218/blog/393372),我用自己的方法同样可以实现app加壳;

2.网上搜了说加壳技术可以用一些比较好的算法,这样安全系数更高,但是毕竟才看了那么小几天,所以没时间去具体看,后期我也会深入研究的,如果有好的意见和建议,非常欢迎大家给我指正,指导。


三,app资源保护

这个问题困扰了我好久,我搞了好几天思路倒是出来好几个,但是网上搜了一大片,反正具体的做法目前还没有推敲出来(后期同样自己会深入研究)。

大体思路如下:

1.资源以二进制流形式保存,并且加密;

2.使用AAPT技术,同样是二进制流形式操作。

困惑:最大的困惑是我在网上下载了QQ,微信,美团等等Apk,但是我使用apktool反编译,只能编译出smali文件(网上搜了一大圈,没找到具体思路),这是为什么,天啦,为什么????谁能告诉我大哭


但是我也有解决办法得意

那就是用爱加密,百度一下爱加密,注意啦,又不要钱的哈,不要钱不用白不用,不要那么傻。能走捷径就走,心里非常不爽,想把这个技术研究透搞(想我这样的),但是这样节约时间,回头有时间在去研究是一样地,说不定还有意外收获。说说大体做法吧:

1.爱加密注册,登录......省略一万字,自己去想;

2.用户个人中心有个加密记录,将自己打包签名的apk(注意啦,以上代码混淆和加壳技术都完成啦),提交加密


输入相关信息,加密应该不会超过半小时,反正我的10分钟内就加密成功。然后再列表中下载加密后的apk

2.爱加密后的apk不能直接用,需要用爱加密自己的签名工具再次签名打包(自己在里面下载apk签名工具)


3.下载签名工具之后打包下apk就OK啦,然后测试下,测试没问题就可以引用啦~



以上就是我一个星期的成果,其实不算自己研发,应该算是对前辈们的小总结,如果有什么不对的地方非常欢迎和大家交流,我的QQ:1105107264。后期同样还会深入研究安全方面的问题。

1 0
原创粉丝点击