Android安全讲座第六层 [二] APK加壳技术方案

本文章由Jack_Jia编写,转载请注明出处。

文章链接:http://blog.csdn.net/jiazhijun/article/details/8809542

作者:Jack_Jia   邮箱: 309zhijun@163.com


一、序言


       在上篇“Android APK加壳技术方案”(http://blog.csdn.net/jiazhijun/article/details/8678399)博文中,根据加壳数据在解壳程序Dex文件所处的位置,我提出了两种Android Dex加壳技术实现方案,本片博文将对方案1代码实现进行讲解。博友可以根据方案1的代码实现原理对方案2自行实现。

      在方案1的代码实现过程中,各种不同的问题接踵出现,最初的方案也在不同问题的出现、解决过程中不断的得到调整、优化。

      本文的代码实现了对整个APK包的加壳处理。加壳程序不会对源程序有任何的影响。


二、代码实现


    本程序基于Android2.3代码实现,因为牵扯到系统代码的反射修改,本程序不保证在其它android版本正常工作,博友可以根据实现原理,自行实现对其它Android版本的兼容性开发。


    1、 加壳程序流程及代码实现

                 1、加密源程序APK为解壳数据

                 2、把解壳数据写入解壳程序DEX文件末尾,并在文件尾部添加解壳数据的大小。

                 3、修改解壳程序DEX头中checksum、signature 和file_size头信息。


      代码实现如下:

[java] view plain copy
  1. package com.android.dexshell;  

  2. import java.io.ByteArrayOutputStream;  

  3. import java.io.File;  

  4. import java.io.FileInputStream;  

  5. import java.io.FileOutputStream;  

  6. import java.io.IOException;  

  7. import java.security.MessageDigest;  

  8. import java.security.NoSuchAlgorithmException;  

  9. import java.util.zip.Adler32;  

  10. publicclass DexShellTool {  

  11. /**

  12.     * @param args

  13.     */

  14. publicstaticvoid main(String[] args) {  

  15. // TODO Auto-generated method stub

  16. try {  

  17.            File payloadSrcFile = new File("g:/payload.apk");  

  18.            File unShellDexFile = new File("g:/unshell.dex");  

  19. byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));  

  20. byte[] unShellDexArray = readFileBytes(unShellDexFile);  

  21. int payloadLen = payloadArray.length;  

  22. int unShellDexLen = unShellDexArray.length;  

  23. int totalLen = payloadLen + unShellDexLen +4;  

  24. byte[] newdex = newbyte[totalLen];  

  25. //添加解壳代码

  26.            System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);  

  27. //添加加密后的解壳数据

  28.            System.arraycopy(payloadArray, 0, newdex, unShellDexLen,  

  29.                    payloadLen);  

  30. //添加解壳数据长度

  31.            System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4);  

  32. //修改DEX file size文件头

  33.            fixFileSizeHeader(newdex);  

  34. //修改DEX SHA1 文件头

  35.            fixSHA1Header(newdex);  

  36. //修改DEX CheckSum文件头

  37.            fixCheckSumHeader(newdex);  

  38.            String str = "g:/classes.dex";  

  39.            File file = new File(str);  

  40. if (!file.exists()) {  

  41.                file.createNewFile();  

  42.            }  

  43.            FileOutputStream localFileOutputStream = new FileOutputStream(str);  

  44.            localFileOutputStream.write(newdex);  

  45.            localFileOutputStream.flush();  

  46.            localFileOutputStream.close();  

  47.        } catch (Exception e) {  

  48. // TODO Auto-generated catch block

  49.            e.printStackTrace();  

  50.        }  

  51.    }  

  52. //直接返回数据,读者可以添加自己加密方法

  53. privatestaticbyte[] encrpt(byte[] srcdata){  

  54. return srcdata;  

  55.    }  

  56. privatestaticvoid fixCheckSumHeader(byte[] dexBytes) {  

  57.        Adler32 adler = new Adler32();  

  58.        adler.update(dexBytes, 12, dexBytes.length - 12);  

  59. long value = adler.getValue();  

  60. int va = (int) value;  

  61. byte[] newcs = intToByte(va);  

  62. byte[] recs = newbyte[4];  

  63. for (int i = 0; i < 4; i++) {  

  64.            recs[i] = newcs[newcs.length - 1 - i];  

  65.            System.out.println(Integer.toHexString(newcs[i]));  

  66.        }  

  67.        System.arraycopy(recs, 0, dexBytes, 8, 4);  

  68.        System.out.println(Long.toHexString(value));  

  69.        System.out.println();  

  70.    }  

  71. publicstaticbyte[] intToByte(int number) {  

  72. byte[] b = newbyte[4];  

  73. for (int i = 3; i >= 0; i--) {  

  74.            b[i] = (byte) (number % 256);  

  75.            number >>= 8;  

  76.        }  

  77. return b;  

  78.    }  

  79. privatestaticvoid fixSHA1Header(byte[] dexBytes)  

  80. throws NoSuchAlgorithmException {  

  81.        MessageDigest md = MessageDigest.getInstance("SHA-1");  

  82.        md.update(dexBytes, 32, dexBytes.length - 32);  

  83. byte[] newdt = md.digest();  

  84.        System.arraycopy(newdt, 0, dexBytes, 12, 20);  

  85.        String hexstr = "";  

  86. for (int i = 0; i < newdt.length; i++) {  

  87.            hexstr += Integer.toString((newdt[i] & 0xff) + 0x100, 16)  

  88.                    .substring(1);  

  89.        }  

  90.        System.out.println(hexstr);  

  91.    }  

  92. privatestaticvoid fixFileSizeHeader(byte[] dexBytes) {  

  93. byte[] newfs = intToByte(dexBytes.length);  

  94.        System.out.println(Integer.toHexString(dexBytes.length));  

  95. byte[] refs = newbyte[4];  

  96. for (int i = 0; i < 4; i++) {  

  97.            refs[i] = newfs[newfs.length - 1 - i];  

  98.            System.out.println(Integer.toHexString(newfs[i]));  

  99.        }  

  100.        System.arraycopy(refs, 0, dexBytes, 32, 4);  

  101.    }  

  102. privatestaticbyte[] readFileBytes(File file) throws IOException {  

  103. byte[] arrayOfByte = newbyte[1024];  

  104.        ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();  

  105.        FileInputStream fis = new FileInputStream(file);  

  106. while (true) {  

  107. int i = fis.read(arrayOfByte);  

  108. if (i != -1) {  

  109.                localByteArrayOutputStream.write(arrayOfByte, 0, i);  

  110.            } else {  

  111. return localByteArrayOutputStream.toByteArray();  

  112.            }  

  113.        }  

  114.    }  

  115. }  



   2、 解壳程序流程及代码实现

         在解壳程序的开发过程中需要解决如下几个关键的技术问题:

        (1)解壳代码如何能够第一时间执行?

                 Android程序由不同的组件构成,系统在有需要的时候启动程序组件。因此解壳程序必须在Android系统启动组件之前运行,完成对解壳数                据的解壳及APK文件的动态加载,否则会使程序出现加载类失败的异常。

                 Android开发者都知道Applicaiton做为整个应用的上下文,会被系统第一时间调用,这也是应用开发者程序代码的第一执行点。因此通过对              AndroidMainfest.xml的application的配置可以实现解壳代码第一时间运行。

