在应用市场找了一个简单的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开启了函数名混淆,所以大部分时间花在了找关键函数上而不是加密算法破解上,看似顺利的破解过程其实也踩了不少的坑,本来总的来说还是不够熟练,需要多加练习。