app逆向入门案例一

app逆向入门案例一

样本

JXU2NjEzJXU3M0VEJTIwNS4xLjA=

目标:实现登录

1、抓包分析

接口:https://m.yiban.cn/api/v4/passport/login

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?在这里插入图片描述

多次抓包发现只有password参数在发生变化。

2、跟踪password参数的生成过程

objection -N -h <ip> -p 57575 -g com.yiban.app explore -P ~/.objection/plugins

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?在这里插入图片描述

用objection的搜索发现okhttp3.

所以直接选择在点击登录之后直接查看okhttp3.Request$Builder的实例

plugin wallbreaker objectsearch okhttp3.Request$Builder

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?在这里插入图片描述
选择最新生成的那个对象并查看
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdmin%5CDesktop%5C%E7%AC%94%E8%AE%B0%5Cimages%5Cimage-20231123180420488.png!%5Bimage-在这里插入图片描述

运气很好发现password参数就在Builder的tags中,我们直接把tag方法 hook住,查看它的调用栈。

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdmin%5CDesktop%5C%E7%在这里插入图片描述

配合静态代码(dexdump将dexdump出并用010去修复文件头https://github.com/hluwa/frida-dexdump)分析password的加密过程在m70.j中

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdmin%5CDesktop%5C%在这里插入图片描述

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?在这里插入图片描述

str:明文密码
JNIHelper.getEncodePwdPublicKey():加密密钥   固定参数 加密密钥 通过hook获取到
WXMediaMessage.DESCRIPTION_LENGTH_LIMIT :1024 固定参数

在这里插入图片描述

3、password加密参数生成

尝试过完全脱机去生成password的加密,但是java的Cipher不支持”RSA/None/PKCS1Padding“,又对这个加密不熟悉无法手撸一个。就自己写了一个app去主动调用这个加密方法,然后通过python rpc生成。

android studio

package com.example.yibanpassword;

import android.os.Build;
import android.util.Base64;

import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

public class EncryptPassword {
    public static String getPassword(String password){
        String JNIHelper = "通过hook获取到的加密密钥";
        int length = 1024;
        String key = "RSA/None/PKCS1Padding";
        return new String(c(j(password.getBytes(), a(JNIHelper.getBytes()), length, key, true)));
    };

    private static byte[] j(byte[] bArr, byte[] bArr2, int i, String str, boolean z) {
        KeyFactory keyFactory;
        Key generatePrivate;
        int i2;
        if (bArr != null && bArr.length != 0 && bArr2 != null && bArr2.length != 0) {
            try {
                if (Build.VERSION.SDK_INT < 28) {
                    keyFactory = KeyFactory.getInstance("RSA", "BC");
                } else {
                    keyFactory = KeyFactory.getInstance("RSA");
                }
                if (z) {
                    generatePrivate = keyFactory.generatePublic(new X509EncodedKeySpec(bArr2));
                } else {
                    generatePrivate = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bArr2));
                }
                if (generatePrivate == null) {
                    return null;
                }
                Cipher cipher = Cipher.getInstance(str);
                if (z) {
                    i2 = 1;
                } else {
                    i2 = 2;
                }
                cipher.init(i2, generatePrivate);
                int length = bArr.length;
                int i3 = i / 8;
                if (z && str.toLowerCase().endsWith("pkcs1padding")) {
                    i3 -= 11;
                }
                int i4 = length / i3;
                if (i4 > 0) {
                    byte[] bArr3 = new byte[0];
                    byte[] bArr4 = new byte[i3];
                    int i5 = 0;
                    for (int i6 = 0; i6 < i4; i6++) {
                        System.arraycopy(bArr, i5, bArr4, 0, i3);
                        bArr3 = i(bArr3, cipher.doFinal(bArr4));
                        i5 += i3;
                    }
                    if (i5 != length) {
                        int i7 = length - i5;
                        byte[] bArr5 = new byte[i7];
                        System.arraycopy(bArr, i5, bArr5, 0, i7);
                        return i(bArr3, cipher.doFinal(bArr5));
                    }
                    return bArr3;
                }
                return cipher.doFinal(bArr);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private static byte[] i(byte[] bArr, byte[] bArr2) {
        byte[] bArr3 = new byte[bArr.length + bArr2.length];
        System.arraycopy(bArr, 0, bArr3, 0, bArr.length);
        System.arraycopy(bArr2, 0, bArr3, bArr.length, bArr2.length);
        return bArr3;
    }

    public static byte[] c(byte[] bArr) {
        if (bArr != null && bArr.length != 0) {
            return Base64.encode(bArr, 2);
        }
        return new byte[0];
    }
    private static byte[] a(byte[] bArr) {
        return Base64.decode(bArr, 2);
    }
}

js

function encryptPassword(inputStr) {
    let result;
    Java.perform(function () {
        var targetClass = Java.use("com.example.yibanpassword.EncryptPassword");
        result = targetClass.getPassword(inputStr)
    })
    return result;
}

rpc.exports = {
    invokemethod01: encryptPassword
}

python

import frida


def message(message, data):
    if message['type'] == 'send':
        print(f"[*] {message['payload']}")
    else:
        print(message)

def rpc_get_password(password):
    session = frida.get_device_manager().add_remote_device('ip:57575').attach('com.example.yibanpassword')
    with open(r"C:\Users\Admin\PycharmProjects\pythonProject\yiban\encryptPassword.js") as f:
        jsCode = f.read()

    # print("加载代码", jsCode)
    script = session.create_script(jsCode)
    script.on("message", message)
    script.load()

    return script.exports.invokemethod01(password)
import time
from time import sleep
import rpcpassword
import requests


def login(encrypt_password):
    headers = {
        "AppVersion": "5.1.0",
        "User-Agent": "YiBan/5.1.0 Mozilla/5.0 (Linux; Android 9; Pixel 3 XL Build/PQ3A.190801.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36",
        "smdid": "BIopBuOyfxs1eV8zgcKMAWevXxUVBNa3yZznRWhGuNUH4r3uKJoARNZlN05qmqSob/V5r3rw2FNp5eWRy0ePl0Q==",
        "Host": "m.yiban.cn"
    }
    url = "https://m.yiban.cn/api/v4/passport/login"
    data = {
        "app": "1",
        "sig": "70c934af06fff8d2",
        "ct": "2",
        "password": encrypt_password,
        "authCode": "",
        "identify": "c4d74c3b-3698-4386-9feb-33df88523e61",
        "v": "5.1.0",
        "mobile": "自己的电话",
        "sversion": "28",
        "device": "Google:Pixel 3 XL",
        "apn": "wifi",
        "token": ""
    }
    response = requests.post(url, headers=headers, data=data)

    print(response.text)
    print(response)

if __name__ == '__main__':
    login(rpcpassword.rpc_get_password("自己的密码"))

4、效果展示

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdmin%5CDesktop%5C%E7%AC%94%E8%在这里插入图片描述

nse = requests.post(url, headers=headers, data=data)

print(response.text)
print(response)

if name == ‘main’:
login(rpcpassword.rpc_get_password(“自己的密码”))


## 4、效果展示

[外链图片转存中...(img-Vxze9yKO-1700735510314)]

至此完成登录拿到了access_token。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值