[html] view plain copy
  1. <application

  2. android:icon="@drawable/ic_launcher"

  3. android:label="@string/app_name"

  4. android:theme="@style/AppTheme"android:name="<span style="color: rgb(255, 0, 0);"><em><strong>com.android.dexunshell.ProxyApplication</strong></em></span>" >

  5. </application>


        (2)如何替换回源程序原有的Application?

                 当在AndroidMainfest.xml文件配置为解壳代码的Application时。源程序原有的Applicaiton将被替换,为了不影响源程序代码逻辑,我们需要              在解壳代码运行完成后,替换回源程序原有的Application对象。我们通过在AndroidMainfest.xml文件中配置原有Applicaiton类信息来达到我们              的目的。解壳程序要在运行完毕后通过创建配置的Application对象,并通过反射修改回原Application。

[html] view plain copy
  1. <application

  2. android:icon="@drawable/ic_launcher"

  3. android:label="@string/app_name"

  4. android:theme="@style/AppTheme"android:name="<em><strong><span style="color: rgb(255, 0, 0);">com.android.dexunshell.ProxyApplication</span></strong></em>" >

  5. <spanstyle="color: rgb(255, 0, 0);"><em><strong><meta-dataandroid:name="APPLICATION_CLASS_NAME"android:value="com.***.Application"/></strong></em></span>

  6. </application>


         (3)如何通过DexClassLoader实现对apk代码的动态加载。

                 我们知道DexClassLoader加载的类是没有组件生命周期的,也就是说即使DexClassLoader通过对APK的动态加载完成了对组件类的加载,              当系统启动该组件时,还会出现加载类失败的异常。为什么组件类被动态加载入虚拟机,但系统却出现加载类失败呢?

                 通过查看Android源代码我们知道组件类的加载是由另一个ClassLoader来完成的,DexClassLoader和系统组件ClassLoader并不存在关                  系,系统组件ClassLoader当然找不到由DexClassLoader加载的类,如果把系统组件ClassLoader的parent修改成DexClassLoader,我们就可              以实现对apk代码的动态加载。


(4)如何使解壳后的APK资源文件被代码动态引用。

                代码默认引用的资源文件在最外层的解壳程序中,因此我们要增加系统的资源加载路径来实现对借壳后APK文件资源的加载。


       解壳实现代码:

