目录
猿人学 App Match 第一题
/    

猿人学 App Match 第一题

题目: 计算前 100 页的数字加在一块 求和。

考察的是 Java 层加密

对于 App 的爬虫,我首先想到的是抓包、找到数据接口,请求接口拿到数据。

往往事情没那么简单😤

对于抓包的方式有很多种,例如:安装App HttpCanary、PC端Filddler、Charles等等

这些方法我都试过了,App会检测是否使用了代理、验证证书之类的,导致我们无法正常使用 App。

App防止抓包可以参考:https://www.jianshu.com/p/9921db646c59

可以使用 frida hook 具体的函数(需要搭建环境,没用过)

还有一种方法可以通过 xp模块,如 JustTrutMe 来忽略证书验证,就是手机需要 root,安装 xp框架,但是目前 App 不支持模拟器运行。

抓包方法

工具:红米 K50 Pro、HttpCanary、VMOS Pro

1、首先手机安装上述两种软件

2、在VMOS中创建虚拟机,安装 Xposed、Root权限,下载模块 TrustMeAlready(和JustTrustMe差不多)

3、在真机上安装 HttpCanary证书(在软件中,需要自己找),并将证书导出

4、然后在VMOS虚拟机中也安装HttpCanary,在虚拟机中导入从真机导出的证书

5、在虚拟机中打开需要抓包的软件,在真机HttpCanary中对VMOS软件进行抓包。

按照这个差不多就能抓到包了。(还有一种方法,fiddler 和 drony 搭配)

抓到了接口

这里 sign 值就是一个加密的参数,每一次都不一样,并且还具有时效性。

获取 Sign - 反编译

为了获取 Sign 的值,我们只能反编译 这个 Apk,找到了这个参数的算法。

我一开始用的 MT管理器进行的反编译;后来直接换成了 jadx;

下面会有Jadx 工具编译命令。

使用 jadx 打开 Apk 文件进行反编译;

然后就是搜索各种关键字 例如:/app1sign 找呀找找🤔

找到了这个类 ChallengeOneFragment

发现了这一行代码

public /* synthetic */ o00Ooo lambda$initListeners$0(o00O000.OooOO0O oooOO0O) throws Exception {
  StringBuilder sb = new StringBuilder();
  sb.append("page=");
  sb.append(this.page);
  long longValue = oooOO0O.OooO00o().longValue();
  sb.append(longValue);
  return ((o0O0ooO.OooO0O0) this.mRequest.OooOOO0(o0O0ooO.OooO0O0.class)).OooO00o(Integer.valueOf(this.page), new Sign().sign(sb.toString().getBytes(StandardCharsets.UTF_8)), Long.valueOf(longValue));
}

参数分别为:page、sign、t时间戳

然后就是把 Sign 这个类以及 sign 方法关联的类都搞到我们的 idea 尝试跑通这个方法;

最终定位到了这三个类:OooO0O0OooO0OOOooO00oSign

这里sign值是由 encrypt("page=" + page + timestamp) 加密后得到的

Sign类中sign方法返回的就是我们要的sign值

使用最新版 Jadx 反编译的坑,有几块代码反编译的有问题,导致代码无法运行或者是导致最终生成的Sign值与实际中不一样。

第一个地方

package o00OO;

/* compiled from: proguard-dict.txt */
/* loaded from: classes2.dex */
public class OooO00o {

    /* renamed from: OooO00o  reason: collision with root package name */
    public static final String[] f8180OooO00o;

