跳转到帖子
View in the app

A better way to browse. Learn more.

网域社区-让世界触手可及

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
欢迎来到网域社区,网域社区以延续互联网共享精神为荣!我们免费分享开心版(破解版)软件、php源码等;

推荐的帖子

发布于

千里之行,始于某瓣,web是从某瓣开始的,app也不忘初心,练手试试{:1_932:}

环境准备
firda:16.5.6
frida-tools:13.6.0
jadx
小黄鸟Reqable(看大佬们自己喜好)
scrcpy
某瓣app 7.5.0
算法助手

一、抓包分析
          直接打开小黄鸟,选择手机协助pc的抓包分析,小黄鸟直接梭哈证书安装挺方便的
               093743m5057mnz9pu5lua0.png
                093933ir5a6zfk4pddkdr5.png
         手机上这样显示就是跑通了(记得抓包的时候打开黄色小飞机),然后我们打开某瓣app
                                    094222xqzo936a5g33ey3k.png
             下拉刷新抓包
                094435tnrg11xg3uzbwdpk.png
                   很明显就可以看到一个抓包信息,以及返回值,这里可以勾选json数据进行过滤
               094555kc9u3y0ehcw9ycho.png
                本次逆向核心是_sig参数,先验验长度
             094710haoe6xmoj6fjfjoz.png
                猜一下可能是hash算法,看到熟悉的=号,可以base64解码测试一下
             095112scccckm144k9049o.png
             base64out了,可以正式干活分析了:dizzy:


二、参数分析
          把apk包丢进jadx中,直接搜索关键参数_sig
                   095245a7sa82faa62vuf1n.png
                   搜索我们发现有一个看起来非常可疑
                   095419pzxilxvxv3fuffhi.png
                   非常熟悉的一个reuest,这是在构造请求体,同时传进去了还有_ts参数,进去分析瞅瞅
                095548w0mwxyyt0tmb0cww.png
                      简单对结构进行分析

 复制代码 隐藏代码
    public Response intercept(Interceptor.Chain chain) {
        Request request = chain.request();
        if (request != null) {
            if (!this.a.contains(request.url().host())) {
                return chain.proceed(request);
            }
            if (!TextUtils.equals(request.method(), "GET")) {
                RequestBody body = request.body();
                if (body != null && (body instanceof ProgressRequestBody)) {
                    request = a(request);
                } else if (body == null || !(body instanceof FormBody)) {
                    if (body != null && (body instanceof MultipartBody)) {
                        request = b(request);
                    }
                } else {
                    Pair<String, String> a = ApiSignatureHelper.a(request);
                    if (a != null) {
                        FormBody formBody = (FormBody) request.body();
                        FormBody.Builder builder = new FormBody.Builder();
                        int size = formBody.size();
                        for (int i = 0; i < size; i++) {
                            String name = formBody.name(i);
                            if (!TextUtils.equals(name, "_sig") && !TextUtils.equals(name, "_ts")) {
                                builder.add(formBody.name(i), formBody.value(i));
                            }
                        }
                        builder.add("_sig", (String) a.first);
                        builder.add("_ts", (String) a.second);
                        FormBody build = builder.build();
                        request = TextUtils.equals(request.method(), com.douban.push.internal.api.Request.METHOD_PUT) ? request.newBuilder().put(build).removeHeader(com.douban.push.internal.api.Request.HEADER_CONTENT_LENGTH).header(com.douban.push.internal.api.Request.HEADER_CONTENT_LENGTH, String.valueOf(build.contentLength())).build() : TextUtils.equals(request.method(), com.douban.push.internal.api.Request.METHOD_DELETE) ? request.newBuilder().delete(build).removeHeader(com.douban.push.internal.api.Request.HEADER_CONTENT_LENGTH).header(com.douban.push.internal.api.Request.HEADER_CONTENT_LENGTH, String.valueOf(build.contentLength())).build() : request.newBuilder().post(build).removeHeader(com.douban.push.internal.api.Request.HEADER_CONTENT_LENGTH).header(com.douban.push.internal.api.Request.HEADER_CONTENT_LENGTH, String.valueOf(build.contentLength())).build();
                    }
                }
            } else {
                Pair<String, String> a2 = ApiSignatureHelper.a(request);
                if (a2 != null) {
                    request = request.newBuilder().url(request.url().newBuilder().setQueryParameter("_sig", (String) a2.first).setQueryParameter("_ts", (String) a2.second).build()).build();
                }
            }
        }
        return chain.proceed(request);


             这段代码是一个拦截器的实现,同时根据请求方式GET,POST的判断进入不同的分支,进行签名校验,很明确我们使用到的请求方式是GET,走的分支应该是
            

 复制代码 隐藏代码
else { 
    Pair<String, String> a2 = ApiSignatureHelper.a(request);
    if (a2 != null) {
        request = request.newBuilder()
            .url(request.url()
                .newBuilder()
                .setQueryParameter("_sig", (String) a2.first)  
                .setQueryParameter("_ts", (String) a2.second)  
                .build()
            )
            .build();
    }
}


                  可以看到_sig参数是从a2中取到的,a2是 ApiSignatureHelper.a(request)产生的,跟进去看看
               100928cgx4x0oi8yac8eoi.png
             这里的返回值继续调用了一个a方法,继续跟一下,发现就在下面一点
           

 复制代码 隐藏代码
    public static Pair<String, String> a(String str, String str2, String str3) {
        String decode;
        if (TextUtils.isEmpty(str)) {
            return null;
        }
        String str4 = FrodoApi.a().e.b;
        if (TextUtils.isEmpty(str4)) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(str2);
        String encodedPath = HttpUrl.parse(str).encodedPath();
        if (encodedPath == null || (decode = Uri.decode(encodedPath)) == null) {
            return null;
        }
        if (decode.endsWith("/")) {
            decode = decode.substring(0, decode.length() - 1);
        }
        sb.append(StringPool.AMPERSAND);
        sb.append(Uri.encode(decode));
        if (!TextUtils.isEmpty(str3)) {
            sb.append(StringPool.AMPERSAND);
            sb.append(str3);
        }
        long currentTimeMillis = System.currentTimeMillis() / 1000;
        sb.append(StringPool.AMPERSAND);
        sb.append(currentTimeMillis);
        return new Pair<>(HMACHash1.a(str4, sb.toString()), String.valueOf(currentTimeMillis));
    }
}


           一眼梭哈过去,可以看到一个HMACHash1,很明显的一个加密方法,跟进去看看
          101147s0s5vlve0x90v9od.png
           看着像是调用的标准库,我们直接hook一下试试,同时打印传入的参数
         

