吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 575|回复: 8
上一主题 下一主题
收起左侧

[Android 原创] 团团分身逆向分析

  [复制链接]
跳转到指定楼层
楼主
shizuku 发表于 2026-5-3 19:41 回帖奖励

1. 样本软件版本号为2.7.6,有ShadowSafety加固,需要先dump dex,过程略(使用AdClose一键操作即可)

2. 先打开样本软件,发现侧边栏有个"点击登录"文字,把apk拖入jadx,搜索该字符串,右键全局搜索,来到唯一调用处

    <string name="click_close">点击关闭</string>
    <string name="click_login">点击登录</string>
    <string name="close">关闭</string>
@Override // defpackage.C5863.InterfaceC5867
    /* JADX INFO: renamed from: ݿ, reason: contains not printable characters */
    public void mo7171() {
        if (!C5863.m16532().m16546()) {
            this.tv_account.setEnabled(true);
            this.tv_account.setText(R.string.click_login);
            this.iv_account_vip.setVisibility(8);
            this.tv_vip_time.setVisibility(4);
            this.iv_icon.setImageResource(R.drawable.icon_tt);
            return;
        }
        StringBuilder sb = new StringBuilder(C5863.m16532().m16538());
        if (C5056.m14246()) {
            try {
                sb.replace(3, 7, C5958.m16773("Hcjldg==\n", "N+LPXMp3PhY=\n"));
            } catch (Exception unused) {
                System.exit(0);
            }
        }
        this.tv_account.setText(sb.toString());
        if (!C5863.m16532().m16547()) {
            this.iv_icon.setImageResource(R.drawable.icon_tt);
            this.iv_account_vip.setVisibility(8);
            this.tv_vip_time.setVisibility(4);
            return;
        }
        this.iv_account_vip.setVisibility(0);
        this.tv_vip_time.setVisibility(0);
        this.iv_icon.setImageResource(R.drawable.icon_tt_vip);
        if (C5863.m16532().m16544()) {
            this.tv_vip_time.setText(R.string.forever_vip);
        } else {
            this.tv_vip_time.setText(getString(R.string.vip_surplus_time, DateUtil.getSurplusDate(getContext(), C5863.m16532().m16541() - System.currentTimeMillis())));
        }
    }

3.双击m16546来到C5863类,从getAccount之类的方法名可以看出这是个账号信息管理类,观察m16543和m16549方法,再结合f10990字段的SPUtil这个类,可以推测出前者在读取SharedPreferences保存的账号信息,后者在往SharedPreferences写入账号信息,所以我们可以在m16543方法执行完后主动调用m16548方法,写入一个伪造的LoginBean对象

package defpackage;

import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.android.libs.util.SPUtil;
import com.android.libs.util.T;
import com.android.libs.util.ThreadPool;
import com.jy.x.separation.manager.R;
import com.jy.x.separation.manager.bean.LoginBean;
import com.jy.x.separation.manager.ui.buy.view.BuyActivity;
import com.jy.x.separation.manager.ui.login.view.LoginActivity;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/* JADX INFO: renamed from: ʼˉיˆ */
/* JADX INFO: loaded from: classes2.dex */
public class C5863 {

    /* JADX INFO: renamed from: ݿ */
    public static C5863 f10989;

    /* JADX INFO: renamed from: ݵ */
    public SPUtil f10990;

    /* JADX INFO: renamed from: ݶ */
    public String f10991;

    /* JADX INFO: renamed from: ݷ */
    public String f10992;

    /* JADX INFO: renamed from: ݸ */
    public long f10993;

    /* JADX INFO: renamed from: ݹ */
    public int f10994;

    /* JADX INFO: renamed from: ݺ */
    public boolean f10995;

    /* JADX INFO: renamed from: ݻ */
    public boolean f10996;

    /* JADX INFO: renamed from: ݼ */
    public long f10997;

    /* JADX INFO: renamed from: ݽ */
    public C11420 f10998;

    /* JADX INFO: renamed from: ݾ */
    public List<InterfaceC5867> f10999;

    /* JADX INFO: renamed from: ʼˉיˆ$ݵ */
    public class C5864 extends AbstractC11989<C11272> {
        public C5864() {
        }

        @Override // defpackage.AbstractC11989, io.reactivex.Observer
        public void onError(Throwable th) {
        }

        @Override // defpackage.AbstractC11989
        /* JADX INFO: renamed from: ݶ */
        public void mo7044(C11272 c11272) {
        }
    }

    /* JADX INFO: renamed from: ʼˉיˆ$ݶ */
    public class RunnableC5865 implements Runnable {
        public RunnableC5865() {
        }

