Android应用安全方面的设计

本文详细介绍了SO文件和Keystore两种加解密工具的原理与实现过程,探讨了它们在Android应用安全防护中的作用。SO文件加解密通过包名签名和预置密钥进行校验,而Keystore利用硬件级存储实现加解密,确保密钥的安全。文章还分析了两种方法的优缺点,并提供了GitHub上的示例代码。

1.so文件加解密工具

1.原理

1.通过将app的包名签名签名的hash值预置到c++写的代码里面

2.将上述信息和object获取到的调用app的包名签名以及hash进行对比进行校验

3.在c++层里面预置非对称加密的公钥或者对称加密的密钥。

4.选择加密算法

5.将加密结果返回调用层,同时释放占用的内存

6.需要注意的是:

需要防止调试

防止内存获取

 

2.代码

GitHub地址:

 

2.keystore加解密工具

1.原理

keystore是Android系统提供硬件级别的存储加解密工具又存储加解析密钥的工具。

keystore加密一次会将加解密工具和密钥存在keychain中。keychain具有硬件级别的安全功能.

keychain既取不出来也无法在其他手机上使用,本手机上也必须初始化运行过一次。

2.代码

github地址:https://github.com/MatrixSpring/EncryptApp

关键代码:

public class KeyStoreUtils {
    private static KeyStoreUtils sKeyUtils;
    private static String AES_CBC = "AES/CBC/PKCS7Padding";
    KeyStore keyStore;
    KeyGenerator keyGenerator;
    Cipher cipher;
    public static String alias = "12345678";
    public static int iv_length = 32;

    private KeyStoreUtils(){
        createKey(alias);

    }

    public static KeyStoreUtils getInstance(){
        if(sKeyUtils == null){
            synchronized (KeyStoreUtils.class){
                if(sKeyUtils == null){
                    sKeyUtils = new KeyStoreUtils();
                }
            }
        }
        return sKeyUtils;
    }

    public void createKey(String keyAlias) { //I call this method only once in the onCreate() method of another activity, with keyAlias "A"
        try {
            keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
            cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            keyStore.load(null);
            keyGenerator.init(
                    new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                            .setUserAuthenticationRequired(false)
                            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                            .setRandomizedEncryptionRequired(false)
                            .build());
            keyGenerator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String encrypt(String plainText){ //I call this method many times with the same keyAlias "A" and same plaintext in the same activity
        try {
            keyStore.load(null);
            SecretKey key = (SecretKey) keyStore.getKey(alias, null);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            //return byteToHex(cipher.doFinal(plainText.getBytes()));
            String ivStr = byteToHex(cipher.getIV());
            Log.d("11111111","11111111 : "+cipher.getIV()+" 1: "+cipher.getIV().length+" 2: "+ivStr.length());
            return ivStr + byteToHex(cipher.doFinal(plainText.getBytes()));
        }catch(Exception e){
            e.printStackTrace();
        }
        return "BUG";
    }


    public String decrypt(String strTemp){
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(AES_CBC);
            SecretKey key = (SecretKey) keyStore.getKey(alias, null);
            String ivStr = strTemp.substring(0,iv_length);
            String cryptStr = strTemp.substring(iv_length);
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(hexStringToByteArray(ivStr));
            cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
            byte[] original = cipher.doFinal(hexStringToByteArray(cryptStr));

            return new String(original);
        } catch (Exception e) {
            e.printStackTrace();
        }


        return "";
    }

    private String byteToHex(byte[] byteArray){
        StringBuilder buf = new StringBuilder();
        for (byte b : byteArray)
            buf.append(String.format("%02X", b));
        String hexStr = buf.toString();
        return hexStr;
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] b = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            // 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节
            b[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character
                    .digit(s.charAt(i + 1), 16));
        }
        return b;
    }
}

 

3.加解密的优缺点

1.so文件的优缺点

1.通过反编译绕过包名和签名检测

2.keystore的优缺点

2.如果有些手机有些特殊功能比如手机克隆,或者是app升级需要特殊处理一下。

4.加密解密的算法类型

5.

