APP逆向 day17某站逆向 part2

本章节只用来教学,无其他不良指引 

一.前言

今天还是上次的那个接口 https://api.bilibili.com/x/report/click/android2

我们今天需要破解的是请求头中的buvid  session_id  fp_local,这三个值,这三个值的破解还是有点难度的,所以今天我会尽可能的讲的详细一点,希望大家都能够听得懂

二.buvid破解

   我们把让b站反编译之后,搜索report/click/android2,可以看到一个,然后我们带你进去

我们看到@RequestInterceptor,很能理解,请求头都是在拦截器中进行处理的,我们点进蓝七七里面的方法 

很明显,这里我们还看不出来啥,但是杜义熙逻辑就知道,获得一个对象方法,然后e(h)对这个进行添加方法,那我们进一下e看看怎么个回事。

发现了这些东西,我们就确定了位置

我们点进去 

我们点进来,发现这个个回事,就要一步步查找用例,这里我给出hook查看全部调用栈的代码 

import frida
import sys

rdev = frida.get_remote_device()
pid = rdev.spawn(["tv.danmaku.bili"])
session = rdev.attach(pid)

scr = """
Java.perform(function () {
    var c = Java.use("com.bilibili.api.c");

    c.b.implementation = function(arg0){   
       console.log("buvid=",arg0);
       console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
       this.b(arg0);
    }
});
"""
script = session.create_script(scr)


def on_message(message, data):
    print(message, data)


script.on("message", on_message)
script.load()
rdev.resume(pid)
sys.stdin.read()

hook结果 ,这个就是全部调用栈信息了,方便我们筛选

  

我们最后一直查找用例,找到下面这个位置

这个str就是buvid了 ,那我们就是得找到这个this.a了,然后我们往下找找,可以发现

第一和第二个,他是从xml中拿,怎么判断的呢,因为点击去多几次可以看见SharedPreferences,这个就是之前和大家说的xml持久化存储 ,我们就可以知道,生成位置是在第三个uperCase里,那我们重点是看这个,我们点进去

可以发现这个就是生成逻辑,我这里先说一下生成逻辑

e是区字符串中2,12,22的位置

14 buvid生成方案四种--e是取字符串的2,12和22个位置值
    1 "XZ" + e(d) + d;
        XZ+e(手机状态字符串md5签名)+手机状态字符串md5签名
    2 "XY" + e(d4) + d4;
        XY+e(mac地址字符串md5签名)+mac地址态字符串md5签名
    3 "XX" + e(d4) + d4;
        XX+e(安卓id-md5签名)+安卓id-md5签名
    4 "XW" + e(replace) + replace;
        XW+e(uuid)+uuid 

我就带大家看一下最简单的XW开头的,因为XW开头的python最好模拟

 点进来就发现是uuid了,然后外面就是把uuid的-替换成空。

至此,buvid已经可以任意选择四套逻辑进行破解,这里我给出源代码 

import uuid
import hashlib
import random

# mac地址生成方案
def create_random_mac(sep=":"):
    # 00:90:4C:11:22:33
    data_list = []
    for i in range(1, 7):
        part = "".join(random.sample("0123456789ABCDEF", 2))
        data_list.append(part)
    mac = sep.join(data_list)
    return mac


def get_buvid_by_wifi_mac():
    mac = create_random_mac()
    md5 = hashlib.md5()
    md5.update(mac.encode('utf-8'))
    v0_1 = md5.hexdigest()
    return "XY{}{}{}{}".format(v0_1[2], v0_1[12], v0_1[22], v0_1).upper()


if __name__ == '__main__':
    buvid = get_buvid_by_wifi_mac()
    print(buvid)



###uuid方案
# u=str(uuid.uuid4()).replace('-','')
# print("XW{}{}{}{}".format(u[2], u[12], u[22], u).upper())

至此,buvid破解完成

三.session_id破解

我们回到开始的位置

我们点到头发现下面这个

 发现这个是一个接口,没有具体实现,那我们就要在外面找,我们退一层

发现这个就是赋值的地方,那我们是不是需要hook一下o函数,找到b的具体类,再去找这个类里面找到getSessionId的具体实现方法,这里给出hook代码

import frida
import sys

rdev = frida.get_remote_device()
pid = rdev.spawn(["tv.danmaku.bili"])
session = rdev.attach(pid)