        @Override // java.lang.Runnable
        public void run() {
            try {
                Thread.sleep(1000L);
                C5863.this.f10997 = 0L;
                C5863.this.m16559();
                Thread.sleep(5000L);
                if (C5863.this.m16547()) {
                    return;
                }
                C5863.this.f10997 = 0L;
                C5863.this.m16559();
                Thread.sleep(5000L);
                if (C5863.this.m16547()) {
                    return;
                }
                C5863.this.f10997 = 0L;
                C5863.this.m16559();
            } catch (Throwable unused) {
            }
        }
    }

    /* JADX INFO: renamed from: ʼˉיˆ$ݷ */
    public class C5866 extends AbstractC11989<LoginBean> {
        public C5866() {
        }

        @Override // defpackage.AbstractC11989, io.reactivex.Observer
        public void onError(Throwable th) {
            super.onError(th);
        }

        @Override // defpackage.AbstractC11989
        /* JADX INFO: renamed from: ݶ */
        public void mo7044(LoginBean loginBean) {
            if (loginBean.isSuccess()) {
                C5863.this.m16549(loginBean, true);
                return;
            }
            C5863.this.m16550(C5958.m16773("AwxwVM0spwiS3o7ZG2nbNM1Z\n", "d2MbMaPLPrM=\n") + loginBean.getMsg());
            T.show(loginBean.getMsg());
        }
    }

    /* JADX INFO: renamed from: ʼˉיˆ$ݸ */
    public interface InterfaceC5867 {
        /* JADX INFO: renamed from: ݿ */
        void mo7171();
    }

    /* JADX INFO: renamed from: ݼ */
    public static C5863 m16532() {
        if (f10989 == null) {
            f10989 = new C5863();
        }
        return f10989;
    }

    /* JADX INFO: renamed from: ݶ */
    public void m16533(InterfaceC5867 interfaceC5867) {
        if (this.f10999 == null) {
            m16543();
        }
        this.f10999.add(interfaceC5867);
    }

    /* JADX INFO: renamed from: ݷ */
    public void m16534() {
        if (System.currentTimeMillis() - this.f10997 < 1800000) {
            return;
        }
        m16559();
    }

    /* JADX INFO: renamed from: ݸ */
    public final void m16535() {
        if ((m16542() + (((long) m16540()) * 86400000)) - System.currentTimeMillis() < 0) {
            m16556(false);
        }
    }

    /* JADX INFO: renamed from: ݹ */
    public boolean m16536() {
        String strM16538 = m16532().m16538();
        return TextUtils.isEmpty(strM16538) || strM16538.equals(this.f10991);
    }

    /* JADX INFO: renamed from: ݺ */
    public boolean m16537(Context context) {
        if (!m16546()) {
            T.show(R.string.login_first);
            context.startActivity(new Intent(context, (Class<?>) LoginActivity.class));
            return true;
        }
        if (m16547()) {
            return false;
        }
        T.show(R.string.no_vip_tip);
        context.startActivity(new Intent(context, (Class<?>) BuyActivity.class));
        return true;
    }

    /* JADX INFO: renamed from: ݻ */
    public String m16538() {
        return this.f10991;
    }

    /* JADX INFO: renamed from: ݽ */
    public String m16539() {
        return this.f10992;
    }

    /* JADX INFO: renamed from: ݾ */
    public int m16540() {
        return this.f10994;
    }

    /* JADX INFO: renamed from: ݿ */
    public long m16541() {
        return this.f10993 + (((long) this.f10994) * 86400000);
    }

    /* JADX INFO: renamed from: ހ */
    public long m16542() {
        return this.f10993;
    }

    /* JADX INFO: renamed from: ށ */
    public void m16543() {
        this.f10999 = new ArrayList();
        this.f10990 = C10230.m27210();
        this.f10998 = new C11420();
        this.f10992 = this.f10990.getString(C5958.m16773("wjgIUUJH3h/SJQ==\n", "t0ttIx0zsXQ=\n"));
        this.f10991 = this.f10990.getString(C5958.m16773("+m1fnfA885nga1Sb\n", "jx46769dkPo=\n"));
        this.f10995 = this.f10990.getBoolean(C5958.m16773("hHbn7aqwOSA=\n", "8QWCn/XGUFA=\n"));
        this.f10996 = this.f10990.getBoolean(C5958.m16773("JZQXF1Bv+qw1\n", "UOdyZQ8IlcI=\n"));
        this.f10993 = this.f10990.getLong(C5958.m16773("PVMLtrCR3w8XUxqlnZPpCyFNCw==\n", "SCBuxO/ntn8=\n"));
        this.f10994 = this.f10990.getInt(C5958.m16773("xE2eKZKmcT7uX5cyu7VHOthTng==\n", "sT77W83QGE4=\n"));
        m16535();
    }

