安卓市场某app破解
2020-04-04 11:15:09 Author: forum.90sec.com(查看原文) 阅读量:393 收藏

在应用市场找了一个简单的app,没有壳,回顾一下简单的绕过java层的签名验证、破解加密算法、静态分析

用到的工具:

  • jeb
  • jadx-gui-1.1.0
  • Android Studio
  • apktool
  • apktoolbox1.6.4
  • Android Killer
  • ApkScan-PKID(查壳)

一个桌面时钟软件,无壳,看看程序都有什么功能:

只有一个PopupMenu,功能还是挺多的,可以设置闹钟,屏保,颜色渐变,时间格式等功能,随便点点,找到了一个打赏作者,点击之后会把作者的支付宝账号复制到剪贴板

所以可以将目标确定下来,就是将作者的账号换成自己的

先尝试能不能直接在ak中搜索到相关的字符串,结果不管是Toast还是AlertDialog还是剪贴板的内容都搜不到,说明这些字符串都经过了加密。

为了找到具体的加密/解密函数,从res文件夹下的menu入手:

得到了元素id为bm。

从OnMenuItemClickListener的对应case中找到了点击该元素所调用的方法g()

进函数声明看看,猜测这些奇怪的字符串就是加密后的文本了,所以f()应该就是解密函数

f()发现嵌套了很多层逻辑解密,还注意到不远处有一个函数e()长的和f()很像,也是多层函数嵌套,可以猜测e()就是加密函数。先把解密函数破解了,验证一下猜想。

分析这四个函数之后很容易得出解密逻辑:

  • 大写字母变小写,小写字母变大写
  • 数字0~9分别加一,比如1变成2,2变成3,9变成0
  • 字符串反转
  • 根据字符串长度在后面添加等号,然后base64解码
  • 得到明文

可以自己写一遍解密逻辑,或者直接用反编译的代码,我比较懒就复制粘贴了。在Android Studio中还原解密函数,加密函数同理可以还原

package com.example.fridatestapp2;

import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    EditText username_et;
    EditText password_et;
    TextView message_tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        f("/3P61sl7QMy6WIy63Iy6nsA6YEB64+y6Msl7D7A6yUl6VsP6");

    }
    public void e(String str) {
        String a;
        a = j(h(g(c(str))));
        Log.d("enc:",a);
    }
    public void f(String str) {
        String a;
        a = d(j(i(g(str))));
        Log.d("dec:",a);
    }

    public String c(String str) {
        String str2 = "1234";
        try {
            str2 = Base64.encodeToString(str.toString().getBytes("UTF-8"), 0);
        } catch (Exception e2) {

        }
        return str2.replace("=", "").replace("\n", "");
    }

    public String g(String str) {
        String str2;
        char[] charArray = str.toCharArray();
        for (int i2 = 0; i2 < charArray.length; i2++) {
            char c2 = charArray[i2];
            if (Character.isLetter(c2)) {
                if (Character.isUpperCase(c2)) {
                    charArray[i2] = Character.toLowerCase(c2);
                } else {
                    charArray[i2] = Character.toUpperCase(c2);
                }
            }
        }
        str2 = new String(charArray);
        return str2;
    }

    public String h(String str) {
        String str2;
        char[] charArray = str.toCharArray();
        for (int i2 = 0; i2 < charArray.length; i2++) {
            char c2 = charArray[i2];
            if (Character.isDigit(c2)) {
                if (c2 == '9') {
                    charArray[i2] = (char) 48;
                } else {
                    char[] cArr = charArray;
                    int i3 = i2;
                    cArr[i3] = (char) (cArr[i3] + 1);
                }
            }
        }
        str2 = new String(charArray);
        return str2;
    }

    public String j(String str) {
        return new StringBuffer(str).reverse().toString();
    }

    public String i(String str) {
        String str2;
        char[] charArray = str.toCharArray();
        for (int i2 = 0; i2 < charArray.length; i2++) {
            char c2 = charArray[i2];
            if (Character.isDigit(c2)) {
                if (c2 == '0') {
                    charArray[i2] = (char) 57;
                } else {
                    char[] cArr = charArray;
                    int i3 = i2;
                    cArr[i3] = (char) (cArr[i3] - 1);
                }
            }
        }
        str2 = new String(charArray);
        return str2;
    }

    public String d(String arg12) {
        String v7;
        String v1 = arg12;
        String v3 = "5L2g5aW9";
        String v4 = "5L2g5aW9";
        if(v1.length() % 4 == 0) {
            v4 = v1;
        }
        else {
            int v5 = 4 - v1.length() % 4;
            if(v5 == 1) {
                v4 = new StringBuffer().append(v1).append("=").toString();
            }
            if(v5 == 2) {
                v4 = new StringBuffer().append(v1).append("==").toString();
            }
            if(v5 == 3) {
                v4 = new StringBuffer().append(v1).append("===").toString();
            }
        }
        try {
            v7 = new String(Base64.decode(v4.toString().getBytes("UTF-8"), 0));
            return v7;
        }
        catch(Exception v6) {
        }
        return "no";
    }
}

破解了加密算法之后还要找到作者支付宝的字符串存放的位置,因为我们有了加密函数,所以直接将剪贴板的内容加密然后在ak里面搜索就行了

根据密文找到了目标函数,str2被复制到剪贴板之后还有一次验证,如果二次加密的密文和预置字符串不匹配则会弹出toast提醒用户软件已被破解,为了不让这个toast弹出所以这里的预置字符串也要一并修改

接下来直接进入相应smali文件内修改这两处字符串即可

修改完成后,apktool b打包apk,重新签名,安装运行app直接闪退,猜测是有签名校验,而这个app没有lib文件,应该是java层的签名校验,只需要找到校验函数位置然后nop掉即可。

java层的签名校验一般通过对比signature的值来判断程序是否被修改过,搜索signature定位校验代码的位置

找到了之后在调用处注释掉该函数

保存,打包,签名,运行

看样子已经完成破解了,但是等它正常运行一会儿后发现又闪退了,有时候刚打开就闪退,有时候又可以运行几十秒才闪退,观察了一下规律发现当秒针计时器变为00的时候就会闪退,找到问题所在了。

既然破解就不能损坏程序原本的功能。通过搜索”00“找到了出问题的地方,把这里也注释掉就不会闪退了。

技术含量不高的一次破解,但对理解破解流程很有帮助,由于app开启了函数名混淆,所以大部分时间花在了找关键函数上而不是加密算法破解上,看似顺利的破解过程其实也踩了不少的坑,本来总的来说还是不够熟练,需要多加练习。


文章来源: https://forum.90sec.com/t/topic/936/1
如有侵权请联系:admin#unsafe.sh