创建 script.js
parent
23640b607a
commit
528b0d8885
|
|
@ -0,0 +1,339 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue