草庐IT

android - 简单隐藏/混淆 APK 中的字符串?

coder 2023-11-22 原文

有时您需要在应用程序本身中存储密码,例如用于与您自己的服务器通信的用户名/密码。在这些情况下,不可能遵循存储密码的正常过程——即散列密码、存储散列、与散列用户输入进行比较——因为你没有任何用户输入来与散列进行比较。密码需要由应用程序本身提供。那么如何保护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 和/或使用其他更复杂的技术?

最佳答案

tl;dr 如果您知道如何反编译 APK,则无论代码多么困惑,您都可以轻松获取密码。不要在 APK 中存储密码,它不安全。

I know ProGuard has some obfuscation capabilities, but I'm curious about what the above "obfuscation" technique does when it's compiled, and how hard it would be for someone to figure it out by looking in the APK and/or using other more sophisticated techniques?

我会告诉你这有多么简单。这是一个 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:

<?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"/>

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

让我们反编译一个 APK:

  1. unzip myapp.apk 或右键单击 APK 并Unzip hereclasses.dex 文件出现。
  2. 使用 dex2jarclasses.dex 转换为 JAR 文件.执行dex2jar.sh classes.dex后,classes_dex2jar.jar文件 出现。
  3. classes_dex2jar.jar 上使用一些 Java 反编译器,例如 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());
        }
    }
    

ProGuard 帮不上什么忙,代码仍然易于阅读。

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

Would a password-generating function like the one below be reasonably secure?

没有。如您所见,它稍微增加了阅读去混淆代码的难度。我们不应该以这种方式混淆代码,因为:

  1. 它给人一种安全的错觉。
  2. 这是在浪费开发者的时间。
  3. 它降低了代码的可读性。

在官方 Android 文档中,在 Security and Design部分,他们建议这样做是为了保护您的 Google Play 公钥:

To keep your public key safe from malicious users and hackers, do not embed it in any code as a literal string. Instead, construct the string at runtime from pieces or use bit manipulation (for example, XOR with some other string) to hide the actual key. The key itself is not secret information, but you do not want to make it easy for a hacker or malicious user to replace the public key with another key.

好的,让我们试试看:

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

TextView 上给出 $()&HDI?=!,很好。

反编译版本:

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(),我们也始终可以运行反编译代码并查看它产生了什么。或逐步调试。

关于android - 简单隐藏/混淆 APK 中的字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22949801/

有关android - 简单隐藏/混淆 APK 中的字符串?的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  3. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  4. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  5. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  6. ruby-on-rails - unicode 字符串的长度 - 2

    在我的Rails(2.3,Ruby1.8.7)应用程序中,我需要将字符串截断到一定长度。该字符串是unicode,在控制台中运行测试时,例如'א'.length,我意识到返回了双倍长度。我想要一个与编码无关的长度,以便对unicode字符串或latin1编码字符串进行相同的截断。我已经了解了Ruby的大部分unicode资料,但仍然有些一头雾水。应该如何解决这个问题? 最佳答案 Rails有一个返回多字节字符的mb_chars方法。试试unicode_string.mb_chars.slice(0,50)

  7. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  8. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  9. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  10. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

随机推荐