在APK中简单隐藏/混淆字符串?

Mag*_*s W 11 obfuscation android decompiling deobfuscation

有时您需要在应用程序本身中存储密码,例如用于与您自己的服务器通信的用户名/密码.在这些情况下,不可能遵循存储密码的正常过程 - 即散列密码,存储散列,与散列用户输入进行比较 - 因为您没有任何用户输入来比较散列.密码需要由应用程序本身提供.那么如何保护APK中存储的密码呢?如下所示的密码生成功能是否合理安全?

纯文本:

String password = "$()&HDI?=!";
Run Code Online (Sandbox Code Playgroud)

简单的混淆:

private String getPassword(){
    String pool = "%&/@$()7?=!656sd8KJ%&HDI!!!G98y/&%=?=*^%&ft4%(";
    return pool.substring(4, 7) + pool.substring(20, 24) + pool.substring(8, 11);
}
Run Code Online (Sandbox Code Playgroud)

我知道ProGuard有一些混淆功能,但我很好奇上面的"混淆"技术在编译时会做什么,以及通过查看APK和/或使用其他更复杂的东西来弄清楚它有多难技术?

Ada*_*zyk 16

tl; dr如果您知道如何反编译APK,无论代码如何混淆,您都可以轻松获取密码.不要在APK中存储密码,这是不安全的.

我知道ProGuard有一些混淆功能,但我很好奇上面的"混淆"技术在编译时会做什么,以及通过查看APK和/或使用其他更复杂的东西来弄清楚它有多难技术?

我会告诉你它有多容易.这是我们将反编译的Android SSCCE:

MyActivity.java:

public class MyActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        TextView text = (TextView) findViewById(R.id.text);
        text.setText(getPassword());
    }

    private String getPassword() {
        String pool = "%&/@$()7?=!656sd8KJ%&HDI!!!G98y/&%=?=*^%&ft4%(";
        return pool.substring(4, 7) + pool.substring(20, 24) + pool.substring(8, 11);
    }
}
Run Code Online (Sandbox Code Playgroud)

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/text"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"/>
Run Code Online (Sandbox Code Playgroud)

编译并运行,我们可以看到后$()&HDI?=!一个TextView.

让我们反编译APK:

  1. unzip myapp.apk或右键单击APK和Unzip here. classes.dex文件出现.
  2. classes.dex使用dex2jar转换为JAR文件.执行后dex2jar.sh classes.dex,classes_dex2jar.jar出现文件.
  3. 使用一些Java反编译器classes_dex2jar.jar,例如JD-GUI,我们从MyActivity.class以下位置检索这样的Java代码:

    public class MyActivity extends Activity
    {
        private String getPassword()
        {
            return "%&/@$()7?=!656sd8KJ%&HDI!!!G98y/&%=?=*^%&ft4%(".substring(4, 7) 
    + "%&/@$()7?=!656sd8KJ%&HDI!!!G98y/&%=?=*^%&ft4%(".substring(20, 24) 
    + "%&/@$()7?=!656sd8KJ%&HDI!!!G98y/&%=?=*^%&ft4%(".substring(8, 11);
        }
    
        public void onCreate(Bundle paramBundle)
        {
            super.onCreate(paramBundle);
            setContentView(2130903040);
            ((TextView)findViewById(2131034112)).setText(getPassword());
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

ProGuard无济于事,代码仍然易于阅读.

基于以上所述,我已经可以给你一个这个问题的答案:

如下所示的密码生成功能是否合理安全?

不可以.正如您所看到的,它会增加一点点读取反混淆代码的难度.我们不应该以这种方式混淆代码,因为:

  1. 它给人一种安全感.
  2. 这浪费了开发人员的时间.
  3. 它降低了代码的可读性.

在官方Android文档中,在安全和设计部分,他们建议保护您的Google Play公钥:

为了保护您的公钥免受恶意用户和黑客的攻击,请不要将其作为文字字符串嵌入任何代码中.相反,在运行时从片段构造字符串或使用位操作(例如,XOR与其他字符串)来隐藏实际的键.密钥本身不是秘密信息,但您不希望黑客或恶意用户轻易用其他密钥替换公钥.

好的,让我们试试看:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    TextView text = (TextView) findViewById(R.id.text);
    text.setText(xor("A@NCyw&IHY", "ehge13ovux"));
}

private String xor(String a, String b) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < a.length() && i < b.length(); i++) {
        sb.append((char) (a.charAt(i) ^ b.charAt(i)));
    }
    return sb.toString();
}
Run Code Online (Sandbox Code Playgroud)

给人$()&HDI?=!一个TextView好.

反编译版本:

public void onCreate(Bundle paramBundle)
{
    super.onCreate(paramBundle);
    setContentView(2130903040);
    ((TextView)findViewById(2131034112)).setText(xor("A@NCyw&IHY", "ehge13ovux"));
}

private String xor(String paramString1, String paramString2)
{
    StringBuilder localStringBuilder = new StringBuilder();
    for (int i = 0; (i < paramString1.length()) && (i < paramString2.length()); i++) {
        localStringBuilder.append((char)(paramString1.charAt(i) ^ paramString2.charAt(i)));
    }
    return localStringBuilder.toString();
}
Run Code Online (Sandbox Code Playgroud)

和之前非常相似的情况.

即使我们具有极其复杂的功能soStrongObfuscationOneGetsBlind(),我们也可以始终运行反编译代码并查看它产生的内容.或者一步一步地调试它.

  • 我喜欢你的答案,但我缺乏建议该做什么."不要在APK中存储密码"没有多大帮助,因为有必要在某处讲述API密钥. (12认同)