好久没发帖了,最近刚好分析了一款软件,顺便做个笔记记录一下。
一、算法助手定位
1.老规矩,直接签名看看是否有签名校验,果不其然,闪退!
2.算法助手打开拦截应用退出和防止应用闪退
3.运行软件,触发闪退,打开算法助手查看日志,看第一个触发的,接着根据堆栈调用去搜索
二、jadx-gui静态分析
1.定位到onCreate方法
代码如下:
public void onCreate() {
int i2;
super.onCreate();
j0 j0Var = j0.f22466a;
String V = com.blankj.utilcode.util.w.V(com.blankj.utilcode.util.c.r()); // r方法的值赋给字符串V
k0.o(V, "encryptMD5ToString(signatureMd5)"); //md5加密一次
byte[] bytes = V.getBytes(f.f26826a);
k0.o(bytes, "(this as java.lang.String).getBytes(charset)");//再base64加密
if (!k0.g(new C().s(), Base64.encodeToString(bytes, 0))) { //比较不通过就闪退
System.exit(0);
}
SharedPreferences defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
String string = defaultSharedPreferences.getString(getString(R.string.key_pref_ui_mode), com.wisdom.ticker.util.s.x);
com.blankj.utilcode.util.b.d(getResources(), 375);
if (string == null) {
i2 = -1;
} else {
i2 = Integer.parseInt(string);
}
com.wisdom.ticker.util.n0.e.h(i2);
a aVar = f20953a;
aVar.n(new com.wisdom.ticker.service.f());
aVar.j(new com.wisdom.ticker.service.c());
aVar.k(new com.wisdom.ticker.service.d());
f20955c = new ThreadPoolExecutor(1, 3, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), a.f20974a);
g = j0Var.h(this);
y.f22622a.f(this);
SharedPreferences sharedPreferences = getSharedPreferences(com.wisdom.ticker.service.core.g.a.f21353b, 0);
k0.o(sharedPreferences, "getSharedPreferences(AppConfig.SHARED_NAME, Context.MODE_PRIVATE)");
aVar.l(sharedPreferences);
z.f22624a.j(this);
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
m.f22482a.c(defaultSharedPreferences.getBoolean(com.wisdom.ticker.service.core.g.a.I, true));
DBBox.INSTANCE.init(this);
com.wisdom.ticker.service.core.h.a.f21364a.d().o(this);
if (defaultSharedPreferences.getBoolean(com.wisdom.ticker.service.core.g.a.q, true)) {
for (Moment moment : i.f21294a.u()) {
LogUtils.l("升级计时:" + moment.getName() + '-' + moment.getTime() + '-' + moment.getSolarDate());
moment.setNeedUpdate(true);
i.H(i.f21294a, moment, false, 2, null);
}
defaultSharedPreferences.edit().putBoolean(com.wisdom.ticker.service.core.g.a.q, false).apply();
}
com.wisdom.ticker.util.o0.b.j(this);
com.wisdom.ticker.util.o0.b e2 = com.wisdom.ticker.util.o0.b.e();
k0.o(e2, "createInstance()");
e2.a(new com.wisdom.ticker.util.m0.c()).a(new com.wisdom.ticker.util.m0.e()).a(new j()).a(new com.wisdom.ticker.util.m0.d()).a(new com.wisdom.ticker.util.m0.i()).a(new g()).a(new com.wisdom.ticker.util.m0.f());
if (!defaultSharedPreferences.getBoolean(com.wisdom.ticker.service.core.g.a.A, true)) {
e2.a(new h());
h0.f22453a.d(this);
}
e2.n();
e2.b();
org.koin.core.d.a.c(new c(this));
f.b.a.c h1 = f.b.a.c.h1();
LogUtils.D("启动耗时:" + p0.P0(h, h1).k0() + 's');
if (!defaultSharedPreferences.contains(f.j)) {
defaultSharedPreferences.edit().putLong(f.j, h1.e()).apply();
}
}
2.总的来说,就是先获取r方法的值,然后MD5加密一次,接着base64加密,最后和C类里的s方法的值进行对比,如果不相等就触发System.exit(0);
3.跟进一下r方法,代码如下:
private static String q(String str, String str2) {
Signature[] p;
if (!k1.A0(str) && (p = p(str)) != null && p.length > 0) {
return k1.l(k1.j0(p[0].toByteArray(), str2)).replaceAll("(?<=[0-9A-F]{2})[0-9A-F]{2}", ":$0");
}
return "";
}
public static String r() {
return s(i1.a().getPackageName()); //获取包名信息传入s方法
}
public static String s(String str) {
return q(str, "MD5"); //MD5加密
}
4.很明显r方法的返回值就是包名信息的MD5,这个可以直接用MT查看,当然我们也可以用Frida验证一下
三、Frida动态Hook
1.编写hook代码
function hookTest(){
var money = Java.use("com.blankj.utilcode.util.c");
money.r.implementation = function(){
var retval = this.r();
console.log("r方法的返回值",retval);
return retval;
}
}
function main(){
Java.perform(function(){
hookTest(); //执行的方法
});
}
setImmediate(main);
2.启动Frida并挂起apk(注意这里要用原版的apk!!!)
3.查看hook结果:84:19:59:BE:9D:B3:72:E5:A1:42:90:9D:92:E1:AC:61
和MT查看的一致
4.接下来,我们再跟进一下C类里的s方法,是不是如我们所想的一样。一看是native方法,拖出对应的libitime.so,ida加载
5.方法很少,直接定位到s方法
6.这里读取了一串很可疑的字符串:RDFBRUE3NTUyMUIxMjk0MjhFMjE2Q0EzNTU4RjJGNjk=
7.双击字符串发现跳转到.rodata段(只读数据段)
8.根据我们前面的猜测,获取r方法的值,然后MD5加密一次,接着base64加密。
9.结果一致,看样子我们的猜测没有错。由于是只读数据段,所以我们只能修改dex中的内容,把r方法的返回值写死即可,代码如下:
.method public static r()Ljava/lang/String;
.registers 1
.line 1
const-string v0, "84:19:59:BE:9D:B3:72:E5:A1:42:90:9D:92:E1:AC:61"
return-object v0
.end method
10.运行测试,正常不闪退!
ps:至于高级版很简单,你们可以动手试试