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

news2024/12/24 11:23:10

环境:

  • 粉笔考研 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 方式实现。

模拟器中执行 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.coloradmin.cn/o/1951583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端开发知识-vue

大括号里边放键值对,即是一个对象。 一、vue可以简化前端javascript的操作。 主要特点是可以实现视图、数据的双向绑定。 使用vue主要分为三个步骤: 1.javascript中引入vue.js 可以src中可以是vue的网址,也可以是本地下载。 2.在javasc…

地形材质制作(能使地面湿润)

如图,创建一个材质并写以下逻辑 Landscape Layer Blend节点能使在地形模式绘制中有三个选择,根据以上逻辑,Red是原材质,Green是绿色材质也就是草,Blue为水(这个我认为比较重要) Blue的颜色最好为这个 这个节…

董宇辉离职,我一点都不意外!只不过感觉来的太快

下面这张图,是我在半年多前写的一段随笔,没想到来的这么快! 碰巧的是今天中午,在开发者群里有两位老铁自曝,本以为能公司干到老,但公司却不给机会,已经不在是公司员工了。 最近,晓衡…

一些关于颜色的网站

欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 1、中国传统色 2、网页颜色选择器 3、渐变色网站 4、多风味色卡生成 5、波浪生成 6、半透明磨砂框 色卡组合

苍穹外卖01

0. 配置maven (仅一次的操作 1.项目导入idea 2. 保证nginx服务器运行 (nginx.exe要在非中文的目录下) 开启服务: start nginx 查看任务进程是否存在: tasklist /fi "imagename eq nginx.exe" 关闭ngi…

SAPUI5基础知识20 - 对话框和碎片(Dialogs and Fragments)

1. 背景 在 SAPUI5 中,Fragments 是一种轻量级的 UI 组件,类似于视图(Views),但它们没有自己的控制器(Controller)。Fragments 通常用于定义可以在多个视图中重用的 UI 片段,从而提…

【数据结构--排序】

目录 一、排序概述1.1、排序的相关定义1.2、排序用到的结构与函数 二、常见排序算法2.1、冒泡算法(交换顺序)(1)算法(2)性能分析 2.2、简单选择排序(1)算法(2&#xff09…

express连接mysql

一、 安装express npm install express --save二、express配置 //引入 const express require("express"); //创建实例 const app express(); //启动服务 app.listen(8081, () > {console.log("http://localhost:8081"); });三、安装mysql npm i m…

《昇思25天学习打卡营第6天|ResNet50图像分类》

写在前面 从本次开始,接触一些上层应用。 本次通过经典的模型,开始本次任务。这里开始学习resnet50网络模型,应该也会有resnet18,估计18的模型速度会更快一些。 resnet 通过对论文的结论进行展示,说明了模型的功能&…

第2章 编译SDK

安装编译依赖 sudo apt-get update sudo apt-get install clang-format astyle libncurses5-dev build-essential python-configparser sconssudo apt-get install repo git ssh make gcc libssl-dev liblz4-tool \ expect g patchelf chrpath gawk texinfo chrpath diffstat …

springboot促进高等教育可持续发展管理平台-计算机毕业设计源码36141

摘 要 随着全球对可持续发展的日益关注,高等教育作为培养未来领导者和创新者的摇篮,其在推动可持续发展中的角色日益凸显。然而,传统的高等教育管理模式在应对复杂多变的可持续发展挑战时,显得力不从心。因此,构建一个…

stm32入门-----USART串口实现数据包的接收和发送

目录 前言 数据包 1.HEX数据包 2.文本数据包 C编程实现stm32收发数据包 1.HEX数据包的收发 2.文本数据包的收发 前言 前面几期讲解了USART串口发送数据和接收数据的原理,那本期在前面的基础上学习stm32 USART串口发送和接收数据包。本期包括两个项目&a…

数据库作业四

1. 修改 student 表中年龄( sage )字段属性,数据类型由 int 改变为 smallint : ALTER TABLE student MODIFY Sage SMALLINT; 2. 为 Course 表中 Cno 课程号字段设置索引,并查看索引: ALTER TABLE…

Linux系统下非root用户自行安装的命令切换为root权限时无法使用,提示comman not found解决办法

今天在开发的时候遇上了一个问题就是要去我们数据平台中进行数据的提取,数据存储用的是minio,一个MinIO部署由一组存储和计算资源组成,运行一个或多个 minio server 节点,共同作为单个对象存储库。独立的MinIO实例由具有单个 mini…

多区域DNS以及主从DNS的搭建

搭建多域dns服务器: 搭建DNS多区域功能(Multi-Zone DNS)主要是为了满足复杂网络环境下的多样化需求,提高DNS服务的灵活性、可扩展性和可靠性。 适应不同网络环境: 在大型组织、跨国公司或跨地域服务中,网…

微服务安全——SpringSecurity6详解

文章目录 说明SpringSecurity认证快速开始设置用户名密码基于application.yml方式基于Java Bean配置方式 设置加密方式自定义用户加载方式自定义登录页面前后端分离认证认证流程 SpringSecurity授权web授权:基于url的访问控制自定义授权失败异常处理方法授权:基于注解的访问控制…

2024上半年热门网络安全产品和工具TOP10_wiz安全产品

今年上半年,利用生成式人工智能(GenAI)的网络安全工具继续激增。许多供应商正在利用GenAI的功能来自动化安全运营中心(SOC)的工作,特别是在自动化日常活动方面,如收集威胁信息和自动创建查询。 …

element 结合 {} 实现自适应布局

通过el-row el-col 实现 例如 :xl“{ 1: 24, 2: 12, 3: 8, 4: 6 }[tableData.length] || 6” length 1 2 3 4 、代码数量为 1 2 3 4 >4 时不同卡片数量时尺寸的配置

LLM模型之基于MindSpore通过GPT实现情感分类

前言 # 该案例在 mindnlp 0.3.1 版本完成适配,如果发现案例跑不通,可以指定mindnlp版本,执行!pip install mindnlp0.3.1 !pip install mindnlp !pip install jieba %env HF_ENDPOINThttps://hf-mirror.com 导入对应的包 import osimport m…

【因数之和】python求解方法

输入两个整数A和B,求A的B次方的因子和,结果对1000000007取模。 def mod_exp(base, exp, mod):result 1while exp > 0:if exp % 2 1:result (result * base) % modbase (base * base) % modexp // 2return resultdef sum_of_factors(n):total 0…