{************************************************************** 浅谈软件安全设计(一) code by 黑夜彩虹 & vxin with almost pure delphi 网站:http://soft.eastrise.net 2007-03-07 --- 转载时请保留作者信息。 **************************************************************} 此CM的设计模式: 1、插入一些花指令 2、写了一些代码迷惑Cracker 3、有简单的Anti_DEDE 和检测调试器 话不多说,请看以下代码: unit main; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,strutils; Const C1= 17856; C2= 23589; type TForm1 = class(TForm) Image1: TImage; Edit1: TEdit; Label1: TLabel; Label2: TLabel; Edit2: TEdit; Button1: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} Procedure Anti_DeDe();//检测DEDE反编译器 var DeDeHandle:THandle; i:integer; begin DeDeHandle:=FindWindow(nil,chr($64)+chr($65)+chr($64)+chr($65)); if DeDeHandle0 then begin For i:=1 to 4500 do SendMessage(DeDeHandle,WM_CLOSE,0,0); end; end; Function ABC42():Boolean; //检测调试器; var YInt,NInt:Integer; begin asm mov eax,fs:[30h] movzx eax,byte ptr[eax+2h] or al,al jz @No jnz @Yes @No: mov NInt,1 @Yes: Mov YInt,1 end; if YInt=1 then Result:=True; if NInt=1 then Result:=False; end; function EncryptModule(SourceStr:String;Key:Word;N:Integer):String; var //加密函数 I:Integer; begin SetLength(Result,Length(SourceStr));//利用SetLength函数指定密文长度 //对每一个索引元素进行变换 for I:=1 to Length(SourceStr) do begin Result[I]:=Char(byte(SourceStr[I]) xor (Key Shr N)); Key:= (byte(Result[I]) + Key)*C1+C2; end; end; //==========以下是549的函数,据说没有暴破点,顺便试一试 //========函数作用:动态改变程序运行罗辑 function GetEIP: Integer;//自动生成address的方法 asm mov eax, [esp]; sub eax, 5; //call GetEIP占用5字节 end; function PatchOneItem(PatchItem: String): Boolean; var PatchAddress: Integer; PatchLength: DWord; PatchData: Pointer; PatchDataStr: String; i: Integer; PatchByte: Byte; PID, PHandle: THandle; WriteCount: DWord; begin Result := False; if Length(PatchItem) < 11 then Exit; PatchAddress := StrToInt(\'0x\' + LeftStr(PatchItem, 8)); for i := 1 to Length(PatchItem) do begin if PatchItem[i] \' \' then PatchDataStr := PatchDataStr + PatchItem[i]; end; PatchLength := (Length(PatchDataStr) - 9) div 2; GetMem(PatchData, PatchLength); try for i := 0 to PatchLength - 1 do begin PatchByte := StrToInt(\'0x\'+PatchDataStr[10 + i * 2] + PatchDataStr[10 + i * 2 + 1]); Byte(Pointer(Integer(PatchData) + i)^) := PatchByte; end; GetWindowThreadProcessId(Application.Handle, PID); PHandle := OpenProcess(PROCESS_ALL_ACCESS, False, PID); WriteProcessMemory(PHandle, Pointer(PatchAddress), PatchData, PatchLength, WriteCount); CloseHandle(PHandle); finally FreeMem(PatchData, PatchLength); end; Result := PatchLength = WriteCount; end; procedure Patch(PatchFile: String); var PatchItems: TStrings; PatchIndex: Integer; begin if not FileExists(PatchFile) then Exit; PatchItems := TStringList.Create; try PatchItems.LoadFromFile(PatchFile); for PatchIndex := 0 to PatchItems.Count - 1 do begin PatchOneItem(PatchItems[PatchIndex]); end; finally PatchItems.Free; end; end; procedure TForm1.FormCreate(Sender: TObject); begin Anti_DeDe; //检测DEDE,检测到关闭它。 if ABC42 then ExitProcess(0); //检测调试器,终止。 end; procedure TForm1.Button1Click(Sender: TObject); //注册按纽,开始检测 begin //========在这里插入一些花指令 asm jz @Start jnz @Start db 0E8h, 24h, 0, 0 ; db 0, 8Bh, 44h, 24h db 4, 8Bh, 0, 3Dh db 4, 0, 0, 80h db 75h, 8, 8Bh, 64h db 24h, 8, 0EBh, 4 db 58h, 0EBh, 0Ch, 0E9h db 64h, 8Fh, 5, 0 db 0, 0, 0, 74h db 0F3h, 75h, 0F1h, 0EBh db 24h, 64h, 0FFh, 35h db 0, 0, 0, 0 db 0EBh, 12h, 0FFh, 9Ch db 74h, 3, 75h, 1 db 0E9h, 81h, 0Ch, 24h db 0, 1, 0, 0 db 9Dh, 90h, 0EBh, 0F4h db 64h, 89h, 25h, 0 db 0, 0, 0, 0EBh db 0E6h db 0EBh, 1, 0Fh, 31h ; db 0F0h, 0EBh, 0Ch, 33h db 0C8h, 0EBh, 3, 0EBh db 9, 0Fh, 59h, 74h db 5, 75h, 0F8h, 51h db 0EBh, 0F1h db 0B9h, 4, 0, 0 ; @Start: end; if length(edit2.Text)>3 then //比较大于3位 begin //============再写一些骗Cracker if edit2.Text=EncryptModule(Edit1.Text,12345,10) then begin showmessage(\'请重启本软件。\'); //=======还可以再写一些记号,这里我就不写了 end; PatchOneItem(edit2.Text); //真正的比较 showmessage(\'ok\'); //弹出OK对话框 end; end; end.  //====附件为CM,会破解的兄弟可以试试其强度....
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MatrixData

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值