当前位置: 首页 > news >正文

爬虫 APP 逆向 ---> 粉笔考研

环境

  • 粉笔考研 v6.3.15:https://www.wandoujia.com/apps/1220941/history_v6031500
  • 雷电9 模拟器:https://www.ldmnq.com/
  • 安装 magisk:https://blog.csdn.net/Ruaki/article/details/135580772
  • 安装 Dia 插件 (作用:禁用弹窗):https://github.com/Xposed-Modules-Repo/dialog.box

Dia 插件 功能

  • 取消弹窗。(取消 app 的强制升级。例如:得物app、粉笔考研app)
  • 禁用退出(退出+完成)(可以与键控制组合)
  • 按关键字禁用弹出框(悬停窗口+对话框)(可与按键控制结合使用)
  • 版本自定义(版本号+版本名称)
  • 伪装系统时间(系统时间+ GPS时间+本地访问服务器时间)
  • 此模块的反检测,Xposed,Root
  • 启动时禁用网络(可设置禁用时间)
  • 强制结束当前活动(Activity)(通过组合键控制)
  • 反禁用Xposed(简单禁用)

开始 逆向

直接搜索关键字 "major/school_score"

major/major_score

分析后发现是 rsa 加密方式,rsa 是非对称加密,需要一个公钥、一个私钥。

通过 hook 验证,发现 函数a 就是用来解密的。

  • 方法 1:逆向算法,直接 python 实现
  • 方法 2:用 java 实现,然后打包成 jar 包,python 调用 jar 包
  • 方法 3:通过 rpc 方式

RPC 代码实现

下面 通过 rpc 方式实现。

模拟器中执行 frida-server,并设置端口转发。

rcp 方式调用

import time import frida import uvicorn from pathlib import Path from fastapi import FastAPI, Request js_code = """ console.log("Script loaded successfully "); function callDecryptFunc(mi_str) { //定义导出函数 let local_result; Java.perform(function x() { console.log("hook 成功"); console.log(mi_str); let li9 = Java.use("li9"); let result = li9.a(mi_str); console.log(`result ---> ${result}`); local_result = result return result; }); return local_result; } rpc.exports = { // 导出名不可以有大写字母或者下划线 calldecrypyfunc: callDecryptFunc }; """ def my_message_handler(message, payload): print(message) print(payload) def get_session(): device = frida.get_usb_device() time.sleep(2) # 睡眠2秒, 防止程序运行过快从而导致附加不上 session = device.attach("粉笔考研") return session g_session = get_session() script = g_session.create_script(js_code) script.on("message", my_message_handler) script.load() app = FastAPI() @app.get("/decrypt_data") @app.post("/decrypt_data") async def root(request: Request): data_dict = await request.json() encrypt_data = data_dict['encrypt_data'] global script decrypt_data = script.exports_sync.calldecrypyfunc(encrypt_data) ret_val = { 'encrypt_data': encrypt_data, 'decrypt_data': decrypt_data } return ret_val if __name__ == '__main__': uvicorn.run(f'{Path(__file__).stem}:app', host="0.0.0.0", port=6666) pass

执行结果

测试脚本。"Cookie": "换成自己的cookies"

