以 Android10 - OkHttp 的实现为例。

因为 OkHttp 的实现与Android的版本有关。

在Android 10 及以上,SSLSocket 的实现类是 Java8EngineSocket,这个打个断点就能看出来**。**

SSLSocket 的使用

SSLSocket 只是在 Socket 的基础上套了一层加密。所以,它的用法与 Socket 也差不多,看一个例子:

public static void sslSocket2() throws Exception {  
    SSLContext context = SSLContext.getInstance("SSL");  
               // 初始化  
    context.init(null,  
            new TrustManager[] { new Test2.MyX509TrustManager() },  
            new SecureRandom());  
    SSLSocketFactory factory = context.getSocketFactory();  
    SSLSocket s = (SSLSocket) factory.createSocket("localhost", 10002);  
    System.out.println("ok");  
  
    OutputStream output = s.getOutputStream();  
    InputStream input = s.getInputStream();  
  
    output.write("alert".getBytes());  
    System.out.println("sent: alert");  
    output.flush();  
  
    byte[] buf = new byte[1024];  
    int len = input.read(buf);  
    System.out.println("received:" + new String(buf, 0, len));  
} 

这里,我们使用 SSLSocket 发送了一个 alert 字符串。

server 端接收如下:

public static void sslSocketServer() throws Exception {  
  
    ...
  
    // 监听和接收客户端连接  
    SSLServerSocketFactory factory = context.getServerSocketFactory();  
    SSLServerSocket server = (SSLServerSocket) factory  
            .createServerSocket(10002);  
    System.out.println("ok");  
    Socket client = server.accept();  
    System.out.println(client.getRemoteSocketAddress());  
  
    // 向客户端发送接收到的字节序列  
    OutputStream output = client.getOutputStream();  
  
    // 当一个普通 socket 连接上来, 这里会抛出异常  
    // Exception in thread "main" javax.net.ssl.SSLException: Unrecognized  
    // SSL message, plaintext connection?  
    InputStream input = client.getInputStream();  
    byte[] buf = new byte[1024];  
    int len = input.read(buf);  
    System.out.println("received: " + new String(buf, 0, len));  
    output.write(buf, 0, len);  
    output.flush();  
    output.close();  
    input.close();  
  
    // 关闭socket连接  
    client.close();  
    server.close();  
}  

总的来说,还是通过流来传递数据。

我们需要注意的是,SSLSocket 往流里面写入或者读取数据的时候,它是明文。如果我们hook这个写入与读取的方法,是不是就可以拿到请求与相应的明文数据呢?

OKHTTP 的请求写入逻辑

OkHttp 使用 RealConnection 这个类来描述一个链接。

先创建一个 socket 对象,并且保存其输入输出流:

okhttp3.internal.connection.RealConnection#connectSocket

  @Throws(IOException::class)
  private fun connectSocket(
    connectTimeout: Int,
    readTimeout: Int,
    call: Call,
    eventListener: EventListener
  ) {
    ...
    this.rawSocket = rawSocket

    ...

    // The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0
    // More details:
    // <https://github.com/square/okhttp/issues/3245>
    // <https://android-review.googlesource.com/#/c/271775/>
    try {
      source = rawSocket.source().buffer()
      sink = rawSocket.sink().buffer()
    } catch (npe: NullPointerException) {
      ...
    }
  }

sourcesinkOKIO 中的 api

@Throws(IOException::class)
fun Socket.source(): Source {
  val timeout = SocketAsyncTimeout(this)
  val source = InputStreamSource(getInputStream(), timeout)
  return timeout.source(source)
}

@Throws(IOException::class)
fun Socket.sink(): Sink {
  val timeout = SocketAsyncTimeout(this)
  val sink = OutputStreamSink(getOutputStream(), timeout)
  return timeout.sink(sink)
}