scr = """
Java.perform(function () {
    var a = Java.use("com.bilibili.api.a");

    a.o.implementation = function(arg0){   
       console.log("obj=",arg0);
       console.log("obj=",JSON.stringify(arg0));
       this.o(arg0);
    }
});
"""
script = session.create_script(scr)


def on_message(message, data):
    print(message, data)


script.on("message", on_message)
script.load()
rdev.resume(pid)
sys.stdin.read()

JSON.stringfy就是打印出具体类型 

 可以看到:是com.bilibili.api.a$b接口类型
 具体类型是:tv.danmaku.bili.utils.p$a 

$符号代表的是p里面还有个a的子类

那我们就搜索这个类名

发现这个就是要找的位置,我们就接着点进去

发现还是一个接口,那我们就得确定 com.bilibili.lib.foundation.e.b()的具体类型,那我们们点进去

hook这个的返回值类型,这里给出hook代码

import frida
import sys

rdev = frida.get_remote_device()
pid = rdev.spawn(["tv.danmaku.bili"])
session = rdev.attach(pid)

scr = """
Java.perform(function () {
    var e = Java.use("com.bilibili.lib.foundation.e");

    e.b.implementation = function(){   
        var res = this.b();
       console.log("res=",JSON.stringify(res));
       return res;
    }
});
"""
script = session.create_script(scr)


def on_message(message, data):
    print(message, data)


script.on("message", on_message)
script.load()
rdev.resume(pid)
sys.stdin.read()

结果发现疯狂打印,但是实际只有一个,那我们就搜索 com.bilibili.lib.foundation.DefaultApps

搜到的结果点进去,然后找到getsessionid

这个的逻辑就是,去xml中找,找不到的话就用l,那我们就去找l

这个代码可以直接把中间的打印日志去掉,丢给gpt 

清晰明了

这里给出生成代码

# python 3.9及以后
import random
session_id = "".join([hex(item)[2:] for item in random.randbytes(4)])
print(session_id)


# python 3.8及以前
import random
session_id = "".join([hex(random.randint(0,255))[2:] for i in range(4)])
print(session_id)

四.fp_local破解

Tips:这个是今天的重点和难点

我们再次回到最开始的位置

点进去

发现还是接口,那么就要回到上一步,还得是之前的额方法咯,但是很巧的是,b的类型我们已经确定过了 tv.danmaku.bili.utils.p$a 

那我们再去搜索,点进去找到里面的F

然后一直点,找到这

 我们发现理论上返回的就是a,那我们往上找,发现找不到a,看到下面这一大坨

 我们就要猜测是新版的反编译不全,那我们就用老版的试试

0.老版jadx加内存

这里我单独加个教大家 老版本的jadx加内存

 jadx-gui 反编译app的时候内存不足
1.使用记事本或者notpad++打开jadx-gui.bat
2.找到 set DEFAULT_JVM_OPTS="-Xms128M" "-Xmx4g"
3.将其修改为 set DEFAULT_JVM_OPTS="-Xms128M" "-Xmx16g" 后保存就ok了 (你要4g 提升到16g把-Xmx4g改成-Xmx16g)

在老版本里面找到这个

那我们在这里,再点进去

发现我们要破的东西特别多

str2:MiscHelperKt.a(f(str, aVar)) + h() + MiscHelperKt.a(g())
    返回:str2+b(str2)
    # 咱们要破的:
        - f(str, aVar) 
        - h()        
        - g()       
        - MiscHelperKt.a() 
        - b()   

这里我就不一一和大家截图说了,直接给你们代码讲解      

1.f(str, aVar) 

buvid+手机型号+手机品牌 使 用md5签名

private static final byte[] f(String str, a aVar) {
    # 1 传入的aVar 调用a方法,变成字典
    Map<String, String> a2 = aVar.a();
    # 2 e是md5签名,
    # str通过hook得到就是buvid
    # public static final String KEY_PUB_MODEL = "model"; 手机型号
    # band:手机品牌
    return e(str + a2.get(PersistEnv.KEY_PUB_MODEL) + a2.get("band"));
}

2.h()     

时间戳
private static final String h() {
    String format = a.format(new Date(System.currentTimeMillis()));
    return format;
}

3.g() 

