失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > android apk 防止反编译技术第一篇-加壳技术

android apk 防止反编译技术第一篇-加壳技术

时间:2019-08-08 03:06:04

相关推荐

android apk 防止反编译技术第一篇-加壳技术

做android framework方面的工作将近三年的时间了,现在公司让做一下android apk安全方面的研究,于是最近就在网上找大量的资料来学习。现在将最近学习成果做一下整理总结。学习的这些成果我会做成一个系列慢慢写出来与大家分享,共同进步。这篇主要讲apk的加壳技术,废话不多说了直接进入正题。

一、加壳技术原理

所谓apk的加壳技术和pc exe的加壳原理一样,就是在程序的外面再包裹上另外一段代码,保护里面的代码不被非法修改或反编译,在程序运行的时候优先取得程序的控制权做一些我们自己想做的工作。(哈哈,跟病毒的原理差不多)

PC exe的加壳原理如下:

二、android apk加壳实现

要想实现加壳需要解决的技术点如下:

(1)怎么第一时间执行我们的加壳程序?

首先根据上面的原理我们在apk中要想优先取得程序的控制权作为android apk的开发人员都知道Application会被系统第一时间调用而我们的程序也会放在这里执行。

(2)怎么将我们的加壳程序和原有的android apk文件合并到一起?

我们知道android apk最终会打包生成dex文件,我们可以将我们的程序生成dex文件后,将我们要进行加壳的apk和我们dex文件合并成一个文件,然后修改dex文件头中的checksum、signature和file_size的信息,并且要附加加壳的apk的长度信息在dex文件中,以便我们进行解壳保证原来apk的正常运行。加完壳后整个文件的结构如下:

(3)怎么将原来的apk正常的运行起来?

