现在主要工具是接触 SDK,为了防止游戏包被破解编译,以及发现加密串,我来分享下以下几点:
防破解技术主要有四种实现方式:
1. 代码混淆(ProGuard)技术
2. 签名比对技术
3.NDK .so 动态库技术
4. 动态加载技术
5. 第三方平台加密以及检测漏洞
这个在 这篇文章中也提及到了相关的知识点。
主要优点有:
? ???? 1. 核心代码在被加密的 jar 中,所以破解者无法解压出 class 文件,如果加密秘钥被破解者拿到,那将是另外一层面的安全问题了。
????? 2. 该技术也可以有效防止加壳技术,代码是动态加载上来的,破解者的壳程序无法加入到已加密的 jar 包中,及时破解者注入壳程序入口,壳程序因为不在 ClassLoader 的 jar 包中,所以也无法被执行起来,除非破解者替换 ClassLoader 的 jar 包,关掉 NDK 解密代码. 但这种安装到手机上,已经不在是我们的应用,用户一定会将其卸载掉。
所以综合起来比较,第四种动态加载技术是最安全的,但效率问题,本人并没做严格测试,粗略实验了一下,效率并没有明显降低。
- // 1.Jar包加密加密解密文件//
- public static boolean enOrDecryptFile(byte[] paramArrayOfByte,
- String sourceFilePath, String destFilePath,int mode){
- File sourceFile = new File(sourceFilePath);
- File destFile = new File(destFilePath);
- CipherOutputStream cout = null;
- FileInputStream in = null;
- FileOutputStream out = null;
- if (sourceFile.exists() && sourceFile.isFile()) {
- if (!destFile.getParentFile().exists()) {
- destFile.getParentFile().mkdirs();
- }
- try {
- destFile.createNewFile();
- in = new FileInputStream(sourceFile);
- out = new FileOutputStream(destFile);
- // 获取密钥//
- init();
- SecretKeySpec secretKeySpec = new SecretKeySpec(defPassword, "AES");
- Cipher cipher;
- cipher = Cipher.getInstance("AES");
- cipher.init(mode, secretKeySpec);
- cout = new CipherOutputStream(out, cipher);
- byte[] cache = new byte[CACHE_SIZE];
- int nRead = 0;
- while ((nRead = in.read(cache)) != -1) {
- cout.write(cache, 0, nRead);
- cout.flush();
- }
- }catch (IOException e) {
- e.printStackTrace();
- return false;
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- return false ;
- } catch (NoSuchPaddingException e) {
- e.printStackTrace();
- return false ;
- }catch (InvalidKeyException e) {
- e.printStackTrace();
- return false;
- }finally{
- if(cout != null){
- try {
- cout.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- if(out != null){
- try {
- out.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- if(in != null){
- try {
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return true;
- }
- return false;
- }
jar 用 SDK\platform-tools \ 下的 dx 命令进行 dex 格式转化
- dx --dex --output=生成的目标文件的地址(绝对路径) 需要转化的jar文件(绝对路径)
- 例如:dx --dex --output=H:\classdex.jar H:\dujinyang-KARL.jar
然后再用加密工具将生成 jar 文件进行加密处理
最后通过代码动态加载:
- File file = new File("/data/data/" + base.getPackageName() + "/.cache/");
- if (!file.exists()) {
- file.mkdirs();
- }
- try {
- Runtime.getRuntime().exec("chmod 755 " + file.getAbsolutePath()).waitFor();
- } catch (InterruptedException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- } catch (IOException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- Util.copyJarFile(this);
- Object currentActivityThread = RefInvoke.invokeStaticMethod(
- "android.app.ActivityThread", "currentActivityThread",
- new Class[] {}, new Object[] {});
- String packageName = getPackageName();
- HashMap mPackages = (HashMap) RefInvoke.getFieldOjbect(
- "android.app.ActivityThread", currentActivityThread,
- "mPackages");
- WeakReference wr = (WeakReference) mPackages.get(packageName);
- MyClassLoader dLoader = new MyClassLoader("/data/data/"
- + base.getPackageName() + "/.cache/classdex.jar", "/data/data/"
- + base.getPackageName() + "/.cache", "/data/data/"
- + base.getPackageName() + "/.cache/", base.getClassLoader());
- try {
- Class class1 = dLoader.loadClass("com.example.test.TestActivity");
- Log.i("b364","----------->class1: "+class1);
- } catch (ClassNotFoundException e){
- Log.i("b364","----------->class not found Exception!");
- e.printStackTrace();
- }
- Log.i("b364","------>PackageInfo: "+wr.get());
- // 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);
处理完成,如果在 Application 中做特别处理也是可行的。之前就有人分析了爱加密的加密方式,不过这里不做阐述,有兴趣可以一起讨论。
下篇文章中我们来讲讲如何 逆向 apk 的动态库
来源: http://www.bubuko.com/infodetail-1953306.html