[java] view plain copy
  1. package com.android.dexunshell;  

  2. import java.io.BufferedInputStream;  

  3. import java.io.ByteArrayInputStream;  

  4. import java.io.ByteArrayOutputStream;  

  5. import java.io.DataInputStream;  

  6. import java.io.File;  

  7. import java.io.FileInputStream;  

  8. import java.io.FileOutputStream;  

  9. import java.io.IOException;  

  10. import java.lang.ref.WeakReference;  

  11. import java.util.ArrayList;  

  12. import java.util.HashMap;  

  13. import java.util.Iterator;  

  14. import java.util.zip.ZipEntry;  

  15. import java.util.zip.ZipInputStream;  

  16. import dalvik.system.DexClassLoader;  

  17. import android.app.Application;  

  18. import android.content.pm.ApplicationInfo;  

  19. import android.content.pm.PackageManager;  

  20. import android.content.pm.PackageManager.NameNotFoundException;  

  21. import android.os.Bundle;  

  22. publicclass ProxyApplication extends Application {  

  23. privatestaticfinal String appkey = "APPLICATION_CLASS_NAME";  

  24. private String apkFileName;  

  25. private String odexPath;  

  26. private String libPath;  

  27. protectedvoid attachBaseContext(Context base) {  

  28. super.attachBaseContext(base);  

  29. try {  

  30.            File odex = this.getDir("payload_odex", MODE_PRIVATE);  

  31.            File libs = this.getDir("payload_lib", MODE_PRIVATE);  

  32.            odexPath = odex.getAbsolutePath();  

  33.            libPath = libs.getAbsolutePath();  

  34.            apkFileName = odex.getAbsolutePath() + "/payload.apk";  

  35.            File dexFile = new File(apkFileName);  

  36. if (!dexFile.exists())  

  37.                dexFile.createNewFile();  

  38. // 读取程序classes.dex文件

  39. byte[] dexdata = this.readDexFileFromApk();  

  40. // 分离出解壳后的apk文件已用于动态加载

  41. this.splitPayLoadFromDex(dexdata);  

  42. // 配置动态加载环境

  43.            Object currentActivityThread = RefInvoke.invokeStaticMethod(  

  44. "android.app.ActivityThread", "currentActivityThread",  

  45. new Class[] {}, new Object[] {});  

  46.            String packageName = this.getPackageName();  

  47.            HashMap mPackages = (HashMap) RefInvoke.getFieldOjbect(  

  48. "android.app.ActivityThread", currentActivityThread,  

  49. "mPackages");  

  50.            WeakReference wr = (WeakReference) mPackages.get(packageName);  

  51.            DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,  

  52.                    libPath, (ClassLoader) RefInvoke.getFieldOjbect(  

  53. "android.app.LoadedApk", wr.get(), "mClassLoader"));  

  54.            RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader",  

  55.                    wr.get(), dLoader);  

  56.        } catch (Exception e) {  

  57. // TODO Auto-generated catch block

  58.            e.printStackTrace();  

  59.        }  

  60.    }  

  61. publicvoid onCreate() {  

  62.        {  

  63. // 如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。

  64.            String appClassName = null;  

  65. try {  

  66.                ApplicationInfo ai = this.getPackageManager()  

  67.                        .getApplicationInfo(this.getPackageName(),  

  68.                                PackageManager.GET_META_DATA);  

  69.                Bundle bundle = ai.metaData;  

  70. if (bundle != null

  71.                        && bundle.containsKey("APPLICATION_CLASS_NAME")) {  

  72.                    appClassName = bundle.getString("APPLICATION_CLASS_NAME");  

  73.                } else {  

  74. return;  

  75.                }  

  76.            } catch (NameNotFoundException e) {  

  77. // TODO Auto-generated catch block

  78.                e.printStackTrace();  

  79.            }  

  80.            Object currentActivityThread = RefInvoke.invokeStaticMethod(  

  81. "android.app.ActivityThread", "currentActivityThread",  

  82. new Class[] {}, new Object[] {});  

  83.            Object mBoundApplication = RefInvoke.getFieldOjbect(  

  84. "android.app.ActivityThread", currentActivityThread,  

  85. "mBoundApplication");  

  86.            Object loadedApkInfo = RefInvoke.getFieldOjbect(  

  87. "android.app.ActivityThread$AppBindData",  

  88.                    mBoundApplication, "info");  

  89.            RefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication",  

  90.                    loadedApkInfo, null);  

  91.            Object oldApplication = RefInvoke.getFieldOjbect(  

  92. "android.app.ActivityThread", currentActivityThread,  

  93. "mInitialApplication");  

  94.            ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke  

  95.                    .getFieldOjbect("android.app.ActivityThread",  

  96.                            currentActivityThread, "mAllApplications");  

  97.            mAllApplications.remove(oldApplication);  

  98.            ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke  

  99.                    .getFieldOjbect("android.app.LoadedApk", loadedApkInfo,  

  100. "mApplicationInfo");  

  101.            ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke  

  102.                    .getFieldOjbect("android.app.ActivityThread$AppBindData",  

  103.                            mBoundApplication, "appInfo");  

  104.            appinfo_In_LoadedApk.className = appClassName;  

  105.            appinfo_In_AppBindData.className = appClassName;  

  106.            Application app = (Application) RefInvoke.invokeMethod(  

  107. "android.app.LoadedApk", "makeApplication", loadedApkInfo,  

  108. new Class[] { boolean.class, Instrumentation.class },  

  109. new Object[] { false, null });  

  110.            RefInvoke.setFieldOjbect("android.app.ActivityThread",  

  111. "mInitialApplication", currentActivityThread, app);  

  112.            HashMap mProviderMap = (HashMap) RefInvoke.getFieldOjbect(  

  113. "android.app.ActivityThread", currentActivityThread,  

  114. "mProviderMap");  

  115.            Iterator it = mProviderMap.values().iterator();  

  116. while (it.hasNext()) {  

  117.                Object providerClientRecord = it.next();  

  118.                Object localProvider = RefInvoke.getFieldOjbect(  

  119. "android.app.ActivityThread$ProviderClientRecord",  

  120.                        providerClientRecord, "mLocalProvider");  

  121.                RefInvoke.setFieldOjbect("android.content.ContentProvider",  

  122. "mContext", localProvider, app);  

  123.            }  

  124.            app.onCreate();  

  125.        }  

  126.    }  

  127. privatevoid splitPayLoadFromDex(byte[] data) throws IOException {  

  128. byte[] apkdata = decrypt(data);  

  129. int ablen = apkdata.length;  

  130. byte[] dexlen = newbyte[4];  

  131.        System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4);  

  132.        ByteArrayInputStream bais = new ByteArrayInputStream(dexlen);  

  133.        DataInputStream in = new DataInputStream(bais);  

  134. int readInt = in.readInt();  

  135.        System.out.println(Integer.toHexString(readInt));  

  136. byte[] newdex = newbyte[readInt];  

  137.        System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt);  

  138.        File file = new File(apkFileName);  

  139. try {  

  140.            FileOutputStream localFileOutputStream = new FileOutputStream(file);  

  141.            localFileOutputStream.write(newdex);  

  142.            localFileOutputStream.close();  

  143.        } catch (IOException localIOException) {  

  144. thrownew RuntimeException(localIOException);  

  145.        }  

  146.        ZipInputStream localZipInputStream = new ZipInputStream(  

  147. new BufferedInputStream(new FileInputStream(file)));  

  148. while (true) {  

  149.            ZipEntry localZipEntry = localZipInputStream.getNextEntry();  

  150. if (localZipEntry == null) {  

  151.                localZipInputStream.close();  

  152. break;  

  153.            }  

  154.            String name = localZipEntry.getName();  

  155. if (name.startsWith("lib/") && name.endsWith(".so")) {  

  156.                File storeFile = new File(libPath + "/"

  157.                        + name.substring(name.lastIndexOf('/')));  

  158.                storeFile.createNewFile();  

  159.                FileOutputStream fos = new FileOutputStream(storeFile);  

  160. byte[] arrayOfByte = newbyte[1024];  

  161. while (true) {  

  162. int i = localZipInputStream.read(arrayOfByte);  

  163. if (i == -1)  

  164. break;  

  165.                    fos.write(arrayOfByte, 0, i);  

  166.                }  

  167.                fos.flush();  

  168.                fos.close();  

  169.            }  

  170.            localZipInputStream.closeEntry();  

  171.        }  

  172.        localZipInputStream.close();  

  173.    }  

  174. privatebyte[] readDexFileFromApk() throws IOException {  

  175.        ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream();  

  176.        ZipInputStream localZipInputStream = new ZipInputStream(  

  177. new BufferedInputStream(new FileInputStream(  

  178. this.getApplicationInfo().sourceDir)));  

  179. while (true) {  

  180.            ZipEntry localZipEntry = localZipInputStream.getNextEntry();  

  181. if (localZipEntry == null) {  

  182.                localZipInputStream.close();  

  183. break;  

  184.            }  

  185. if (localZipEntry.getName().equals("classes.dex")) {  

  186. byte[] arrayOfByte = newbyte[1024];  

  187. while (true) {  

  188. int i = localZipInputStream.read(arrayOfByte);  

  189. if (i == -1)  

  190. break;  

  191.                    dexByteArrayOutputStream.write(arrayOfByte, 0, i);  

  192.                }  

  193.            }  

  194.            localZipInputStream.closeEntry();  

  195.        }  

  196.        localZipInputStream.close();  

  197. return dexByteArrayOutputStream.toByteArray();  

  198.    }  

  199. // //直接返回数据,读者可以添加自己解密方法

  200. privatebyte[] decrypt(byte[] data) {  

  201. return data;  

  202.    }  

