首页
社区
课程
招聘
5
[原创]TK抓包协议分析:So层逆向(底部交流群)
发表于: 2025-3-16 17:07 4025

[原创]TK抓包协议分析:So层逆向(底部交流群)

2025-3-16 17:07
4025

前言

  • 免责声明: 本文内容仅供学习交流使用,请勿用于其他用途,如因使用本文内容而产生任何问题,与作者无关,如果本文侵害贵司权益,请联系 zhiyitracer@163.com 下架处理

  • 第一章搞定了,Tk的上网环境,但是还不能抓包,因为Tk系的协议不太一样用的是QUIC协议

    图片描述

  • 那我们今天的目标是绕过这个协议,让他降级,实现我们成功抓包的目的(还有会番外篇非降级抓包,可以小小期待一下)

  • 文章尽量还原每一步,尽可能写的详细,如果还是有问题可以加我微信沟通 Alive0501

环境

  • 手机
    • 小米8
  • 版本
    • 33.4.3

准备

  • 抓包失败

    图片描述

  • 开了抓包以后APP直接是网络无连接了,并且抓到的请求也是不正常的,下面我们就开始定位

日志分析

  • 可以从日志分析入手,看看有没有什么异常的报错

  • 使用 adb logcat 命令查看

    • 图片描述

      1. 先用 adb logcat -c 请一下日志
      2. 执行 adb logcat 命令
      3. 然后打开tiktok app
      4. 出现发生错误这个页面后停止 adb locat 命令
      5. 把文件保存到本地进行分析
  • 定位(整个日志中,只有此处有发送网络请求而且失败,所以一眼定位这里)

    图片描述

  • 日志里找到有一个请求URL的位置并且有一个|Exception in CronetUrlRequest 像是网络错误的请求所以分析一下这个位置

jadx反编译

  1. 拿上面的这个Exception in CronetUrlRequest 去jdax里进行搜索

    图片描述

  2. 上面搜索到三个位置,后两个是同一个函数,所以hook一下这两个地方,打印下堆栈

    图片描述

  3. 直接右键复制为Frida片段就可以了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Java.perform(function () {
        let g = Java.use("org.chromium.g");
        g["LIZ"].implementation = function (i, i2, str) {
            console.log(`g.LIZ is called: i=${i}, i2=${i2}, str=${str}`);
            let result = this["LIZ"](i, i2, str);
            console.log(`g.LIZ result=${result}`);
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
            return result;
        };
     
        let CronetUrlRequest = Java.use("com.ttnet.org.chromium.net.impl.CronetUrlRequest");
        CronetUrlRequest["onError"].implementation = function (i, i2, i3, str, j) {
            console.log(`CronetUrlRequest.onError is called: i=${i}, i2=${i2}, i3=${i3}, str=${str}, j=${j}`);
            this["onError"](i, i2, i3, str, j);
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
        };
    })
  4. 打印结果

    图片描述

  5. 上面图片可以看到所有的打印都是走的CronetUrlRequest.onError这个函数,他是一个Native层的方法 那下面来分析一下So层

定位So

  • 直接把app文件 重命名一下 .apk 改为.zip

  • 直接找到tiktok-33-4-3.zip\lib\arm64-v8a 这里面有很多so文件,怎么定位到我们需要的so文件那

  • 我这里使用grep进行搜索

    grep的作用就是搜索到我们要定位的字符串在那个So文件里

    grep的安装可以看这个 https://blog.csdn.net/qq_29752857/article/details/140169107

  1. 把.apk后缀改为.zip

  2. 压解到一个文件夹

  3. cd 到解压文件夹的 \lib\arm64-v8a目录 打开cmd

  4. 执行命令 grep -r "CronetUrlRequest" *

    图片描述

  5. 显示在 libsscronet.so 这个So文件中

