您的位置:首页 > 科技 > 能源 > 汕头个人网站建设_中国软件是外包吗_收录网站_网站广告投放价格表

汕头个人网站建设_中国软件是外包吗_收录网站_网站广告投放价格表

2024/12/23 10:49:24 来源:https://blog.csdn.net/a5right/article/details/142631885  浏览:    关键词:汕头个人网站建设_中国软件是外包吗_收录网站_网站广告投放价格表
汕头个人网站建设_中国软件是外包吗_收录网站_网站广告投放价格表

以 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) {...}}

source 与 sink 是 OKIO 中的 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)
}

可以看到,source 是对 getInputStream 的包装,sink 是对 getOutputStream 的包装。

接下来链接 tls:

okhttp3.internal.connection.RealConnection#connectTls

  @Throws(IOException::class)private fun connectTls(connectionSpecSelector: ConnectionSpecSelector) {...try {// Create the wrapper over the connected socket.sslSocket = sslSocketFactory!!.createSocket(rawSocket, address.url.host, address.url.port, true /* autoClose */) as SSLSocket...// Force handshake. This can throw!sslSocket.startHandshake()// block for session establishmentval sslSocketSession = sslSocket.sessionval unverifiedHandshake = sslSocketSession.handshake()...source = sslSocket.source().buffer()sink = sslSocket.sink().buffer()...} finally {...}}

这里面还会校验握手拿到的证书等逻辑,具体就不展开了。

我们看到 source 与 sink 被重新赋值了,sslSocket 就是对 rawSockt 的包装:

    Java8EngineSocket(Socket socket, String hostname, int port, boolean autoClose,SSLParametersImpl sslParameters) throws IOException {super(socket, hostname, port, autoClose, sslParameters);}

具体可看:

external/conscrypt/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptSocket.java

SSLSocket数据加密逻辑分析

我们继续分析请求逻辑,看看数据是如何写出去的,以及数据是什么时候加密的。

请求发出去的逻辑类为 CallServerInterceptor :

exchange.writeRequestHeaders(request)
codec.writeRequestHeaders(request)
writeRequest(request.headers, requestLine)fun writeRequest(headers: Headers, requestLine: String) {check(state == STATE_IDLE) { "state: $state" }sink.writeUtf8(requestLine).writeUtf8("\r\n")for (i in 0 until headers.size) {sink.writeUtf8(headers.name(i)).writeUtf8(": ").writeUtf8(headers.value(i)).writeUtf8("\r\n")}sink.writeUtf8("\r\n")state = STATE_OPEN_REQUEST_BODY}

最终还是调用了 sink 的write方法,而这个 sink 就是 RealConnection 的 sink,具体传递链就不跟了。

我们跟一下,SSLSocket 的输出流的 write 方法。

external/conscrypt/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java

private final class SSLOutputStream extends OutputStream {@Overridepublic void write(byte[] b, int off, int len) throws IOException {waitForHandshake();synchronized (writeLock) {writeInternal(ByteBuffer.wrap(b, off, len));}}
}writeInternal()
// buffer 是明文
engineResult = engine.wrap(buffer, target);
writeToSocket();

wrap 方法看注释:

Attempts to encode plaintext bytes from a subsequence of data buffers into SSL/TLS network data. 

所以,writeToSocket 写的就是加密后的数据了。

继续跟一下 wrap 真正是实现会发现这样的一个方法:

public SSLEngineResult wrap(ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer dst)throws SSLException {// Write plaintext application data to the SSL engineint result = writePlaintextData(outputBuffer, min(SSL3_RT_MAX_PLAIN_LENGTH, outputBuffer.remaining()));}

最终会来到:

    int writeDirectByteBuffer(long sourceAddress, int sourceLength) throws IOException {lock.readLock().lock();try {return NativeCrypto.ENGINE_SSL_write_direct(ssl, this, sourceAddress, sourceLength, handshakeCallbacks);} finally {lock.readLock().unlock();}}

这里有一个JNI调用,会调用到:

static int NativeCrypto_ENGINE_SSL_write_direct(JNIEnv* env, jclass, jlong ssl_address,CONSCRYPT_UNUSED jobject ssl_holder, jlong address,jint len, jobject shc) {}

而这个方法又会调用到:

int SSL_write(SSL *ssl, const void *buf, int num) {}

注意这里还是明文,而这个方法就是一些通杀库HOOK的点了。

数据处理

当我们Hook到了数据之后,由于它是一个二进制的流,并不方便我们直观的查看。

我们可以看一下别人是如何处理的:

https://github.com/r0ysue/r0capture

将Hook到的数据储存为 pcap 格式,然后使用 wireshark 打开。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com