[java] view plain copy
  1. package com.android.dexunshell;  

  2. import java.io.BufferedInputStream;  

  3. import java.io.ByteArrayInputStream;  

  4. import java.io.ByteArrayOutputStream;  

  5. import java.io.DataInputStream;  

  6. import java.io.File;  

  7. import java.io.FileInputStream;  

  8. import java.io.FileOutputStream;  

  9. import java.io.IOException;  

  10. import java.lang.ref.WeakReference;  

  11. import java.util.ArrayList;  

  12. import java.util.HashMap;  

  13. import java.util.Iterator;  

  14. import java.util.zip.ZipEntry;  

  15. import java.util.zip.ZipInputStream;  

  16. import dalvik.system.DexClassLoader;  

  17. import android.app.Application;  

  18. import android.content.pm.ApplicationInfo;  

  19. import android.content.pm.PackageManager;  

  20. import android.content.pm.PackageManager.NameNotFoundException;  

  21. import android.os.Bundle;  

  22. publicclass ProxyApplication extends Application {  

  23. <span style="white-space:pre">  </span>privatestaticfinal String appkey = "APPLICATION_CLASS_NAME";  

  24. <span style="white-space:pre">  </span>private String apkFileName;  

  25. <span style="white-space:pre">  </span>private String odexPath;  

  26. <span style="white-space:pre">  </span>private String libPath;  

  27. <span style="white-space:pre">  </span>protectedvoid attachBaseContext(Context base) {  

  28. <span style="white-space:pre">      </span>super.attachBaseContext(base);  

  29. <span style="white-space:pre">      </span>try {  

  30. <span style="white-space:pre">          </span>File odex = this.getDir("payload_odex", MODE_PRIVATE);  

  31. <span style="white-space:pre">          </span>File libs = this.getDir("payload_lib", MODE_PRIVATE);  

  32. <span style="white-space:pre">          </span>odexPath = odex.getAbsolutePath();  

  33. <span style="white-space:pre">          </span>libPath = libs.getAbsolutePath();  

  34. <span style="white-space:pre">          </span>apkFileName = odex.getAbsolutePath() + "/payload.apk";  

  35. <span style="white-space:pre">          </span>File dexFile = new File(apkFileName);  

  36. <span style="white-space:pre">          </span>if (!dexFile.exists())  

  37. <span style="white-space:pre">              </span>dexFile.createNewFile();  

  38. <span style="white-space:pre">          </span>// 读取程序classes.dex文件

  39. <span style="white-space:pre">          </span>byte[] dexdata = this.readDexFileFromApk();  

  40. <span style="white-space:pre">          </span>// 分离出解壳后的apk文件已用于动态加载

  41. <span style="white-space:pre">          </span>this.splitPayLoadFromDex(dexdata);  

  42. <span style="white-space:pre">          </span>// 配置动态加载环境

  43. <span style="white-space:pre">          </span>Object currentActivityThread = RefInvoke.invokeStaticMethod(  

  44. <span style="white-space:pre">                  </span>"android.app.ActivityThread", "currentActivityThread",  

  45. <span style="white-space:pre">                  </span>new Class[] {}, new Object[] {});  

  46. <span style="white-space:pre">          </span>String packageName = this.getPackageName();  

  47. <span style="white-space:pre">          </span>HashMap mPackages = (HashMap) RefInvoke.getFieldOjbect(  

  48. <span style="white-space:pre">                  </span>"android.app.ActivityThread", currentActivityThread,  

  49. <span style="white-space:pre">                  </span>"mPackages");  

  50. <span style="white-space:pre">          </span>WeakReference wr = (WeakReference) mPackages.get(packageName);  

  51. <span style="white-space:pre">          </span>DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,  

  52. <span style="white-space:pre">                  </span>libPath, (ClassLoader) RefInvoke.getFieldOjbect(  

  53. <span style="white-space:pre">                          </span>"android.app.LoadedApk", wr.get(), "mClassLoader"));  

  54. <span style="white-space:pre">          </span>RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader",  

  55. <span style="white-space:pre">                  </span>wr.get(), dLoader);  

  56. <span style="white-space:pre">      </span>} catch (Exception e) {  

  57. <span style="white-space:pre">          </span>// TODO Auto-generated catch block

  58. <span style="white-space:pre">          </span>e.printStackTrace();  

  59. <span style="white-space:pre">      </span>}  

  60. <span style="white-space:pre">  </span>}  

  61. <span style="white-space:pre">  </span>publicvoid onCreate() {  

  62. <span style="white-space:pre">      </span>{  

  63. <span style="white-space:pre">          </span>// 如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。

  64. <span style="white-space:pre">          </span>String appClassName = null;  

  65. <span style="white-space:pre">          </span>try {  

  66. <span style="white-space:pre">              </span>ApplicationInfo ai = this.getPackageManager()  

  67. <span style="white-space:pre">                      </span>.getApplicationInfo(this.getPackageName(),  

  68. <span style="white-space:pre">                              </span>PackageManager.GET_META_DATA);  

  69. <span style="white-space:pre">              </span>Bundle bundle = ai.metaData;  

  70. <span style="white-space:pre">              </span>if (bundle != null

  71. <span style="white-space:pre">                      </span>&& bundle.containsKey("APPLICATION_CLASS_NAME")) {  

  72. <span style="white-space:pre">                  </span>appClassName = bundle.getString("APPLICATION_CLASS_NAME");  

  73. <span style="white-space:pre">              </span>} else {  

  74. <span style="white-space:pre">                  </span>return;  

  75. <span style="white-space:pre">              </span>}  

  76. <span style="white-space:pre">          </span>} catch (NameNotFoundException e) {  

  77. <span style="white-space:pre">              </span>// TODO Auto-generated catch block

  78. <span style="white-space:pre">              </span>e.printStackTrace();  

  79. <span style="white-space:pre">          </span>}  

  80. <span style="white-space:pre">          </span>Object currentActivityThread = RefInvoke.invokeStaticMethod(  

  81. <span style="white-space:pre">                  </span>"android.app.ActivityThread", "currentActivityThread",  

  82. <span style="white-space:pre">                  </span>new Class[] {}, new Object[] {});  

  83. <span style="white-space:pre">          </span>Object mBoundApplication = RefInvoke.getFieldOjbect(  

  84. <span style="white-space:pre">                  </span>"android.app.ActivityThread", currentActivityThread,  

  85. <span style="white-space:pre">                  </span>"mBoundApplication");  

  86. <span style="white-space:pre">          </span>Object loadedApkInfo = RefInvoke.getFieldOjbect(  

  87. <span style="white-space:pre">                  </span>"android.app.ActivityThread$AppBindData",  

  88. <span style="white-space:pre">                  </span>mBoundApplication, "info");  

  89. <span style="white-space:pre">          </span>RefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication",  

  90. <span style="white-space:pre">                  </span>loadedApkInfo, null);  

  91. <span style="white-space:pre">          </span>Object oldApplication = RefInvoke.getFieldOjbect(  

  92. <span style="white-space:pre">                  </span>"android.app.ActivityThread", currentActivityThread,  

  93. <span style="white-space:pre">                  </span>"mInitialApplication");  

  94. <span style="white-space:pre">          </span>ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke  

  95. <span style="white-space:pre">                  </span>.getFieldOjbect("android.app.ActivityThread",  

  96. <span style="white-space:pre">                          </span>currentActivityThread, "mAllApplications");  

  97. <span style="white-space:pre">          </span>mAllApplications.remove(oldApplication);  

  98. <span style="white-space:pre">          </span>ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke  

  99. <span style="white-space:pre">                  </span>.getFieldOjbect("android.app.LoadedApk", loadedApkInfo,  

  100. <span style="white-space:pre">                          </span>"mApplicationInfo");  

  101. <span style="white-space:pre">          </span>ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke  

  102. <span style="white-space:pre">                  </span>.getFieldOjbect("android.app.ActivityThread$AppBindData",  

  103. <span style="white-space:pre">                          </span>mBoundApplication, "appInfo");  

  104. <span style="white-space:pre">          </span>appinfo_In_LoadedApk.className = appClassName;  

  105. <span style="white-space:pre">          </span>appinfo_In_AppBindData.className = appClassName;  

  106. <span style="white-space:pre">          </span>Application app = (Application) RefInvoke.invokeMethod(  

  107. <span style="white-space:pre">                  </span>"android.app.LoadedApk", "makeApplication", loadedApkInfo,  

  108. <span style="white-space:pre">                  </span>new Class[] { boolean.class, Instrumentation.class },  

  109. <span style="white-space:pre">                  </span>new Object[] { false, null });  

  110. <span style="white-space:pre">          </span>RefInvoke.setFieldOjbect("android.app.ActivityThread",  

  111. <span style="white-space:pre">                  </span>"mInitialApplication", currentActivityThread, app);  

  112. <span style="white-space:pre">          </span>HashMap mProviderMap = (HashMap) RefInvoke.getFieldOjbect(  

  113. <span style="white-space:pre">                  </span>"android.app.ActivityThread", currentActivityThread,  

  114. <span style="white-space:pre">                  </span>"mProviderMap");  

  115. <span style="white-space:pre">          </span>Iterator it = mProviderMap.values().iterator();  

  116. <span style="white-space:pre">          </span>while (it.hasNext()) {  

  117. <span style="white-space:pre">              </span>Object providerClientRecord = it.next();  

  118. <span style="white-space:pre">              </span>Object localProvider = RefInvoke.getFieldOjbect(  

  119. <span style="white-space:pre">                      </span>"android.app.ActivityThread$ProviderClientRecord",  

  120. <span style="white-space:pre">                      </span>providerClientRecord, "mLocalProvider");  

  121. <span style="white-space:pre">              </span>RefInvoke.setFieldOjbect("android.content.ContentProvider",  

  122. <span style="white-space:pre">                      </span>"mContext", localProvider, app);  

  123. <span style="white-space:pre">          </span>}  

  124. <span style="white-space:pre">          </span>app.onCreate();  

  125. <span style="white-space:pre">      </span>}  

  126. <span style="white-space:pre">  </span>}  

  127. <span style="white-space:pre">  </span>privatevoid splitPayLoadFromDex(byte[] data) throws IOException {  

  128. <span style="white-space:pre">      </span>byte[] apkdata = decrypt(data);  

  129. <span style="white-space:pre">      </span>int ablen = apkdata.length;  

  130. <span style="white-space:pre">      </span>byte[] dexlen = newbyte[4];  

  131. <span style="white-space:pre">      </span>System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4);  

  132. <span style="white-space:pre">      </span>ByteArrayInputStream bais = new ByteArrayInputStream(dexlen);  

  133. <span style="white-space:pre">      </span>DataInputStream in = new DataInputStream(bais);  

  134. <span style="white-space:pre">      </span>int readInt = in.readInt();  

  135. <span style="white-space:pre">      </span>System.out.println(Integer.toHexString(readInt));  

  136. <span style="white-space:pre">      </span>byte[] newdex = newbyte[readInt];  

  137. <span style="white-space:pre">      </span>System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt);  

  138. <span style="white-space:pre">      </span>File file = new File(apkFileName);  

  139. <span style="white-space:pre">      </span>try {  

  140. <span style="white-space:pre">          </span>FileOutputStream localFileOutputStream = new FileOutputStream(file);  

  141. <span style="white-space:pre">          </span>localFileOutputStream.write(newdex);  

  142. <span style="white-space:pre">          </span>localFileOutputStream.close();  

  143. <span style="white-space:pre">      </span>} catch (IOException localIOException) {  

  144. <span style="white-space:pre">          </span>thrownew RuntimeException(localIOException);  

  145. <span style="white-space:pre">      </span>}  

  146. <span style="white-space:pre">      </span>ZipInputStream localZipInputStream = new ZipInputStream(  

  147. <span style="white-space:pre">              </span>new BufferedInputStream(new FileInputStream(file)));  

  148. <span style="white-space:pre">      </span>while (true) {  

  149. <span style="white-space:pre">          </span>ZipEntry localZipEntry = localZipInputStream.getNextEntry();  

  150. <span style="white-space:pre">          </span>if (localZipEntry == null) {  

  151. <span style="white-space:pre">              </span>localZipInputStream.close();  

  152. <span style="white-space:pre">              </span>break;  

  153. <span style="white-space:pre">          </span>}  

  154. <span style="white-space:pre">          </span>String name = localZipEntry.getName();  

  155. <span style="white-space:pre">          </span>if (name.startsWith("lib/") && name.endsWith(".so")) {  

  156. <span style="white-space:pre">              </span>File storeFile = new File(libPath + "/"

  157. <span style="white-space:pre">                      </span>+ name.substring(name.lastIndexOf('/')));  

  158. <span style="white-space:pre">              </span>storeFile.createNewFile();  

  159. <span style="white-space:pre">              </span>FileOutputStream fos = new FileOutputStream(storeFile);  

  160. <span style="white-space:pre">              </span>byte[] arrayOfByte = newbyte[1024];  

  161. <span style="white-space:pre">              </span>while (true) {  

  162. <span style="white-space:pre">                  </span>int i = localZipInputStream.read(arrayOfByte);  

  163. <span style="white-space:pre">                  </span>if (i == -1)  

  164. <span style="white-space:pre">                      </span>break;  

  165. <span style="white-space:pre">                  </span>fos.write(arrayOfByte, 0, i);  

  166. <span style="white-space:pre">              </span>}  

  167. <span style="white-space:pre">              </span>fos.flush();  

  168. <span style="white-space:pre">              </span>fos.close();  

  169. <span style="white-space:pre">          </span>}  

  170. <span style="white-space:pre">          </span>localZipInputStream.closeEntry();  

  171. <span style="white-space:pre">      </span>}  

  172. <span style="white-space:pre">      </span>localZipInputStream.close();  

  173. <span style="white-space:pre">  </span>}  

  174. <span style="white-space:pre">  </span>privatebyte[] readDexFileFromApk() throws IOException {  

  175. <span style="white-space:pre">      </span>ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream();  

  176. <span style="white-space:pre">      </span>ZipInputStream localZipInputStream = new ZipInputStream(  

  177. <span style="white-space:pre">              </span>new BufferedInputStream(new FileInputStream(  

  178. <span style="white-space:pre">                      </span>this.getApplicationInfo().sourceDir)));  

  179. <span style="white-space:pre">      </span>while (true) {  

  180. <span style="white-space:pre">          </span>ZipEntry localZipEntry = localZipInputStream.getNextEntry();  

  181. <span style="white-space:pre">          </span>if (localZipEntry == null) {  

  182. <span style="white-space:pre">              </span>localZipInputStream.close();  

  183. <span style="white-space:pre">              </span>break;  

  184. <span style="white-space:pre">          </span>}  

  185. <span style="white-space:pre">          </span>if (localZipEntry.getName().equals("classes.dex")) {  

  186. <span style="white-space:pre">              </span>byte[] arrayOfByte = newbyte[1024];  

  187. <span style="white-space:pre">              </span>while (true) {  

  188. <span style="white-space:pre">                  </span>int i = localZipInputStream.read(arrayOfByte);  

  189. <span style="white-space:pre">                  </span>if (i == -1)  

  190. <span style="white-space:pre">                      </span>break;  

  191. <span style="white-space:pre">                  </span>dexByteArrayOutputStream.write(arrayOfByte, 0, i);  

  192. <span style="white-space:pre">              </span>}  

  193. <span style="white-space:pre">          </span>}  

  194. <span style="white-space:pre">          </span>localZipInputStream.closeEntry();  

  195. <span style="white-space:pre">      </span>}  

  196. <span style="white-space:pre">      </span>localZipInputStream.close();  

  197. <span style="white-space:pre">      </span>return dexByteArrayOutputStream.toByteArray();  

  198. <span style="white-space:pre">  </span>}  

  199. <span style="white-space:pre">  </span>// //直接返回数据,读者可以添加自己解密方法

  200. <span style="white-space:pre">  </span>privatebyte[] decrypt(byte[] data) {  

  201. <span style="white-space:pre">      </span>return data;  

  202. <span style="white-space:pre">  </span>}  


       RefInvoke为反射调用工具类:


