首页
社区
课程
招聘
[转帖]某音adult_version防护分析&加密分析&邀请机制刷vip分析
发表于: 2023-1-29 22:31 8497

[转帖]某音adult_version防护分析&加密分析&邀请机制刷vip分析

2023-1-29 22:31
8497
  1. 本文包括
    防护分析:模拟器检测、root检测、反hook、反抓包等
    加密分析:数据加密分析、身份验证的Token参数生成分析
    设备注册逻辑分析:无限获得新帐号、绑定邀请码刷vip
    b站视频链接:https://www.bilibili.com/video/BV12e411V7a5/?vd_source=23b9de401c27e819abddbd5551eddbda
    一、防护分析
    提示不能在模拟器里运行
    图片描述

定位检测代码

 

入口:com.niming.weipa.ui.splash.SplashActivity

1
2
3
4
5
6
7
8
9
10
11
12
home.php?mod=space&uid=1892347  // com.niming.weipa.base.BaseActivity
protected void onCreate(home.php?mod=space&uid=1043391 Bundle arg2) {
 if(com.blankj.utilcode.util.e.d()) {
     com.blankj.utilcode.util.e.b(this, false);
 }
 
 super.onCreate(arg2);
 Intent v2 = this.getIntent();
 Intrinsics.checkExpressionValueIsNotNull(v2, "intent");
 if((v2.getFlags() & 0x400000) != 0) {
     this.finish();
 }

两次super到父类的onCreate函数中,查看整个启动逻辑,定位到initView函数

1
2
3
4
5
6
7
8
9
10
11
12
@Override  // androidx.appcompat.app.AppCompatActivity
protected void onCreate(@Nullable Bundle arg5) {
 super.onCreate(arg5);
 this.supportRequestWindowFeature(1);
 /* other codes */
 this.activity = this;
 this.loadingDialogFragment = new com.niming.framework.widget.dialog.LoadingDialogFragment.a().a(true).a();
 this.pageStatusHelper = new PageStatusHelper(this, this.getPageBuilder());
 this.pageStatusHelper.a(this);
 this.init();
 this.initView(arg5); // key
}

回到SplashActivity的initView函数,发现this.m()函数

1
2
3
4
5
6
7
8
@Override  // com.niming.weipa.base.BaseActivity
public void initView(@Nullable Bundle arg1) {
 super.initView(arg1);
 v0.d(this);
 com.blankj.utilcode.util.e.d(this, false);
 com.blankj.utilcode.util.e.a(false);
 this.m(); // key
}

进入this.m(),发现检测逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private final void m() {
     LogUtils.b("niming", new Object[]{"===AppUtils.isAppRoot():" + com.blankj.utilcode.util.c.isAppRoot() + " DeviceUtils.isDeviceRooted() " + w.isDeviceRoot()});
     if(!this.checkRoot() && !n.isEmulator(this.activity)) {
         if(this.checkProxy(this)) {
             NoticeAppDialogFragment v0 = NoticeAppDialogFragment.a("检测到您使用了代{过}{滤}理软件,不允许继续使用");
                             /* notice codes */
             v0.c(this.activity);
             return;
         }
 
         h.a().a("api_domain", "");
         this.i();
         return;
     }
 
     NoticeAppDialogFragment v0_1 = NoticeAppDialogFragment.a("检测到您使用的是模拟器或者设备已经root,不允许继续使用");
     /* notice codes */
     v0_1.c(this.activity);
 }

root检测方法

 

通过getRuntime尝试执行su命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static b a(String[] arg8, String[] arg9, boolean arg10, boolean arg11) {
    StringBuilder v8_2;
    StringBuilder v11;
    StringBuilder v10_2;
    Process v9 = null;
    BufferedReader v4 = null;
    BufferedReader v5 = null;
    DataOutputStream v10 = null;
    String v1 = "";
    int v2 = -1;
    if(arg8 != null && arg8.length != 0) {
        BufferedReader v3 = null;
        try {
            Runtime v4_1 = Runtime.getRuntime();
            String v10_1 = arg10 ? "su" : "sh";
            v9 = v4_1.exec(v10_1, arg9, null);
            v10 = new DataOutputStream(v9.getOutputStream());
        }
                /* other codess */
 
        return new b(v2, v8_6, v1);
    }
 
    return new b(-1, "", "");
}

检查特定目录下的su文件是否存在

1
2
3
4
5
6
7
8
9
10
11
public static boolean isDeviceRoot() {
    String[] v0 = {"/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/", "/system/sbin/", "/usr/bin/", "/vendor/bin/"};
    int v3;
    for(v3 = 0; v3 < v0.length; ++v3) {
        if(new File(v0[v3] + "su").exists()) {
            return true;
        }
    }
 
    return false;
}

emulator检测方法

 

检测系统属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static final boolean c(Context arg1) {
    return "1".equals(n.a(arg1, "ro.kernel.qemu"));
}
检测特征值
 复制代码 隐藏代码
if((Build.MANUFACTURER.equals("unknown")) || (Build.MANUFACTURER.equals("Genymotion")) || (Build.MANUFACTURER.contains("Andy")) || (Build.MANUFACTURER.contains("MIT")) || (Build.MANUFACTURER.contains("nox")) || (Build.MANUFACTURER.contains("TiantianVM"))) {
    ++v15;
}
/* 很多特征值 */
if((Build.HARDWARE.equals("goldfish")) || (Build.HARDWARE.equals("vbox86")) || (Build.HARDWARE.contains("nox")) || (Build.HARDWARE.contains("ttVM_x86"))) {
    ++v15;
}
/* 很多特征值 */
if((Build.FINGERPRINT.contains("generic/sdk/generic")) || (Build.FINGERPRINT.contains("generic_x86/sdk_x86/generic_x86")) || (Build.FINGERPRINT.contains("Andy")) || (Build.FINGERPRINT.contains("ttVM_Hdragon")) || (Build.FINGERPRINT.contains("generic_x86_64")) || (Build.FINGERPRINT.contains("generic/google_sdk/generic")) || (Build.FINGERPRINT.contains("vbox86p")) || (Build.FINGERPRINT.contains("generic/vbox86p/vbox86p"))) {
    ++v15;
}

检测OPENGL的属性值
复制代码 隐藏代码
try {
String v2_1 = GLES20.glGetString(0x1F01);
if(v2_1 != null) {
if(v2_1.contains("Bluestacks")) {
goto label_260;
}
else {
boolean v2_2 = v2_1.contains("Translator");
goto label_257;
}
}
}
catch(Exception v2) {
v2.printStackTrace();
}
goto label_262;
label_257:
if(v2_2) {
v15 += 10;
goto label_262;
label_260:
v15 += 10;
}
检测共享文件夹

1
2
3
4
5
6
7
8
9
try {
     label_262:
     boolean v2_4 = new File(Environment.getExternalStorageDirectory().toString() + File.separatorChar + "windows" + File.separatorChar + "BstSharedFolder").exists();
 }
catch(Exception v2_3) {
    v2_3.printStackTrace();
    n.b = v15;
    return n.b > 3;
}

处理:hook掉

 

hook失败,定位反hook代码

 

SplashActivity中没有发现相关代码,到application的app com.niming.weipa.App 的onCreate函数中寻找

 

<application android:allowBackup="true" android:appComponentFactory="androidx.core.app.CoreComponentFactory" android:icon="@drawable/icon_douying_logo" android:label="抖x" android:name="com.niming.weipa.App"
发现disableXposed函数 (名字当然是我改的...)

 

复制代码 隐藏代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override  // com.niming.framework.base_app.BaseApplication
public void onCreate() {
 super.onCreate();
 App.u0 = this;
 if(this.a(this)) {
     g1.a(this);
     this.b();
     this.c();
     com.shuyu.gsyvideoplayer.i.c.a(com.niming.weipa.d.c.class);
     com.shuyu.gsyvideoplayer.f.a.a(com.niming.weipa.d.d.class);
             /* other codes */
     com.lahm.library.d.disableXposed(); // 反hook
             /* other codes */
 }
}

查看disableXposed实现,通过反射设置disableHooks字段为true

1
2
3
4
5
6
7
8
9
public boolean disableXposed() {
 try {
     Field v1_3 = ClassLoader.getSystemClassLoader().loadClass("de.robv.android.xposed.XposedBridge").getDeclaredField("disableHooks");
     v1_3.setAccessible(true);
     v1_3.set(null, Boolean.TRUE);
     return true;
 }
/* exception codes return false */
}

处理:hook掉,在函数返回后,同样使用反射将disableHooks设置为false

 

抓不到包,分析反抓包机制

 

定位反抓包代码,从app的onCreate入手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override  // com.niming.framework.base_app.BaseApplication
public void onCreate() {
 super.onCreate();
 App.u0 = this;
 if(this.a(this)) {
     g1.a(this);
     this.b();
     this.c();
     com.shuyu.gsyvideoplayer.i.c.a(com.niming.weipa.d.c.class);
     com.shuyu.gsyvideoplayer.f.a.a(com.niming.weipa.d.d.class);
             /* other codes */
     com.lahm.library.d.disableXposed(); // 反hook
             /* other codes */
 }
}

进入this.c(),进行了大量关于http请求的配置,AntiCapture是我一开关注的类,后来发现这个类什么都没干;最终得出结论:抓不到包是因为设置了NO_PROXY属性,app不走系统代{过}{滤}理,因此抓不到包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void c() {
 HttpHeaders v0 = new HttpHeaders();
 v0.put("User-Agent", "Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleDart/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17");
 try {
     this.t0 = new b();
     this.t0.d(30000L, TimeUnit.MILLISECONDS);
     /* other codes */
     AntiCapture v1 = a.a(); // 这里获得有关ssl证书的参数,并在下一行设置
     this.t0.a(v1.a, v1.b); // 经过分析,这两个参数都没用,并不是反抓包的实现机制
     this.t0.a(new c());
     this.t0.a(new com.niming.weipa.app.a.a());
     this.t0.a(new com.niming.weipa.app.a.b());
     /* other codes */
     this.t0.a(new ProxySelector() {
         @Override
         public void connectFailed(URI arg1, SocketAddress arg2, IOException arg3) {
         }
 
         @Override
         public List select(URI arg1) {
             /* 抓不到包的原因所在! */
             return Collections.singletonList(Proxy.NO_PROXY);
             /* 抓不到包的原因所在! */
         }
     });
     c.f.a.b.k().a(this).a(this.t0.a()).a(CacheMode.NO_CACHE).a(-1L).a(0).a(v0);
 }
 /* other codes */
}

处理:hook掉,在函数调用前,将参数设置为Proxy.getDefault(),默认走系统代{过}{滤}理

 

签名校验

 

生成可调试版本后,app会停在某个页面,通过查看日志发现是native层作了签名校验
我一般不分析签名校验,有兴趣可以自己分析
防护类分析

 

通过disableXposed那个函数,发现了一个很完整的java层加密类,里面实现了很多的防护函数,这里完全拷贝下来,名称我都作了修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
package com.lahm.library;
 
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.os.Debug;
import android.os.Process;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Iterator;
 
public class i {
 static class b {
     private static final i a;
 
     static {
         b.a = new i(null);
     }
 }
 
 private static final String a = "de.robv.android.xposed.XposedHelpers";
 private static final String b = "de.robv.android.xposed.XposedBridge";
 
 private i() {
 }
 
 i(com.lahm.library.i.a arg1) {
 }
 
 public String getAppMetaData(Context arg1, String arg2) {
     return arg1.getApplicationInfo().metaData.getString(arg2);
 }
 
 public boolean isDebuggerConnected() { // 检测调试器是否连接
     return Debug.isDebuggerConnected();
 }
 
 public boolean isPortAvailable(int arg2) { // 检测端口是否被占用,可用于反调试、反hook
     try {
         return this.isNetAddressAvailable("127.0.0.1", arg2);
     }
     catch(Exception unused_ex) {
         return true;
     }
 }
 
 public boolean isApkDebuggable(Context arg1) { // 检测apk:debuggable属性是否为true
     return (arg1.getApplicationInfo().flags & 2) != 0;
 }
 
 public boolean isLibOrJarLoaded(String arg6) { // 检测加载的jar/so库
     try {
         HashSet v0 = new HashSet();
         BufferedReader v1 = new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/maps"));
         while(true) {
             String v2 = v1.readLine();
             if(v2 != null) {
                 goto label_34;
             }
 
             v1.close();
             Iterator v0_1 = v0.iterator();
             while(true) {
             label_24:
                 if(!v0_1.hasNext()) {
                     return false;
                 }
 
                 Object v1_1 = v0_1.next();
                 if(!((String)v1_1).contains(arg6)) {
                     goto label_24;
                 }
 
                 return true;
             label_34:
                 if(!v2.endsWith(".so") && !v2.endsWith(".jar")) {
                     break;
                 }
 
                 v0.add(v2.substring(v2.lastIndexOf(" ") + 1));
                 break;
             }
         }
     }
     catch(Exception unused_ex) {
         return false;
     }
 }
 
 public boolean isNetAddressAvailable(String arg2, int arg3) throws UnknownHostException {
     InetAddress v2 = InetAddress.getByName(arg2); // 检测网络地址是否可用
     try {
         new Socket(v2, arg3);
         return true;
     }
     catch(IOException unused_ex) {
         return false;
     }
 }
 
 public boolean isDeviceRooted() { //检测设备是否root,里面两个函数的实现在下面
     return this.isDeviceRootedByRoSecure() == 0 ? true : this.isDeviceRootedByFile();
 }
 
 public boolean isEmulatorByBattery(Context arg4) { // 通过检查电量检测模拟器
     Intent v4 = arg4.registerReceiver(null, new IntentFilter("android.intent.action.BATTERY_CHANGED"));
     return v4 == null ? false : v4.getIntExtra("plugged", -1) == 2;
 }
 
 public String getPackageSignature(Context arg5) { // 获取签名
     try {
         Signature[] v5_1 = arg5.getPackageManager().getPackageInfo(arg5.getPackageName(), 0x40).signatures;
         StringBuilder v0 = new StringBuilder();
         int v2;
         for(v2 = 0; v2 < v5_1.length; ++v2) {
             v0.append(v5_1[v2].toCharsString());
         }
 
         return v0.toString();
     }
     catch(PackageManager.NameNotFoundException v5) {
         v5.printStackTrace();
         return "";
     }
 }
 
 public boolean isXposedExistByStack() { // 通过异常栈检测Xposed框架
     try {
         throw new Exception("gg");
     }
     catch(Exception v0) {
         StackTraceElement[] v0_1 = v0.getStackTrace();
         int v3;
         for(v3 = 0; v3 < v0_1.length; ++v3) {
             if(v0_1[v3].getClassName().contains("de.robv.android.xposed.XposedBridge")) {
                 return true;
             }
         }
 
         return false;
     }
 }
 
 @Deprecated
 public boolean isXposedExistByClassLoader() { // 通过类加载检测Xposed框架
     try {
         ClassLoader.getSystemClassLoader().loadClass("de.robv.android.xposed.XposedHelpers").newInstance();
     }
     catch(InstantiationException v0_1) {
         v0_1.printStackTrace();
         return true;
     }
     catch(IllegalAccessException v0) {
         v0.printStackTrace();
         return true;
     }
     catch(ClassNotFoundException v1) {
         v1.printStackTrace();
         return false;
     }
 
     try {
         ClassLoader.getSystemClassLoader().loadClass("de.robv.android.xposed.XposedBridge").newInstance();
         return true;
     }
     catch(InstantiationException v0_3) {
         v0_3.printStackTrace();
         return true;
     }
     catch(IllegalAccessException v0_2) {
         v0_2.printStackTrace();
         return true;
     }
     catch(ClassNotFoundException v1_1) {
         v1_1.printStackTrace();
         return false;
     }
 }
 
 public boolean isDebuggingByTarcePid() { // 通过TracePid检测程序是否出于被调试状态
     try {
         BufferedReader v1 = new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/status"));
         String v2 = "";
         do {
         label_17:
             String v3 = v1.readLine();
             if(v3.contains("TracerPid")) {
                 v2 = v3.substring(v3.indexOf(":") + 1, v3.length()).trim();
             }
             else if(v3 != null) {
                 goto label_17;
             }
 
             break;
         }
         while(true);
 
         v1.close();
         return !"0".equals(v2);
     }
     catch(Exception unused_ex) {
         return false;
     }
 }
 
 public boolean disableXposed() { // disable xposed
     try {
         Field v1_3 = ClassLoader.getSystemClassLoader().loadClass("de.robv.android.xposed.XposedBridge").getDeclaredField("disableHooks");
         v1_3.setAccessible(true);
         v1_3.set(null, Boolean.TRUE);
         return true;
     }
     catch(NoSuchFieldException v1_2) {
         v1_2.printStackTrace();
         return false;
     }
     catch(ClassNotFoundException v1_1) {
         v1_1.printStackTrace();
         return false;
     }
     catch(IllegalAccessException v1) {
         v1.printStackTrace();
         return false;
     }
 }
 
 public static final i getInstance() {
     return b.a;
 }
 
 private int isDeviceDebuggable() { // ro.debuggable为1的设备可以调试任何应用,即时应用的debuggable属性为false
     String v0 = c.a().b("ro.debuggable");
     return v0 == null || !"0".equals(v0) ? 1 : 0;
 }
 
 private int isDeviceRootedByRoSecure() { // 检测ro.secure
     String v0 = c.a().b("ro.secure");
     return v0 == null || !"0".equals(v0) ? 1 : 0;
 }
 
 private boolean isDeviceRootedByFile() { // 通过文件检测设备是否root
     String[] v0 = {"/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su"};
     int v3;
     for(v3 = 0; v3 < v0.length; ++v3) {
         if(new File(v0[v3]).exists()) {
             return true;
         }
     }
 
     return false;
 }
 
}

二、加密分析
定位加密代码

 

这个app通过拦截器实现请求加密,以及携带特殊参数,通过app的onCreate,进入this.c(),接着定位到加密拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void c() {
 HttpHeaders v0 = new HttpHeaders();
 v0.put("User-Agent", "Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleDart/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17");
 try {
    /* other codes */
     this.t0.a(new c()); // TOKEN拦截器
     this.t0.a(new com.niming.weipa.app.a.a()); // 加密拦截器
     this.t0.a(new com.niming.weipa.app.a.b());
    /* other codes */
 }
 catch(Exception v0_1) {
     v0_1.printStackTrace();
 }
}

进入com.niming.weipa.app.a.a(),发现其中的b函数

1
2
3
4
5
6
7
8
9
10
private b0 a(b0 arg9) {
 /* other codes */
 v1.put("data", v2);
 String v1_1 = g.a(v1);
 LogUtils.b(new Object[]{"===realParamsString: " + v1_1});
 String v1_2 = URLEncoder.encode(com.niming.weipa.utils.a.b(v1_1)); // 加密函数
 LogUtils.b(new Object[]{"===encrypt realParamsString: " + v1_2});
 new okhttp3.s.a().b("data", v1_2);
 return arg9;
}

定位到加密类com.niming.weipa.utils.a,使用AES加密,ECB模式,PKCS7的padding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.niming.weipa.utils;
 
import android.text.TextUtils;
import com.blankj.utilcode.util.y;
 
public class a {
 private static String a = "";
 private static String b = "pcv#Cg1vbdl#r2hm";
 private static String c = "AES/ECB/PKCS7Padding"; // 加密算法、加密模式、padding模式
 private static String d; // 加密密钥
 
 static {
     /* other codes */
         v0 = TextUtils.equals("release", "staging") ? TestUtil.getSecretPre() : TestUtil.getSecret();
     a.d = v0;
 }
 
 public static String b(String arg3) {
     if(a.c.equals("AES/ECB/NoPadding")) {
         while(arg3.getBytes().length % 16 != 0) {
             arg3 = arg3 + ' ';
         }
     }
 
     byte[] v0 = a.d.getBytes(); // 获得密钥
     String v1 = a.c; // 加密方式
     byte[] v3 = y.k(arg3.getBytes(), v0, v1, null);
     return v3 == null ? "" : new String(v3);
 }
}

获得加密密钥

 

密钥通过native函数获得,可通过hook方式得到加密密钥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.niming.weipa.utils;
public class TestUtil {
 static {
     System.loadLibrary("security");
 }
 public static native String getSecret() {
 }
 public static native String getSecret2() {
 }
 public static native String getSecret3() {
 }
 public static native String getSecretPre() {
 }
 public static native String getSecretVP() {
 }
}

验证加密算法

 

没有改动加密算法及结果,可直接通过再现网站验证

 

数据 {"data":"SaiwLbHeTk0xAbNBv8Q9cnc4os2kD8foP+3Ie57JTkc=","handshake":"v20200429"}

 

图片描述

 

三、设备注册分析
本地sqlite数据库

 

用户信息存储在本地sqlite数据库中,可以通过sqlite3或者导出数据的方式查看数据库数据,数据库路径为/data/data/package_name/database/db_name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.niming.framework.basedb;
 
import android.content.Context;
import androidx.room.Database;
import androidx.room.RoomDatabase;
import androidx.room.q1;
 
@Database(entities = {e.class, a.class}, version = 1)
public abstract class BaseAppDatabase extends RoomDatabase {
 private static volatile BaseAppDatabase a;
 
 static BaseAppDatabase a(Context arg2) {
     Class v0 = BaseAppDatabase.class;
     if(BaseAppDatabase.a == null) {
         synchronized(v0) {
             if(BaseAppDatabase.a == null) {
                 BaseAppDatabase.a = (BaseAppDatabase)q1.a(arg2.getApplicationContext(), v0, "base_database").a().b();
             }
 
             return BaseAppDatabase.a;
         }
     }
 
     return BaseAppDatabase.a;
 }
}

如果本地不存在数据库以及用户信息,会触发新设备注册逻辑,可以手动删除数据库进而触发该逻辑

 

设备注册逻辑

 

首次注册设备时,向服务器发送的未加密数据格式为,其中device_no唯一标识一个设备,且在客户端生成,可以随意修改

1
{"channel":"","code":"","device_no":"cd17c0a9-1d41-3377-8b3c-2599755976f8","device_type":"A","version":"4.0.5"}

在服务器返回的响应中会包含token,与device_no对应,用于生成XTOKEN请求头参数,作为用户身份标识,后续所有请求需携带该字段

 

XTOKEN认证参数生成

 

可直接通过搜索XTOKEN关键字定位到生成逻辑,很简单,通过注册设备时发送给服务器的deivce信息以及服务器返回的token信息,经序列化后加密即生成了最终的X-TOKEN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override  // okhttp3.w
public d0 a(a arg6) throws IOException {
 /* other codes */
 HashMap v2 = new HashMap();
 v2.put("token", v0_3);
 v2.put("device_no", h.a().c("device_id"));
 v2.put("device_type", "A");
 v2.put("version", "4.0.5");
 JSONObject v3 = new JSONObject(v2);
 if(!TextUtils.isEmpty(v0_3)) {
     v1.a("X-TOKEN", com.niming.weipa.utils.a.b(String.valueOf(v3)));
 }
 return arg6.a(v1.a());
}

获得新帐号、绑定邀请码、获得vip

 

在以上分析的基础上,通过不同的device_no,生成相应的XTOKEN即可拥有新用户帐号,利用该app邀请码绑定送vip机制,可以获得大量用户凭证并绑定自己的邀请码,获得持续vip功能,演示可以观看bilibili视频


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-1-29 22:42 被熊趴趴来编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (3)
雪    币: 562
活跃值: (4190)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
我以为是抖音啊
2023-1-30 10:31
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
大致涉及到哪些工具啊,我是新手
2023-2-1 17:15
0
雪    币: 0
活跃值: (532)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
大哥,样本哪里下载啊
2023-2-22 16:53
0
游客
登录 | 注册 方可回帖
返回
//