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

jv2fixgn  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(222)

有时你需要在应用程序本身中存储一个密码,例如用于与自己的服务器通信的用户名/密码。在这些情况下,不可能按照存储密码的正常过程进行操作,即散列密码、存储散列、与散列用户输入进行比较,因为您没有任何用户输入可与散列进行比较。密码需要由应用程序本身提供。那么如何保护apk中存储的密码呢?像下面这样的密码生成函数是合理安全的吗?
纯文本:

String password = "$()&HDI?=!";

简单的模糊处理:

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

我知道proguard有一些模糊处理功能,但我很好奇上面的“模糊处理”技术在编译时会做什么,以及通过查看apk和/或使用其他更复杂的技术来找出它有多难?

ni65a41a

ni65a41a1#

热释光;如果你知道如何反编译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);
    }
}
``` `main.xml` :

编译并运行之后,我们可以看到 `$()&HDI?=!` 在 `TextView` .
让我们反编译一个apk: `unzip myapp.apk` 或者右键单击apk `Unzip here` .  `classes.dex` 文件出现。
转换 `classes.dex` 使用dex2jar创建jar文件。执行后 `dex2jar.sh classes.dex` ,  `classes_dex2jar.jar` 文件出现。
在上使用一些java反编译器 `classes_dex2jar.jar` ,例如jdgui,我们从 `MyActivity.class` :

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

proguard帮不了什么忙,代码仍然很容易阅读。
基于以上,我已经可以回答你这个问题了:
像下面这样的密码生成函数是合理安全的吗?
不,正如你所看到的,它增加了阅读去泡沫代码的难度。我们不应以这种方式混淆代码,因为:
它给人一种安全感的错觉。
这是浪费开发者的时间。
它降低了代码的可读性。
在android官方文档中,在安全和设计部分,他们建议这样做以保护您的google play公钥:
为了防止您的公钥受到恶意用户和黑客的攻击,请不要将其作为文本字符串嵌入到任何代码中。相反,在运行时从片段构造字符串或使用位操作(例如,与其他字符串异或)来隐藏实际的键。密钥本身不是秘密信息,但您不想让黑客或恶意用户轻易地用另一个密钥替换公钥。
好吧,让我们试试:

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

给予 `$()&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();
}

情况和以前很相似。
即使我们有非常复杂的功能 `soStrongObfuscationOneGetsBlind()` ,我们总是可以运行反编译代码,看看它产生了什么。或者一步一步地调试。

相关问题