[java] view plain copy
  1. package com.android.dexunshell;  

  2. import java.lang.reflect.Field;  

  3. import java.lang.reflect.InvocationTargetException;  

  4. import java.lang.reflect.Method;  

  5. publicclass RefInvoke {  

  6. publicstatic  Object invokeStaticMethod(String class_name, String method_name, Class[] pareTyple, Object[] pareVaules){  

  7. try {  

  8.            Class obj_class = Class.forName(class_name);  

  9.            Method method = obj_class.getMethod(method_name,pareTyple);  

  10. return method.invoke(null, pareVaules);  

  11.        } catch (SecurityException e) {  

  12. // TODO Auto-generated catch block

  13.            e.printStackTrace();  

  14.        }  catch (IllegalArgumentException e) {  

  15. // TODO Auto-generated catch block

  16.            e.printStackTrace();  

  17.        } catch (IllegalAccessException e) {  

  18. // TODO Auto-generated catch block

  19.            e.printStackTrace();  

  20.        } catch (NoSuchMethodException e) {  

  21. // TODO Auto-generated catch block

  22.            e.printStackTrace();  

  23.        } catch (InvocationTargetException e) {  

  24. // TODO Auto-generated catch block

  25.            e.printStackTrace();  

  26.        } catch (ClassNotFoundException e) {  

  27. // TODO Auto-generated catch block

  28.            e.printStackTrace();  

  29.        }  

  30. returnnull;  

  31.    }  

  32. publicstatic  Object invokeMethod(String class_name, String method_name, Object obj ,Class[] pareTyple, Object[] pareVaules){  

  33. try {  

  34.            Class obj_class = Class.forName(class_name);  

  35.            Method method = obj_class.getMethod(method_name,pareTyple);  

  36. return method.invoke(obj, pareVaules);  

  37.        } catch (SecurityException e) {  

  38. // TODO Auto-generated catch block

  39.            e.printStackTrace();  

  40.        }  catch (IllegalArgumentException e) {  

  41. // TODO Auto-generated catch block

  42.            e.printStackTrace();  

  43.        } catch (IllegalAccessException e) {  

  44. // TODO Auto-generated catch block

  45.            e.printStackTrace();  

  46.        } catch (NoSuchMethodException e) {  

  47. // TODO Auto-generated catch block

  48.            e.printStackTrace();  

  49.        } catch (InvocationTargetException e) {  

  50. // TODO Auto-generated catch block

  51.            e.printStackTrace();  

  52.        } catch (ClassNotFoundException e) {  

  53. // TODO Auto-generated catch block

  54.            e.printStackTrace();  

  55.        }  

  56. returnnull;  

  57.    }  

  58. publicstatic Object getFieldOjbect(String class_name,Object obj, String filedName){  

  59. try {  

  60.            Class obj_class = Class.forName(class_name);  

  61.            Field field = obj_class.getDeclaredField(filedName);  

  62.            field.setAccessible(true);  

  63. return field.get(obj);  

  64.        } catch (SecurityException e) {  

  65. // TODO Auto-generated catch block

  66.            e.printStackTrace();  

  67.        } catch (NoSuchFieldException e) {  

  68. // TODO Auto-generated catch block

  69.            e.printStackTrace();  

  70.        } catch (IllegalArgumentException e) {  

  71. // TODO Auto-generated catch block

  72.            e.printStackTrace();  

  73.        } catch (IllegalAccessException e) {  

  74. // TODO Auto-generated catch block

  75.            e.printStackTrace();  

  76.        } catch (ClassNotFoundException e) {  

  77. // TODO Auto-generated catch block

  78.            e.printStackTrace();  

  79.        }  

  80. returnnull;  

  81.    }  

  82. publicstatic Object getStaticFieldOjbect(String class_name, String filedName){  

  83. try {  

  84.            Class obj_class = Class.forName(class_name);  

  85.            Field field = obj_class.getDeclaredField(filedName);  

  86.            field.setAccessible(true);  

  87. return field.get(null);  

  88.        } catch (SecurityException e) {  

  89. // TODO Auto-generated catch block

  90.            e.printStackTrace();  

  91.        } catch (NoSuchFieldException e) {  

  92. // TODO Auto-generated catch block

  93.            e.printStackTrace();  

  94.        } catch (IllegalArgumentException e) {  

  95. // TODO Auto-generated catch block

  96.            e.printStackTrace();  

  97.        } catch (IllegalAccessException e) {  

  98. // TODO Auto-generated catch block

  99.            e.printStackTrace();  

  100.        } catch (ClassNotFoundException e) {  

  101. // TODO Auto-generated catch block

  102.            e.printStackTrace();  

  103.        }  

  104. returnnull;  

  105.    }  

  106. publicstaticvoid setFieldOjbect(String classname, String filedName, Object obj, Object filedVaule){  

  107. try {  

  108.            Class obj_class = Class.forName(classname);  

  109.            Field field = obj_class.getDeclaredField(filedName);  

  110.            field.setAccessible(true);  

  111.            field.set(obj, filedVaule);  

  112.        } catch (SecurityException e) {  

  113. // TODO Auto-generated catch block

  114.            e.printStackTrace();  

  115.        } catch (NoSuchFieldException e) {  

  116. // TODO Auto-generated catch block

  117.            e.printStackTrace();  

  118.        } catch (IllegalArgumentException e) {  

  119. // TODO Auto-generated catch block

  120.            e.printStackTrace();  

  121.        } catch (IllegalAccessException e) {  

  122. // TODO Auto-generated catch block

  123.            e.printStackTrace();  

  124.        } catch (ClassNotFoundException e) {  

  125. // TODO Auto-generated catch block

  126.            e.printStackTrace();  

  127.        }    

  128.    }  

  129. publicstaticvoid setStaticOjbect(String class_name, String filedName, Object filedVaule){  

  130. try {  

  131.            Class obj_class = Class.forName(class_name);  

  132.            Field field = obj_class.getDeclaredField(filedName);  

  133.            field.setAccessible(true);  

  134.            field.set(null, filedVaule);  

  135.        } catch (SecurityException e) {  

  136. // TODO Auto-generated catch block

  137.            e.printStackTrace();  

  138.        } catch (NoSuchFieldException e) {  

  139. // TODO Auto-generated catch block

  140.            e.printStackTrace();  

  141.        } catch (IllegalArgumentException e) {  

  142. // TODO Auto-generated catch block

  143.            e.printStackTrace();  

  144.        } catch (IllegalAccessException e) {  

  145. // TODO Auto-generated catch block

  146.            e.printStackTrace();  

  147.        } catch (ClassNotFoundException e) {  

  148. // TODO Auto-generated catch block

  149.            e.printStackTrace();  

  150.        }        

  151.    }  

  152. }  



三、总结


      本文代码基本实现了APK文件的加壳及脱壳原理,该代码作为实验代码还有诸多地方需要改进。比如:

            1、加壳数据的加密算法的添加。

            2、脱壳代码由java语言实现,可通过C代码的实现对脱壳逻辑进行保护,以达到更好的反逆向分析效果。


你可能感兴趣的:(android,APK加壳技术方案)