您的位置:首页 > 编程语言

迷你播放器--第一阶段(7)--安全攻防第一战--对抗反编译,代码混淆和对抗动态调试

2014-08-08 11:05 471 查看
迷你播放器--第一阶段(7)
安全攻防第一战--对抗反编译,代码混淆和对抗动态调试;

本文章为CSDN作者原创,转载请保留出处:/article/10521282.html

查看项目源代码请前往:迷你播放器-综述

请注意,该项目为android项目,如果已经学习过这一节请阅读下一篇。

对于软件开发人员来说,最痛苦的事莫过于自己花费大量的时间与精力,辛辛苦苦弄了大半年才完成的项目,却在短时间内被人破解了。如何防止软件被人反编译,保证软件的核心代码不被人剿窃,这才是作为Android软件开发者最关心的事吧!出于中国国情,国民还没养成付费的习惯,Android收费软件在国内几乎很难存活,获利模式多是采用免费发布加广告展示来获取广告收入。免费发布的软件没有任何的授权访问机制来控制,都是直接暴露在互联网上的,任何一个想要逆向分析该软件的人,都可以从网上直接下载到apk文件。

我们的apk到底有多不安全呢,看看这位大牛的破解记录,http://bbs.csdn.net/topics/390598569

回想一下逆向Android软件的步骤:首先是对其进行反编译,然后是阅读反汇编代码,如果有必要还会对其进行动态调试,找到突破口后注入或直接修改反汇编代码,最后重新编译软件进行测试。整个过程可分为反编译、静态分析、动态调试、重编译等4个环节,因此,我们可以在这四个环节寻找突破口

1、对抗反编译

对抗反编译主要利用工具的缺陷以及系统的限制,使得apk文件无法通过工具(apktool,BakSmali,dex2jar)反编译或者反编译不能得到正确的代码。对于大部分工具,反编译后都要写入磁盘,但是系统对文件名是有限制的,例如Windows下含路径在内长度不能超过260,Linux下限制更严,连目录数也会限制,因此,我们可以故意制造超长的类名,是的反编译的时候无法生成该类的实现方法。如何生成呢,在proguard-project.txt添加如下声明即可(在对抗静态编译会详细介绍如何使用):
注:windows.txt位于SDK的tools\proguard\examples\dictionaries目录下,可以拷贝只工程目录并按需修改为超长文件名。原文件是利用Windows保留名来对抗反编译,但是最新版本的反编译工具均已经修复好这一个bug,但是超长文件名的漏洞是无法修复的了的,除非不把反编译的smali文件生成到磁盘目录下,
#使用windows保留名或超长文件名以增加破解难度。
-classobfuscationdictionary ./dictionaries/windows.txt
-packageobfuscationdictionary ./dictionaries/windows.txt
对抗的另一种方法是使用花指令,这一点我研究的不是很深入。花指令来源于汇编,指的是程序中的无用代码,程序对它没影响,少了它也能正常运行,没有改变特征码的位置,但是他改变了程序执行顺序,有的也能改变文件结构。其实反编译工具是很笨的,花指令在跳转的时候可以跳转到一些不存在的地方是的反编译工具崩溃,让破解者无法清楚正确地反汇编程序的内容,迷失方向。jd-gui的bug其实挺多的,很多特殊代码块或者字段集也会让其崩溃无法反编译出源码。。。,例如在每个类添加如下代码可使得jd-gui崩溃,不过别高兴,最新版已经修复好该bug了
private static final char[] wJ = "0123456789abcdef".toCharArray();
public static String imsi = "204046330839890";
public static String p = "0";
public static String keyword = "电话";
public static String tranlateKeyword = "%E7%94%B5%E8%AF%9D";


2、对抗静态编译

反编译工具的漏洞是会修复好的,其实这也是编译于反编译一直对抗的结果。我们不能指望反编译工具永远无法反编译你的apk包,那么怎么办呢,一个初级的保护方法是在java层使用代码混淆技术。
Java语言编写的代码本身就很容易被反编译,Google很早就意识到了这一点,在Andrnid2.3的SDK中正式加入了ProGuard代码混淆工具,开发人员可以使用该工具对自己的代码进行混淆。ProGuard提供了压缩(Shrinking)、混淆(obfuscation)、优化(optimition) Java代码以及反混淆栈跟踪(ReTrace)的功能。使用ProGuard前需要编写混淆配置文件,使用Eolipse+ADT开发Android应用程序,会默认生成project.propcrties与proguard.cfg两个文件,要想使用ProGuard混淆软件,需要手动的配置它们,首先需要在project.properties文件取消注释该行代码即可
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt

接着在工程文件夹目录下的proguard-project.txt做相应处理即可,一些是部分总结:
1、过滤R文件的混淆:
-keep class **.R$* { *; }
2、过滤第三方包的混淆:
-keep class packagename.** {*;} #(其中packagename为第三方包的包名)
3、一份平常可能会用到的proguard-project.txt文件:
	-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-ignorewarnings #这1句是屏蔽警告,脚本中把这行注释去掉
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

-libraryjars libs/mmbilling.jar #这1句是导入第三方的类库mmbilling.jar,防止混淆时候读取包内容出错,脚本中把这行注释去掉

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keep classeswithmembernames class * {
native <methods>;
}
-keep classeswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keep classeswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keep classmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

#这4句是不混淆第三方包中的指定内容,脚本中把这行注释去掉,具体处理。
-keep class com.ccit.** {*; }
-keep class ccit.** { *; }
-keep class com.aspire.**
-keep class mm.vending.**

#不要混淆,防止发生错误android-support-v4而发生混淆错误,导致无法打包。
-libraryjars libs/android-support-v4.jar #这里根据你的jar包的路径做改动
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment

#不混淆webview中对javascript接口,具体处理
-keep public class com.igrs.dlna.activity.webviewActivity.JavaScriptInterface
-keep classmembers class com.igrs.dlna.activity.webviewActivity.JavaScriptInterface{
void showSource(java.lang.String,java.lang.String);
}
-keep classmembers class com.igrs.dlna.activity.webviewActivity$InJavaScriptLocalObj {
public void showSource(java.lang.String,java.lang.String);
public void showTitle(java.lang.String);
}

#使用windows保留名或超长文件名以增加破解难度。 -classobfuscationdictionary ./dictionaries/windows.txt -packageobfuscationdictionary ./dictionaries/windows.txt

4、如果项目中使用了jar库,那么上面的配置中 -optimizationpasses 只能为1,2。3以上的话混淆很容易出错!
5、给出一些常用的参数
-target 1.6 			#指定版本号
-forceprocessing		#强制执行,即使过期
-allowaccessmodification	#指定,当执行修改方法或属性的modifer范围
-printmapping			#指定混淆后,类或方法生成的map,后跟指定的路径及文件名 *.map
-overloadaggressively
-repackageclasses		#把执行后的类重新放在某一个目录下,后跟一个目录名
-dontpreverify			#不用预先检查
-verbose			#不用输出详细的过程
-dontwarn			#不用输出警告
-dontnote			#不用输出通知


3、对抗动态调试:
1、检查调试器:
if ((getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
System.out.println("程序被改为可调试状态");
android.os.Process.killProcess(android.os.Process.myPid());
}

或者
if (android.os.Debug.isDebuggerConnected()) {
System.out.println("程序被改为可调试状态");
android.os.Process.killProcess(android.os.Process.myPid());
}


2、检测模拟器:
boolean isRunningInEmualtor() {
boolean qemuKernel = false;
Process process = null;
DataOutputStream os = null;
try{
process = Runtime.getRuntime().exec("getprop ro.kernel.qemu");
os = new DataOutputStream(process.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream(),"GBK"));
os.writeBytes("exit\n");
os.flush();
process.waitFor();
// getprop ro.kernel.qemu == 1  在模拟器
// getprop ro.product.model == "sdk"  在模拟器
// getprop ro.build.tags == "test-keys"  在模拟器
qemuKernel = (Integer.valueOf(in.readLine()) == 1);
Log.d("com.droider.checkqemu", "检测到模拟器:" + qemuKernel);
} catch (Exception e){
qemuKernel = false;
Log.d("com.droider.checkqemu", "run failed" + e.getMessage());
} finally {
try{
if (os != null) {
os.close();
}
process.destroy();
} catch (Exception e) {

}
Log.d("com.droider.checkqemu", "run finally");
}
return qemuKernel;
}


本小节源代码下载 : http://download.csdn.net/detail/lrs0304/7730235
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: