índice
1. Use o fiddler para capturar o aplicativo
Ao capturar o pacote, você pode ver que X-App-Token: e8f1c71569a7166b6aa9723342923606edc38cb9-c72d-3bc4-8e82-6fd9212d77a00x5fdb0663
cada solicitação nos parâmetros da solicitação muda. Aqui você precisa descompilar o aplicativo para uma análise posterior.
Em segundo lugar, descompile o aplicativo para localizar códigos de chave
Use jadx diretamente para descompilar o aplicativo e localizar o código relevante, procurando por parâmetros relevantes
private String[] createHeaders() {
Locale locale = Locale.getDefault();
String valueOf = String.valueOf(VERSION.SDK_INT);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(locale.getLanguage());
stringBuilder.append(Constants.ACCEPT_TIME_SEPARATOR_SERVER);
stringBuilder.append(locale.getCountry());
String stringBuilder2 = stringBuilder.toString();
String as = AuthUtils.getAS(this.appContext, this.deviceId);
StringBuilder stringBuilder3 = new StringBuilder();
stringBuilder3.append(getAndroidId());
stringBuilder3.append("; ");
stringBuilder3.append(getImeiOrMeid());
stringBuilder3.append("; ");
stringBuilder3.append(getImsi());
stringBuilder3.append("; ");
stringBuilder3.append(getMacAddress());
stringBuilder3.append("; ");
stringBuilder3.append(Build.MANUFACTURER);
stringBuilder3.append("; ");
stringBuilder3.append(Build.BRAND);
stringBuilder3.append("; ");
stringBuilder3.append(Build.MODEL);
String replaceAll = new StringBuilder(Base64.encodeToString(stringBuilder3.toString().getBytes(), 0)).reverse().toString().replaceAll("\\r\\n|\\r|\\n|=", "");
String str = "0";
if (AppHolder.getAppTheme().isNightTheme()) {
str = "1";
} else if (AppHolder.getAppTheme().isAmoledTheme()) {
str = "2";
}
return new String[]{
HttpHeaders.USER_AGENT, this.userAgent, "X-Requested-With", "XMLHttpRequest", "X-Sdk-Int", valueOf, "X-Sdk-Locale", stringBuilder2, "X-App-Id", BuildConfig.APPLICATION_ID, "X-App-Token", as, "X-App-Version", this.appVersionName, "X-App-Code", String.valueOf(this.appVersionCode), "X-Api-Version", EntityListFragment.APK_TYPE_DYH, "X-App-Device", replaceAll, "X-Dark-Mode", str};
}
Analise o código para descobrir que o valor de as é atribuído a X-App-Token,as = AuthUtils.getAS(this.appContext, this.deviceId);
Dica: Quando frida-rpc chama um método remotamente, se o método precisa passar parâmetros, você precisa montar manualmente os parâmetros corretos sozinho.
Conforme mostrado na figura acima, chamar getAS requer a passagem de dois parâmetros context
e str
:
O contexto é montado da seguinte forma:
//拿到context上下文
var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();
O parâmetro str é desconhecido para nós. Podemos realizar operações de gancho comuns em getAS para obter o valor de str.
import frida #导入frida模块
import sys #导入sys模块
jscode = """
Java.perform(function(){
var AuthUtils = Java.use('com.coolapk.market.util.AuthUtils') // 类的加载路径
AuthUtils.getAS.implementation = function(a,b){ // str为getAS的参数,原getAS需要几个参数就写几个
send(a);
send(b); // 这里的b就是str的值
var as = this.getAS(a,b); // 源函数有返回值 这里我们也将得到的返回值return
return as
};
});
"""
def on_message(message,data): #js中执行send函数后要回调的函数
if message["type"] == "send":
print("[*] {0}".format(message["payload"]))
else:
print(message)
process = frida.get_usb_device().attach('com.coolapk.market') # app包名
script = process.create_script(jscode) #创建js脚本
script.on('message',on_message) #加载回调函数,也就是js中执行send函数规定要执行的python函数
script.load() #加载脚本
sys.stdin.read()
Observação: esse método de chamada exige que acionemos manualmente a api para chamar o método getAS para executar nosso script correspondente.
str é um valor fixoedc38cb9-c72d-3bc4-8e82-6fd9212d77a0
Três, frida-rpc chama métodos relacionados
Use este método de chamada para chamar ativamente métodos relacionados para gerar tokens criptografados
import codecs
import frida
import os
def adbforward():
os.system("adb forward tcp:27042 tcp:27042")
os.system("adb forward tcp:27043 tcp:27043")
hook_code = '''
rpc.exports = {
// 函数名gethello
gethello: function(str){
send('heelo');
Java.perform(function(){
//拿到context上下文
var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();
// use 加载的类路径
var AuthUtils = Java.use('com.coolapk.market.util.AuthUtils');
//f = tt.$new();
var sig = AuthUtils.getAS(context, str); // context,str组要自己组装
send(sig);
}
)
}
};
'''
def on_message(message, data):
if message['type'] == 'send':
print(message['payload'])
elif message['type'] == 'error':
print(message['stack'])
process = frida.get_usb_device().attach('com.coolapk.market')
script = process.create_script(hook_code)
script.on('message', on_message)
script.load()
script.exports.gethello('edc38cb9-c72d-3bc4-8e82-6fd9212d77a0')
comparação de tokens
# rpc调用生成的
4bdc740d8fff25d577ed9b28cca6b34cedc38cb9-c72d-3bc4-8e82-6fd9212d77a00x5fdb2185
# 抓包得到的
e8f1c71569a7166b6aa9723342923606edc38cb9-c72d-3bc4-8e82-6fd9212d77a00x5fdb0663
De acordo com este método de chamada ativa, podemos construir um serviço da web para uso externo.
O método de gancho normal exige que disparemos manualmente a execução do método relevante antes de executar o script correspondente.O método rpc pode chamar ativamente o método sem que o disparemos manualmente.