import json import requests requests.packages.urllib3.disable_warnings() headers = { "User-Agent": "fenbi-android", "Host": "schoolapi.fenbi.com", "Cookie": "换成自己的cookies" } def get_decrypt_data(data_dict=None): url = "http://127.0.0.1:6666/decrypt_data" resp_2 = requests.post(url, json=data_dict, verify=False) ret_val = { 'name': data_dict['name'], 'decrypt_data': resp_2.json()['decrypt_data'], } return ret_val def main(): url_1 = "https://schoolapi.fenbi.com/kaoyan/android/kyzz/major/school_score" url_2 = "https://schoolapi.fenbi.com/kaoyan/android/kyzz/major/major_score" college_info = [ {"dm": "10001", "name": "北京大学"}, {"dm": "10003", "name": "清华大学"}, ] for item in college_info: dm = item['dm'] name = item['name'] querystring_1 = { # "school": "10007", "type": "01", "year": "0", "school": dm, "type": "01", "year": "0", # "client_context_id": "2F3B0BCDA482C2DE2D23", # "version": "6.3.15", "vendor": "Huawei", # "app": "kaoyan", "av": "69", "kav": "27", "hav": "4", # "deviceId": "AKJWMHTPfy/pM1yprG3inw==", # "quizId": "0", "imei": "", "oaid": "", } querystring_2 = { "school": dm, "type": "01", "year": "0", "department": "", # "client_context_id": "A28D05AA5A8943BF6D8F", # "version": "6.3.15", "vendor": "Huawei", # "app": "kaoyan", "av": "69", "kav": "27", "hav": "4", # "deviceId": "AKJWMHTPfy/pM1yprG3inw==", # "quizId": "0", "imei": "", "oaid": "", } resp = requests.get(url_1, headers=headers, params=querystring_1, verify=False) resp_json = resp.json() post_data = { 'name': name, 'encrypt_data': resp_json['data'] } data_1 = get_decrypt_data(post_data) resp = requests.get(url_2, headers=headers, params=querystring_2, verify=False) resp_json = resp.json() post_data = { 'name': name, 'encrypt_data': resp_json['data'] } data_2 = get_decrypt_data(post_data) data = { 'name': name, 'school_score': data_1['decrypt_data'], 'major_score': data_2['decrypt_data'] } print(data) if __name__ == '__main__': main() pass

执行结果

查看 app 验证结果

http://www.cnnetsun.cn/news/1351633.html

相关文章:

  • AtomPePacker 开源项目教程
  • 北航论文格式零失误:BUAAthesis模板中图表、公式与代码块的规范使用
  • 从明文暴露到安全存储:Keyring彻底解决Python密码管理痛点
  • PDF4QT命令行工具详解:自动化处理PDF文档的实用技巧
  • java基于微信小程序的陕西省红色旅游管理系统_dkv6632x
  • leetcode153.寻找旋转排序数组中的最小值
  • MySQL数据库初体验
  • 【即插即用完整代码】CVPR 2026新方法归一化空间与通道注意力,无额外参数,轻量且高效,超越CBAM,快速涨点,发表论文!
  • BetterNCM 插件导致网易云音乐启动失败问题分析
  • Camera:实时监控与数据交互的智能设备服务
  • 事件日志清理与痕迹擦除:wmiexec-Pro的eventlog模块深度应用
  • 告别厂商限制:Tuya TH05Z温湿度传感器接入Zigbee2MQTT完全指南
  • 复购率不理想如何用产品线组合提升长期价值
  • SimpleMem快速上手指南:5分钟搭建LLM智能体记忆系统
  • 基于LangChain的RAG与Agent智能体开发 - 使用LangChain调用大语言模型
  • MiniChain与Hugging Face Datasets:实现高效文档嵌入与相似度搜索的完整指南
  • Deepagents数据可视化:展示AI代理工作成果的终极指南
  • LDNetDiagnoService_IOS架构解析:Ping与Traceroute原理在iOS中的实现
  • 突破Ebitengine着色器限制:多重赋值问题的优雅解决方案
  • 告别繁琐切换!Micro编辑器智能文件类型识别功能详解:让编码效率提升300%的秘诀
  • STK信号处理秘籍:BiQuad滤波器与Chorus效果的应用技巧
  • Python入门:Python3 Pickle模块全面学习教程
  • Deepagents边境安全:监控边境活动的AI代理
  • 打造个性化观影系统:embyToLocalPlayer高级设置与自定义技巧
  • Performer-PyTorch核心组件解析:FastAttention如何实现O(n)复杂度
  • 提升家庭影院体验:embyToLocalPlayer与Kodi、Plex联动使用指南
  • cp-ddd-framework扩展机制详解:@Extension注解让业务逻辑灵活扩展
  • cp-ddd-framework架构演进:如何支撑业务系统从单体到微服务
  • 新手必读:Awesome Maintainers项目中的贡献指南与最佳实践
  • 解决Vim用户痛点:vim-quickui让命令交互变得简单直观的5个案例