首页
社区
课程
招聘
frida-analykit wireshark 流量抓包(上)- 初探libssl体验wireshark无视证书校验的实时https抓包
发表于: 2025-4-16 23:51 18095

frida-analykit wireshark 流量抓包(上)- 初探libssl体验wireshark无视证书校验的实时https抓包

2025-4-16 23:51
18095

在Android上抓HTTPS包,我们会通常使用不限于charles, fiddler等工具作为中间人:

在上面这些抓包工具的实现原理中,伪造证书这一点就是他们毕生无法绕过的坎,基于这个破绽就可以有下面的检测方式:

判断在请求的时候获取到的服务端证书链中的leaf证书公钥指纹真实的服务端leaf证书的公钥指纹(发版时候硬编码到app) 是否吻合来确定是不是伪造来判断是否有中间人,这种方式就是我们所说的ssl pinning

而mtls则是更严格的校验,服务端也要验客户端的证书(硬编码在app),就意味着你必须要用拥有服务端自签发的信任证书的客户端发起的访问,服务端才受理。既然这样,在中间人伪造了证书在没有配置信任客户端证书的情况下,请求会被拦在跟服务端握手时候的证书校验上。

对于常规的java层证书校验方式,如TrustKit-Android依赖的是Android框架层的Java TLS API(HttpsURLConnection、SSLSocket、X509TrustManager等),所以针对于这些证书校验,一些工具譬如JustTrustMeobjection,就是hook掉所有涉及到的常规证书校验的java层校验类来强制验证通过。

即便这些工具在java层已经hook了这么多方法,但是依然存在着会使用native层来进行证书校验的方式,存在魔改的http或者自定义协议的通信的方式,这些对于http协议的抓包工具来说都是束手无策的。

当然,其实前面说这么多,并不是为了贬低中间人代理类抓包工具,也不是为了争个高低对错,我只是想表达的是中间人代理这类抓包软件作为首选永远不会错,但是当你感到迷茫的时候,请不要忘记还有wireshark站在你的背后。

好了,屁话有点多了。进入正题,下面我会基于从目标出发,来说明如何在不使用中间人代理的方式让wireshark实时抓包并且自动解密tls。

文章会分为上下两篇:

wireshark是支持对用户提供的sslkey.log来解密对应匹配的tls流量,持续的写入sslkey.log,wireshark会使用类似于tail的方式读取新sslkey.

wireshark需要的sslkey.log包含的信息会是类似于下面这样:

每一行可以分成三段:

label: 通常会有CLIENT_RANDOM, CLIENT_HANDSHAKE_TRAFFIC_SECRET, EXPORTER_SECRET等label,其中最关键的是 CLIENT_RANDOM

Client Random: 32字节(64个hex字符),握手阶段(Cient Hello)客户端发起Hello时候的随机数,这个值也是wireshark用来定位要解密tls报文的ID

Master Secret: 48字节(96个hex字符),TLS 握手计算出来的主密钥。简单的可以理解成这是用于生成后续客户端和服务端通信的加密数据的密钥的重要参数。( 可以理解成gen_data_secret(master_secret) )

wireshark中配置sslkey_log文件的位置图1

wireshark中的client_random位置图2

有了前面这些认识,再次确认我们需要完成的目标:通过任何可行的方式,让要抓包的app上tls流量的sslkey实时写出到pc日志文件上,以让wireshark能够读取解密tls流量。

在Android 7之后,aosp的ssl就从openssl换到了boringssl,所以这里我们只会从boringssl项目来研究(虽然我们需要关注的地方大概率并没有区别)。

