SSL_get_fd返回-1的含义及无套接字SSL连接的对等端地址查询
Let's break down your questions clearly—they touch on some really flexible parts of OpenSSL's design that are easy to overlook:
1. What does SSL_get_fd returning -1 actually mean?
The SSL_get_fd() function is built to fetch the standard socket file descriptor tied to an SSL object—but only if that SSL instance uses a socket-based BIO (like the ones created with BIO_s_connect()). Returning -1 confirms exactly what you noted: there’s no traditional socket FD linked to this SSL object.
Importantly, this doesn’t mean the SSL connection is broken. Your confirmed SSL_get_rbio just proves a BIO exists, not that it’s a socket BIO. OpenSSL’s BIO layer is designed to work with all kinds of I/O channels, not just sockets.
2. How can OpenSSL connect to a server without a socket?
OpenSSL doesn’t depend on sockets to function—it only needs a BIO that can handle reading and writing SSL records. Here are the most common scenarios where this happens:
- Memory BIOs: The program might first load SSL-encrypted data into a memory buffer, then send it over a non-socket transport (like inter-process communication, a custom UDP wrapper, or even a serial port). The SSL object is bound to
BIO_s_mem()instead of a socket BIO, soSSL_get_fd()has no FD to return. - Custom BIO implementations: Some apps build their own BIO methods (using
BIO_meth_new()) to route SSL traffic through proprietary channels. For example, a program might tunnel SSL data through an existing HTTP connection or a peer-to-peer network layer—no socket FD is involved here. - Dynamic BIO switching: An app could initialize an SSL object without a BIO, then attach a non-socket BIO later. If you call
SSL_get_fd()before a socket BIO is bound (or after it’s replaced with another BIO type), you’ll get-1.
3. How to get the peer address/port without accessing the underlying socket?
Since there’s no socket FD to run getpeername() on, you’ll need to work around OpenSSL’s abstraction or hook into the app’s own logic. Here are practical Frida-focused approaches:
- Hook the app’s address storage: Most programs track the server’s address somewhere before setting up the SSL connection. Look for variables, structs, or functions where the app stores the IP/port (e.g., right after calling
getaddrinfo()). Example Frida script:// Capture target addresses when the app resolves hostnames Interceptor.attach(Module.findExportByName(null, 'getaddrinfo'), { onEnter: function(args) { const hostname = ptr(args[0]).readUtf8String(); const servname = ptr(args[1]).readUtf8String(); console.log(`Resolving target: ${hostname}:${servname}`); } }); - Check SSL ex_data: Apps often use
SSL_set_ex_data()to attach custom data (like peer address) to SSL objects. You can hookSSL_get_ex_data()or directly read the ex_data slots from the SSL struct (you’ll need to reverse-engineer the correct index for your target app):Interceptor.attach(Module.findExportByName('libssl.so', 'SSL_get_ex_data'), { onEnter: function(args) { const ssl = args[0]; const idx = args[1].toInt32(); const peerData = args[2]; console.log(`SSL ex_data index ${idx}: ${peerData}`); // If you find the right index, parse the address from peerData } }); - Track low-level I/O calls: Even if SSL uses a non-socket BIO, the app has to send/receive data eventually. Hook functions like
sendto(),write(), or the app’s custom I/O methods to capture the peer address when data is transmitted.
内容的提问来源于stack exchange,提问作者He1n