    static {
        f8180OooO00o = r0;
        String[] strArr = {"茅茑馇筀ꆕ茪馃筆ꆄ茪首筎ꆅ荖駳筕ꆧ荡馢筬ꆨ荢茆馇筀ꆕ茍馕筍ꆇ茴首筗ꆈ荂茆馇筀ꆕ茆馇筀ꆕ茑馇筀ꆕ茪馃筆ꆄ茪首筎ꆅ荖駳筕ꆧ荡馢筬ꆨ荢茕駣笵ꇴ荽駣笵ꇴ荽駣笵ꇴ荽駣笵ꇴ荽茎\ueaf9㖿猪茨馇筕ꆖጃ췗\u20fc썑茌馈筌ꆅ荎香筋ꆇ荈馃茁馔筄ꆈ荎茄駮茄駯茕ᘟ㔯並찜왎\u1ad7廊췅힎㕚쵧\ue40c\uec9f㔎庘籛茎\ueaf9㖿猪茨馇筕ꆖጃ췗\u20fc썑茉\ueaf9㖿猪茨馇筕ꆖጃ췗ᢗ⦊\uea99茀\ue97f⫾\uf8f2Ꮽ曇茍졋᠌\uefc6\ue824\u09c6⫿\u10358a茚馮筱ꆲ荵馵笿ꇩ茪馧筵ꆶ荨馧筱ꆥ荭駨筼ꆳ荤馨筷ꆣ荫馾筰ꆣ茫馥筪ꆫ茇P\ue370茎\ueaf9㖿猪茨馇筕ꆖጃ췗\u20fc썑茇穀\uf349茉\ueaf9㖿猪茨馇筕ꆖጃ췗ᢗ⦊\uea99茁馔筄ꆈ荎茌馈筌ꆅ荎香筋ꆇ荈馃茌馈筌ꆅ荎香筋ꆇ荈馃茄駶茄駮茄駯茏馥筤ꆥ荠馴筱ꇨ荵馣筨茇馊答茋馥筪ꆶ荼馅筠ꆴ荱馀筬ꆪ荠駼笥茆馧筵ꆭ茄駩茂馫筪ꆳ荫馲筠ꆢ茅茄駩茏馥筤ꆥ荠馴筱ꇨ荵馣筨茇馊答茏馥筤ꆥ荠馴筱ꇨ荵馣筨茁馊答ꇫ茻茂馫筤ꆲ荦馮笵ꇵ茈駷笽ꇶ茫駱笳ꇨ茳駶笫ꇴ茱駲茁駣笵ꇲ荡茁馊答ꇫ茻茄駪茂馶筤ꆢ荡馯筫ꆡ茂馶筤ꆢ荡馯筫ꆡ茧馓筫ꆣ荽馶筠ꆥ荱馣筡ꇦ荡馣筣ꆧ荰馪筱ꇦ荱馴筰ꆵ荱駦筨ꆧ荫馧筢ꆣ荷馵笿茆馄筎ꆕ茉馥筩ꆯ荠馨筱ꆅ荄駨筧ꆭ荶茕馋筟ꇲ荦馩筿ꆟ茽馗筰ꇵ茷馓筿ꆁ荠茕馋筟ꇲ荦馩筿ꆟ茽馗筰ꇵ茷馓筿ꆁ荠茆馒等ꆕ茞馮筱ꆲ荵馵笿ꇩ茪駷笽ꇶ茫駱笳ꇨ茳駶笫ꇴ茱駲笿ꇷ茽駲笱ꇵ茆馅筋ꇻ茄駪茀馞笫ꇳ茵駿蝪駫笨ꇫ茨駫筇ꆃ荂馏筋ꇦ荆馃筗ꆒ荌馀筌ꆅ荄馒筀ꇫ茨駫笨ꇫ茏馋筌ꆏ荁馁筯ꆅ荆馇筢ꆏ荆馅答ꆅ荇馔筗ꆬ荨馍筷ꆃ荐馾筿ꆇ荋馄筢ꆭ荴馮筮ꆯ荂駿筲ꇶ荇馇答ꆵ荃馇筁ꆄ荊馋答ꆵ荲馅答ꆟ荁馐答ꆗ荂馃筲ꆌ茱駌筠ꆂ荀馊筈ꆇ荮馁筄ꇷ荐馃筆ꆇ荲馅筠ꆎ荢馾筆ꆼ荄馌筇ꆡ荋馐筇ꆇ荦馋筄ꆨ荭駲筈ꆗ荶馱筆ꆗ荜馂筓ꆗ荔馍筁ꆇ荏駲筠ꆂ荀馊筈ꆇ荮馁筄ꇷ荐馃笏ꆅ荲馱筆ꆣ荍馡筽ꆅ荿馇筏ꆄ荢馈筓ꆄ荄馋筈ꆇ荫馮笱ꆋ荆馇筝ꆂ荑馏筼ꆋ荁馋筽ꆉ荑馏筿ꆋ荑馥筿ꆋ茴馩筜ꆂ荿馏筽ꆋ药馏筲ꆋ药馏筿ꆋ药馋筽ꇌ荋馼筈ꆼ荒馬筇ꆉ荈馗筶ꆱ荆馗筜ꆂ荓馗答ꆁ荀馱筏ꇲ荠馂筀ꆊ荈馇筮ꆁ荄駷筐ꆃ荆馇筲ꆅ荠馎筢ꆾ荆馼筄ꆌ荇馡筋ꆐ荇馇筦ꆋ荄馨筭ꇲ荈馗筶ꆱ茏馅答ꆟ荁馐答ꆗ荎馂筄ꆌ茱馣筁ꆃ草馋筄ꆭ荂馇笴ꆓ荀馅筲ꆱ荆馣筍ꆡ荽馅筿ꆇ荏馄筢ꆈ荓馄筄ꆋ荈馇筫ꆮ茱馋筌ꆏ荇馏筯ꆇ荋馄筢ꆭ荴馮筮ꆯ荂駌笼ꆱ茵馄筄ꆗ荀馀筄ꆇ荊馅筄ꆗ茽馇筈ꆏ荌馄筆ꆡ荎馅筄ꆗ荀馇笳ꆭ荐馳筩ꆄ荠馟筋ꆅ荄馨答ꇰ荼馼筫ꆷ荣馨筝ꆯ荵駳筡ꆬ茳馵笼ꆎ荂馕答ꆈ荭馥笏ꇲ荃馲筳ꆪ荔馾筮ꆜ荧馬筜ꆣ荫馷笶ꇷ荓馑笱ꆶ荠馥筍ꇭ茱駲筲ꆡ荋馄筶ꆴ荩馒筰ꆰ茵馂筇ꆷ荢駴筐ꇶ茵馨筤ꆷ荦駴等ꆖ茳馏筭ꆤ荜馩筓ꆭ荿駵策ꇌ荴馧筯ꆠ荲馊筂ꆁ荌駾筵ꆄ荳馮筵ꆵ荠馐筇ꆗ荼馰筶ꆱ茴馾笷ꇵ荦馴筀ꆁ茼馶筇ꆏ荂駳笰ꇰ荋馳筶ꆊ荧馅筜ꆤ荲馾筯ꇲ荶馷筨ꇿ荫馱筌ꇱ荼馔筇ꆃ茏馎筒ꆵ荈馒策ꆗ荪馒筌ꆪ荇馓筕ꆞ荕駰策ꆮ荓駳筶ꆐ荡馩筑ꆥ荁馢等ꆢ荖馅筂ꆂ荪馋筦ꇭ茴馬筊ꆢ茼馕筧ꆫ荷馫筈ꆨ荈馏笴ꇱ荖馔笷ꆄ荊駶筷ꇩ荁駌筏ꆞ荆馭笷ꇾ荱馕筑ꆾ荄馶筜ꆁ荓駭筜ꆇ茼馫筟ꇵ荏馒筬ꇭ荓駱筝ꆂ荬馿笰ꆅ荮馣筧ꇾ荼馥筓ꇭ荀駾筦ꆨ荡馍筗ꆵ荍駶筐ꆳ荈馫筊ꆶ荄馩筌ꆱ茰馍笏ꆎ荒馋筼ꆼ茪馉笪ꆧ荽馣筐ꆴ茼馔笳ꆠ荏馗筃ꆷ药馗筐ꆃ荎馞筍ꆾ荖駷筆ꆣ荦駭笮ꆬ茳馴笽ꇵ药馇笪ꆏ荝馫筷ꇴ荲馏筁ꆇ荔馇筇ꆋ荄駶筂ꆅ荖馷筂ꇌ荖馏筧ꇵ荁馗筀ꆄ荆馱筐ꆇ荄駲筌ꆄ荄馗筄ꆢ荿馾筒ꆮ茵駱筒ꆿ茱駶笷ꆖ荡馇策ꇾ荵馌筇ꆃ荤馾筆ꆤ茲馞笲ꆕ茵駴答ꆮ荃駱筴ꆂ茱駷筜ꆨ草馞筍ꆣ茏馤筟ꆮ茼馊筱ꆌ荡馲筧ꆕ茵馈筠ꆨ荰駰筀ꆇ茶馧筭ꆗ荝馃笱ꆗ荆馬筓ꆥ荄馯筕ꆃ荁馍笴ꆫ茮馰筶ꇶ荼馤筀ꆥ荗馉筀ꆪ荿馀筷ꆅ茵馾筭ꇾ荋馾筍ꆭ荗駌笽ꆳ荱馟筯ꆋ荟馡筭ꇶ荪馲筰ꇿ茱駾笱ꆒ荒馫筌ꆉ荊馬筬ꆯ茽馟筜ꆧ荄馰筀ꇰ草馊等ꇩ荜駾筈ꆭ茷馪筏ꇿ荍馕筵ꆢ荀馒筶ꆓ草馌筽ꆒ荦馿筧ꆌ茲馞笏ꆗ荒駰筎ꇱ荇馣筝ꆞ茽駲策ꆢ荍馶筨ꆼ茵馎筁ꆕ荨駾筄ꇳ荇馧筫ꆮ荩馳笷ꆷ荁馪筓ꇲ荄馏筀ꆾ荐馓答ꇩ荽馵筀ꆉ茵馐筧ꆠ荀駴筭ꆉ荟駵筿ꆩ茴馔筡ꇌ荑馶筜ꆃ荦馠筒ꆒ茷馣筗ꇷ荭馐筯ꆇ荴馋筎ꆷ荫馉筎ꇷ荴駭筫ꆧ荴馨筄ꆊ药駱笴ꆴ荌馎筬ꆍ荍馄筷ꆁ荖馄筡ꆾ药馎筗ꆂ荝馕筲ꆳ荇馃筳ꆜ荧首筬ꆀ茏馏笮ꆔ荐馁筎ꆔ药馭筟ꆍ荤馬筬ꆗ荧馀筧ꇵ荔馉筽ꆴ荪駰筫ꆑ荶馵筦ꆷ荦馅筳ꇩ荔馃答ꆔ荶駌笨ꇫ茨駫笨ꆃ荋馂笥ꆅ荀馔筑ꆏ荃馏筆ꆇ荑馃笨ꇫ茨駫笨ꇌ茄駶蝪駫笨ꇫ茨駫筇ꆃ荂馏筋ꇦ荆馃筗ꆒ荌馀筌ꆅ荄馒筀ꇫ茨駫笨ꇫ茏馋筌ꆏ荁馁筯ꆅ荆馇筢ꆏ荆馅答ꆅ荇馔筗ꆬ荨馍筷ꆃ荐馾筿ꆇ荋馄筢ꆭ荴馮筮ꆯ荂駿筲ꇶ荇馇答ꆵ荃馇筁ꆄ荊馋答ꆵ荲馅答ꆟ荁馐答ꆗ荂馃筲ꆌ茱駌筠ꆂ荀馊筈ꆇ荮馁筄ꇷ荐馃筆ꆇ荲馅筠ꆎ荢馾筆ꆼ荄馌筇ꆡ荋馐筇ꆇ荦馋筄ꆨ荭駲筈ꆗ荶馱筆ꆗ荜馂筓ꆗ荔馍筁ꆇ荏駲筠ꆂ荀馊筈ꆇ荮馁筄ꇷ荐馃笏ꆅ荲馱筆ꆣ荍馡筽ꆅ荿馇筏ꆄ荢馈筓ꆄ荄馋筈ꆇ荫馮笱ꆋ荆馇筝ꆂ荑馏筼ꆋ荁馋筽ꆉ荑馏筿ꆋ荑馥筿ꆋ茴馩筜ꆂ荿馏筽ꆋ药馏筲ꆋ药馏筿ꆋ药馋筽ꇌ荋馼筈ꆼ荒馬筇ꆉ荈馗筶ꆱ荆馗筜ꆂ荓馗答ꆁ荀馱筏ꇲ荠馂筀ꆊ荈馇筮ꆁ荄駷筐ꆃ荆馇筲ꆅ荠馎筢ꆾ荆馼筄ꆌ荇馡筋ꆐ荇馇筦ꆋ荄馨筭ꇲ荈馗筶ꆱ茏馅答ꆟ荁馐答ꆗ荎馂筄ꆌ茱馣筁ꆃ草馋筄ꆭ荂馇笴ꆓ荀馅筲ꆱ荆馣筍ꆡ荽馅筿ꆇ荏馄筢ꆈ荓馄筄ꆋ荈馇筫ꆮ茱馋筌ꆏ荇馏筯ꆇ荋馄筢ꆭ荴馮筮ꆯ荂駌笼ꆱ茵馄筄ꆗ荀馀筄ꆇ荊馅筄ꆗ茽馇筈ꆏ荌馄筆ꆡ荎馅筄ꆗ荀馇笳ꆭ荐馳筩ꆄ荠馟筋ꆅ荄馨答ꇰ荼馼筫ꆷ荣馨筝ꆯ荵駳筡ꆬ茳馵笼ꆎ荂馕答ꆈ荭馥笏ꇲ荃馲筳ꆪ荔馾筮ꆜ荧馬筜ꆣ荫馷笶ꇷ荓馑笱ꆶ荠馥筍ꇭ茱駲筲ꆡ荋馄筶ꆴ荩馒筰ꆰ茵馂筇ꆷ荢駴筐ꇶ茵馨筤ꆷ荦駴等ꆖ茳馏筭ꆤ荜馩筓ꆭ荿駵策ꇌ荴馧筯ꆠ荲馊筂ꆁ荌駾筵ꆄ荳馮筵ꆵ荠馐筇ꆗ荼馰筶ꆱ茴馾笷ꇵ荦馴筀ꆁ茼馶筇ꆏ荂駳笰ꇰ荋馳筶ꆊ荧馅筜ꆤ荲馾筯ꇲ荶馷筨ꇿ荫馱筌ꇱ荼馔筇ꆃ茏馎筒ꆵ荈馒策ꆗ荪馒筌ꆪ荇馓筕ꆞ荕駰策ꆮ荓駳筶ꆐ荡馩筑ꆥ荁馢等ꆢ荖馅筂ꆂ荪馋筦ꇭ茴馬筊ꆢ茼馕筧ꆫ荷馫筈ꆨ荈馏笴ꇱ荖馔笷ꆄ荊駶筷ꇩ荁駌筏ꆞ荆馭笷ꇾ荱馕筑ꆾ荄馶筜ꆁ荓駭筜ꆇ茼馫筟ꇵ荏馒筬ꇭ荓駱筝ꆂ荬馿笰ꆅ荮馣筧ꇾ荼馥筓ꇭ荀駾筦ꆨ荡馍筗ꆵ荍駶筐ꆳ荈馫筊ꆶ荄馩筌ꆱ茰馍笏ꆎ荒馋筼ꆼ茪馉笪ꆧ荽馣筐ꆴ茼馔笳ꆠ荏馗筃ꆷ药馗筐ꆃ荎馞筍ꆾ荖駷筆ꆣ荦駭笮ꆬ茳馴笽ꇵ药馇笪ꆏ荝馫筷ꇴ荲馏筁ꆇ荔馇筇ꆋ荄駶筂ꆅ荖馷筂ꇌ荖馏筧ꇵ荁馗筀ꆄ荆馱筐ꆇ荄駲筌ꆄ荄馗筄ꆢ荿馾筒ꆮ茵駱筒ꆿ茱駶笷ꆖ荡馇策ꇾ荵馌筇ꆃ荤馾筆ꆤ茲馞笲ꆕ茵駴答ꆮ荃駱筴ꆂ茱駷筜ꆨ草馞筍ꆣ茏馤筟ꆮ茼馊筱ꆌ荡馲筧ꆕ茵馈筠ꆨ荰駰筀ꆇ茶馧筭ꆗ荝馃笱ꆗ荆馬筓ꆥ荄馯筕ꆃ荁馍笴ꆫ茮馰筶ꇶ荼馤筀ꆥ荗馉筀ꆪ荿馀筷ꆅ茵馾筭ꇾ荋馾筍ꆭ荗駌笽ꆳ荱馟筯ꆋ荟馡筭ꇶ荪馲筰ꇿ茱駾笱ꆒ荒馫筌ꆉ荊馬筬ꆯ茽馟筜ꆧ荄馰筀ꇰ草馊等ꇩ荜駾筈ꆭ茷馪筏ꇿ荍馕筵ꆢ荀馒筶ꆓ草馌筽ꆒ荦馿筧ꆌ茲馞笏ꆗ荒駰筎ꇱ荇馣筝ꆞ茽駲策ꆢ荍馶筨ꆼ茵馎筁ꆕ荨駾筄ꇳ荇馧筫ꆮ荩馳笷ꆷ荁馪筓ꇲ荄馏筀ꆾ荐馓答ꇩ荽馵筀ꆉ茵馐筧ꆠ荀駴筭ꆉ荟駵筿ꆩ茴馔筡ꇌ荑馶筜ꆃ荦馠筒ꆒ茷馣筗ꇷ荭馐筯ꆇ荴馋筎ꆷ荫馉筎ꇷ荴駭筫ꆧ荴馨筄ꆊ药駱笴ꆴ荌馎筬ꆍ荍馄筷ꆁ荖馄筡ꆾ药馎筗ꆂ荝馕筲ꆳ荇馃筳ꆜ荧首筬ꆀ茏馏笮ꆔ荐馁筎ꆔ药馭筟ꆍ荤馬筬ꆗ荧馀筧ꇵ荔馉筽ꆴ荪駰筫ꆑ荶馵筦ꆷ荦馅筳ꇩ荔馃答ꆔ荶駌笨ꇫ茨駫笨ꆃ荋馂笥ꆅ荀馔筑ꆏ荃馏筆ꆇ荑馃笨ꇫ茨駫笨ꇌ茍葉\u0d81⩸\uda02쐴\uf3aeﷇ彩茂馶筤ꆢ荡馯筫ꆡ茂馶筤ꆢ荡馯筫ꆡ茂馫筤ꆲ荦馮笵ꇾ茂馫筤ꆲ荦馮笵ꇴ茈駷笽ꇶ茫駱笳ꇨ茳駶笫ꇴ茱駲茁馊答ꇫ茻茂馫筤ꆲ荦馮笵ꇲ茂馶筤ꆢ荡馯筫ꆡ茂馫筤ꆲ荦馮笵ꇱ茂馫筤ꆲ荦馮笵ꇰ茂馶筤ꆢ荡馯筫ꆡ茂馶筤ꆢ荡馯筫ꆡ茂馶筤ꆢ荡馯筫ꆡ"};
    }