boringssl项目: 529K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4L8$3!0Y4L8r3g2Q4x3V1k6T1L8%4u0A6L8X3N6K6M7$3H3`.

在aosp中boringssl会生成libssl.so,是java层SSLSocket / SSLContext等的native支持。这是一句没有提供任何依据的结论描述。对于没有了解过java层是怎么和native关联起来的人来说,这其实很依赖死记硬背,虽然了解这些并这不是必要的,但如果感兴趣的可以看:下面我会以一个不知道这个事实的角度来,从一个简单的例子中跟踪为什么java层定位到libssl.so。(如果已经了解或并不在意,可省略该部分跳转

为什么是这个方法?

下面的代码段来自 boringssl项目的src/ssl/ssl_lib.cc文件

确定要hook的函数的时候,最先想到的可能会是:

为什么呢?最快速确定就是通过readelf来确定符号名称是否存在,或者使用hex编辑器来搜索字符串来确定存在并且在.symtab段(事实上如果是使用ida pro的话,他能显示并不能反映符号的存在,因为他会依赖其他的信息手段来确定函数符号名);elf是否能够保留STB_LOCAL函数符号究其根本在编译的时候就确定了,通过external/boringssl/Android.bp的配置也能确定这些事实。

SSL_CTX_set_keylog_callback作为导出符号,自然获取其符号地址起来也是非常的简单,在这之前,我们需要拿到要注册回调函数的SSL_CTX *ctx才能够继续,这个要怎么办呢?

前面我们在跟踪java层到native层中,可以看到建立连接,首先需要的是 SSL_new

所以我们的策略是,通过attach符号SSL_new,使用参数SSL_CTX* ctx来使用符号SSL_CTX_set_keylog_callback进行注册回调函数。

到这里我们完成了对libssl.so中tls的密钥打印。但这就是所有了吗?不,还没有,wireshark还拿不到这些数据,我们还需要使用rpc来接管所有的sslkey。

frida提供了一套rpc的通信实现:允许脚本中使用rpc.exports来注册导出函数定义,并且使用send和recv来通信:(frida-analykit实现了这一套,按照文档配置使用即可)

frida-analykit的导出函数注册位于 frida-analykit/script/rpc.ts

frida-analykit的python的rpc接受的数据代理实现位于 frida-analykit/agent/rpc/resolver.py

这一部分我只列举了实现原理依据,其余都是代码开发、项目组织层面的,这些我就不过多说了(真想理解看代码自己消化更高效)。实现细节可以跳转阅读frida-analykit

光是第一眼看到有这么多文字,耐心就足以被削去一半。说到这些,其实我是更倾向于直接把演示测试这一段放到前头(先通过图片看看怎么个事,再决定要不要细看,毕竟大家时间都很宝贵),但是想了想这会使得文章的行文组织过于跳脱、突兀,所以作罢,仅留下一把跳伞用于定位。

下面的资源都可以跳转工具下载,其中下面的测试资源都是相对于该目录下的

测试样本app功能说明图3

图3可以看到app的大概情况,接下来我们会演示验证下面两点:

验证frida-analykit + wireshark是否能规避类似于ssl pinning的证书校验。(虽然从原理上是显而易见的,但是没有比能看到事实更让人感到安心的)

wireshark能够实时抓包解密tls到并且展示http协议。

在win上使用softAP方式开热点的通常会自动创建Wi-Fi Direct网卡,在wireshark 捕获选项中选择该网卡(通过任务管理器可以确定名称)

推荐使用这种方式来让设备进行捕获,不然会混杂其他设备过多的流量还需要写过滤规则

wireshark选择捕获热点网卡的流量图4

./ptpython_spawn.sh来启动脚本index.ts

点击"发起请求"按钮,发起https请求,

sslkey.log文件只会在有数据进来的时候才会创建,所以一般会在发起一次请求后才去按图1配置,当然你手动创建也不是不行

wireshark抓包解密后的http协议流量图5
发起请求图6
frida-analykit注入控制台输出图7

图5可以看出报文已经成功解密,其中标注了三个框:上框=过滤规则(只保留http协议相关);中框=关联报文(关联的请求和响应);下框=报文内容。

从wireshark不是针对http协议的,所以用起来是没有charles,fiddler直观

wireshark抓包开启了ssl-pinning的流量图8

wireshark抓包开启了ssl-pinning的流量图9

wireshark抓包开启了ssl-pinning的流量图10

这篇主要是借用libssl.so这个有明显符号导出的boringssl进行hook,一个简单的例子来说明这一套流量抓包的流程和原理。

但是实战中会能这么简单如愿的hook到ssl_log_secret吗?答案是否定的,现实情况是,即便是常用的webview,用的都不是动态库libssl.so,而是自己静态链接了boringssl的,这就意味着除了常规的java层原生实现的证书校验外,其余的tls流量都无法解密,如果仅仅只是到这种程度,当然算不上可用。所以下篇会针对性的讨论我们该如何根据特征来对静态链接了boringssl(譬如flutter和webview),但没有导出符号的ssl_log_secret函数进行hook(实际上也适用于libssl.so)。

// java例子
URL url = new URL("e69K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2P5r3q4E0M7r3I4W2i4K6u0W2j5$3!0E0");
URLConnection conn = url.openConnection();
conn.connect()
 
// 跟踪步骤
java.net.URL.openConnection()
    v
    abstract URLStreamHandler
        ├── com.android.okhttp.HttpsHandler.openConnection()
        ├── com.android.okhttp.HttpHandler.openConnection()
        ├── com.android.okhttp.HttpHandler.newOkUrlFactory()
        ├── com.android.okhttp.OkUrlFactory.open()
        ├── com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl()
        ├── com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection()
        ├── com.squareup.okhttp.internal.huc.HttpURLConnectionImpl()
        v
    abstract URLConnection
    v
com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.connect()
    v
com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute()
    v
com.squareup.okhttp.internal.http.HttpEngine.sendRequest()
    v
com.squareup.okhttp.internal.http.HttpEngine.connect()
    v
com.squareup.okhttp.internal.http.StreamAllocation.newStream()
    v
com.squareup.okhttp.internal.http.StreamAllocation.findHealthyConnection()
    v
com.squareup.okhttp.internal.http.StreamAllocation.findConnection()
    v
com.squareup.okhttp.internal.io.RealConnection()
    v
com.squareup.okhttp.internal.io.RealConnection.connect()
    v
com.squareup.okhttp.internal.io.RealConnection.connectSocket()
    v
com.squareup.okhttp.internal.io.RealConnection.connectTls()
    v
    abstract SSLSocketFactory
        ├── org.conscrypt.OpenSSLSocketFactoryImpl.createSocket()
        ├── org.conscrypt.Platform.createFileDescriptorSocket()
        ├── org.conscrypt.ConscryptFileDescriptorSocket()
        ├── org.conscrypt.newSsl()
        ├── org.conscrypt.NativeSsl.newInstance()
        ├── org.conscrypt.NativeCrypto.SSL_new()  // native
        v
    static native long SSL_new(long ssl_ctx, AbstractSessionContext holder) throws SSLException;
    v
    // 到达native函数,找到注册native的地方:
    v
com.android.org.conscrypt.NativeCryptoJni.init() // external/conscrypt/common/src/main/java/org/conscrypt/NativeCrypto.java
    v
    System.loadLibrary("javacrypto") // => libjavacrypto.so
        ├── // external/conscrypt/Android.bp:编译libjavacrypto.so的配置
        |   cc_defaults {
        |       name: "libjavacrypto-defaults",
        |    
        |       cflags: [
        |           "-Wall",
        |           "-Wextra",
        |           "-Werror",
        |           "-Wunused",
        |           "-fvisibility=hidden",
        |       ],
        
        |       srcs: ["common/src/jni/main/cpp/**/*.cc"],
        |       local_include_dirs: ["common/src/jni/main/include"],
        |   }
        |    
        |   cc_library_shared {
        |       name: "libjavacrypto",
        |       // ...
        |       shared_libs: [
        |           "libcrypto",
        |           "liblog",
        |           "libssl", // libjavacrypto.so依赖libssl.so
        |       ],
        |       // ...
        |       apex_available: [
        |           "com.android.conscrypt",
        |           "test_com.android.conscrypt",
        |       ],
        |       // ...
        |   }
        
        ├── // external/conscrypt/common/src/jni/main/cpp/conscrypt/jniload.cc
        |   jint libconscrypt_JNI_OnLoad(JavaVM* vm, void*) // 注册native方法, NativeCrypto_{libssl.exports}
        |  
        ├── // external/conscrypt/common/src/jni/main/cpp/conscrypt/native_crypto.cc
        |   NativeCrypto::registerNativeMethods(env);
        |       ├── ...
        |       ├── CONSCRYPT_NATIVE_METHOD(SSL_new, "(J" REF_SSL_CTX ")J")
        |       ├── ...
        |   static jlong NativeCrypto_SSL_new(JNIEnv* env, jclass, jlong ssl_ctx_address, CONSCRYPT_UNUSED jobject holder)
        |       ├── SSL_new() // #include <openssl/ssl.h>
        |
        ├── // external/conscrypt/common/src/jni/main/cpp/conscrypt/jniutil.cc
        |   void jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods)
        |   env->RegisterNatives() 
        v
    static native long SSL_new(long ssl_ctx, AbstractSessionContext holder) throws SSLException;
    v   
........
(end)
 
// 以上,已经从 **java层** 一直跟踪到 **native层**,并且最后定位到 **libssl.so**,从而是可以确定事实:
//      `libssl.so`是java层SSLSocket / SSLContext等类的native支持.
// java例子
URL url = new URL("e69K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2P5r3q4E0M7r3I4W2i4K6u0W2j5$3!0E0");
URLConnection conn = url.openConnection();
conn.connect()
 
// 跟踪步骤
java.net.URL.openConnection()
    v
    abstract URLStreamHandler
        ├── com.android.okhttp.HttpsHandler.openConnection()
        ├── com.android.okhttp.HttpHandler.openConnection()
        ├── com.android.okhttp.HttpHandler.newOkUrlFactory()
        ├── com.android.okhttp.OkUrlFactory.open()
        ├── com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl()
        ├── com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection()
        ├── com.squareup.okhttp.internal.huc.HttpURLConnectionImpl()
        v
    abstract URLConnection
    v
com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.connect()
    v
com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute()
    v
com.squareup.okhttp.internal.http.HttpEngine.sendRequest()
    v
com.squareup.okhttp.internal.http.HttpEngine.connect()
    v
com.squareup.okhttp.internal.http.StreamAllocation.newStream()
    v
com.squareup.okhttp.internal.http.StreamAllocation.findHealthyConnection()
    v
com.squareup.okhttp.internal.http.StreamAllocation.findConnection()
    v
com.squareup.okhttp.internal.io.RealConnection()
    v
com.squareup.okhttp.internal.io.RealConnection.connect()
    v
com.squareup.okhttp.internal.io.RealConnection.connectSocket()
    v
com.squareup.okhttp.internal.io.RealConnection.connectTls()
    v
    abstract SSLSocketFactory
        ├── org.conscrypt.OpenSSLSocketFactoryImpl.createSocket()
        ├── org.conscrypt.Platform.createFileDescriptorSocket()
        ├── org.conscrypt.ConscryptFileDescriptorSocket()
        ├── org.conscrypt.newSsl()
        ├── org.conscrypt.NativeSsl.newInstance()
        ├── org.conscrypt.NativeCrypto.SSL_new()  // native
        v
    static native long SSL_new(long ssl_ctx, AbstractSessionContext holder) throws SSLException;
    v
    // 到达native函数,找到注册native的地方:
    v
com.android.org.conscrypt.NativeCryptoJni.init() // external/conscrypt/common/src/main/java/org/conscrypt/NativeCrypto.java
    v
    System.loadLibrary("javacrypto") // => libjavacrypto.so
        ├── // external/conscrypt/Android.bp:编译libjavacrypto.so的配置
        |   cc_defaults {
        |       name: "libjavacrypto-defaults",
        |    
        |       cflags: [
        |           "-Wall",
        |           "-Wextra",
        |           "-Werror",
        |           "-Wunused",
        |           "-fvisibility=hidden",
        |       ],
        
        |       srcs: ["common/src/jni/main/cpp/**/*.cc"],
        |       local_include_dirs: ["common/src/jni/main/include"],
        |   }
        |    
        |   cc_library_shared {
        |       name: "libjavacrypto",
        |       // ...
        |       shared_libs: [
        |           "libcrypto",
        |           "liblog",
        |           "libssl", // libjavacrypto.so依赖libssl.so
        |       ],
        |       // ...
        |       apex_available: [
        |           "com.android.conscrypt",
        |           "test_com.android.conscrypt",
        |       ],
        |       // ...
        |   }
        
        ├── // external/conscrypt/common/src/jni/main/cpp/conscrypt/jniload.cc
        |   jint libconscrypt_JNI_OnLoad(JavaVM* vm, void*) // 注册native方法, NativeCrypto_{libssl.exports}
        |  
        ├── // external/conscrypt/common/src/jni/main/cpp/conscrypt/native_crypto.cc
        |   NativeCrypto::registerNativeMethods(env);
        |       ├── ...
        |       ├── CONSCRYPT_NATIVE_METHOD(SSL_new, "(J" REF_SSL_CTX ")J")
        |       ├── ...
        |   static jlong NativeCrypto_SSL_new(JNIEnv* env, jclass, jlong ssl_ctx_address, CONSCRYPT_UNUSED jobject holder)
        |       ├── SSL_new() // #include <openssl/ssl.h>
        |
        ├── // external/conscrypt/common/src/jni/main/cpp/conscrypt/jniutil.cc
        |   void jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods)
        |   env->RegisterNatives() 
        v
    static native long SSL_new(long ssl_ctx, AbstractSessionContext holder) throws SSLException;
    v   
........
(end)
 
// 以上,已经从 **java层** 一直跟踪到 **native层**,并且最后定位到 **libssl.so**,从而是可以确定事实:
//      `libssl.so`是java层SSLSocket / SSLContext等类的native支持.
int ssl_log_secret(const SSL *ssl, const char *label, const uint8_t *secret,
                   size_t secret_len) {
  // 如果没有注册callback就不做处理
  if (ssl->ctx->keylog_callback == NULL) {
    return 1;
  }
 
  // 编码random 和 master_secret成字符串
  ScopedCBB cbb;
  uint8_t *out;
  size_t out_len;
  if (!CBB_init(cbb.get(), strlen(label) + 1 + SSL3_RANDOM_SIZE * 2 + 1 +
                          secret_len * 2 + 1) ||
      !CBB_add_bytes(cbb.get(), (const uint8_t *)label, strlen(label)) ||
      !CBB_add_bytes(cbb.get(), (const uint8_t *)" ", 1) ||
      !cbb_add_hex(cbb.get(), ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
      !CBB_add_bytes(cbb.get(), (const uint8_t *)" ", 1) ||
      !cbb_add_hex(cbb.get(), secret, secret_len) ||
      !CBB_add_u8(cbb.get(), 0 /* NUL */) ||
      !CBB_finish(cbb.get(), &out, &out_len)) {
    return 0;
  }
 
  // 通知回调函数
  ssl->ctx->keylog_callback(ssl, (const char *)out);
  OPENSSL_free(out);
  return 1;
}
 
 
struct ssl_ctx_st {
  // ...
  // keylog_callback, if not NULL, is the key logging callback. See
  // |SSL_CTX_set_keylog_callback|.
  void (*keylog_callback)(const SSL *ssl, const char *line) = nullptr;
  // ...
}
int ssl_log_secret(const SSL *ssl, const char *label, const uint8_t *secret,
                   size_t secret_len) {
  // 如果没有注册callback就不做处理
  if (ssl->ctx->keylog_callback == NULL) {
    return 1;
  }
 
  // 编码random 和 master_secret成字符串
  ScopedCBB cbb;
  uint8_t *out;
  size_t out_len;
  if (!CBB_init(cbb.get(), strlen(label) + 1 + SSL3_RANDOM_SIZE * 2 + 1 +
                          secret_len * 2 + 1) ||
      !CBB_add_bytes(cbb.get(), (const uint8_t *)label, strlen(label)) ||
      !CBB_add_bytes(cbb.get(), (const uint8_t *)" ", 1) ||
      !cbb_add_hex(cbb.get(), ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
      !CBB_add_bytes(cbb.get(), (const uint8_t *)" ", 1) ||
      !cbb_add_hex(cbb.get(), secret, secret_len) ||
      !CBB_add_u8(cbb.get(), 0 /* NUL */) ||
      !CBB_finish(cbb.get(), &out, &out_len)) {
    return 0;
  }
 
  // 通知回调函数
  ssl->ctx->keylog_callback(ssl, (const char *)out);
  OPENSSL_free(out);
  return 1;
}
 
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2025-4-25 18:30 被zsa233编辑 ,原因: 回填下篇地址,改成点赞回复可见
收藏
免费 105
支持
分享
最新回复 (73)
雪    币: 163
活跃值: (2778)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
github第一个给你点赞
2025-4-17 18:56
0
雪    币: 227
活跃值: (1231)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
tql
2025-4-28 10:20
0
雪    币: 4464
活跃值: (1296)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
tql
2025-4-28 10:30
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
tql
2025-4-28 10:49
0
雪    币: 90
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
66666
2025-4-28 11:34
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
11
2025-4-28 13:12
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
没看懂和ssl_log_secret有啥关系 最终是hook SSL_new 调用SSL_CTX_set_keylog_callback 注册回调函数来输出keylog    知不知道ssl_log_secret地址好像完全不影响
2025-4-28 13:42
0
雪    币: 405
活跃值: (269)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
9
厉害啊
2025-4-28 16:39
0
雪    币: 533
活跃值: (685)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
10
自己能 没看懂和ssl_log_secret有啥关系 最终是hook SSL_new 调用SSL_CTX_set_keylog_callback 注册回调函数来输出keylog 知不知道ssl_log_ ...
在上篇中提到的ssl_log_secret并不是为了hook它,它是sslkey.log日志生成的代码实现,当要通过SSL_CTX_set_keylog_callback设置回调的时候就是为了在这里面生效,所以需要贴出来这些代码和概念来理清这些思路。至于ssl_log_secret的地址定位是下篇的内容,这里面也是提前给下篇埋个点
2025-4-28 16:44
0
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
666666
2025-4-28 17:23
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
zsa233 在上篇中提到的ssl_log_secret并不是为了hook它,它是sslkey.log日志生成的代码实现,当要通过SSL_CTX_set_keylog_callback设置回调的时候就是为了在这里面 ...
上篇和下篇还是有点割裂的  看完完全不知道 定位ssl_log_secret有啥作用 看源码才知道 后面实际上是hook ssl_log_secret 然后自己实现了根据入参生成out的代码. 
也就是说 SSL_CTX_set_keylog_callback来获取信息的话和ssl_log_secret 完全没关系.
hook ssl_log_secret 来获取信息的话也和SSL_CTX_set_keylog_callback完全没关系
是这样的吗?
2025-4-28 17:38
0
雪    币: 533
活跃值: (685)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
13
自己能 上篇和下篇还是有点割裂的 看完完全不知道 定位ssl_log_secret有啥作用 看源码才知道 后面实际上是hook ssl_log_secret 然后自己实现了根据入参生成out的代码. 也 ...
1. 先回答你的问题:
- SSL_CTX_set_keylog_callback来获取信息的话和ssl_log_secret 完全没关系了吗?hook ssl_log_secret 来获取信息的话也和SSL_CTX_set_keylog_callback完全没关系吗?

--- 如果但就从结果上来看,是的。因为最后的目标都是要获取到sslkey。但是如果从处理方式上来看的话,用`SSL_CTX_set_keylog_callback`来达到的话类似于是“原生实现”的方式获取信息;而用ssl_log_secret达到的话就是侵入式的获取信息,而所说的侵入式的意思就是你这里面提到的:通过解析ssl_log_secret的SSL结果参数来 “自己实现了根据入参生成out的代码“。


2. 在上下篇中确实没有提到是如何运用获取到的ssl_log_secret地址,缺少这一点确实会造成一定的困惑。主要的原因是我在写文章的时候是按照文章主讲思路,然后代码是作为补全的一环。而在这部分内容是希望实现在不长篇大论的情况下,内容更具有针对性的讨论hook的思路。不过但就现在的结果上来看有困惑的会自然而然的去看代码理解的,所以你也有了属于你自己的理解。同时我也认同直接看代码是更高效的。
2025-4-28 18:10
1
雪    币: 847
活跃值: (1741)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
6
2025-4-29 12:18
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
6
2025-4-29 13:58
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
6
2025-4-29 17:16
0
雪    币: 178
活跃值: (1593)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
6666
2025-4-29 17:25
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
6666
2025-4-29 18:03
0
雪    币: 395
活跃值: (447)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
19
6.66.6666
2025-4-29 21:18
0
雪    币: 102
活跃值: (2785)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
20
mark
2025-5-3 10:44
0
雪    币: 762
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
6666感谢
2025-5-4 15:00
0
雪    币: 38
活跃值: (36)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
厉害
2025-5-5 08:32
0
雪    币: 8
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
看看
2025-5-5 11:16
0
雪    币: 0
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
感谢分享
2025-5-5 11:28
0
雪    币: 10
活跃值: (1873)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
感谢分享
2025-5-5 13:57
1
游客
登录 | 注册 方可回帖
返回