首页
社区
课程
招聘
[原创]android系统加固的思路及实现
发表于: 2018-6-28 19:15 5593

[原创]android系统加固的思路及实现

2018-6-28 19:15
5593

本人安全与逆向都是菜鸟一名,在做智能硬件的开发,前段时间发现应用组那边使用第三方付费加固方案,我就觉得奇怪了,apk是跑在我们自己开发的智能硬件上,还用得着用第三方方案吗?我个人觉得第三方加固方案基本上都是针对公开的apk进行加固的,他们的方案不一定比自己配合android系统做的加固方案好吧。于是乎,半个月前开始上网看一些破解与逆向的帖子,文章。于是就根据他们的破解逆向思路,设计了我自己的加固方案。放上来是想向各位大神求助几个问题,以及想求证一下,我这样子做是否真的对加固有帮助,是否有意义。


1.dex加固
我看文章,java的逆向,基本上都是逆向dex。既然逆这个,那就针对这点做文章吧。我是在android4.4上做开发,用的dalvik虚拟机。
apk编译时dx工具把.class文件转换成dex文件,那我就改dx工具源码就行了。实现也比较简单,修改了magic,以及判断了magic,如果是
我修改过的就对dex进行加密。
diff --git a/android/dalvik/dx/src/com/android/dx/dex/DexOptions.java b/android/dalvik/dx/src/com/android/dx/dex/DexOptions.java
old mode 100644
new mode 100755
index db0c7a2..66e00a9
--- a/android/dalvik/dx/src/com/android/dx/dex/DexOptions.java
+++ b/android/dalvik/dx/src/com/android/dx/dex/DexOptions.java
@@ -29,10 +29,44 @@ public class DexOptions {
     public boolean forceJumbo = false;
 
     /**
+     * API level to target in order to produce the most modern file
+     * format
+     */
+    public static final int API_CURRENT = 14;
+
+    /** API level to target in order to suppress extended opcode usage */
+    public static final int API_NO_EXTENDED_OPCODES = 13;
+
+    /** common prefix for all dex file "magic numbers" */
+    public static final String MAGIC_PREFIX = "dox\n";
+
+    /** common suffix for all dex file "magic numbers" */
+    public static final String MAGIC_SUFFIX = "\0";
+
+    /** dex file version number for the current format variant */
+    public static final String VERSION_CURRENT = "036";
+
+    /** dex file version number for API level 13 and earlier */
+    public static final String VERSION_FOR_API_13 = "035";
+
+    public static String apiToMagic(int targetApiLevel) {
+        String version;
+
+        if (targetApiLevel >= API_CURRENT) {
+            version = VERSION_CURRENT;
+        } else {
+            version = VERSION_FOR_API_13;
+        }
+
+        return MAGIC_PREFIX + version + MAGIC_SUFFIX;
+    }
+
+    /**
      * Gets the dex file magic number corresponding to this instance.
      */
     public String getMagic() {
-        return DexFormat.apiToMagic(targetApiLevel);
+    	return DexFormat.apiToMagic(targetApiLevel);
+//        return apiToMagic(targetApiLevel);
     }
 
     /**
diff --git a/android/dalvik/dx/src/com/android/dx/dex/file/DexFile.java b/android/dalvik/dx/src/com/android/dx/dex/file/DexFile.java
old mode 100644
new mode 100755
index 01a5e4b..83c548c
--- a/android/dalvik/dx/src/com/android/dx/dex/file/DexFile.java
+++ b/android/dalvik/dx/src/com/android/dx/dex/file/DexFile.java
@@ -36,6 +36,10 @@ import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.zip.Adler32;
 
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+
 /**
  * Representation of an entire {@code .dex} (Dalvik EXecutable)
  * file, which itself consists of a set of Dalvik classes.
@@ -589,6 +593,11 @@ public final class DexFile {
         calcSignature(barr);
         calcChecksum(barr);
 
+		String magic = getDexOptions().getMagic();
+		if(magic.startsWith("dox")) {
+			encryptDex(barr);
+		}
+
         if (annotate) {
             wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM,
                     "\nmethod code index:\n\n");
@@ -660,4 +669,21 @@ public final class DexFile {
         bytes[10] = (byte) (sum >> 16);
         bytes[11] = (byte) (sum >> 24);
     }
+
+    private static void encryptDex(byte[] bytes) {
+    	try {
+			byte[] tmp = new byte[bytes.length];
+			System.arraycopy(bytes, 0, tmp, 0, bytes.length);
+			SecretKeySpec keySpec = new SecretKeySpec("myencryptokeyAES".getBytes(), "AES");
+			IvParameterSpec ivspec = new IvParameterSpec("AESmyencryptokey".getBytes());
+			Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
+			int Len = (bytes.length-0x70)/16;
+			int iLen = Len*16;
+			cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);
+			cipher.doFinal(tmp, bytes.length-iLen, iLen, bytes, bytes.length-iLen);
+    	} catch (Exception ex) {
+			throw new RuntimeException(ex);
+    	}
+    }
+
 }

diff --git a/android/dalvik/dx/src/com/android/dx/dex/DexOptions.java b/android/dalvik/dx/src/com/android/dx/dex/DexOptions.java
old mode 100644
new mode 100755
index db0c7a2..66e00a9
--- a/android/dalvik/dx/src/com/android/dx/dex/DexOptions.java
+++ b/android/dalvik/dx/src/com/android/dx/dex/DexOptions.java
@@ -29,10 +29,44 @@ public class DexOptions {
     public boolean forceJumbo = false;
 
     /**
+     * API level to target in order to produce the most modern file
+     * format
+     */
+    public static final int API_CURRENT = 14;
+
+    /** API level to target in order to suppress extended opcode usage */
+    public static final int API_NO_EXTENDED_OPCODES = 13;
+
+    /** common prefix for all dex file "magic numbers" */
+    public static final String MAGIC_PREFIX = "dox\n";
+
+    /** common suffix for all dex file "magic numbers" */
+    public static final String MAGIC_SUFFIX = "\0";
+
+    /** dex file version number for the current format variant */
+    public static final String VERSION_CURRENT = "036";
+
+    /** dex file version number for API level 13 and earlier */
+    public static final String VERSION_FOR_API_13 = "035";
+
+    public static String apiToMagic(int targetApiLevel) {
+        String version;
+
+        if (targetApiLevel >= API_CURRENT) {
+            version = VERSION_CURRENT;
+        } else {
+            version = VERSION_FOR_API_13;
+        }
+
+        return MAGIC_PREFIX + version + MAGIC_SUFFIX;
+    }
+
+    /**
      * Gets the dex file magic number corresponding to this instance.
      */
     public String getMagic() {
-        return DexFormat.apiToMagic(targetApiLevel);
+    	return DexFormat.apiToMagic(targetApiLevel);
+//        return apiToMagic(targetApiLevel);
     }
 
     /**
diff --git a/android/dalvik/dx/src/com/android/dx/dex/file/DexFile.java b/android/dalvik/dx/src/com/android/dx/dex/file/DexFile.java
old mode 100644
new mode 100755
index 01a5e4b..83c548c
--- a/android/dalvik/dx/src/com/android/dx/dex/file/DexFile.java
+++ b/android/dalvik/dx/src/com/android/dx/dex/file/DexFile.java
@@ -36,6 +36,10 @@ import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.zip.Adler32;
 
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+
 /**
  * Representation of an entire {@code .dex} (Dalvik EXecutable)
  * file, which itself consists of a set of Dalvik classes.
@@ -589,6 +593,11 @@ public final class DexFile {
         calcSignature(barr);
         calcChecksum(barr);
 
+		String magic = getDexOptions().getMagic();
+		if(magic.startsWith("dox")) {
+			encryptDex(barr);
+		}
+
         if (annotate) {
             wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM,
                     "\nmethod code index:\n\n");
@@ -660,4 +669,21 @@ public final class DexFile {
         bytes[10] = (byte) (sum >> 16);
         bytes[11] = (byte) (sum >> 24);
     }
+
+    private static void encryptDex(byte[] bytes) {
+    	try {
+			byte[] tmp = new byte[bytes.length];
+			System.arraycopy(bytes, 0, tmp, 0, bytes.length);
+			SecretKeySpec keySpec = new SecretKeySpec("myencryptokeyAES".getBytes(), "AES");
+			IvParameterSpec ivspec = new IvParameterSpec("AESmyencryptokey".getBytes());
+			Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
+			int Len = (bytes.length-0x70)/16;
+			int iLen = Len*16;
+			cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);
+			cipher.doFinal(tmp, bytes.length-iLen, iLen, bytes, bytes.length-iLen);
+    	} catch (Exception ex) {
+			throw new RuntimeException(ex);
+    	}
+    }
+
 }

2.dalvik解密dex
既然dex被加密了,那么dalvik就需要做解密操作,解密也比较简单,判断一下magic,发现是加密过的就进行解密。
diff --git a/android/dalvik/libdex/DexSwapVerify.cpp b/android/dalvik/libdex/DexSwapVerify.cpp
old mode 100644
new mode 100755
index ff47ab5..aac9f3b
--- a/android/dalvik/libdex/DexSwapVerify.cpp
+++ b/android/dalvik/libdex/DexSwapVerify.cpp
@@ -31,6 +31,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <openssl/aes.h>
+#include <openssl/pkcs7.h>
+
 #ifndef __BYTE_ORDER
 # error "byte ordering not defined"
 #endif
@@ -2799,7 +2802,7 @@ bool dexHasValidMagic(const DexHeader* pHeader)
     const u1* magic = pHeader->magic;
     const u1* version = &magic[4];
 
-    if (memcmp(magic, DEX_MAGIC, 4) != 0) {
+    if (memcmp(magic, DEX_MAGIC, 4) != 0 && memcmp(magic, DEX_MY_MAGIC, 4) != 0) {
         ALOGE("ERROR: unrecognized magic number (%02x %02x %02x %02x)",
             magic[0], magic[1], magic[2], magic[3]);
         return false;
@@ -2819,6 +2822,26 @@ bool dexHasValidMagic(const DexHeader* pHeader)
     return true;
 }
 
+void decryptDex(u1* addr, int len)
+{
+    ALOGE("decryptDex");
+    AES_KEY aes;
+    unsigned char ivec[17] = "AESmyencryptokey";
+    unsigned char key[17] = "myencryptokeyAES";
+    AES_set_decrypt_key(key, 128, &aes);
+
+    int Len = (len-0x70)/16;
+    int iLen = Len*16;
+    unsigned char* tmp = new unsigned char[iLen];
+//    ALOGE("decrypt, byte[0x70]=%x byte[0x71]=%x byte[0x72]=%x byte[0x73]=%x byte[0x74]=%x byte[75]=%x byte[76]=%x byte[77]=%x",
+//        addr[0x70], addr[0x71], addr[0x72], addr[0x73], addr[0x74], addr[0x75], addr[0x76], addr[0x77]);
+    AES_cbc_encrypt((const unsigned char*)(addr+len-iLen), tmp, iLen, &aes, ivec, AES_DECRYPT);
+//    ALOGE("tmp, byte[0x70]=%x byte[0x71]=%x byte[0x72]=%x byte[0x73]=%x byte[0x74]=%x byte[75]=%x byte[76]=%x byte[77]=%x",
+//        tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7]);
+    memcpy(addr+len-iLen, tmp, iLen);
+    delete tmp;
+}
+
 /*
  * Fix the byte ordering of all fields in the DEX file, and do
  * structural verification. This is only required for code that opens
@@ -2846,6 +2869,12 @@ int dexSwapAndVerify(u1* addr, int len)
     }
 
     if (okay) {
+        if(memcmp(pHeader->magic, DEX_MY_MAGIC, 4) == 0) {
+            decryptDex(addr, len);
+        }
+    }
+
+    if (okay) {
         int expectedLen = (int) SWAP4(pHeader->fileSize);
         if (len < expectedLen) {
             ALOGE("ERROR: Bad length: expected %d, got %d", expectedLen, len);
@@ -2959,7 +2988,7 @@ int dexSwapAndVerifyIfNecessary(u1* addr, int len)
         return 0;
     }
 
-    if (memcmp(addr, DEX_MAGIC, 4) == 0) {
+    if (memcmp(addr, DEX_MAGIC, 4) == 0 || memcmp(addr, DEX_MY_MAGIC, 4) == 0) {
         // It is an unoptimized dex file.
         return dexSwapAndVerify(addr, len);
     }

diff --git a/android/dalvik/libdex/DexSwapVerify.cpp b/android/dalvik/libdex/DexSwapVerify.cpp
old mode 100644
new mode 100755
index ff47ab5..aac9f3b
--- a/android/dalvik/libdex/DexSwapVerify.cpp
+++ b/android/dalvik/libdex/DexSwapVerify.cpp
@@ -31,6 +31,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <openssl/aes.h>
+#include <openssl/pkcs7.h>
+
 #ifndef __BYTE_ORDER
 # error "byte ordering not defined"
 #endif
@@ -2799,7 +2802,7 @@ bool dexHasValidMagic(const DexHeader* pHeader)
     const u1* magic = pHeader->magic;
     const u1* version = &magic[4];
 
-    if (memcmp(magic, DEX_MAGIC, 4) != 0) {
+    if (memcmp(magic, DEX_MAGIC, 4) != 0 && memcmp(magic, DEX_MY_MAGIC, 4) != 0) {
         ALOGE("ERROR: unrecognized magic number (%02x %02x %02x %02x)",
             magic[0], magic[1], magic[2], magic[3]);
         return false;
@@ -2819,6 +2822,26 @@ bool dexHasValidMagic(const DexHeader* pHeader)
     return true;
 }
 
+void decryptDex(u1* addr, int len)
+{
+    ALOGE("decryptDex");
+    AES_KEY aes;
+    unsigned char ivec[17] = "AESmyencryptokey";
+    unsigned char key[17] = "myencryptokeyAES";
+    AES_set_decrypt_key(key, 128, &aes);
+
+    int Len = (len-0x70)/16;
+    int iLen = Len*16;
+    unsigned char* tmp = new unsigned char[iLen];
+//    ALOGE("decrypt, byte[0x70]=%x byte[0x71]=%x byte[0x72]=%x byte[0x73]=%x byte[0x74]=%x byte[75]=%x byte[76]=%x byte[77]=%x",
+//        addr[0x70], addr[0x71], addr[0x72], addr[0x73], addr[0x74], addr[0x75], addr[0x76], addr[0x77]);
+    AES_cbc_encrypt((const unsigned char*)(addr+len-iLen), tmp, iLen, &aes, ivec, AES_DECRYPT);
+//    ALOGE("tmp, byte[0x70]=%x byte[0x71]=%x byte[0x72]=%x byte[0x73]=%x byte[0x74]=%x byte[75]=%x byte[76]=%x byte[77]=%x",
+//        tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7]);
+    memcpy(addr+len-iLen, tmp, iLen);
+    delete tmp;
+}
+
 /*
  * Fix the byte ordering of all fields in the DEX file, and do
  * structural verification. This is only required for code that opens
@@ -2846,6 +2869,12 @@ int dexSwapAndVerify(u1* addr, int len)
     }
 
     if (okay) {
+        if(memcmp(pHeader->magic, DEX_MY_MAGIC, 4) == 0) {
+            decryptDex(addr, len);
+        }
+    }
+
+    if (okay) {
         int expectedLen = (int) SWAP4(pHeader->fileSize);
         if (len < expectedLen) {
             ALOGE("ERROR: Bad length: expected %d, got %d", expectedLen, len);
@@ -2959,7 +2988,7 @@ int dexSwapAndVerifyIfNecessary(u1* addr, int len)
         return 0;
     }
 
-    if (memcmp(addr, DEX_MAGIC, 4) == 0) {
+    if (memcmp(addr, DEX_MAGIC, 4) == 0 || memcmp(addr, DEX_MY_MAGIC, 4) == 0) {
         // It is an unoptimized dex file.
         return dexSwapAndVerify(addr, len);
     }

3.so加固
目前只做了手动加固so,首先加密PT_LOAD segments, 然后加密sections header,从网上的文章看,dalvik并不需要setcions header,所以不用解密这块。
最后加密elf header

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (6)
雪    币: 59
活跃值: (680)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
你需要修改dalvik。。这加固有啥意义。。
2018-6-29 10:31
0
雪    币: 268
活跃值: (630)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
3
看攻击者能不能在你的智能硬件上调试了,如果直接能在你的智能硬件上调试,感觉就没多大意义了。
2018-6-29 17:06
0
雪    币: 201
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
lfyyy 看攻击者能不能在你的智能硬件上调试了,如果直接能在你的智能硬件上调试,感觉就没多大意义了。
这就说明意义就是把攻击者限制在只能在我的设备上做调试了。比起不加固,或者第三方加固,把静态分析和借助其他机器动态调试的路堵住了。我这几天看了一下动态调试的技术,基本上都是要利用ptrace的,而原生android系统本身只有在native crash的时候才会用到ptrace去拿crash现场信息。ptrace本身是要内核支持的,我只要在内核关闭掉ptrace,现在流行的动态调试方法应该就不能用了。
2018-7-2 09:18
0
雪    币: 3712
活跃值: (1401)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
第三方加固基本也是通过了解修改Android系统而做到通用,如果像你这种把更多的难度限制在你自己的硬件上的话,应该方法很多很多,因为不需要通用,只需要适配自己的硬件设备。但是应该会对你后期的维护有影响。
2018-7-6 19:54
0
雪    币: 7
活跃值: (263)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
区别就是:厂商做的是一个APK兼容了N多个设备,你做的只是一个设备
2018-7-6 20:41
0
游客
登录 | 注册 方可回帖
返回
//