    public static String OooO00o(long j) {
        return OooO0O0.OooO0O0(j, f8180OooO00o);
    }
}

就是这里,r0 这个参数根本找不到,我们直接查看 原本 smail 代码得知,直接改成下面代码就行

ss = new String[]{"xxx字符串就省略了。。。。"};

第二个地方

package o00OO;

import com.google.common.primitives.UnsignedInts;

/* compiled from: proguard-dict.txt */
/* loaded from: classes2.dex */
public class OooO0O0 {
    public static long OooO00o(int i, String[] strArr, long j) {
        return (strArr[i / 8191].charAt(i % 8191) << 32) ^ OooO0OO.OooO00o(j);
    }

    public static String OooO0O0(long j, String[] strArr) {
        long OooO00o2 = OooO0OO.OooO00o(OooO0OO.OooO0OO(UnsignedInts.INT_MASK & j));
        long OooO00o3 = OooO0OO.OooO00o(OooO00o2);
        int i = (int) (((j >>> 32) ^ ((OooO00o2 >>> 32) & 65535)) ^ ((OooO00o3 >>> 16) & (-65536)));
        long OooO00o4 = OooO00o(i, strArr, OooO00o3);
        int i2 = (int) ((OooO00o4 >>> 32) & 65535);
        char[] cArr = new char[i2];
        for (int i3 = 0; i3 < i2; i3++) {
            OooO00o4 = OooO00o(i + i3 + 1, strArr, OooO00o4);
            cArr[i3] = (char) ((OooO00o4 >>> 32) & 65535);
        }
        return new String(cArr);
    }
}

