Proguard很好地删除了琐碎的日志调用。(带有assumenosideeffects
关键字)
但它处理非琐碎的日志调用很差。
我所说的"非琐碎"指的不仅仅是一根绳子
例如:Log.i(TAG,"velocity="+velocity)
"。
Proguard将new StringBuilder("velocity=")
和附加变量保留在值中,不会太模糊该变量。它只删除对Log的最终调用
字符串将保留在那里,浪费内存和cpu周期,也有助于破解者理解代码。
因此,为了解决这个问题,我用if(BuildConfig.DEBUG){...}
封装了应用程序中的每个非琐碎的调试日志调用。但是,用if(..){..}
包装每个日志是乏味且容易出错的
它肯定不干(不要重复)
是否有一种方法可以标记Proguard(或通过任何其他方式)完全删除的方法,包括所有调用方法?
类似于:
@proguard_purge
public static void vanishingDebug(String whatever) {
Log.i(TAG,whatever);
}
所以这个方法会被模糊处理程序消失,而且对这个方法的所有调用也会递归消失?
ELABORATION
混淆将优化代码并删除未使用或排除的方法
但是,代码编译会在方法调用之前生成额外的字节代码,这些代码将被删除,并且之前的代码即使在模糊处理后也会保留,并留下以下代码:
new StringBuilder("velocity=").append(a)
(假设a
在编译时无法确定。要进行测试,请使用velocity=Math.random();
)
这使得混淆的代码很难理解。
为了重现这个问题,您需要安装dex2jar来将apk转换为jar,并安装JAD来将jar转换为java代码。
你会看到真正留下的东西,并感到震惊。
示例
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("TAG", "Simple Comment"); // << Disappears well!
double index = Math.random();
index++;
Log.i("TAG2", "log_index=" + index); // << hmm... (!)
// class with only log calls inside
new ReferencedClass();
// simple method call
MyLogger.notLog("no_log" + index); // << stays, as expected
// simple method call with only Log call inside (aka "Log Wrapper")
MyLogger.log("log" + index); // << stays, as expected
Log.i("TAG2", "This is random:" + Math.random()); // << stays, same as above
setContentView(R.layout.activity_main);
}
使用这种模糊配置:
-assumenosideeffects class android.util.Log {
public static *** isLoggable(java.lang.String, int);
public static *** d(...);
public static *** v(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
}
它将被模糊到一个可以反编译和反模糊的状态:
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
double d = 1.0D + Math.random();
new StringBuilder("log_index=").append(d).toString();
new b();
a.a("no_log" + d);
new StringBuilder("log").append(d).toString();
a.a();
new StringBuilder("This is random:").append(Math.random()).toString();
setContentView(2130903064);
}
就我的研究而言,爱德华根本做不到这一点。不过,您可以做的是设置ant构建脚本。
<target name="-commentoutlogs">
<replaceregexp match="(Log..*?;s*n)" replace="/*1*/" flags="gs" byline="false">
<fileset dir="src">
<include name="**/*.java"/>
</fileset>
</replaceregexp>
</target>
<target name="-uncommentlogs">
<replaceregexp match="/*(Log..*?;s*n)*/" replace="1" flags="gs" byline="false">
<fileset dir="src">
<include name="**/*.java"/>
</fileset>
</replaceregexp>
</target>
这是一个简单的基于正则表达式的脚本,可以在build.xml文件中使用,并将其添加到ant发布目标中,如下所示:
<target name="release"
depends="-uncommentlogsbefore, -commentoutlogs, -set-release-mode, -release-obfuscation-check, -package, -post-package, -release-prompt-for-password, -release-nosign, -release-sign, -uncommentlogsafter, -post-build"
description="Builds the application in release mode.">
</target>
当然,您还需要创建一个名为uncommentlogsbefore的目标,其主体与uncommentlogs 相同
这基本上将/*放在任何Log之前。和*/最接近之后);
我得出的结论是,ProGuard不可能知道StringBuilder
与他被要求删除的日志有关。
因此,不能有ProGuard规则来删除以下类型的复杂日志项:
Log.i("TAG2", "This is random:" + Math.random());
而且它总是会导致一个带有剩余代码的模糊代码,这些代码可以被解码为:
new StringBuilder("This is random:").append(Math.random()).toString();
消除遗留问题的唯一方法是用包装每个非平凡的日志调用
if (BuildConfig.DEBUG) Log.i(...)
您可以将以下规则添加到proguard文件中,以便删除对调试日志打印输出的所有调用:
-assumenosideeffects class android.util.Log {
public static *** d(...); }
例如,以下代码中的所有行都将从模糊的jar文件中消失:
String logevent = "log event";
android.util.Log.d("Tag", "This is my ");
android.util.Log.d("Tag", "This is my " + logevent);
p.S.
不要忘记删除或注释掉这一行,如果你有:-dontoptimize