/** * Initializes 'addresses' dictionary and NativeFunctions. */ "use strict"; rpc.exports = { setssllib: function (name) { console.log("setSSLLib => " + name); libname = name; initializeGlobals(); return; } }; var addresses = {}; var SSL_get_fd = null; var SSL_get_session = null; var SSL_SESSION_get_id = null; var getpeername = null; var getsockname = null; var ntohs = null; var ntohl = null; var SSLstackwrite = null; var SSLstackread = null; var libname = "*libssl*"; function uuid(len, radix) { var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); var uuid = [], i; radix = radix || chars.length; if (len) { // Compact form for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; } else { // rfc4122, version 4 form var r; // rfc4122 requires these characters uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; uuid[14] = '4'; // Fill in random data. At i==19 set the high bits of clock sequence as // per rfc4122, sec. 4.1.5 for (i = 0; i < 36; i++) { if (!uuid[i]) { r = 0 | Math.random() * 16; uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; } } } return uuid.join(''); } function return_zero(args) { return 0; } function initializeGlobals() { var resolver = new ApiResolver("module"); var exps = [ [Process.platform == "darwin" ? "*libboringssl*" : "*libssl*", ["SSL_read", "SSL_write", "SSL_get_fd", "SSL_get_session", "SSL_SESSION_get_id"]], // for ios and Android [Process.platform == "darwin" ? "*libsystem*" : "*libc*", ["getpeername", "getsockname", "ntohs", "ntohl"]] ]; // console.log(exps) for (var i = 0; i < exps.length; i++) { var lib = exps[i][0]; var names = exps[i][1]; for (var j = 0; j < names.length; j++) { var name = names[j]; // console.log("exports:" + lib + "!" + name) var matches = resolver.enumerateMatchesSync("exports:" + lib + "!" + name); if (matches.length == 0) { if (name == "SSL_get_fd") { addresses["SSL_get_fd"] = 0; continue; } throw "Could not find " + lib + "!" + name; } else if (matches.length != 1) { // Sometimes Frida returns duplicates. var address = 0; var s = ""; var duplicates_only = true; for (var k = 0; k < matches.length; k++) { if (s.length != 0) { s += ", "; } s += matches[k].name + "@" + matches[k].address; if (address == 0) { address = matches[k].address; } else if (!address.equals(matches[k].address)) { duplicates_only = false; } } if (!duplicates_only) { throw "More than one match found for " + lib + "!" + name + ": " + s; } } addresses[name] = matches[0].address; } } if (addresses["SSL_get_fd"] == 0) { SSL_get_fd = return_zero; } else { SSL_get_fd = new NativeFunction(addresses["SSL_get_fd"], "int", ["pointer"]); } SSL_get_session = new NativeFunction(addresses["SSL_get_session"], "pointer", ["pointer"]); SSL_SESSION_get_id = new NativeFunction(addresses["SSL_SESSION_get_id"], "pointer", ["pointer", "pointer"]); getpeername = new NativeFunction(addresses["getpeername"], "int", ["int", "pointer", "pointer"]); getsockname = new NativeFunction(addresses["getsockname"], "int", ["int", "pointer", "pointer"]); ntohs = new NativeFunction(addresses["ntohs"], "uint16", ["uint16"]); ntohl = new NativeFunction(addresses["ntohl"], "uint32", ["uint32"]); } initializeGlobals(); function ipToNumber(ip) { var num = 0; if (ip == "") { return num; } var aNum = ip.split("."); if (aNum.length != 4) { return num; } num += parseInt(aNum[0]) << 0; num += parseInt(aNum[1]) << 8; num += parseInt(aNum[2]) << 16; num += parseInt(aNum[3]) << 24; num = num >>> 0;//这个很关键,不然可能会出现负数的情况 return num; } /** * Returns a dictionary of a sockfd's "src_addr", "src_port", "dst_addr", and * "dst_port". * @param {int} sockfd The file descriptor of the socket to inspect. * @param {boolean} isRead If true, the context is an SSL_read call. If * false, the context is an SSL_write call. * @return {dict} Dictionary of sockfd's "src_addr", "src_port", "dst_addr", * and "dst_port". */ function getPortsAndAddresses(sockfd, isRead) { var message = {}; var src_dst = ["src", "dst"]; for (var i = 0; i < src_dst.length; i++) { if ((src_dst[i] == "src") ^ isRead) { var sockAddr = Socket.localAddress(sockfd) } else { var sockAddr = Socket.peerAddress(sockfd) } if (sockAddr == null) { // 网络超时or其他原因可能导致socket被关闭 message[src_dst[i] + "_port"] = 0 message[src_dst[i] + "_addr"] = 0 } else { message[src_dst[i] + "_port"] = (sockAddr.port & 0xFFFF) message[src_dst[i] + "_addr"] = ntohl(ipToNumber(sockAddr.ip.split(":").pop())) } } return message; } /** * Get the session_id of SSL object and return it as a hex string. * @param {!NativePointer} ssl A pointer to an SSL object. * @return {dict} A string representing the session_id of the SSL object's * SSL_SESSION. For example, * "59FD71B7B90202F359D89E66AE4E61247954E28431F6C6AC46625D472FF76336". */ function getSslSessionId(ssl) { var session = SSL_get_session(ssl); if (session == 0) { return 0; } var len = Memory.alloc(4); var p = SSL_SESSION_get_id(session, len); len = Memory.readU32(len); var session_id = ""; for (var i = 0; i < len; i++) { // Read a byte, convert it to a hex string (0xAB ==> "AB"), and append // it to session_id. session_id += ("0" + Memory.readU8(p.add(i)).toString(16).toUpperCase()).substr(-2); } return session_id; } Interceptor.attach(addresses["SSL_read"], { onEnter: function (args) { var message = getPortsAndAddresses(SSL_get_fd(args[0]), true); message["ssl_session_id"] = getSslSessionId(args[0]); message["function"] = "SSL_read"; message["stack"] = SSLstackread; this.message = message; this.buf = args[1]; }, onLeave: function (retval) { retval |= 0; // Cast retval to 32-bit integer. if (retval <= 0) { return; } send(this.message, Memory.readByteArray(this.buf, retval)); } }); Interceptor.attach(addresses["SSL_write"], { onEnter: function (args) { var message = getPortsAndAddresses(SSL_get_fd(args[0]), false); message["ssl_session_id"] = getSslSessionId(args[0]); message["function"] = "SSL_write"; message["stack"] = SSLstackwrite; send(message, Memory.readByteArray(args[1], parseInt(args[2]))); }, onLeave: function (retval) { } }); if (Java.available) { Java.perform(function () { function storeP12(pri, p7, p12Path, p12Password) { var X509Certificate = Java.use("java.security.cert.X509Certificate") var p7X509 = Java.cast(p7, X509Certificate); var chain = Java.array("java.security.cert.X509Certificate", [p7X509]) var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); ks.load(null, null); ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain); try { var out = Java.use("java.io.FileOutputStream").$new(p12Path); ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray()) } catch (exp) { console.log(exp) } } //在服务器校验客户端的情形下,帮助dump客户端证书,并保存为p12的格式,证书密码为r0ysue Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function () { var result = this.getPrivateKey() var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName(); storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue'); var message = {}; message["function"] = "dumpClinetCertificate=>" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + ' pwd: r0ysue'; message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); var data = Memory.alloc(1); send(message, Memory.readByteArray(data, 1)) return result; } Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function () { var result = this.getCertificateChain() var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName(); storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue'); var message = {}; message["function"] = "dumpClinetCertificate=>" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + ' pwd: r0ysue'; message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); var data = Memory.alloc(1); send(message, Memory.readByteArray(data, 1)) return result; } //SSLpinning helper 帮助定位证书绑定的关键代码a Java.use("java.io.File").$init.overload('java.io.File', 'java.lang.String').implementation = function (file, cert) { var result = this.$init(file, cert) var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); if (file.getPath().indexOf("cacert") >= 0 && stack.indexOf("X509TrustManagerExtensions.checkServerTrusted") >= 0) { var message = {}; message["function"] = "SSLpinning position locator => " + file.getPath() + " " + cert; message["stack"] = stack; var data = Memory.alloc(1); send(message, Memory.readByteArray(data, 1)) } return result; } Java.use("java.net.SocketOutputStream").socketWrite0.overload('java.io.FileDescriptor', '[B', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount) { var result = this.socketWrite0(fd, bytearry, offset, byteCount); var message = {}; message["function"] = "HTTP_send"; message["ssl_session_id"] = ""; message["src_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); message["src_port"] = parseInt(this.socket.value.getLocalPort().toString()); message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); message["dst_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); var ptr = Memory.alloc(byteCount); for (var i = 0; i < byteCount; ++i) Memory.writeS8(ptr.add(i), bytearry[offset + i]); send(message, Memory.readByteArray(ptr, byteCount)) return result; } Java.use("java.net.SocketInputStream").socketRead0.overload('java.io.FileDescriptor', '[B', 'int', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount, timeout) { var result = this.socketRead0(fd, bytearry, offset, byteCount, timeout); var message = {}; message["function"] = "HTTP_recv"; message["ssl_session_id"] = ""; message["src_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); message["src_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); message["dst_port"] = parseInt(this.socket.value.getLocalPort()); message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); if (result > 0) { var ptr = Memory.alloc(result); for (var i = 0; i < result; ++i) Memory.writeS8(ptr.add(i), bytearry[offset + i]); send(message, Memory.readByteArray(ptr, result)) } return result; } if (parseFloat(Java.androidVersion) > 8) { Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { var result = this.write(bytearry, int1, int2); SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); return result; } Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { var result = this.read(bytearry, int1, int2); SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); return result; } } else { Java.use("com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { var result = this.write(bytearry, int1, int2); SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); return result; } Java.use("com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { var result = this.read(bytearry, int1, int2); SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); return result; } } } ) }