就是 return (strArr[i / 8191].charAt(i % 8191) << 32) ^ OooO0OO.OooO00o(j); 这一行代码,会导致我们代码一直运行不成功,数组下标越界异常。

需要改成 return ((long) strArr[i / 8191].charAt(i % 8191) << 32) ^ OooO0OO.OooO00o(j);

最后一个地方

是Sign.java

package cn.lacknb.test.com.proxyqwcde.core;

import cn.lacknb.test.com.proxyqwcde.core.OooO00o;

import java.util.ArrayList;

/* compiled from: proguard-dict.txt */
/* loaded from: classes2.dex */
public class Sign {
    private static final int A = 1732584193;
    private static final int B = -271733879;
    private static final int C = -1732584194;
    private static final int D = 271733878;

    private static int f(int i, int i2, int i3) {
        return ((~i) & i3) | (i2 & i);
    }

    private static int ff(int i, int i2, int i3, int i4, int i5, int i6) {
        return rotateLeft(i + f(i2, i3, i4) + i5, i6);
    }

    private static int g(int i, int i2, int i3) {
        return (i & i3) | (i & i2) | (i2 & i3);
    }

    private static int gg(int i, int i2, int i3, int i4, int i5, int i6) {
        return rotateLeft(i + g(i2, i3, i4) + i5 + 1518565785, i6);
    }

