如何配置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);
}

所以这个方法会被模糊处理程序消失,而且对这个方法的所有调用也会递归消失?

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

最新更新