    /* JADX INFO: renamed from: ނ */
    public boolean m16544() {
        return m16547() && m16541() - System.currentTimeMillis() > 315360000000L;
    }

    /* JADX INFO: renamed from: ރ */
    public boolean m16545() {
        return this.f10996;
    }

    /* JADX INFO: renamed from: ބ */
    public boolean m16546() {
        return (TextUtils.isEmpty(this.f10992) || this.f10992.equals(C5958.m16773("2Qw=\n", "9D0Kx5SeEio=\n"))) ? false : true;
    }

    /* JADX INFO: renamed from: ޅ */
    public boolean m16547() {
        return m16546() && this.f10995;
    }

    /* JADX INFO: renamed from: ކ */
    public void m16548(LoginBean loginBean) {
        m16549(loginBean, false);
    }

    /* JADX INFO: renamed from: އ */
    public void m16549(LoginBean loginBean, boolean z) {
        m16553(loginBean.getAccount());
        m16555(loginBean.getToken());
        if (loginBean.getStatus() == 0) {
            m16556(true);
        } else {
            if (m16547()) {
                T.show(R.string.tip_vip_expired, 1);
            }
            m16556(false);
        }
        m16554(loginBean.getGone() != 0);
        m16557(loginBean.getAliveTime());
        m16558(loginBean.getTime());
        m16535();
        m16551();
    }

    /* JADX INFO: renamed from: ވ */
    public void m16550(String str) {
        this.f10998.mo30295(this.f10991, str, new C5864());
        m16553("");
        m16555("");
        m16556(false);
        m16557(1);
        m16558(1L);
        m16551();
    }

    /* JADX INFO: renamed from: މ */
    public void m16551() {
        Iterator<InterfaceC5867> it = this.f10999.iterator();
        while (it.hasNext()) {
            it.next().mo7171();
        }
    }

    /* JADX INFO: renamed from: ފ */
    public void m16552(InterfaceC5867 interfaceC5867) {
        this.f10999.remove(interfaceC5867);
    }

    /* JADX INFO: renamed from: ދ */
    public void m16553(String str) {
        this.f10991 = str;
        this.f10990.put(C5958.m16773("7bKwoXWqSwj3tLun\n", "mMHV0yrLKGs=\n"), str);
    }

    /* JADX INFO: renamed from: ތ */
    public void m16554(boolean z) {
        this.f10996 = z;
        this.f10990.put(C5958.m16773("vCodICLDoIqs\n", "yVl4Un2kz+Q=\n"), z);
    }

    /* JADX INFO: renamed from: ލ */
    public void m16555(String str) {
        this.f10992 = str;
        this.f10990.put(C5958.m16773("ZNY14mSQXRJ0yw==\n", "EaVQkDvkMnk=\n"), str);
    }

    /* JADX INFO: renamed from: ގ */
    public void m16556(boolean z) {
        this.f10995 = z;
        this.f10990.put(C5958.m16773("Tty5dcmhFY0=\n", "O6/cB5bXfP0=\n"), z);
    }

    /* JADX INFO: renamed from: ޏ */
    public void m16557(int i) {
        this.f10994 = i;
        this.f10990.put(C5958.m16773("w/aaar2czivp5JNxlI/4L9/omg==\n", "toX/GOLqp1s=\n"), i);
    }

    /* JADX INFO: renamed from: ސ */
    public void m16558(long j) {
        this.f10993 = j;
        this.f10990.put(C5958.m16773("rnH1ZUWT5pCEceR2aJHQlLJv9Q==\n", "2wKQFxrlj+A=\n"), j);
    }

    /* JADX INFO: renamed from: ޑ */
    public void m16559() {
        if (m16546()) {
            this.f10997 = System.currentTimeMillis();
            this.f10998.mo30297(this.f10992, new C5866());
        }
    }

    /* JADX INFO: renamed from: ޒ */
    public void m16560() {
        ThreadPool.getInstance().execute(new RunnableC5865());
    }
}
package com.android.libs.util;

import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Base64;
import com.tencent.mmkv.MMKV;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/* JADX INFO: loaded from: classes6.dex */
public class SPUtil {

    /* JADX INFO: renamed from: ݹ, reason: contains not printable characters */
    public static final int f1266 = 1;