[JavaScript] 纯文本查看 复制代码

?

1

frida -U -f com.douban.frodo -l .\demo_douban.js


         
           

 复制代码 隐藏代码
function main() {
    Java.perform(function () {
        let HMACHash1 = Java.use("com.douban.frodo.utils.crypto.HMACHash1");
        HMACHash1["a"].implementation = function (str, str2) {
            let result = this["a"](str, str2);
            console.log('参数1---->', str)
            console.log('参数2---->', str2)
            console.log('返回值---->', result)
            console.log('---------------')
            return result;
        };
    })
}

setImmediate(main)


          101616aln7u5jwgh52xgg7.png
           可以看到,我们刷新页面请求接口确实调用了这个函数,我们再刷新hook一次和抓包返回的_sig值进行对比
         101843t5q7buzuubgu7bqg.png
            还是很明显,都是一样的,说明他确实是HMACSHA1加密,我们用在线网站测试一下参数的密钥和参数是不是标准的加密方法,记得还要用base64编码处理一下
         102436w1zp5c8r8x9edpn4.png
         
      

 复制代码 隐藏代码
def hmac_sha1_encode(key: str, data: str) -> str:
    key_bytes = key.encode('utf-8')
    data_bytes = data.encode('utf-8')

    hmac_obj = hmac.new(key_bytes, data_bytes, hashlib.sha1)
    hash_result = hmac_obj.digest()

    base64_result = base64.b64encode(hash_result).decode('utf-8')

    return base64_result

print(hmac_sha1_encode('bf7dddc7c9cfe6f7', '自己测试参数'))


          frida一把嗦这个参数就搞定了
         重新换个姿势来试试,算法助手一把嗦,需要刷入lsp
       102721e18zcv1yb8dd9cb8.png
          把这个打开,同时再lsp勾选需要注入的应用
          102808gz55ulfhngoqfltq.png
             当我们进入app是看到这个内容就知道注入成功了,我们继续打开抓包工具,刷新抓包
            102908lub69qsbpufq32hi.png
             复制我们需要的参数的值,到我们算法助手日志中去搜索
          103038amw9rwxmbrwxll3x.png
           如果全部内容搜索不到可以,取前面一部分测试
            103251wkzsylolollczkoz.png
          103508u6mv464d4rm668aa.png
                非常熟悉的内容参数,虽然有着细微的偏差,但是我们可以找到他的调用堆栈直接一把梭哈
             104017quq0qon0vv4783oy.png

参与讨论

你可以现在发布并稍后注册. 如果你有帐户,现在就登录发布帖子.

游客
回帖…

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.