分析So

  • \lib\arm64-v8a下面找到libsscronet.so这个文件丢到ida64中

    图片描述

  • 按Shift F12 字符串搜索 CronetUrlRequest

    图片描述

  • 点击com/ttnet/org/chromium/net/impl/CronetUrlRequest

    图片描述

  • 进入sub_190028+8↓o 然后按tab键

    图片描述

  • Hook下这个位置 190028

    • 这里为了方便注释Hook的代码,拆分开写的

    • 主函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      function hook_dlopen(module_name, fun) {
          var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
          if (android_dlopen_ext) {
              Interceptor.attach(android_dlopen_ext, {
                  onEnter: function (args) {
                      pass
                  },
                  onLeave: function (retval) {
                      passa
                  }
              });
          }
      }
       
      function main() {
          hook_dlopen("libsscronet.so")
      }
      setImmediate(main)
      • 注释
        • Hook android_dlopen_ext 监控 .so 加载
        • 判断是不是我们要hook的"libsscronet.so"文件
    • onEnter

      1
      2
      3
      4
      5
      6
      7
      8
      9
      onEnter: function (args) {
          var pathptr = args[0];  // 读取第一个参数,它是一个指向动态库路径的指针
          if (pathptr) {  // 确保指针有效,避免 `null` 访问错误
              this.path = pathptr.readCString();  // 将指针解析成字符串,得到动态库的路径
              if (this.path.indexOf(module_name) >= 0) {  // 判断路径是否包含目标库名
                  this.canhook = true// 发现目标库,标记 `this.canhook = true`,表示可以 hook
              }
          }
      }
      • 注释
        1. onEnter 在目标函数(android_dlopen_ext)执行前触发
        2. 如果路径是否包含目标库名,把canhook设置为True
    • onLeave

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      onLeave: function (retval) { 
          if (this.canhook) {  // 只有在 onEnter 里确认目标库被加载了,才会执行 hook 逻辑
              let base_libsscronet = Module.findBaseAddress("libsscronet.so");  // 获取 libsscronet.so 的基地址
              let sub_190028 = base_libsscronet.add(0x190028);  // 计算 sub_190028 函数的地址(基地址 + 偏移 0x190028)
              console.log("hook", sub_190028);  // 打印目标函数的地址,便于调试
              Interceptor.attach(sub_190028, {  // Hook 目标函数
                  onEnter(args) { 
                      console.log("sub_190028 called, return address:", DebugSymbol.fromAddress(this.returnAddress)); 
                      // `this.returnAddress` 是调用 `sub_190028` 的上层函数的返回地址
                      // `DebugSymbol.fromAddress` 解析该地址,尝试获取调用 `sub_190028` 的函数名
                  }
              });
          }
      }
      • 注释

        1. this.returnAddress 返回的是调用者的地址

        2. DebugSymbol.fromAddress 是为了把地址转为函数名

  • 打印结果

    图片描述

  • 按G跳转到 0x18fae8

    图片描述

    • 偏移量在V16上
  • 点进 sub_18209C这个函数

    图片描述

    • .cc 文件是 C++ 源代码文件,它的作用与 .cpp 文件相同,通常用于存放 C++ 代码
  • 跟进sub_182194

    图片描述

  • 看到这块信息Hook一下 sub_20E814

    可以看到第二个是一个String ,所以hook代码要修改一下把第二个参数String也打印

    onLeave: function (retval) {  
        if (this.canhook) {  
            let base_libsscronet = Module.findBaseAddress("libsscronet.so");  
            let sub_20E814 = base_libsscronet.add(0x20E814);  
            console.log("hook", sub_20E814);  
            Interceptor.attach(sub_20E814, {  
                onEnter(args) {  
                    console.log("sub_20E814 called, return address:",args[1].readCString()), DebugSymbol.fromAddress(this.returnAddress);  
                }
            });
        }
    }
    
  • 输出结果

    图片描述

    • 这个地方明显不是,app已经出现连接失败了,后面过了一会才有的打印,说明这个打印已经晚了,如果是我们想要的位置,他应该在检测中进行打印
    • 上面的输出其实并没有什么有效信息

    分析引用

  • 那这样我们线索已经断了,先按X看一下这个函数都是有谁引用了 看看有没有线索

    图片描述

    • 看了半天感觉也没啥有用的东西,那下面我们整理一下已经有信息,来猜测一下,后续怎么进行

      1. 那目前我们根据已经有信息,看到代码里有用 ../../net/tt_net/ipc/ipc_channel_reader.cc" 这样的方式去写的

      2. 那可以猜测,我们真正需要Hook的那个函数也有可能是这样加载的,可以字符串搜索一下../../

        图片描述

      3. 看到有几个目录net base components等,我们是为了解决抓包问题,那很有可能是在net目录下(network),那来搜索一下 ../../net

        图片描述

      4. 还是比较多的,这个时候可以有选择的看一下,跟网络请求相关的quic ssl

      5. 最后定位到是 ../../net/socket/ssl_client_socket_impl.cc这个文件

        图片描述

    • 然后点进来看一下

      图片描述

    • hook下 20E7EC

      图片描述

    • 看到有一个叫HandleVerifyResult比较像,我们跳进去看一下0x3174dc

      • 如果打印不出 HandleVerifyResult 可以换个手机试下

      图片描述

    • 然后向上滑找到函数看一下是谁在引用, 因为怀疑这一段是 错误处理 所以先看一下谁调用他

      图片描述

    • 这里有两个点那个都可以,最后都是一个位置,进来之后继续找到函数看一下引用

      图片描述

    • 接着点进去,进去以后看到很多个函数都点开看一下,最后定位到sub_4913E8()上面

      图片描述

      图片描述

    • 我们可以搜一下SSL_CTX_set_custom_verify这是什么意思

      图片描述

      图片描述

    • 第三个参数是一个回调函数,这里他的返回值1表示成功,0表示失败,那我们手动把他Hook为0,让他不支持quic协议,实现降级

    • 就能成功绕过校验了~!

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      function hook_dlopen(module_name, fun) {
          var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
          if (android_dlopen_ext) {
              Interceptor.attach(android_dlopen_ext, {
                  onEnter: function (args) {
                      var pathptr = args[0];
                      if (pathptr) {
                          this.path = (pathptr).readCString();
                          if (this.path.indexOf(module_name) >= 0) {
                              this.canhook = true;
                          }
                      }
                  },
                  onLeave: function (retval) {
                      if (this.canhook) {
                          let verifyadd = Module.getExportByName("libsscronet.so","SSL_CTX_set_custom_verify");
                          Interceptor.attach(verifyadd,{
                              onEnter(args){
                                  Interceptor.attach(args[2],{
                                      onLeave(retval){
                                          retval.replace(0x0);
                                      }
                                  })
                              }
                          })
                      }
                  }
              });
          }
      }