    /* JADX INFO: renamed from: ݺ, reason: contains not printable characters */
    public static final int f1267 = 2;

    /* JADX INFO: renamed from: ݻ, reason: contains not printable characters */
    public static final String f1268 = "app";

    /* JADX INFO: renamed from: ݵ, reason: contains not printable characters */
    public SharedPreferences f1269;

    /* JADX INFO: renamed from: ݶ, reason: contains not printable characters */
    public SharedPreferences f1270;

    /* JADX INFO: renamed from: ݷ, reason: contains not printable characters */
    public SharedPreferences.Editor f1271;

    /* JADX INFO: renamed from: ݸ, reason: contains not printable characters */
    public SharedPreferences.Editor f1272;

    public SPUtil() {
        this("app");
    }

    public void clear() {
        this.f1271.clear();
        m1846();
        this.f1272.clear();
    }

    public Map<String, ?> getAll() {
        HashMap map = new HashMap();
        Map<String, ?> all = this.f1270.getAll();
        for (String str : all.keySet()) {
            map.put(str, all.get(str));
        }
        return map;
    }

    public boolean getBoolean(String str) {
        return getBoolean(str, false);
    }

    public float getFloat(String str) {
        return getFloat(str, 0.0f);
    }

    public int getInt(String str) {
        return getInt(str, 0);
    }

    public long getLong(String str) {
        return getLong(str, 0L);
    }

    public Object getObject(String str) {
        String string = getString(str, "");
        if (string.isEmpty()) {
            return null;
        }
        return IOUtil.object4bytes(Base64.decode(string, 0));
    }

    public SharedPreferences getSharedPreferences() {
        return this.f1270;
    }

    public String getString(String str) {
        return getString(str, "");
    }

    public void put(String str, String str2) {
        this.f1271.putString(str, str2);
        m1846();
    }

    public void remove(String str) {
        this.f1271.remove(str);
        m1846();
        this.f1272.remove(str);
    }

    /* JADX INFO: renamed from: ݵ, reason: contains not printable characters */
    public final void m1846() {
        this.f1271.commit();
    }

    public SPUtil(String str) {
        this(str, 1);
    }

    public boolean getBoolean(String str, boolean z) {
        if (this.f1270.contains(str)) {
            return this.f1270.getBoolean(str, z);
        }
        boolean z2 = this.f1269.getBoolean(str, z);
        if (z != z2) {
            put(str, z2);
        }
        return z2;
    }

    public float getFloat(String str, float f) {
        if (this.f1270.contains(str)) {
            return this.f1270.getFloat(str, f);
        }
        float f2 = this.f1269.getFloat(str, f);
        if (f != f2) {
            put(str, f2);
        }
        return f2;
    }

    public int getInt(String str, int i) {
        if (this.f1270.contains(str)) {
            return this.f1270.getInt(str, i);
        }
        int i2 = this.f1269.getInt(str, i);
        if (i != i2) {
            put(str, i2);
        }
        return i2;
    }

    public long getLong(String str, long j) {
        if (this.f1270.contains(str)) {
            return this.f1270.getLong(str, j);
        }
        long j2 = this.f1269.getLong(str, j);
        if (j != j2) {
            put(str, j2);
        }
        return j2;
    }

    public String getString(String str, String str2) {
        if (this.f1270.contains(str)) {
            return this.f1270.getString(str, str2);
        }
        String string = this.f1269.getString(str, str2);
        if (!TextUtils.isEmpty(string) && !str2.equals(string)) {
            put(str, string);
        }
        return string;
    }

    public SPUtil(String str, int i) {
        str = TextUtils.isEmpty(str) ? "app" : str;
        MMKV mmkvMmkvWithID = MMKV.mmkvWithID(str, i);
        this.f1269 = mmkvMmkvWithID;
        this.f1272 = mmkvMmkvWithID.edit();
        AESSharedPreferences aESSharedPreferences = new AESSharedPreferences(str);
        this.f1270 = aESSharedPreferences;
        this.f1271 = aESSharedPreferences.edit();
    }

    public void put(String str, Serializable serializable) {
        this.f1271.putString(str, Base64.encodeToString(IOUtil.object2bytes(serializable), 0));
        m1846();
    }

    public void put(String str, long j) {
        this.f1271.putLong(str, j);
        m1846();
    }

    public void put(String str, float f) {
        this.f1271.putFloat(str, f);
        m1846();
    }

    public void put(String str, int i) {
        this.f1271.putInt(str, i);
        m1846();
    }

    public void put(String str, boolean z) {
        this.f1271.putBoolean(str, z);
        m1846();
    }
}

