题目分析

定位到 ChallengeTwoFragment,hook 一下 sign 函数,直接右键 -> 复制为 frida片段就行

image.png

hook代码:

1
2
3
4
5
6
7
let ChallengeTwoFragment = Java.use("com.yuanrenxue.match2022.fragment.challenge.ChallengeTwoFragment");
ChallengeTwoFragment["sign"].implementation = function (str) {
console.log('sign is called' + ', ' + 'str: ' + str);
let ret = this.sign(str);
console.log('sign ret value is ' + ret);
return ret;
};

hook结果:

image.png

可以看出参数很简单,就是 page:时间戳,看一下sign函数的定义:

image.png

有native关键字,说明是本地so文件加载的,到这里有三种解题思路,一种是frida rpc的方式,一种是python调用Unidbg,一种是硬刚so文件

rpc的上一篇已经写过,没什么新意,这里介绍后面两种,也当作给自己积累经验 🤑🤑🤑

第一种 unidbg 加载 so

首先是拿到 so文件 ,这里使用 apktool 反编译

1
apktool d yuanrenxuem107.apk

找到 lib 文件夹下的so文件

image.png

使用 HEX工具判断一下so文件是32位还是64位,等会unidbg要用到

参考:https://chowdera.com/2022/04/202204200352162086.html

image.png

01 代表32位,02代表64位

所以是64位的so文件

用IDA打开so文件,可以看到函数静态注册的,可以直接调用

image.png

unidbg的环境搭建

可以参考这篇,初试 Unidbg 环境搭建

简单来说就三个步骤,拉取项目,idea打开项目安装依赖,运行测试项目

搭建 unidbg server(失败)

unidbg调试好后,就需要想办法让python调用,这里有两种方案,一种是把unidbg-android 整个项目打包成 jar,然后用jpype调用,一种是本地起一个server服务,通过http的方式调用,这里我演示第二种,参考方案:

找了大半天,没有一个可以运行的方案,主要还是对java项目不熟,算了,老老实实打包成jar了 😥😥😥

unidbg项目打包成jar

参考的例子不太恰当,被卡了好久 🥶🥶🥶,自己整理一个规范的吧

首先是在 unidbg-android 下创建包,**包名类名没什么讲究,随意就行,不需要和原APP保持一致**

image.png

编写Cxs.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
package com.yrxapp2;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.StringObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;

import java.io.File;
import java.io.IOException;

public class Cxs {

private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
private final DvmClass ChallengeTwoFragment;
private final boolean logging;

// 作用类型__init__函数
public Cxs(boolean logging) {
this.logging = logging;
// 传入进程名,防检测
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.yuanrenxue.match2022").build();
final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
// 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
// 注意 apk的路径,后面python调用会用到
vm = emulator.createDalvikVM(new File("unidbg-android/src/test/java/com/yrxapp2/yuanrenxuem107.apk"));
vm.setVerbose(logging);
// 加载apk中的so,不需要自己传入文件
DalvikModule dm = vm.loadLibrary("match02", true);
dm.callJNI_OnLoad(emulator);
module = dm.getModule();
// native method 所在 class 的 smali写法
ChallengeTwoFragment = vm.resolveClass("com/yuanrenxue/match2022/fragment/challenge/ChallengeTwoFragment");
}

public void destroy() throws IOException {
emulator.close();
}

// main 检测结果
public static void main(String[] args) throws Exception {
Cxs cxs = new Cxs(false);
System.out.println(cxs.sign("1:1652965963"));
cxs.destroy();
}

// 加密函数
public String sign(String str) {
StringObject sign = ChallengeTwoFragment.newObject(null).callJniMethodObject(
emulator,
"sign(Ljava/lang/String;)Ljava/lang/String;",
vm.addLocalObject(new StringObject(vm, str))
);
return sign.getValue();
}

}

创建好后,运行,结果和抓包请求保持一致就说明成功了

然后就是重点了,打成jar包供python调用,在网上找了一圈资料,发现没有一个规范的教程,最后还是靠自己一点点摸索出来的 🤡

首先是 File -> Project Structure,然后

image.png

这里有五个需要注意的点,配好后点击 ok

image.png

然后就不需要改什么东西了,直接点击 Apply -> OK

然后开始构建,在主界面依次点击 Build -> Build Artifacts -> unidbg-android.jar -> Build

稍等一会儿,在 unidbg\out\artifacts\unidbg_android_jar目录下,你就会发现生成好的jar包

image.png

最后一个 unidbg-android.jar 就包含我们刚才编写的逻辑,

把所有生成的jar包复制到py目录,再新建一个目录,用于存放apk文件

image.png

最后只要编写py文件,调用,就终于搞定啦!🤗🤗🤗

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
# -*- coding: utf-8 -*-
# time: 2022/10/4 17:19
# author: Chen

import time

import jpype
import requests


jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=unidbg-android.jar")
jclass = jpype.JPackage("com.yrxapp2").Cxs
ChallengeTwoFragment = jclass(False)
# print(ChallengeTwoFragment.sign("1:1664938752"))


results = []
for page in range(1, 101):
ts = int(time.time())
url = "https://appmatch.yuanrenxue.com/app2"
data = {
"page": page,
"ts": ts,
"sign": str(ChallengeTwoFragment.sign(f"{page}:{ts}")), # 这里注意使用 str() 格式化
}
resp_json = requests.post(url, data=data).json()
num_lst = [int(d["value"].strip()) for d in resp_json["data"]]
print(f"第 {page} 页:", num_lst)
results.extend(num_lst)

print("100页面总和为:", sum(results))

运行结果 & 挑战成功

image.png

本来想写ida调试的,发现篇幅有点大,知识点也有点多,后面再新开一篇写写吧,另外提一嘴,

这个so比较简单,没有签名校验,不用补环境,以后有遇到了可以参考这篇:

https://www.jianshu.com/p/615cff5683bb

还有第二关用frida rpc 方案也可以过,但总感觉有些胜之不武,就不介绍了 😁😁😁

net.dongliu.requests 方案

直接在java代码里面请求,就不需要用python调用了

用法:https://blog.csdn.net/dxh0823/article/details/81137400

1
2
3
4
5
import net.dongliu.requests.Requests;

String url = "https://app9.scrape.center/api/movie/?offset=0&limit=10&token="+token;
String response = Requests.get(url).timeout(10000).send().readToText();
System.out.println(response);