From 690399eb82520cfa371064fec45286472bdabb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=83=B3=E8=A6=81=E6=88=90=E4=B8=BARapStar=E5=90=97?= =?UTF-8?q?=EF=BC=9F?= Date: Wed, 23 Sep 2020 19:46:23 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=B8=80=E7=82=B9=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E5=AE=9E=E7=8E=B0=E7=9A=84=E5=B0=8F=E6=80=9D=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 92 +++++++++++++++++++++ apps/kuaiduizuoye.js | 36 +++++++++ config.py | 18 +++++ main.py | 187 +++++++++++++++++++++++++++++++++++++++++++ parse.js | 36 +++++++++ 5 files changed, 369 insertions(+) create mode 100644 .gitignore create mode 100644 apps/kuaiduizuoye.js create mode 100644 config.py create mode 100644 main.py create mode 100644 parse.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6afa9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,92 @@ +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea +.idea/ +.idea/* +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ +node_modules/* +*.pyc +.vscode/* +package-lock.json \ No newline at end of file diff --git a/apps/kuaiduizuoye.js b/apps/kuaiduizuoye.js new file mode 100644 index 0000000..9fd0d26 --- /dev/null +++ b/apps/kuaiduizuoye.js @@ -0,0 +1,36 @@ +var result = null; +function decrypt_data(data) { + Java.perform(function () { + var rc_class = Java.use("com.baidu.android.common.security.RC4"); + var func_class = Java.use("com.kuaiduizuoye.scan.utils.a.a"); + var func_instance = func_class.$new(); + var stringClass = Java.use("java.lang.String"); + var stringInstance = stringClass.$new("4edb41c838d8a5c38b772854523658ea6a5f03f2d598caf9f8062e2d5f8d0ed16cc5f1e130de80b44a740cf8e01fdfc931cd22941af1d9898bc5ba70303e73a5"); + var rc = rc_class.$new(stringInstance); + result = func_instance.a(data, rc, false); + }); + return result; +} +function generate_url(origin_url) { + Java.perform(function () { + var search_common_api = Java.use("com.kuaiduizuoye.scan.common.net.model.v1.SearchCommonApi$Input"); + var input_base = search_common_api.$new(origin_url); + var func_class = Java.use("com.baidu.homework.common.net.Net"); + result = func_class.appendSign(input_base); + }); + return result; +} +function encrypt_data(grade, subject, versionId, term, text, isHitDayup, bookType, isHitPay, dataType, pn, rn) { + Java.perform(function () { + var func_class = Java.use("com.kuaiduizuoye.scan.base.f"); + var search_search_api = Java.use("com.kuaiduizuoye.scan.common.net.model.v1.SearchSearch$Input"); + var input_base = search_search_api.$new(grade, subject, versionId, term, text, isHitDayup, bookType, isHitPay, dataType, pn, rn); + result = func_class.a(input_base); + }); + return result; +} +rpc.exports = { + decryptData: decrypt_data, + generateUrl: generate_url, + encryptData: encrypt_data +} \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..6202a60 --- /dev/null +++ b/config.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +''' +@File : config.py +@Time : 2020/09/22 18:57:19 +@Author : Lateautumn4lin +@Version : 1.0 +@Contact : Lateautumn4lin +@License : (C)Copyright 2020 +@Desc : None +''' + +apps_config = [ + { + "package_name": "com.kuaiduizuoye.scan", + "script_name": "kuaiduizuoye.js" + } +] diff --git a/main.py b/main.py new file mode 100644 index 0000000..d7a34cc --- /dev/null +++ b/main.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +''' +@File : app.py +@Time : 2020/09/22 18:52:11 +@Author : Lateautumn4lin +@Version : 1.0 +@Contact : Lateautumn4lin +@License : (C)Copyright 2020 +@Desc : None +''' +from pydantic import BaseModel, Field, create_model +from typing import NewType +from ast import * +import types +from fastapi import APIRouter +import subprocess +import time +import frida +from frida import ServerNotRunningError +from fastapi import FastAPI +from loguru import logger +import execjs + +app = FastAPI() +_parse_path = "parse.js" +_package_name = "com.kuaiduizuoye.scan" +_frida_js_path = f"apps/kuaiduizuoye.js" + + +def get_app_info(): + with open(_parse_path, encoding="utf-8") as f, open(_frida_js_path, encoding="utf-8") as f1: + ctx = execjs.compile(f.read()) + result = ctx.call( + 'parse', f1.read() + ) + return result + + +def on_message(message, data): + if message['type'] == 'send': + logger.info("[**] {0}".format(message['payload'])) + else: + logger.info(f"log:{message}") + + +def detect_frida_state(): + logger.info("Begin Detect Frida State") + shell = 'adb shell su -c "ps -ef|grep frida"' + p = subprocess.Popen( + shell, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + stdout, stderr = p.communicate() + stdout = stdout.decode('utf-8') + return ("frida-server" in stdout) + + +def start_frida_server(): + logger.info("Begin Start Frida Server") + shell = 'adb shell su -c "./data/local/tmp/frida-server &"' + subprocess.Popen( + shell, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + + +if not detect_frida_state(): + # 启动frida-server,增加延迟防止附加失败 + start_frida_server() + time.sleep(3) +session = frida.get_usb_device().attach(_package_name) +script = session.create_script( + open(_frida_js_path, encoding="utf-8").read() +) +script.on('message', on_message) +logger.info('[*] Start attach') +script.load() +Url = create_model("User", **{"origin_url": "asdasd"}) +function_ast = FunctionDef( + lineno=2, + col_offset=0, + name='generate_url', + args=arguments( + args=[ + arg( + lineno=2, + col_offset=17, + arg='item', + annotation=Name(lineno=2, col_offset=22, + id='Url', ctx=Load()), + ), + ], + posonlyargs=[], + vararg=None, + kwonlyargs=[], + kw_defaults=[], + kwarg=None, + defaults=[], + ), + body=[ + Expr( + lineno=3, + col_offset=4, + value=Call( + lineno=3, + col_offset=4, + func=Name(lineno=3, col_offset=4, + id='print', ctx=Load()), + args=[Name(lineno=3, col_offset=10, + id='Url', ctx=Load())], + keywords=[], + ), + ), + Assign( + lineno=4, + col_offset=4, + targets=[Name(lineno=4, col_offset=4, + id='res', ctx=Store())], + value=Call( + lineno=4, + col_offset=10, + func=Attribute( + lineno=4, + col_offset=10, + value=Attribute( + lineno=4, + col_offset=10, + value=Name(lineno=4, col_offset=10, + id='script', ctx=Load()), + attr='exports', + ctx=Load(), + ), + attr='generate_url', + ctx=Load(), + ), + args=[ + Attribute( + lineno=4, + col_offset=38, + value=Name(lineno=4, col_offset=38, + id='item', ctx=Load()), + attr='origin_url', + ctx=Load(), + ), + ], + keywords=[], + ), + ), + Return( + lineno=5, + col_offset=4, + value=Name(lineno=5, col_offset=11, id='res', ctx=Load()), + ), + ], + decorator_list=[], + returns=None, +) +module_ast = Module(body=[function_ast], type_ignores=[]) +module_code = compile(module_ast, "<>", "exec") +function_code = [ + c for c in module_code.co_consts if isinstance(c, types.CodeType)][0] +test = types.FunctionType( + function_code, + { + "script": script, + "Url": Url, + "print": print + } +) +test.__annotations__ = {"item": Url} + +router = APIRouter() +for k, v in get_app_info().items(): + router.add_api_route("/generate_url", test, methods=["POST"]) + break +app.include_router(router) + + +# @app.post('/generate_url') +# def generate_url(item: c): +# res = script.exports.generate_url(item.origin_url) +# return res diff --git a/parse.js b/parse.js new file mode 100644 index 0000000..d6016ee --- /dev/null +++ b/parse.js @@ -0,0 +1,36 @@ +const babel = require('@babel/core') +var exports = new Map(); +var functions = new Map(); +var result = new Map(); +function parse(code) { + let visitor = { + // 处理exports节点,获取导出函数对应表 + ExpressionStatement(path) { + let params = path.node.expression.right; + try { + params = params.properties + for (let i = 0; i < params.length; i++) { + exports.set(params[i].value.name, params[i].key.name) + } + } catch { + + } + }, + // 处理function,获取函数名以及对应参数 + FunctionDeclaration(path) { + let params = path.node; + functions.set(params.id.name, params.params.length) + } + } + babel.transform(code, { + plugins: [ + { + visitor + } + ] + }) + exports.forEach(function (value, key, map) { + result.set(value, functions.get(key)) + }) + return Object.fromEntries(result); +} \ No newline at end of file