安卓逆向-泡泡聊天
标签搜索

安卓逆向-泡泡聊天

sana
2024-07-10 / 0 评论 / 8 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年07月10日,已超过329天没有更新,若内容或图片失效,请留言反馈。

一、泡泡聊天

1. 逆向前置

1.1 逆向目标

  • 目标:账号密码登录
  • 版本:v1.7.4
  • 时间:2024-06-10

1.2 简要分析

  • 关于泡泡聊天比较重要的就是抓包证书以及360的壳,直接抓包结果如下:

image-20240610195300609

  • 这是做了服务端证书的校验,所以我们需要拿到证书并且导入到charles,具体步骤如下:
- 获取 bks 或 p12证书 文件
- 获取证书相关密码
- 将证书导入到charles,可以实现抓包(bks格式需要转换p12格式)
- 用requests发送请求时,携带证书去发送请求

2. 逆向实现

2.1 获取证书&密码

  • 使用frida脚本进行hook相应的证书与密码,这是上述步骤的前两步;

image-20240610200823281

  • 可以看到加载了不止一个证书,这是正常的,但是只有一个有密码,所以无疑就是这个了;
  • 那么它的证书就在asset目录下的bks文件;那么此时我们并不知道具体是哪一个bks文件,所以在此直接保存当前获取到的流文件并且保存下来;
Java.perform(function () {
    var KeyStore = Java.use("java.security.KeyStore");
    var String = Java.use("java.lang.String");
    KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (inputStream, v2) {
        var pwd = String.$new(v2);
        console.log('\n--------------------------------------------------------------')
        console.log("密码:" + pwd, this.getType());
        if (this.getType() === "BKS") {
            var myArray = new Array(1024);
            for (var i = 0; i < myArray.length; i++) {
                myArray[i] = 0x0;
            }
            var buffer = Java.array('byte', myArray);
            var file = Java.use("java.io.File").$new("/sdcard/Download/paopao-" + new Date().getTime() + ".bks");
            var out = Java.use("java.io.FileOutputStream").$new(file);
            var r;
            while ((r = inputStream.read(buffer)) > 0) {
                out.write(buffer, 0, r);
            }
            console.log("save success!")
            out.close()
        }
        var res = this.load(inputStream, v2);
        return res;
    };
});
// frida -U -f com.paopaotalk.im -l 2.hook_save.js
  • 在进行这一步之前需将app赋予可读写的权限;随后去观察是否保存成功;

image-20240610203317648

  • 至此证书以及密码我们获取到了;后续就需要导出证书并且导入到charles;

2.2 导出&导入证书

  • 扩展:有时进行hook时会失败,提示找不到类等,是因为有壳导致,可以加上延迟;
  • 此时我们需要将bks证书转换成p12的格式,才支持;使用portecle-1.11工具;

image-20240610211355650

  • 将选择的文件导出,选择p12格式;

image-20240610211444496

  • 此时根据指引输入密码后即可导出;

image-20240610211621584

  • 接下来即可将证书导入到charles中;

image-20240610211818265

  • 选择客户端证书,并且添加;

image-20240610212712887

  • 导入p12证书;

image-20240610212743350

  • 端口等选择所有;

image-20240610212853063

  • 接下来再度抓包即可成功;

image-20240610213101247

2.3 代码实现

  • 具体逻辑比较简单,脱壳使用frida-dumpdex大致实现如下:
frida-dexdump  -U -f 包名
# File:01-泡泡聊天登录实现.py
# Author:下雨天
# Date:2024-06-10 21:50

import hashlib
import random
from requests_pkcs12 import get, post
import json
import uuid
import urllib3

urllib3.disable_warnings()


def md5_hash(password):
    md5_obj = hashlib.md5()
    md5_obj.update(password.encode('utf-8'))
    md5_hash = md5_obj.hexdigest()
    return md5_hash


def double_md5_hash(password, openId):
    # 第一次MD5加密
    md5_obj = hashlib.md5()
    md5_obj.update(password.encode('utf-8'))
    first_md5_hash = md5_obj.hexdigest()

    # 将第一次MD5加密结果与openId和"MOSGRAM"拼接起来
    combined_string = first_md5_hash + openId + "MOSGRAM"
    # 第二次MD5加密
    md5_obj = hashlib.md5()
    md5_obj.update(combined_string.encode('utf-8'))
    second_md5_hash = md5_obj.hexdigest()
    return second_md5_hash


def login():
    password = "aa12121323"
    openId = "008615222225555"
    timestamp = random.randint(100, 999)
    headers = {
        "bundle_id": "com.paopaotalk.im",
        "version": "1.7.4",
        "timestamp": str(timestamp),
        "sign": md5_hash(str(timestamp)),
        "app_id": "qiyunxin",
        "Accept-Language": "zh-CN",
        "package": "com.paopaotalk.im",
        "Content-Type": "application/json; charset=UTF-8",
        "Host": "api.vvchat.im",
        "User-Agent": "okhttp/4.8.1"
    }
    url = "https://api.vvchat.im/userservices/v2/user/login"
    device_id = md5_hash(str(uuid.uuid4()).replace("-", ""))
    data = {
        "device_type": "app",
        "username": openId,
        "password": double_md5_hash(password, openId),
        "device_id": device_id,
        "device_name": "Google Pixel 2 XL",
        "device_model": "Pixel 2 XL"
    }
    data = json.dumps(data, separators=(',', ':'))
    response = post(url, headers=headers, data=data,
                    pkcs12_filename='Client.p12',
                    pkcs12_password='111111',
                    verify=False
                    )

    print(response.text)
    print(response)


if __name__ == '__main__':
    login()
  • 值得注意的就是发送请求时依然需要携带证书;
0

评论 (0)

取消