    private static int h(int i, int i2, int i3) {
        return (i ^ i2) ^ i3;
    }

    private static int hh(int i, int i2, int i3, int i4, int i5, int i6) {
        return rotateLeft(i + h(i2, i3, i4) + i5 + 1859775393, i6);
    }

    private ArrayList<Integer> padding(byte[] bArr) {
        int length = bArr.length * 8;
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (byte b : bArr) {
            arrayList.add(Integer.valueOf(b));
        }
        arrayList.add(128);
        while (((arrayList.size() * 8) + 64) % 512 != 0) {
            arrayList.add(0);
        }
        for (int i = 0; i < 8; i++) {
            arrayList.add(Integer.valueOf((int) ((length >>> (i * 8)) & 255)));
        }
        return arrayList;
    }

    private static int rotateLeft(int i, int i2) {
        return (i >>> (32 - i2)) | (i << i2);
    }

    public String sign(byte[] bArr) {
        ArrayList<Integer> padding = padding(bArr);
        int i = A;
        int i2 = B;
        int i3 = C;
        int i4 = D;
        for (int i5 = 0; i5 < padding.size() / 64; i5++) {
            int[] iArr = new int[16];
            for (int i6 = 0; i6 < 16; i6++) {
                int i7 = (i5 * 64) + (i6 * 4);
                iArr[i6] = (padding.get(i7 + 3).intValue() << 24) | padding.get(i7).intValue() | (padding.get(i7 + 1).intValue() << 8) | (padding.get(i7 + 2).intValue() << 16);
            }
            int[] iArr2 = {0, 4, 8, 12};
            int i8 = i;
            int i9 = i2;
            int i10 = i3;
            int i11 = i4;
            int i12 = 0;
            while (i12 < 4) {
                int i13 = iArr2[i12];
                i8 = ff(i8, i9, i10, i11, iArr[i13], 3);
                int ff = ff(i11, i8, i9, i10, iArr[i13 + 1], 7);
                i10 = ff(i10, ff, i8, i9, iArr[i13 + 2], 11);
                i9 = ff(i9, i10, ff, i8, iArr[i13 + 3], 19);
                i12++;
                i11 = ff;
            }
            int[] iArr3 = {0, 1, 2, 3};
            int i14 = i8;
            int i15 = i11;
            for (int i16 = 0; i16 < 4; i16++) {
                int i17 = iArr3[i16];
                i14 = gg(i14, i9, i10, i15, iArr[i17], 3);
                i15 = gg(i15, i14, i9, i10, iArr[i17 + 4], 5);
                i10 = gg(i10, i15, i14, i9, iArr[i17 + 8], 9);
                i9 = gg(i9, i10, i15, i14, iArr[i17 + 12], 13);
            }
            int[] iArr4 = {0, 2, 1, 3};
            int i18 = i14;
            int i19 = 0;
            while (i19 < 4) {
                int i20 = iArr4[i19];
                int hh = hh(i18, i9, i10, i15, iArr[i20], 3);
                i15 = hh(i15, hh, i9, i10, iArr[i20 + 8], 9);
                i10 = hh(i10, i15, hh, i9, iArr[i20 + 4], 11);
                i9 = hh(i9, i10, i15, hh, iArr[i20 + 12], 15);
                i19++;
                i18 = hh;
            }
            i += i18;
            i2 += i9;
            i3 += i10;
            i4 += i15;
        }
        return String.format(OooO00o.OooO00o(-592855683721616730L), Integer.valueOf(i), Integer.valueOf(i2), Integer.valueOf(i3), Integer.valueOf(i4));
    }
}

