TK抓包协议分析:So层逆向

环境

  • 手机
    • 小米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.youkuaiyun.com/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了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值