1、加殼技術原理 java
所謂apk的加殼技術和pc exe的加殼原理同樣,就是在程序的外面再包裹上另一段代碼,保護裏面的代碼不被非法修改或反編譯,在程序運行的時候優先取得程序的控制權作一些咱們本身想作的工做。 android
PC exe的加殼原理以下: shell
2、android apk加殼實現 app
要想實現加殼須要解決的技術點以下: ide
(1)怎麼第一時間執行咱們的加殼程序? this
首先根據上面的原理咱們在apk中要想優先取得程序的控制權做爲android apk的開發人員都知道Application會被系統第一時間調用而咱們的程序也會放在這裏執行。 加密
(2)怎麼將咱們的加殼程序和原有的android apk文件合併到一塊兒? spa
咱們知道android apk最終會打包生成dex文件,咱們能夠將咱們的程序生成dex文件後,將咱們要進行加殼的apk和咱們dex文件合併成一個文件,而後修改dex文件頭中的checksum、signature和file_size的信息,而且要附加加殼的apk的長度信息在dex文件中,以便咱們進行解殼保證原來apk的正常運行。加完殼後整個文件的結構以下: .net
(3)怎麼將原來的apk正常的運行起來? code
按照(2)中的合併方式在當咱們的程序首先運行起來後,逆向讀取dex文件獲取原來的apk文件經過DexClassLoader動態加載。
具體實現以下:
(1)修改原來apk的AndroidMainfest.xml文件,假如原來apk的AndroidMainfest.xml文件內容以下:
1. <application
2. android:icon="@drawable/ic_launcher"
3. android:label="@string/app_name"
4. android:theme="@style/AppTheme" android:name="com.android.MyApplication" >
5. </application>
修改後的內容以下:
1. <application
2. android:icon="@drawable/ic_launcher"
3. android:label="@string/app_name"
4. android:theme="@style/AppTheme" android:name="com.android.shellApplication" >
5. <meta-data android:name="APPLICATION_CLASS_NAME" android:value="com.android.MyApplication"/>
6. </application>
com.android.shellApplication這個就是咱們的程序的的application的名稱,而
7. <meta-data android:name="APPLICATION_CLASS_NAME" android:value="com.android.MyApplication"/>
是原來的apk的application名稱。
(2)合併文件代碼實現以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
public class ShellTool { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub try { File payloadSrcFile = new File("payload.apk");//咱們要加殼的apk文件 File unShellDexFile = new File("classes.dex");//咱們的程序生成的dex文件 byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile)); byte[] unShellDexArray = readFileBytes(unShellDexFile); int payloadLen = payloadArray.length; int unShellDexLen = unShellDexArray.length; int totalLen = payloadLen + unShellDexLen +4; byte[] newdex = new byte[totalLen]; //添加咱們程序的dex System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen); //添加要加殼的apk文件 System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen); //添加apk文件長度 System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4); //修改DEX file size文件頭 fixFileSizeHeader(newdex); //修改DEX SHA1 文件頭 fixSHA1Header(newdex); //修改DEX CheckSum文件頭 fixCheckSumHeader(newdex);
String str = "outdir/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) { // TODO Auto-generated catch block e.printStackTrace(); } }
//直接返回數據,讀者能夠添加本身加密方法 private static byte[] encrpt(byte[] srcdata){ return srcdata; }
private static void fixCheckSumHeader(byte[] dexBytes) { Adler32 adler = new Adler32(); adler.update(dexBytes, 12, dexBytes.length - 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); System.out.println(Long.toHexString(value)); System.out.println(); }
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; }
private static void fixSHA1Header(byte[] dexBytes) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(dexBytes, 32, dexBytes.length - 32); byte[] newdt = md.digest(); System.arraycopy(newdt, 0, dexBytes, 12, 20); String hexstr = ""; for (int i = 0; i < newdt.length; i++) { hexstr += Integer.toString((newdt[i] & 0xff) + 0x100, 16) .substring(1); } System.out.println(hexstr); }
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); }
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(); } } }
} |
(3)在咱們的程序中加載運行原來的apk文件,代碼以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
public class shellApplication extends Application {
private static final String appkey = "APPLICATION_CLASS_NAME"; private String apkFileName; private String odexPath; private String libPath;
protected void attachBaseContext(Context base) { super.attachBaseContext(base); try { File odex = this.getDir("payload_odex", MODE_PRIVATE); File libs = this.getDir("payload_lib", MODE_PRIVATE); odexPath = odex.getAbsolutePath(); libPath = libs.getAbsolutePath(); apkFileName = odex.getAbsolutePath() + "/payload.apk"; File dexFile = new File(apkFileName); if (!dexFile.exists()) dexFile.createNewFile(); // 讀取程序classes.dex文件 byte[] dexdata = this.readDexFileFromApk(); // 分離出解殼後的apk文件已用於動態加載 this.splitPayLoadFromDex(dexdata); // 配置動態加載環境 Object currentActivityThread = RefInvoke.invokeStaticMethod( "android.app.ActivityThread", "currentActivityThread", new Class[] {}, new Object[] {}); String packageName = this.getPackageName(); HashMap mPackages = (HashMap) RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mPackages"); WeakReference wr = (WeakReference) mPackages.get(packageName); DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath, libPath, (ClassLoader) RefInvoke.getFieldOjbect( "android.app.LoadedApk", wr.get(), "mClassLoader")); RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader", wr.get(), dLoader);
} catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
public void onCreate() { {
// 若是源應用配置有Appliction對象,則替換爲源應用Applicaiton,以便不影響源程序邏輯。 String appClassName = null; try { ApplicationInfo ai = this.getPackageManager() .getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA); Bundle bundle = ai.metaData; if (bundle != null && bundle.containsKey("APPLICATION_CLASS_NAME")) { appClassName = bundle.getString("APPLICATION_CLASS_NAME"); } else { return; } } catch (NameNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); }
Object currentActivityThread = RefInvoke.invokeStaticMethod( "android.app.ActivityThread", "currentActivityThread", new Class[] {}, new Object[] {}); Object mBoundApplication = RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mBoundApplication"); Object loadedApkInfo = RefInvoke.getFieldOjbect( "android.app.ActivityThread$AppBindData", mBoundApplication, "info"); RefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication", loadedApkInfo, null); Object oldApplication = RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mInitialApplication"); ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke .getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mAllApplications"); mAllApplications.remove(oldApplication); ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke .getFieldOjbect("android.app.LoadedApk", loadedApkInfo, "mApplicationInfo"); ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke .getFieldOjbect("android.app.ActivityThread$AppBindData", mBoundApplication, "appInfo"); appinfo_In_LoadedApk.className = appClassName; appinfo_In_AppBindData.className = appClassName; Application app = (Application) RefInvoke.invokeMethod( "android.app.LoadedApk", "makeApplication", loadedApkInfo, new Class[] { boolean.class, Instrumentation.class }, new Object[] { false, null }); RefInvoke.setFieldOjbect("android.app.ActivityThread", "mInitialApplication", currentActivityThread, app);
HashMap mProviderMap = (HashMap) RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mProviderMap"); Iterator it = mProviderMap.values().iterator(); while (it.hasNext()) { Object providerClientRecord = it.next(); Object localProvider = RefInvoke.getFieldOjbect( "android.app.ActivityThread$ProviderClientRecord", providerClientRecord, "mLocalProvider"); RefInvoke.setFieldOjbect("android.content.ContentProvider", "mContext", localProvider, app); } app.onCreate(); } }
private void splitPayLoadFromDex(byte[] data) throws IOException { byte[] apkdata = decrypt(data); int ablen = apkdata.length; byte[] dexlen = new byte[4]; System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4); ByteArrayInputStream bais = new ByteArrayInputStream(dexlen); DataInputStream in = new DataInputStream(bais); int readInt = in.readInt(); System.out.println(Integer.toHexString(readInt)); byte[] newdex = new byte[readInt]; System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt); File file = new File(apkFileName); try { FileOutputStream localFileOutputStream = new FileOutputStream(file); localFileOutputStream.write(newdex); localFileOutputStream.close();
} catch (IOException localIOException) { throw new RuntimeException(localIOException); }
ZipInputStream localZipInputStream = new ZipInputStream( new BufferedInputStream(new FileInputStream(file))); while (true) { ZipEntry localZipEntry = localZipInputStream.getNextEntry(); if (localZipEntry == null) { localZipInputStream.close(); break; } String name = localZipEntry.getName(); if (name.startsWith("lib/") && name.endsWith(".so")) { File storeFile = new File(libPath + "/" + name.substring(name.lastIndexOf('/'))); storeFile.createNewFile(); FileOutputStream fos = new FileOutputStream(storeFile); byte[] arrayOfByte = new byte[1024]; while (true) { int i = localZipInputStream.read(arrayOfByte); if (i == -1) break; fos.write(arrayOfByte, 0, i); } fos.flush(); fos.close(); } localZipInputStream.closeEntry(); } localZipInputStream.close();
}
private byte[] readDexFileFromApk() throws IOException { ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream(); ZipInputStream localZipInputStream = new ZipInputStream( new BufferedInputStream(new FileInputStream( this.getApplicationInfo().sourceDir))); while (true) { ZipEntry localZipEntry = localZipInputStream.getNextEntry(); if (localZipEntry == null) { localZipInputStream.close(); break; } if (localZipEntry.getName().equals("classes.dex")) { byte[] arrayOfByte = new byte[1024]; while (true) { int i = localZipInputStream.read(arrayOfByte); if (i == -1) break; dexByteArrayOutputStream.write(arrayOfByte, 0, i); } } localZipInputStream.closeEntry(); } localZipInputStream.close(); return dexByteArrayOutputStream.toByteArray(); }
// //直接返回數據,讀者能夠添加本身解密方法 private byte[] decrypt(byte[] data) { return data; } |