4.打开LoginBean类,我们可以手动构造一个LoginBean实例

package com.jy.x.separation.manager.bean;

import defpackage.C11272;
import java.io.Serializable;

/* JADX INFO: loaded from: classes3.dex */
public class LoginBean extends C11272 implements Serializable {
    private String account;
    private int aliveTime;
    private int gone;
    private String id;
    private int status;
    private long time;
    private String token;

    public String getAccount() {
        return this.account;
    }

    public int getAliveTime() {
        return this.aliveTime;
    }

    public int getGone() {
        return this.gone;
    }

    public String getId() {
        return this.id;
    }

    public int getStatus() {
        return this.status;
    }

    public long getTime() {
        return this.time;
    }

    public String getToken() {
        return this.token;
    }
}

5.逻辑理清了,那就开始动手hook,这里使用libxposed api 101,以下是代码实现

fun XposedModule.helloWorld(param: PackageReadyParam) {
    val classLoader = param.classLoader
    // 对于加固应用,在 Application onCreate 执行完后才能拿到真实 ClassLoader
    onApplicationCreated {
        // new 一个 LoginBean 对象,并填充必要字段
        val loginBean = classLoader["com.jy.x.separation.manager.bean.LoginBean"].newInstance {
            setField("account", "100")
            setField("time", 4102416000000L)
            setField("token", "100")
        }
        // 先执行原方法,再主动调用 އ 写入 SharedPreferences
        hook(classLoader["ʼˉיˆ", "ށ"]).intercept { chain ->
            chain.proceed()
            classLoader["ʼˉיˆ", "އ", "com.jy.x.separation.manager.bean.LoginBean", Boolean::class.java].invoke(chain.thisObject, loginBean, true)
        }
    }
}
// 一些简易封装的工具函数,剩下的就不写了,本质都是 Java 原生反射库,可以自行使用 Java 原生反射库实现
inline fun XposedModule.onApplicationCreated(crossinline block: XposedModule.() -> Unit) {
    hook(Application::class.java["onCreate"]).intercept { chain ->
        chain.proceed()
        block()
    }
}
fun Class<*>.findField(fieldName: String): Field {
    var clazz: Class<*>? = this
    while (clazz != null) {
        try {
            return clazz.getDeclaredField(fieldName).apply { isAccessible = true }
        } catch (e: NoSuchFieldException) {
            clazz = clazz.superclass
        }
    }
    throw NoSuchFieldException("Field $fieldName not found in $this or its hierarchy")
}
inline fun Class<*>.newInstance(vararg initargs: Any?, block: Any.() -> Unit): Any? = constructors[0].newInstance(*initargs).apply(block)
fun Any.setField(fieldName: String, value: Any?) = javaClass.findField(fieldName).set(this, value)

6.打包安装,勾选作用域,打开样本软件提示“token过期”,继续分析,发现m16559方法会在登陆状态为true时进行网络验证,C5866是个网络回调类,这里就直接拦截该方法

    /* JADX INFO: renamed from: ޑ */
    public void m16559() {
        if (m16546()) {
            this.f10997 = System.currentTimeMillis();
            this.f10998.mo30297(this.f10992, new C5866());
        }
    }
hook(classLoader["ʼˉיˆ", "ޑ"]).intercept {} // 拦截方法执行

7.最后测试Vip功能正常使用

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
晚晚丿轻风 发表于 2026-5-4 10:31
感谢分享
3#
ABC168 发表于 2026-5-4 11:49
这个app目前一堆广告,开分身还要安装各种软件,支持楼主
4#
aiyoniganma 发表于 2026-5-4 11:57
5#
 楼主| shizuku 发表于 2026-5-4 12:42 |楼主
aiyoniganma 发表于 2026-5-4 11:57
暗影安全不是有hook检测吗?

目前他检测不到新版LSP。
6#
liyicha 发表于 2026-5-4 13:36
这个不是有某60加固吗?这个怎么办呀
7#
aiyoniganma 发表于 2026-5-4 14:14
shizuku 发表于 2026-5-4 12:42
目前他检测不到新版LSP。

这样,那我要更新一下了
8#
 楼主| shizuku 发表于 2026-5-4 14:48 |楼主
liyicha 发表于 2026-5-4 13:36
这个不是有某60加固吗?这个怎么办呀

直接拿AdClose一键dump dex就可以了。
9#
smshiwadai 发表于 2026-5-5 16:50
shizuku 发表于 2026-5-4 14:48
直接拿AdClose一键dump dex就可以了。

adclose能分享吗
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-5 17:54

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表