【安卓逆向】WE Learn登录接口iv、pwd参数分析,加密逆向分析

一、抓包与初步分析

首先,使用抓包工具,发现请求体中包含三个关键参数:

account:账号明文

pwd:密文,显然是密码经过加密后的结果

iv:看起来像Base64编码的16字节数据

clientId为固定值welearn_app。

由此推断,pwd是由密码明文经过加密算法(很可能是AES-CBC)生成,iv则是加密时使用的初始向量。

二、逆向脱壳与定位核心代码

  1. 检测加固情况
    使用文件分析工具查看APK,发现被360加固保护。
  1. 脱壳
    使用脱壳工具,得到原始Dex文件。
  1. Jadx分析登录入口
    将脱壳后的Dex导入Jadx,搜索关键词login、pwd等,定位到登录Activity中的核心方法syncSSOLogin。

三、Java加密逻辑还原

syncSSOLogin方法关键代码如下:

java 复制代码
public void syncSSOLogin(String str, String str2) {
    byte[] randomIv = CryptoHelper.getRandomIv();
    String base64 = CryptoHelper.toBase64(randomIv);
    String encrypt = CryptoHelper.encrypt("wtvDPPAE4UmWkcRm", str2, randomIv);
    // ... 构建请求体
}
  1. 生成随机IV
    getRandomIv()实现非常简单:
java 复制代码
public static byte[] getRandomIv() {
    byte[] bArr = new byte[16];
    new SecureRandom().nextBytes(bArr);
    return bArr;
}

生成16字节随机数,作为AES-CBC模式的IV。

  1. AES加密方法
    encrypt方法:
java 复制代码
public static String encrypt(String str, String str2, byte[] bArr) {
    try {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(bArr);
        SecretKeySpec secretKeySpec = new SecretKeySpec(str.getBytes("UTF-8"), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(1, secretKeySpec, ivParameterSpec);
        return Base64.encodeToString(cipher.doFinal(str2.getBytes("UTF-8")), 2);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

算法:AES/CBC/PKCS5Padding

密钥:固定字符串 "wtvDPPAE4UmWkcRm"(转换为字节作为AES密钥)

IV:上面随机生成的16字节

输出:Base64编码的密文

四、Python实现加密与请求

根据上述逻辑,将Java代码转换为Python,使用pycryptodome库完成AES-CBC加密。

python 复制代码
import requests
import os
import base64
from Crypto.Cipher import AES

def encrypt(key_str: str, pwd: str, iv: bytes) -> str:
    """
    执行AES-CBC加密,返回Base64结果
    :param key_str: 密钥字符串
    :param pwd: 明文密码
    :param iv: 16字节IV
    :return: Base64密文
    """
    plaintext = pwd.encode('utf-8')

    # PKCS7填充(AES块大小16字节)
    block_size = AES.block_size
    padding_len = block_size - (len(plaintext) % block_size)
    padding = bytes([padding_len]) * padding_len
    padded_plaintext = plaintext + padding

    # 创建AES-CBC加密器
    cipher = AES.new(key_str.encode('utf-8'), AES.MODE_CBC, iv)
    ciphertext = cipher.encrypt(padded_plaintext)

    return base64.b64encode(ciphertext).decode('ascii')

def wl_login(account: str, password: str):
    # 1. 生成随机IV并转为Base64
    iv = os.urandom(16)
    iv_b64 = base64.b64encode(iv).decode()

    # 2. 加密密码
    encrypted_pwd = encrypt("wtvDPPAE4UmWkcRm", password, iv)

    # 3. 构造请求参数
    url = "https://courseappserver.sflep.com/epapi/api/Ids/SsoLoginEncrypt"
    payload = {
        'iv': iv_b64,
        'clientId': "welearn_app",
        'account': account,
        'pwd': encrypted_pwd
    }
    headers = {
        'User-Agent': "okhttp/4.12.0",
        'Connection': "Keep-Alive",
        'Accept-Encoding': "gzip",
        'platform': "Android_WeLearn_9.2.0416(309)_sdk36",
    }

    # 4. 发起请求
    response = requests.post(url, data=payload, headers=headers)
    print(response.text)

if __name__ == "__main__":
    wl_login("your_account", "your_password")

五、运行测试

将代码中的账号密码替换为真实值,运行后输出服务器返回的JSON,说明加密逻辑正确,接口调用成功。

python 复制代码
{
  "data": {
    "isVerify": "1",
    "openId": "9xxxxxxx7",
    "displayAccount": "1xxxxxxx4",
    "realName": "xxxxxx",
    "role": "0",
    "phone": "1xxxxxxx4"
  },
  "status": 0,
  "msg": null
}
相关推荐
Slow菜鸟1 小时前
Java 开发环境安装指南(7) | Nginx 安装
java·开发语言·nginx
zhangchaoxies1 小时前
如何配置Oracle 19c JSON存储_环境要求与自动类型映射
jvm·数据库·python
沐苏瑶1 小时前
Java反序列化漏洞
java·开发语言·网络安全
进击的荆棘1 小时前
C++起始之路——用哈希表封装myunordered_set和myunordered_map
开发语言·c++·stl·哈希算法·散列表·unordered_map·unordered_set
BU摆烂会噶1 小时前
【工作流的常见模式】LangGraph 常用模式:路由模式(条件分支)
数据库·人工智能·python·langchain
心.c1 小时前
大厂高频手写题
开发语言·前端·javascript
qq_413502022 小时前
AWS CodeBuild 配置 PHP 8.0 运行时的正确方法
jvm·数据库·python
guslegend2 小时前
AI生图第2节:python对接gpt-image-2模型API生图
开发语言·python·gpt
原来是猿2 小时前
Linux线程同步与互斥(四):日志系统与策略模式
linux·运维·开发语言·策略模式