这里

for (int i = 0; i < 8; i++) {
  arrayList.add(Integer.valueOf((int) ((length >>> (i * 8)) & 255)));
}

// 需要改成

for (int i = 0; i < 8; i++) {
  arrayList.add(Integer.valueOf((int) (((long)length >>> (i * 8)) & 255)));
}

精度丢失的问题

听网上的大佬说,用 jadx 1.20 的版本 反编译就没问题了。。。(听说是 M1芯片的原因的,我用的 Mac OS M1)

最后这 sign 方法最后返回的是 String.formart(), 一看第一个返回就是 类似于 %d 的字符串,后面都为参数

经过最后测试运行,这里的值是固定的,其实我们只需要 Sign.java 这一个类就行,最后一行的代码可以改成

return String.format("%02x%02x%02x%02x", Integer.valueOf(i), Integer.valueOf(i2), Integer.valueOf(i3), Integer.valueOf(i4));

Jadx 工具编译

git clone https://ghproxy.com/https://github.com/skylot/jadx.git

cd jadx

./gradlew dist

按照上面的命令编译完成后

cd build/jadx/bin

./jadx-gui

就显示了工具的图形化页面。

测试代码

Java 获取 sign 的测试代码

@Test
public void signTest() {
  // System.out.println(OooO00o.OooO00o(-0x83a3f1a6f4c5d5aL));
  Sign sign = new Sign();
  String s = sign.sign(("page=11652454733").getBytes(StandardCharsets.UTF_8));
  System.out.println(s);
}