按照(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-dataandroid:name="APPLICATION_CLASS_NAME"android:value="com.android.MyApplication"/>

6.</application>

com.android.shellApplication这个就是我们的程序的的application的名称,而

7.<meta-dataandroid:name="APPLICATION_CLASS_NAME"android:value="com.android.MyApplication"/>

是原来的apk的application名称。

(2)合并文件代码实现如下:

public class ShellTool {/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubtry {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];//添加我们程序的dexSystem.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 blocke.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文件,代码如下:

publicclassshellApplicationextendsApplication{privatestaticfinalStringappkey="APPLICATION_CLASS_NAME";privateStringapkFileName;privateStringodexPath;privateStringlibPath;protectedvoidattachBaseContext(Contextbase){super.attachBaseContext(base);try{Fileodex=this.getDir("payload_odex",MODE_PRIVATE);Filelibs=this.getDir("payload_lib",MODE_PRIVATE);odexPath=odex.getAbsolutePath();libPath=libs.getAbsolutePath();apkFileName=odex.getAbsolutePath()+"/payload.apk";FiledexFile=newFile(apkFileName);if(!dexFile.exists())dexFile.createNewFile();//读取程序classes.dex文件byte[]dexdata=this.readDexFileFromApk();//分离出解壳后的apk文件已用于动态加载this.splitPayLoadFromDex(dexdata);//配置动态加载环境ObjectcurrentActivityThread=RefInvoke.invokeStaticMethod("android.app.ActivityThread","currentActivityThread",newClass[]{},newObject[]{});StringpackageName=this.getPackageName();HashMapmPackages=(HashMap)RefInvoke.getFieldOjbect("android.app.ActivityThread",currentActivityThread,"mPackages");WeakReferencewr=(WeakReference)mPackages.get(packageName);DexClassLoaderdLoader=newDexClassLoader(apkFileName,odexPath,libPath,(ClassLoader)RefInvoke.getFieldOjbect("android.app.LoadedApk",wr.get(),"mClassLoader"));RefInvoke.setFieldOjbect("android.app.LoadedApk","mClassLoader",wr.get(),dLoader);}catch(Exceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}}publicvoidonCreate(){{//如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。StringappClassName=null;try{ApplicationInfoai=this.getPackageManager().getApplicationInfo(this.getPackageName(),PackageManager.GET_META_DATA);Bundlebundle=ai.metaData;if(bundle!=null&&bundle.containsKey("APPLICATION_CLASS_NAME")){appClassName=bundle.getString("APPLICATION_CLASS_NAME");}else{return;}}catch(NameNotFoundExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}ObjectcurrentActivityThread=RefInvoke.invokeStaticMethod("android.app.ActivityThread","currentActivityThread",newClass[]{},newObject[]{});ObjectmBoundApplication=RefInvoke.getFieldOjbect("android.app.ActivityThread",currentActivityThread,"mBoundApplication");ObjectloadedApkInfo=RefInvoke.getFieldOjbect("android.app.ActivityThread$AppBindData",mBoundApplication,"info");RefInvoke.setFieldOjbect("android.app.LoadedApk","mApplication",loadedApkInfo,null);ObjectoldApplication=RefInvoke.getFieldOjbect("android.app.ActivityThread",currentActivityThread,"mInitialApplication");ArrayList<Application>mAllApplications=(ArrayList<Application>)RefInvoke.getFieldOjbect("android.app.ActivityThread",currentActivityThread,"mAllApplications");mAllApplications.remove(oldApplication);ApplicationInfoappinfo_In_LoadedApk=(ApplicationInfo)RefInvoke.getFieldOjbect("android.app.LoadedApk",loadedApkInfo,"mApplicationInfo");ApplicationInfoappinfo_In_AppBindData=(ApplicationInfo)RefInvoke.getFieldOjbect("android.app.ActivityThread$AppBindData",mBoundApplication,"appInfo");appinfo_In_LoadedApk.className=appClassName;appinfo_In_AppBindData.className=appClassName;Applicationapp=(Application)RefInvoke.invokeMethod("android.app.LoadedApk","makeApplication",loadedApkInfo,newClass[]{boolean.class,Instrumentation.class},newObject[]{false,null});RefInvoke.setFieldOjbect("android.app.ActivityThread","mInitialApplication",currentActivityThread,app);HashMapmProviderMap=(HashMap)RefInvoke.getFieldOjbect("android.app.ActivityThread",currentActivityThread,"mProviderMap");Iteratorit=mProviderMap.values().iterator();while(it.hasNext()){ObjectproviderClientRecord=it.next();ObjectlocalProvider=RefInvoke.getFieldOjbect("android.app.ActivityThread$ProviderClientRecord",providerClientRecord,"mLocalProvider");RefInvoke.setFieldOjbect("android.content.ContentProvider","mContext",localProvider,app);}app.onCreate();}}privatevoidsplitPayLoadFromDex(byte[]data)throwsIOException{byte[]apkdata=decrypt(data);intablen=apkdata.length;byte[]dexlen=newbyte[4];System.arraycopy(apkdata,ablen-4,dexlen,0,4);ByteArrayInputStreambais=newByteArrayInputStream(dexlen);DataInputStreamin=newDataInputStream(bais);intreadInt=in.readInt();System.out.println(Integer.toHexString(readInt));byte[]newdex=newbyte[readInt];System.arraycopy(apkdata,ablen-4-readInt,newdex,0,readInt);Filefile=newFile(apkFileName);try{FileOutputStreamlocalFileOutputStream=newFileOutputStream(file);localFileOutputStream.write(newdex);localFileOutputStream.close();}catch(IOExceptionlocalIOException){thrownewRuntimeException(localIOException);}ZipInputStreamlocalZipInputStream=newZipInputStream(newBufferedInputStream(newFileInputStream(file)));while(true){ZipEntrylocalZipEntry=localZipInputStream.getNextEntry();if(localZipEntry==null){localZipInputStream.close();break;}Stringname=localZipEntry.getName();if(name.startsWith("lib/")&&name.endsWith(".so")){FilestoreFile=newFile(libPath+"/"+name.substring(name.lastIndexOf('/')));storeFile.createNewFile();FileOutputStreamfos=newFileOutputStream(storeFile);byte[]arrayOfByte=newbyte[1024];while(true){inti=localZipInputStream.read(arrayOfByte);if(i==-1)break;fos.write(arrayOfByte,0,i);}fos.flush();fos.close();}localZipInputStream.closeEntry();}localZipInputStream.close();}privatebyte[]readDexFileFromApk()throwsIOException{ByteArrayOutputStreamdexByteArrayOutputStream=newByteArrayOutputStream();ZipInputStreamlocalZipInputStream=newZipInputStream(newBufferedInputStream(newFileInputStream(this.getApplicationInfo().sourceDir)));while(true){ZipEntrylocalZipEntry=localZipInputStream.getNextEntry();if(localZipEntry==null){localZipInputStream.close();break;}if(localZipEntry.getName().equals("classes.dex")){byte[]arrayOfByte=newbyte[1024];while(true){inti=localZipInputStream.read(arrayOfByte);if(i==-1)break;dexByteArrayOutputStream.write(arrayOfByte,0,i);}}localZipInputStream.closeEntry();}localZipInputStream.close();returndexByteArrayOutputStream.toByteArray();}////直接返回数据,读者可以添加自己解密方法privatebyte[]decrypt(byte[]data){returndata;}

根据上面的讲述相信大家对apk的加壳技术有了一定的了解,下一篇我们将讲解另一种android apk防止反编译技术,期待大家的捧场。如果对这篇讲的技术有任何疑问及想要获得这篇文章讲的技术的工程源码

欢迎关注个人微信公众平台:程序员互动联盟(coder_online),扫一扫下方二维码或搜索微信号coder_online即可关注,我们可以在线交流。

如果觉得《android apk 防止反编译技术第一篇-加壳技术》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。