随机生成8个字节
private static final byte[] g() {
    # 核心是com.bilibili.commons.e.a(8),传了8进去
    byte[] a2 = com.bilibili.commons.e.a(8);
    return a2;
}

4.b()   

把传入的字符串,按3个数字截取--》转成16进制--》g=0;h=60;i=2

public static final String b(String str) {
    int i;
    i iVar = q.S0(q.n1(0, Math.min(str.length() - 1, 62)), 2);
    # 1 从 iVar中执行方法,拿到了 g  h  i3  三个int数字--》固定的
    int g = iVar.g();
    int h = iVar.h();
    int i2 = iVar.i();
    if (i2 < 0 ? g >= h : g <= h) {
        i = 0;
        while (true) {
            # 2 把传入的字符串,取 g 到 g+2的长度--》本质按上面三个数字做截取
            String substring = str.substring(g, g + 2);
            i += Integer.parseInt(substring, b.a(16));
            if (g == h) {
                break;
            }
            g += i2;
        }
    } else {
        i = 0;
    }
    e0 e0Var = e0.a;
    #3 把字符串按三个数字截取完后--》转成16进制,如果不足2位用0 补齐
    String format = String.format("%02x", Arrays.copyOf(new Object[]{Integer.valueOf(i % 256)}, 1));
    return format;
}

5.MiscHelperKt.a() 

这个就很复杂,来和大家说一下这个

我们点进来到这里,发现又无法跳到声明,原因是用的kotlin写的,jadx反编译不了

我们需要借助另外一款工具GDA(国产)

这里给出下载地址

GDA主页-亚洲首款交互式Android反编译器GDA:一款简洁、轻便、快速的交互式Android反编译分析工具.icon-default.png?t=N7T8http://www.gda.wiki:9090/ 下载好之后,我们拖入,搜索 MiscHelperKt

 发现在这里,return了一个  ArraysKt___ArraysKt.Fe(p0, str, p8, str1, i, str3, p6);

那我们再点进去

再点进去,最后位置在这里 

 到这里我就不带大家读了,直接告诉你们结论 把字符串转成16进制

给出python实现代码

import hashlib
import datetime
import random


def gen_local_v1(buvid, phone_model, phone_band):
    """
    fp_local和fp_remote都是用这个算法来生成的,在手机初始化阶段生成 fp_local,
    :param buvid: 根据算法生成的buvid,例如:"XYBA4F3B2789A879EA8AEEDBE2E4118F78303"
    :param phone_model:  手机型号modal,例如:"Mate 10 Pro"
    :param phone_band:  手机品牌band,在模拟器上是空字符串(我猜是程序员想要写成 brand )哈哈哈哈
    :return:
    """

    def misc_helper_kt(data_bytes):
        data_list = []
        v7 = len(data_bytes)
        v0 = 0
        while v0 < v7:
            v2 = data_bytes[v0]
            data_list.append("%02x" % v2)
            v0 += 1
        return ''.join(data_list)

    data_string = "{}{}{}".format(buvid, phone_model, phone_band)
    hash_object = hashlib.md5()
    hash_object.update(data_string.encode('utf-8'))
    data = hash_object.digest()

    arg1 = misc_helper_kt(data)
    arg2 = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    #arg3 = misc_helper_kt(random.randbytes(8))  # python3.9可以
    arg3 = misc_helper_kt([random.randint(1, 255) for i in range(8)])

    return "{}{}{}".format(arg1, arg2, arg3)


def a_b(arg8):
    v3 = 0
    v4 = 60
    v0_1 = 2
    v5 = 0
    while True:
        v6 = arg8[v3:v3 + 2]
        v5 += int(v6, base=16)
        if v3 != v4:
            v3 += v0_1
            continue
        break
    data = "%02x" % (v5 % 0x100,)
    return data


str2 = gen_local_v1("XYBA4F3B2789A879EA8AEEDBE2E4118F78303", "Mate 10 Pro", "")
fp_local = str2 + a_b(str2)
print(fp_local)

 补充

如有需要学习资料和交流加我绿泡泡

这里插入一条广告(希望理解一下,养家糊口)!!!

有需要逆向需求或者是项目,课设等等,都能找我接单,麻烦大家了

公众号(后续会更新相关文章) 

 期待你的关注

  • 27
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

往日情怀酿做酒

往日情怀酿作酒 感谢你的支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值