猿人学APP01实战

环境说明:

  • Nexus 6P Android8.1 已root
  • 抓包软件 Http Canary
  • frida=12.8.10
  • frida-tools=5.4.0
  • objection=1.8.4
  • 猿人学APP 1.0.31
  • Flask=1.1.3

分析思路

先抓包看下是第一题怎么发的包

1
2
3
post 请求 
dst https://www.python-spider.com/api/app1
编码参数 page=1&sign=19fb8a2eb72e4710109759fd992852d4&t=1650780086008

打开Jadx 使用sign 作为搜索关键词

一个一个打开看 然后看到第三个的时候可以确定发包的是用的retrofit 类似的包做的请求

请求的是是会把Field中对应的值做为键来编码

我们这里直接邮件0oo0 查找用法

定位如图的函数

最后这个函数调用了 OooO(Integer.valueOf(this.f7810OooO0o0), OooO2, Long.valueOf(currentTimeMillis)), new OooO00o(o00o0002)); 来发送最终的请求

格式和API 中定义的接口请求大体相似

1
2
3
f7810OooO0o0 就是page 的变量名 第一次请求值是1 请求第二页page 值+1 
t = currentTimeMillis
sign = new Oooo000().OooO(byteArray("page=1"+ str(t)))

加密函数就是这个new Oooo000().OooO

验证

我们使用Objection 来做一个快速验证

1
2
3
object -g com.yuanrenxue.challenge explore

android hooking watch class_method o00oO00o.Oooo000.OooO --dump-args --dump-backtrace --dump-return

然后在手机上点击进入第一题的页面触发Hook 函数 我们可以看到结果输入了sign 的值可以拿着这个值和抓的包对下,确定sign 值是这个 ,那也就说明我们找到了这一题的加密函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
com.yuanrenxue.challenge on (google: 8.1.0) [usb] #  android hooking watch class_method o00oO00o.Oooo000.OooO  --dump-args --dump-backtrace --dump-return                                                                                                                
(agent) Attempting to watch class o00oO00o.Oooo000 and method OooO.
(agent) Hooking o00oO00o.Oooo000.OooO([B)
(agent) Registering job 80ugx93pznf. Type: watch-method for: o00oO00o.Oooo000.OooO
com.yuanrenxue.challenge on (google: 8.1.0) [usb] # (agent) [80ugx93pznf] Called o00oO00o.Oooo000.OooO([B)
(agent) [80ugx93pznf] Backtrace:
o00oO00o.Oooo000.OooO(Native Method)
com.yuanrenxue.challenge.fragment.challenge.ChallengeOneFragment.lambda$initListeners$0(proguard-dict.txt:6)
com.yuanrenxue.challenge.fragment.challenge.ChallengeOneFragment.OooOOoo(Unknown Source:0)
o00o0oOO.o000.OooO0OO(Unknown Source:2)
com.scwang.smartrefresh.layout.SmartRefreshLayout$OooO0o.onAnimationEnd(proguard-dict.txt:4)
android.animation.Animator$AnimatorListener.onAnimationEnd(Animator.java:552)
6)


(agent) [80ugx93pznf] Arguments o00oO00o.Oooo000.OooO([object Object])
(agent) [80ugx93pznf] Return Value: 8fb74c67e2a70aa5dd7beec6cbfecad2

RPC封装

我这里尝试过把JAVA 代码放到一个项目里打成jar包来调用,但是o00o0oOO.o000.OooO0OO 这个加密函数在APP 中运行的结果和在JAVA中运行的结果不一致。猜测里面是做了环境检查的,我这里不想正面硬钢了,所以选择使用RPC远程调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// rpc.js
function getsign(arg) {
var res;
Java.perform(function () {
send("start rpc")
const myClass = Java.use("o00oO00o.Oooo000");
const j_string = Java.use('java.lang.String');
const ret = j_string.$new(arg).getBytes('UTF-8');


res = myClass.$new().OooO(ret);


})
return res // rpc 的返回值在 Java.perform 之后外定义,在Java.perform 函数内重新赋值.
}


rpc.exports = {
getsign: getsign
};


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
// server.py
import json

import frida
from flask import Flask, request

hook = open('rpc.js', 'r', encoding='utf-8').read()


def on_message(message, data):
if message['type'] == 'send':
print(f"send message:{message['payload']}")
elif message['type'] == 'error':
print(message['stack'])


process = frida.get_usb_device().attach('com.yuanrenxue.challenge')
script = process.create_script(hook)
script.on('message', on_message)
script.load()

app = Flask(__name__)


@app.route('/getsign', methods=['POST']) # data解密
def decrypt_class():
data = request.get_data()
json_data = json.loads(data.decode("utf-8"))
page = json_data.get("page")
t = json_data.get("t")
s = "page="+str(page) + t
print(s)
res = script.exports.getsign(s)
return res


if __name__ == '__main__':
app.run()

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
// app01 脚本

import json
import time

import requests as requests

headers = {
'user-agent': "Mozilla/5.0 (Linux; U; Android 8.1.0; zh-cn; Nexus 6P Build/OPM7.181105.004) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
"content-type": "application/x-www-form-urlencoded"
}

if __name__ == '__main__':

s = []

for i in range(1, 101):
currentTimeMillis = str(int(round(time.time() * 1000)))
data = {"page": str(i), "t": currentTimeMillis}

sign = requests.post(url='http://127.0.0.1:5000/getsign', json=data).text
data["sign"] = sign

res = requests.post(url='https://www.python-spider.com/api/app1', data=data, headers=headers).text

content = json.loads(res)
s.extend([i['value'].strip('\r') for i in content['data']])
print(s)
print(sum(int(i) for i in s))





猿人学APP01实战
https://kingjem.github.io/2022/07/17/猿人学/猿人学APP01实战/
作者
Ruhai
发布于
2022年7月17日
许可协议