【Frida】【Android】09_爬虫之Socket

🛫 系列文章导航

🛫 导读

开发环境

版本号 描述
文章日期 2024-04-02
操作系统 Win11 - 22H2 22621.2715
node -v v20.10.0
npm -v 10.2.3
夜神模拟器 7.0.5.8
Android 9
python 3.9.9
frida 16.2.1
frida-tools 12.3.0
objection 1.11.0

1️⃣ Socket抓包原理

前几篇所讨论的HTTP是应用层的协议,如下图所示。
在这里插入图片描述

HTTP数据从应用层发送出去后,依次经过传输层、网络层、链路层,在经过每一层时都会被包裹上头部数据,以保证在数据传输过程中的完整性,然后传输给接收方;接收方以相反的过程依次去除头部数据从而获取真实传输的HTTP数据。
因此,如果对应用进行抓包,那么不仅仅是应用层,在传输层、网络层等应用层往下的所有层级都可以获取传输的全部数据。这正是在传输层进行Socket终极抓包的理论基础。

只要开发者使用了应用层框架,不可避免地会使用系统的Socket进行数据包的收发。
如果使用的是HTTP协议,则直接使用Socket,此时数据如果没有任何代码层面的加解密,直接就是明文,将内容dump下来即可进行分析;
如果使用的是HTTPS协议,那么HTTP包还要“裹上”一层SSL,最终通过SSL的接口进行收发,而SSL也会将加密后和解密前的数据通过Socket与服务器进行通信,如下图所示。
在这里插入图片描述

2️⃣ APP源码说明

本文示例中的APP可以从本篇绑定的资源中下载。主要功能是传递一个url,点击执行HTTP/HTTPS请求。
源码地址: https://gitcode.com/android8/AndroidFridaBeginnersBook
在这里插入图片描述

ezReq

该函数对HttpURLConnection进行封装,执行网络请求。具体含义参考《【Frida】【Android】 07_爬虫之网络通信库HttpURLConnection https://blog.csdn.net/kinghzking/article/details/137211973》。

    public void ezReq(String urlStr) {
        try {
            URL url = new URL(urlStr);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("token","demo");
            connection.setConnectTimeout(8000);
            connection.setReadTimeout(8000);
            connection.connect(); // 开始连接
            InputStream in = connection.getInputStream();
            //if(in.available() > 0){
                // 每次写入1024字节
                int bufferSize = 1024;
                byte[] buffer = new byte[bufferSize];
                StringBuffer sb = new StringBuffer();
                while ((in.read(buffer)) != -1) {
                    sb.append(new String(buffer));
                }
                Log.d("demo", sb.toString());
                connection.disconnect();
            // }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

按钮绑定及遇到问题解决

  • NetworkOnMainThreadException
    在Android 4.0以上,网络连接不能放在主线程上,否则报错android.os.NetworkOnMainThreadException。
    所以,我们需要new Thread创建一个线程将网络请求包进去。
  • Cleartext HTTP traffic to XXX not permitted
    Android 9.0(API级别28)开始,默认情况下限制了明文流量的网络请求,对未加密流量不再信任,直接放弃请求,因此http的url均无法在webview中加载,https 不受影响。
    小编使用的模拟器是Android 9,当执行http请求时就会报上面的问题。可以通过下面步骤解决:
    • res 下新建 xml 目录,创建文件:network_security_config.xml ,内容如下:
      在这里插入图片描述
    • 在 AndroidManifest.xml 的 application 标签添加配置:
      在这里插入图片描述
        Button button = findViewById(R.id.buttonReq);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //请求详情
                        TextView viewById = findViewById(R.id.textInputEditText);
                        String url = viewById.getText().toString();
                        Log.d("url = ", url);
                        ezReq(url);
                    }
                })).start();
            }
        });

3️⃣ HTTP的Socket分析

ZenTracer分析

关于ZenTracer的使用,可以参考文章:《【Frida】【Android】 工具篇:ZenTracer https://blog.csdn.net/kinghzking/article/details/137284648

  • 我们设置好匹配内容为Socket(注意大小写),APP中设置url为http://www.baidu.com,然后点击执行请求:
    在这里插入图片描述

这时候在ZenTracer中,我们可以看到如下内容:
在这里插入图片描述
很明显,read、write函数就是我们想要的函数。

编写代码:jhexdump