在本地搭建一个获取 sign 的接口,然后最后的 Python 计算代码

#!/usr/bin/python3
# --*-- coding: utf-8 --*--
# @Author: gitsilence
# @Time: 2022/5/14 1:02 上午
import requests
import httpx
import time

client = httpx.Client(http2=True)
time_url = "https://appmatch.yuanrenxue.com/time"
data_api_url = "https://appmatch.yuanrenxue.com/app1"

headers = {
    "Host": "appmatch.yuanrenxue.com",
    "User-Agent": "Mozilla/5.0 (Linux; U; Android 7.1.2; zh-cn; 22011211C Build/N6F26Q) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
}
session = requests.session()
session.headers = headers


def get_sign(page, t):
    res = requests.get(f'http://localhost:8080/getSign?t=page={page}{t}')
    sign = res.text
    print("sign: ", sign)
    return sign


def get_time():
    return session.get(time_url).json()['time']


if __name__ == '__main__':
    """
        Sign(page=11652454595)
        page=1&sign=dbd87f06a6486cfe545a4658de03bfed&t=1652454733
        page=2&sign=30b5dcbb89424531924665a44841fe71&t=1652458800
                    d37e65a6dde4b59f738dee69dde0249f
    """
    sum = 0

    for page in range(1, 101):
        t = get_time()
        print("t: ", t)
        data = {
            "page": page,
            "sign": get_sign(page, str(t)),
            "t": t
        }
        # req_url = data_api_url + f"?page={page}&sign={get_sign(page, str(t))}&t={t}"
        # print(req_url)
        print(data)
        res = client.post(data_api_url, headers=headers, data=data)
        datas = res.json()['data']
        for data in datas:
            sum += int(data['value'].replace(r'\r', ''))

        time.sleep(0.5)
    print('sum: ', sum)
    pass

写在最后

本文仅限供学习参考使用

其实我也搭建 ADB 的环境,通过 jadx 远程 debug 对应代码,但是这个软件禁止了远程 debug,由于本人也是现学现做的,并没有什么好的解决办法。

那些坑还是靠网上大佬提示了下,靠我这个新手,这些坑是真的不容易过;只是为了 T恤花了好长时间做了一道题;

其他题目:

  • Java层加密 【简单】
  • so层加密 【简单】
  • so层加密带混淆 【简单】
  • grpc 【中等】
  • 双向认证 【中等】
  • 设备指纹加密 【困难】
  • quic 【困难】
  • upx 【中等】
  • tcp 【中等】
  • tls 【非常困难】

后面看了大佬们的题解,第一题的算法是 MD4 的魔改算法,有大佬用 Python 进行了还原。


标题:猿人学 App Match 第一题
作者:gitsilence
地址:http://blog.lacknb.cn/articles/2022/05/16/1652632028111.html