如何配置Proguard删除非平凡的日志消息?

时间:2013-11-26 14:20:17

标签: android proguard

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);
}

所以这个方法会被混淆器消失,并且对这个方法的所有调用也会以递归方式消失?

拟订一项

混淆将优化代码并删除未使用或排除的方法 但是代码编译会在方法调用中生成额外的字节代码 before ,并且即使在混淆之后,前面的代码保留,并留下代码,例如:

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);
  }

3 个答案:

答案 0 :(得分:2)

就我的研究而言,这一点根本无法做到。你可以做的是设置你的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>

当然你还需要创建一个名为uncommentlogs的目标,与uncommentlogs相同的主体

这基本上把/ *放在任何Log之前。和* /最近的);

答案 1 :(得分:2)

我得出结论,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(...)

答案 2 :(得分:0)

您可以将以下规则添加到proguard文件中,以删除对 debug 日志打印输出的所有调用:

-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