成功搞定

  • 图片描述

  • 就可以抓包并且上TikTok了

交流群

  • 建立了一个爬虫交流群,如果有兴趣进群的话可以加我好友 拉你进群

[注意]看雪招聘,专注安全领域的专业人才平台!

最后于 2025-3-16 18:55 被知一Tracer编辑 ,原因: 修复图片
收藏
免费 5
支持
分享
赞赏记录
参与人
雪币
留言
时间
ggeo
你的分享对大家帮助很大,非常感谢!
2025-3-30 22:07
mb_fvscuabi
+1
谢谢你的细致分析,受益匪浅!
2025-3-29 10:56
git_94581yydeepfam
为你点赞!
2025-3-25 09:34
mb_zpyvrtaq
你的帖子非常有用,感谢分享!
2025-3-17 16:27
猪儿美
+1
期待更多优质内容的分享,论坛有你更精彩!
2025-3-17 14:32
最新回复 (5)
雪    币: 337
活跃值: (426)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
大佬,学习了
2025-3-17 14:33
0
雪    币: 1663
活跃值: (2383)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
厉害
2025-3-18 11:44
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
M.C
4
大佬,膜拜
2025-4-8 16:20
0
雪    币: 200
活跃值: (296)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
这个是真分享 说的非常详细 大佬牛逼
2025-4-11 12:07
0
雪    币: 200
活跃值: (296)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
期待下一篇
2025-4-11 12:08
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册