java.net.SocketOutputStream的第一个参数是[B,表示字节数组,为了使输出的字节数组更加可视化,这里引用了Awakened的jhexdump()函数,如下面的代码清单所示:

function jhexdump(array, len) {
  var ptr = Memory.alloc(len);
  for (var i = 0; i < len; ++i) Memory.writeS8(ptr.add(i), array[i]);
  //console.log(hexdump(ptr, { offset: off, length: len, header: false, ansi: false }));
  console.log(
    hexdump(ptr, {
      offset: 0,
      length: len,
      header: false,
      ansi: false,
    })
  );
}

这部分代码主要是将Java层的byte数组通Memory.writeS8()
函数存放至通过Memory.alloc()这个API手动开辟的内存区域中,再调用hexdump()这个API打印出相应字节的hexdump。

编写代码:hookSocket

代码实现对下面函数的hook

  • java.net.SocketOutputStream.write(bytearray1, int1, int2)
    • 第三个参数int2,表示数组的长度
  • java.net.SocketInputStream.read(bytearray1, int1, int2)
    • 返回值表示读取的长度
    • 对于过长的封包,会分多次读取。
function hookSocket() {
  Java.perform(function () {
    // java.net.SocketOutputStream.write
    // java.net.SocketOutputStream.socketWrite
    Java.use("java.net.SocketOutputStream").socketWrite.overload(
      "[B",
      "int",
      "int"
    ).implementation = function (bytearray1, int1, int2) {
      var result = this.socketWrite(bytearray1, int1, int2);

      console.log(
        "socketWrite result,bytearray1,int1,int2=>",
        result,
        bytearray1,
        int1,
        int2
      );

      var ByteString = Java.use("com.android.okhttp.okio.ByteString");

      jhexdump(bytearray1, int2);
      return result;
    };

    // java.net.SocketInputStream.read
    // java.net.SocketInputStream.socketRead0
    Java.use("java.net.SocketInputStream").read.overload(
      "[B",
      "int",
      "int"
    ).implementation = function (bytearray1, int1, int2) {
      var result = this.read(bytearray1, int1, int2);

      console.log(
        "read result,bytearray1,int1,int2=>",
        result,
        bytearray1,
        int1,
        int2
      );

      var ByteString = Java.use("com.android.okhttp.okio.ByteString");
      //console.log('contents: => ', ByteString.of(bytearray1).hex())
      jhexdump(bytearray1, result);

      return result;
    };
  });
}

验证效果

请求封包,write打印结果:
在这里插入图片描述
在这里插入图片描述

响应封包,read打印结果:
在这里插入图片描述
在这里插入图片描述

4️⃣ HTTPS的Socket分析

ZenTracer分析

分析之前我们将APP中的URL由http://www.baidu.com改成
https://www.baidu.com,再次点击执行请求按钮。
ZenTracer将打印如下内容,可见com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream.write就是我们需要的写函数
在这里插入图片描述
相对的,我们可以在ZenTracer的控制台中搜素read,可以找到读取的函数com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read
在这里插入图片描述

编写代码:

代码实现与HTTP的类似,不做过多解释,代码如下:

function hookSSLSocketAndroid() {
  Java.perform(function () {
    // com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream.write
    Java.use(
      "com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream"
    ).write.overload("[B", "int", "int").implementation = function (
      bytearray1,
      int1,
      int2
    ) {
      var result = this.write(bytearray1, int1, int2);

      console.log(
        "write result,bytearray1,int1,int2=>",
        result,
        bytearray1,
        int1,
        int2
      );

      var ByteString = Java.use("com.android.okhttp.okio.ByteString");
      console.log("contents: => ", ByteString.of(bytearray1).hex());

      return result;
    };

    // com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read
    Java.use(
      "com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream"
    ).read.overload("[B", "int", "int").implementation = function (
      bytearray1,
      int1,
      int2
    ) {
      var result = this.read(bytearray1, int1, int2);

      console.log(
        "read result,bytearray1,int1,int2=>",
        result,
        bytearray1,
        int1,
        int2
      );

      var ByteString = Java.use("com.android.okhttp.okio.ByteString");
      //console.log('contents: => ', ByteString.of(bytearray1).hex())
      jhexdump(bytearray1, result);

      return result;
    };
  });
}

验证效果

打印结果与HTTP类似,不再贴图了。

5️⃣ 验证HTTPS请求流程

至此,Socket的Hook代码已经写完,反过来,我们通过hook相关函数,可以定位到APP的核心代码,下面我们以HTTPS为例,定位APP调用函数。

objection测试

  • objection连接APP:
    objection -g com.roysue.httpurlconnectiondemo explore
  • hook关键函数、打印堆栈
    android hooking watch class_method com.android.org.conscrypt.ConscryptFileDescrip torSocket$SSLOutputStream.write --dump-args --dump-return --dump-backtrace
    在这里插入图片描述
  • 点击请求,查看打印结果
    如下图,我们可以看出,请求从run函数开始,调用ezReq,然后执行一系列函数,最终发送出去的。
    在这里插入图片描述

🛬 文章小结

使用ZenTracer过程中,会出现崩溃的情况,这时候可以:

  • 多试几次
  • 或者根据已有的输出结果,调整删选条件再次尝试。
  • 或者通过objection的-c命令调整输入结果进行尝试。

关于Socket请求,有很多实现方式,大厂可能会自实现网络请求,避开Socket调用。也有的直接调用so层实现网络请求。

📖 参考资料

ps: 文章中内容仅用于技术交流,请勿用于违规违法行为。

相关推荐

  1. 网络编程Socket

    2024-04-03 08:34:04       59 阅读
  2. ACEsocket

    2024-04-03 08:34:04       39 阅读
  3. PHPCURL和Socket

    2024-04-03 08:34:04       43 阅读
  4. python爬虫01-爬虫介绍

    2024-04-03 08:34:04       63 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-03 08:34:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-03 08:34:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-03 08:34:04       87 阅读
  4. Python语言-面向对象

    2024-04-03 08:34:04       96 阅读

热门阅读

  1. 微信小程序中路由跳转方式

    2024-04-03 08:34:04       40 阅读
  2. Python网络爬虫(二):Requests库

    2024-04-03 08:34:04       39 阅读
  3. SQLAlchemy 的数据库引擎engine与连接对象Connection

    2024-04-03 08:34:04       34 阅读
  4. Knife4j配置使用笔记

    2024-04-03 08:34:04       38 阅读
  5. nodejs的express负载均衡

    2024-04-03 08:34:04       35 阅读
  6. 每天学习一个Linux命令之dpkg

    2024-04-03 08:34:04       42 阅读
  7. linux扩展正则表达式之+

    2024-04-03 08:34:04       38 阅读
  8. JVM中一次完整的 GC 流程

    2024-04-03 08:34:04       44 阅读
  9. 2023年网络安全领域新兴技术的发展特点

    2024-04-03 08:34:04       36 阅读
  10. RCE(远程命令执行)漏洞详解

    2024-04-03 08:34:04       39 阅读