Android逆向猿人学2022年app比赛第五题双向验证SSLpinning(步步验证)

news2024/11/26 12:18:01

SSLpinnig

  • 前言
  • 一、起步
  • 二、抓包
  • 三、分析
  • 四、验证
    • 第一种方法:
    • 第二种方法:
  • 借鉴

前言

这题在抓包方面会有点小问题,但是最后结果是正确出来了,如果有了解后面这个问题的读者,请多指教,十分感谢。

一、起步

先打开APP,打开第五题,可以发现是一个双向认证的题目。
在这里插入图片描述

二、抓包

打开代理工具抓包设置一下于Charles的端口,我这里使用的代理工具是Brook。
在这里插入图片描述
代理打开后,访问第五题,会发现一直转不出来结果。
在这里插入图片描述
并且Charles会显示访问失败的信息
在这里插入图片描述

三、分析

从错误信息可以看到这个是跟SSL相关的请求,首先就排除了VPN检测这个问题。接下来就可以启动一下Client校验Server的frida脚本看看是否能够继续访问。(这句话其实是正常逆向要走的一个步骤,但是在本题已经很明确了是双向校验,所以其实可以提前确认这个是走不通的,但是为了有个正常流程还是形式走一下)。
DroidSSLUnpinning.js这份代码是从猿人学课程得到的,可以直接拿去使用,在没有特殊情况下,这份代码算是Client校验Server的优解了


var DroidSSLUnpinning = function(){

function klog(data){
    var message={};
    message["jsname"]="DroidSSLUnpinning";
    message["data"]=data;
    console.log("DroidSSLUnpinning", data);
}

Java.perform(function() {
    console.log("DroidSSLUnpinning","init","DroidSSLUnpinning.js init hook success")
/*
hook list:
1.SSLcontext
2.okhttp
3.webview
4.XUtils
5.httpclientandroidlib
6.JSSE
7.network\_security\_config (android 7.0+)
8.Apache Http client (support partly)
9.OpenSSLSocketImpl
10.TrustKit
11.Cronet
*/

	// Attempts to bypass SSL pinning implementations in a number of
	// ways. These include implementing a new TrustManager that will
	// accept any SSL certificate, overriding OkHTTP v3 check()
	// method etc.
	var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
	var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');
	var SSLContext = Java.use('javax.net.ssl.SSLContext');
	var quiet_output = false;

	// Helper method to honor the quiet flag.

	function quiet_send(data) {

		if (quiet_output) {
			return;
		}
		klog(data);
	}


	// Implement a new TrustManager
	// ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8
	// Java.registerClass() is only supported on ART for now(201803). 所以android 4.4以下不兼容,4.4要切换成ART使用.
	/*
06-07 16:15:38.541 27021-27073/mi.sslpinningdemo W/System.err: java.lang.IllegalArgumentException: Required method checkServerTrusted(X509Certificate[], String, String, String) missing
06-07 16:15:38.542 27021-27073/mi.sslpinningdemo W/System.err:     at android.net.http.X509TrustManagerExtensions.<init>(X509TrustManagerExtensions.java:73)
        at mi.ssl.MiPinningTrustManger.<init>(MiPinningTrustManger.java:61)
06-07 16:15:38.543 27021-27073/mi.sslpinningdemo W/System.err:     at mi.sslpinningdemo.OkHttpUtil.getSecPinningClient(OkHttpUtil.java:112)
        at mi.sslpinningdemo.OkHttpUtil.get(OkHttpUtil.java:62)
        at mi.sslpinningdemo.MainActivity$1$1.run(MainActivity.java:36)
*/
	var X509Certificate = Java.use("java.security.cert.X509Certificate");
	var TrustManager;
	try {
		TrustManager = Java.registerClass({
			name: 'org.wooyun.TrustManager',
			implements: [X509TrustManager],
			methods: {
				checkClientTrusted: function(chain, authType) {},
				checkServerTrusted: function(chain, authType) {},
				getAcceptedIssuers: function() {
					// var certs = [X509Certificate.$new()];
					// return certs;
					return [];
				}
			}
		});
	} catch (e) {
		quiet_send("registerClass from X509TrustManager >>>>>>>> " + e.message);
	}





	// Prepare the TrustManagers array to pass to SSLContext.init()
	var TrustManagers = [TrustManager.$new()];

	try {
		// Prepare a Empty SSLFactory
		var TLS_SSLContext = SSLContext.getInstance("TLS");
		TLS_SSLContext.init(null, TrustManagers, null);
		var EmptySSLFactory = TLS_SSLContext.getSocketFactory();
	} catch (e) {
		quiet_send(e.message);
	}

	quiet_send('Custom, Empty TrustManager ready');

	// Get a handle on the init() on the SSLContext class
	var SSLContext_init = SSLContext.init.overload(
		'[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');

	// Override the init method, specifying our new TrustManager
	SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {

		quiet_send('Overriding SSLContext.init() with the custom TrustManager');

		SSLContext_init.call(this, null, TrustManagers, null);
	};

	/*** okhttp3.x unpinning ***/


	// Wrap the logic in a try/catch as not all applications will have
	// okhttp as part of the app.
	try {

		var CertificatePinner = Java.use('okhttp3.CertificatePinner');

		quiet_send('OkHTTP 3.x Found');

		CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {

			quiet_send('OkHTTP 3.x check() called. Not throwing an exception.');
		}

	} catch (err) {

		// If we dont have a ClassNotFoundException exception, raise the
		// problem encountered.
		if (err.message.indexOf('ClassNotFoundException') === 0) {

			throw new Error(err);
		}
	}

	// Appcelerator Titanium PinningTrustManager

	// Wrap the logic in a try/catch as not all applications will have
	// appcelerator as part of the app.
	try {

		var PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
		quiet_send('Appcelerator Titanium Found')

		PinningTrustManager.checkServerTrusted.implementation = function() {

			quiet_send('Appcelerator checkServerTrusted() called. Not throwing an exception.');
		}

	} catch (err) {

		// If we dont have a ClassNotFoundException exception, raise the
		// problem encountered.
		if (err.message.indexOf('ClassNotFoundException') === 0) {

			throw new Error(err);
		}
	}

	/*** okhttp unpinning ***/


	try {
		var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient");
		OkHttpClient.setCertificatePinner.implementation = function(certificatePinner) {
			// do nothing
			quiet_send("OkHttpClient.setCertificatePinner Called!");
			return this;
		};

		// Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation)
		var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner");
		CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(p0, p1) {
			// do nothing
			quiet_send("okhttp Called! [Certificate]");
			return;
		};
		CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(p0, p1) {
			// do nothing
			quiet_send("okhttp Called! [List]");
			return;
		};
	} catch (e) {
		quiet_send("com.squareup.okhttp not found");
	}

	/*** WebView Hooks ***/

	/* frameworks/base/core/java/android/webkit/WebViewClient.java */
	/* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */
	var WebViewClient = Java.use("android.webkit.WebViewClient");

	WebViewClient.onReceivedSslError.implementation = function(webView, sslErrorHandler, sslError) {
		quiet_send("WebViewClient onReceivedSslError invoke");
		//执行proceed方法
		sslErrorHandler.proceed();
		return;
	};

	WebViewClient.onReceivedError.overload('android.webkit.WebView', 'int', 'java.lang.String', 'java.lang.String').implementation = function(a, b, c, d) {
		quiet_send("WebViewClient onReceivedError invoked");
		return;
	};

	WebViewClient.onReceivedError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function() {
		quiet_send("WebViewClient onReceivedError invoked");
		return;
	};

	/*** JSSE Hooks ***/

	/* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */
	/* public final TrustManager[] getTrustManager() */
	/* TrustManagerFactory.getTrustManagers maybe cause X509TrustManagerExtensions error  */
	// var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
	// TrustManagerFactory.getTrustManagers.implementation = function(){
	//     quiet_send("TrustManagerFactory getTrustManagers invoked");
	//     return TrustManagers;
	// }

	var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
	/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
	/* public void setDefaultHostnameVerifier(HostnameVerifier) */
	HttpsURLConnection.setDefaultHostnameVerifier.implementation = function(hostnameVerifier) {
		quiet_send("HttpsURLConnection.setDefaultHostnameVerifier invoked");
		return null;
	};
	/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
	/* public void setSSLSocketFactory(SSLSocketFactory) */
	HttpsURLConnection.setSSLSocketFactory.implementation = function(SSLSocketFactory) {
		quiet_send("HttpsURLConnection.setSSLSocketFactory invoked");
		return null;
	};
	/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
	/* public void setHostnameVerifier(HostnameVerifier) */
	HttpsURLConnection.setHostnameVerifier.implementation = function(hostnameVerifier) {
		quiet_send("HttpsURLConnection.setHostnameVerifier invoked");
		return null;
	};

	/*** Xutils3.x hooks ***/
	//Implement a new HostnameVerifier
	var TrustHostnameVerifier;
	try {
		TrustHostnameVerifier = Java.registerClass({
			name: 'org.wooyun.TrustHostnameVerifier',
			implements: [HostnameVerifier],
			method: {
				verify: function(hostname, session) {
					return true;
				}
			}
		});

	} catch (e) {
		//java.lang.ClassNotFoundException: Didn't find class "org.wooyun.TrustHostnameVerifier"
		quiet_send("registerClass from hostnameVerifier >>>>>>>> " + e.message);
	}

	try {
		var RequestParams = Java.use('org.xutils.http.RequestParams');
		RequestParams.setSslSocketFactory.implementation = function(sslSocketFactory) {
			sslSocketFactory = EmptySSLFactory;
			return null;
		}

		RequestParams.setHostnameVerifier.implementation = function(hostnameVerifier) {
			hostnameVerifier = TrustHostnameVerifier.$new();
			return null;
		}

	} catch (e) {
		quiet_send("Xutils hooks not Found");
	}

	/*** httpclientandroidlib Hooks ***/
	try {
		var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
		AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String', '[Ljava.lang.String', 'boolean').implementation = function() {
			quiet_send("httpclientandroidlib Hooks");
			return null;
		}
	} catch (e) {
		quiet_send("httpclientandroidlib Hooks not found");
	}

	/***
android 7.0+ network_security_config TrustManagerImpl hook
apache httpclient partly
***/
	var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
	// try {
	//     var Arrays = Java.use("java.util.Arrays");
	//     //apache http client pinning maybe baypass
	//     //https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#471
	//     TrustManagerImpl.checkTrusted.implementation = function (chain, authType, session, parameters, authType) {
	//         quiet_send("TrustManagerImpl checkTrusted called");
	//         //Generics currently result in java.lang.Object
	//         return Arrays.asList(chain);
	//     }
	//
	// } catch (e) {
	//     quiet_send("TrustManagerImpl checkTrusted nout found");
	// }

	try {
		// Android 7+ TrustManagerImpl
		TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
			quiet_send("TrustManagerImpl verifyChain called");
			// Skip all the logic and just return the chain again :P
			//https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/november/bypassing-androids-network-security-configuration/
			// https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650
			return untrustedChain;
		}
	} catch (e) {
		quiet_send("TrustManagerImpl verifyChain nout found below 7.0");
	}
	// OpenSSLSocketImpl
	try {
		var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
		OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, authMethod) {
			quiet_send('OpenSSLSocketImpl.verifyCertificateChain');
		}

		quiet_send('OpenSSLSocketImpl pinning')
	} catch (err) {
		quiet_send('OpenSSLSocketImpl pinner not found');
	}
	// Trustkit
	try {
		var Activity = Java.use("com.datatheorem.android.trustkit.pinning.OkHostnameVerifier");
		Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(str) {
			quiet_send('Trustkit.verify1: ' + str);
			return true;
		};
		Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(str) {
			quiet_send('Trustkit.verify2: ' + str);
			return true;
		};

		quiet_send('Trustkit pinning')
	} catch (err) {
		quiet_send('Trustkit pinner not found')
	}

	try {
		//cronet pinner hook
		//weibo don't invoke

		var netBuilder = Java.use("org.chromium.net.CronetEngine$Builder");

		//https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/CronetEngine.Builder.html#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean)
		netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.implementation = function(arg) {

			//weibo not invoke
			// console.log("Enables or disables public key pinning bypass for local trust anchors = " + arg);
			quiet_send("Enables or disables public key pinning bypass for local trust anchors = " + arg);
			//true to enable the bypass, false to disable.
			var ret = netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this, true);
			return ret;
		};

		netBuilder.addPublicKeyPins.implementation = function(hostName, pinsSha256, includeSubdomains, expirationDate) {
			// console.log("cronet addPublicKeyPins hostName = " + hostName);
			quiet_send("cronet addPublicKeyPins hostName = " + hostName);
			//var ret = netBuilder.addPublicKeyPins.call(this,hostName, pinsSha256,includeSubdomains, expirationDate);
			//this 是调用 addPublicKeyPins 前的对象吗? Yes,CronetEngine.Builder
			return this;
		};

	} catch (err) {
		// console.log('[-] Cronet pinner not found')
		quiet_send('[-] Cronet pinner not found')
	}
});
}

setImmediate(DroidSSLUnpinning);

老样子启动下frida,做个端口转发

./frida-server-16.0.2-android-arm64  -l 0.0.0.0:8881
adb forward tcp:8881 tcp:8881

然后直接cmd使用该DroidSSLUnpinning.js文件

frida -H 127.0.0.1:8881 -f com.yuanrenxue.match2022 -l DroidSSLUnpinning.js

在这里插入图片描述

然后再去打开第五题,看看是否能够正常显示(结果还是一样的,肯定是抓不到的了)

在这里插入图片描述
那么接下来,我们就得走Server校验Client的路子了。
Server校验Client是怎么个流程呢,总结一下就是,拿到证书文件,然后代理工具(中间人)携带证书文件去访问Server,让Server误以为是Client传来的请求。

四、验证

这里分两种方法拿到证书和证书密码:

第一种方法:

使用猿人学课程珍藏的tracer-keystore.js(用来吐证书密码的)

/*
Android keystore hooks + utilities

All instances of keystore are captured by hooking any getInstance() calls.
You can find them in keystoreList variable.

Utilities:

KeystoreListAllAliases()
ListAliasesStatic()
* List all aliases in keystores of known hardcoded types(in keystoreTypes)

KeystoreListAllAliasesOnAllInstances()
ListAliasesRuntime()
* List all aliases in keystores of all instances obtained during app runtime.
* Instances that will be dumped are collected via hijacking
* Keystore.getInstance() -> hookKeystoreGetInstance()

ListAliasesAndroid()
* List all aliases in AndroidKeyStore.

ListAliasesType(String type)
* List all aliases in a keystore of given 'type'.
* Example: ListAliasesType('AndroidKeyStore');

ListAliasesObj(Object obj)
* List all aliases for a given keystore object.
* Example: ListAliasesObj(keystoreObj);

GetKeyStore(String name)
* Retrieve keystore instance from keystoreList.
* Example: GetKeyStore("KeyStore...@af102a");

AliasInfo(String alias)
* List keystore key properties in a JSON object.
* Example: AliasInfo('secret');

*/


Java.perform(function () {
	hookKeystoreGetInstance();
	hookKeystoreGetInstance_Provider();
	hookKeystoreGetInstance_Provider2();
	hookKeystoreConstructor();
	hookKeystoreLoad(false);
	hookKeystoreLoadStream(false);
	hookKeystoreGetKey();
	hookKeystoreSetKeyEntry();
	//hookKeystoreGetCertificate();
	hookKeystoreGetCertificateChain();
	hookKeystoreGetEntry();
	hookKeystoreSetEntry();
	hookKeystoreSetKeyEntry();
	hookKeystoreSetKeyEntry2();
	hookKeystoreStore();
	hookKeystoreStoreStream()


});
console.log("KeyStore hooks loaded!");

var keystoreList = [];
var StringCls = null;
Java.perform(function () {
	StringCls = Java.use('java.lang.String');

});

function hookKeystoreConstructor() {
	var keyStoreConstructor = Java.use('java.security.KeyStore').$init.overload("java.security.KeyStoreSpi", "java.security.Provider", "java.lang.String");
	keyStoreConstructor.implementation = function (keyStoreSpi, provider, type) {
		//console.log("[Call] Keystore(java.security.KeyStoreSpi, java.security.Provider, java.lang.String )")
		console.log("[Keystore()]: KeyStoreSpi: " + keyStoreSpi + ", Provider: " + provider + ", type: " + type);
		return this.$init(keyStoreSpi, provider, type);

	}
}

function hookKeystoreGetInstance() {
	var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String");
	keyStoreGetInstance.implementation = function (type) {
		//console.log("[Call] Keystore.getInstance(java.lang.String )")
		console.log("[Keystore.getInstance()]: type: " + type);
		var tmp = this.getInstance(type);
		keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()
		return tmp;
	}
}

function hookKeystoreGetInstance_Provider() {
	var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String", "java.lang.String");
	keyStoreGetInstance.implementation = function (type, provider) {
		//console.log("[Call] Keystore.getInstance(java.lang.String, java.lang.String )")
		console.log("[Keystore.getInstance2()]: type: " + type + ", provider: " + provider);
		var tmp = this.getInstance(type, proivder);
		keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()
		return tmp;
	}
}

function hookKeystoreGetInstance_Provider2() {
	var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String", "java.security.Provider");
	keyStoreGetInstance.implementation = function (type, provider) {
		//console.log("[Call] Keystore.getInstance(java.lang.String, java.security.Provider )")
		console.log("[Keystore.getInstance2()]: type: " + type + ", provider: " + provider);
		var tmp = this.getInstance(type, proivder);
		keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()
		return tmp;
	}
}

/*
* Hook Keystore.load( ... ), set dump to true if you want to perform dump of available Aliases automatically.
*/
function hookKeystoreLoad(dump) {
	var keyStoreLoad = Java.use('java.security.KeyStore')['load'].overload('java.security.KeyStore$LoadStoreParameter');
	/* following function hooks to a Keystore.load(java.security.KeyStore.LoadStoreParameter) */
	keyStoreLoad.implementation = function (param) {
		//console.log("[Call] Keystore.load(java.security.KeyStore.LoadStoreParameter)")
		console.log("[Keystore.load(LoadStoreParameter)]: keystoreType: " + this.getType() + ", param: " + param);
		this.load(param);
		if (dump) console.log(" Keystore loaded aliases: " + ListAliasesObj(this));
	}
}

/*
* Hook Keystore.load( ... ), set dump to true if you want to perform dump of available Aliases automatically.
*/
function hookKeystoreLoadStream(dump) {
	var keyStoreLoadStream = Java.use('java.security.KeyStore')['load'].overload('java.io.InputStream', '[C');
	/* following function hooks to a Keystore.load(InputStream stream, char[] password) */
	keyStoreLoadStream.implementation = function (stream, charArray) {
		//console.log("[Call] Keystore.load(InputStream stream, char[] password)")
		//var hexString = readStreamToHex (stream);
		console.log("[Keystore.load(InputStream, char[])]: keystoreType: " + this.getType() + ", password: '" + charArrayToString(charArray) + "', inputSteam: " + stream);
		this.load(stream, charArray);
		if (dump) console.log(" Keystore loaded aliases: " + ListAliasesObj(this));
	}
}

function hookKeystoreStore() {
	var keyStoreStoreStream = Java.use('java.security.KeyStore')['store'].overload('java.security.KeyStore$LoadStoreParameter');
	/* following function hooks to a Keystore.store(java.security.KeyStore$LoadStoreParameter) */
	keyStoreStoreStream.implementation = function (param) {
		console.log("[Keystore.store()]: keystoreType: " + this.getType() + ", param: '" + param);
		this.store(stream, charArray);
	}
}

function hookKeystoreStoreStream() {
	var keyStoreStoreStream = Java.use('java.security.KeyStore')['store'].overload('java.io.OutputStream', '[C');
	/* following function hooks to a Keystore.store(OutputStream stream, char[] password) */
	keyStoreStoreStream.implementation = function (stream, charArray) {
		console.log("[Keystore.store(OutputStream, char[])]: keystoreType: " + this.getType() + ", password: '" + charArrayToString(charArray) + "', outputSteam: " + stream);
		this.store(stream, charArray);
	}
}

function hookKeystoreGetKey() {
	var keyStoreGetKey = Java.use('java.security.KeyStore')['getKey'].overload("java.lang.String", "[C");
	keyStoreGetKey.implementation = function (alias, charArray) {
		//console.log("[Call] Keystore.getKey(java.lang.String, [C )")
		console.log("[Keystore.getKey()]: alias: " + alias + ", password: '" + charArrayToString(charArray) + "'");
		return this.getKey(alias, charArray);
	}
}

function hookKeystoreSetEntry() {
	var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setEntry'].overload("java.lang.String", "java.security.KeyStore$Entry", "java.security.KeyStore$ProtectionParameter");
	keyStoreSetKeyEntry.implementation = function (alias, entry, protection) {
		//console.log("[Call] Keystore.setEntry(java.lang.String, java.security.KeyStore$Entry, java.security.KeyStore$ProtectionParameter )")
		console.log("[Keystore.setEntry()]: alias: " + alias + ", entry: " + dumpKeyStoreEntry(entry) + "', protection: " + dumpProtectionParameter(protection));
		return this.setEntry(alias, entry, protection);
	}
}

function hookKeystoreSetKeyEntry() {
	var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setKeyEntry'].overload("java.lang.String", "java.security.Key", "[C", "[Ljava.security.cert.Certificate;");
	keyStoreSetKeyEntry.implementation = function (alias, key, charArray, certs) {
		//console.log("[Call] Keystore.setKeyEntry(java.lang.String, java.security.Key, [C, [Ljava.security.cert.Certificate; )
		console.log("[Keystore.setKeyEntry()]: alias: " + alias + ", key: " + key + ", password: '" + charArrayToString(charArray) + "', certs: " + certs);
		return this.setKeyEntry(alias, key, charArray, certs);
	}
}

function hookKeystoreSetKeyEntry2() {
	var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setKeyEntry'].overload("java.lang.String", "[B", "[Ljava.security.cert.Certificate;");
	keyStoreSetKeyEntry.implementation = function (alias, key, certs) {
		//console.log("[Call] Keystore.setKeyEntry(java.lang.String, [B, [Ljava.security.cert.Certificate; )")
		console.log("[Keystore.setKeyEntry2()]: alias: " + alias + ", key: " + key + "', certs: " + certs);
		return this.setKeyEntry(alias, key, certs);
	}
}

/*
* Usually used to load certs for cert pinning.
*/
function hookKeystoreGetCertificate() {
	var keyStoreGetCertificate = Java.use('java.security.KeyStore')['getCertificate'].overload("java.lang.String");
	keyStoreGetCertificate.implementation = function (alias) {
		//console.log("[Call] Keystore.getCertificate(java.lang.String )")
		console.log("[Keystore.getCertificate()]: alias: " + alias);
		return this.getCertificate(alias);
	}
}

/*
* Usually used to load certs for cert pinning.
*/
function hookKeystoreGetCertificateChain() {
	var keyStoreGetCertificate = Java.use('java.security.KeyStore')['getCertificateChain'].overload("java.lang.String");
	keyStoreGetCertificate.implementation = function (alias) {
		//console.log("[Call] Keystore.getCertificateChain(java.lang.String )")
		console.log("[Keystore.getCertificateChain()]: alias: " + alias);
		return this.getCertificateChain(alias);
	}
}

function hookKeystoreGetEntry() {
	var keyStoreGetEntry = Java.use('java.security.KeyStore')['getEntry'].overload("java.lang.String", "java.security.KeyStore$ProtectionParameter");
	keyStoreGetEntry.implementation = function (alias, protection) {
		//console.log("[Call] Keystore.getEntry(java.lang.String, java.security.KeyStore$ProtectionParameter )")
		console.log("[Keystore.getEntry()]: alias: " + alias + ", protection: '" + dumpProtectionParameter(protection) + "'");
		var entry = this.getEntry(alias, protection);
		console.log("[getEntry()]: Entry: " + dumpKeyStoreEntry(entry));
		return entry;
	}
}

function dumpProtectionParameter(protection) {
	if (protection != null) {
		// android.security.keystore.KeyProtection, java.security.KeyStore.CallbackHandlerProtection, java.security.KeyStore.PasswordProtection, android.security.KeyStoreParameter
		var protectionCls = protection.$className;
		if (protectionCls.localeCompare("android.security.keystore.KeyProtection") == 0) {
			return "" + protectionCls + " [implement dumping if needed]";
		}
		else if (protectionCls.localeCompare("java.security.KeyStore.CallbackHandlerProtection") == 0) {
			return "" + protectionCls + " [implement dumping if needed]";
		}
		else if (protectionCls.localeCompare("java.security.KeyStore.PasswordProtection") == 0) {
			getPasswordMethod = Java.use('java.security.KeyStore.PasswordProtection')['getPassword'];
			password = getPasswordMethod.call(protection);
			return "password: " + charArrayToString(password);
		}
		else if (protectionCls.localeCompare("android.security.KeyStoreParameter") == 0) {
			isEncryptionRequiredMethod = Java.use('android.security.KeyStoreParameter')['isEncryptionRequired'];
			result = isEncryptionRequiredMethod.call(protection);
			return "isEncryptionRequired: " + result;
		}
		else
			return "Unknown protection parameter type: " + protectionCls;
	}
	else
		return "null";

}

function dumpKeyStoreEntry(entry) {
	// java.security.KeyStore$PrivateKeyEntry, java.security.KeyStore$SecretKeyEntry, java.security.KeyStore$TrustedCertificateEntry, android.security.WrappedKeyEntry
	if (entry != null) {
		var entryCls = entry.$className;
		var castedEntry = Java.cast(entry, Java.use(entryCls));
		if (entryCls.localeCompare("java.security.KeyStore$PrivateKeyEntry") == 0) {
			var getPrivateKeyEntryMethod = Java.use('java.security.KeyStore$PrivateKeyEntry')['getPrivateKey'];
			var key = getPrivateKeyEntryMethod.call(castedEntry);

			return "" + entryCls + " [implement key dumping if needed] " + key.$className;
		}
		else if (entryCls.localeCompare("java.security.KeyStore$SecretKeyEntry") == 0) {
			var getSecretKeyMethod = Java.use('java.security.KeyStore$SecretKeyEntry')['getSecretKey'];
			var key = getSecretKeyMethod.call(castedEntry);
			var keyGetFormatMethod = Java.use(key.$className)['getFormat'];
			var keyGetEncodedMethod = Java.use(key.$className)['getEncoded'];
			//console.log(""+key.$className);
			if (key.$className.localeCompare("android.security.keystore.AndroidKeyStoreSecretKey") == 0)
				return "keyClass: android.security.keystore.AndroidKeyStoreSecretKey can't dump";
			return "keyFormat: " + keyGetFormatMethod.call(key) + ", encodedKey: '" + keyGetEncodedMethod.call(key) + "', key: " + key;
		}
		else if (entryCls.localeCompare("java.security.KeyStore$TrustedCertificateEntry") == 0) {
			return "" + entryCls + " [implement key dumping if needed]";
		}
		else if (entryCls.localeCompare("android.security.WrappedKeyEntry") == 0) {
			return "" + entryCls + " [implement key dumping if needed]";
		}
		else
			return "Unknown key entry type: " + entryCls;
	}
	else
		return "null";
}

/*
* Dump all aliasses in keystores of all types(predefined in keystoreTypes)
*/
function ListAliasesStatic() {
	// BCPKCS12/PKCS12-DEF - exceptions
	var keystoreTypes = ["AndroidKeyStore", "AndroidCAStore", /*"BCPKCS12",*/ "BKS", "BouncyCastle", "PKCS12", /*"PKCS12-DEF"*/];
	keystoreTypes.forEach(function (entry) {
		console.log("[ListAliasesStatic] keystoreType: " + entry + " \nAliases: " + ListAliasesType(entry));
	});
	return "[done]";
}

/*
* Dump all aliasses in keystores of all instances obtained during app runtime.
* Instances that will be dumped are collected via hijacking Keystre.getInstance() -> hookKeystoreGetInstance()
*/
function ListAliasesRuntime() {
	Java.perform(function () {
		console.log("[ListAliasesRuntime] Instances: " + keystoreList);
		keystoreList.forEach(function (entry) {
			console.log("[ListAliasesRuntime] keystoreObj: " + entry + " type: " + entry.getType() + " \n" + ListAliasesObj(entry));
		});
	});
	return "[done]";
}

/*
* Dump all aliasses in AndroidKey keystore.
*/
function ListAliasesAndroid() {
	return ListAliasesType("AndroidKeyStore");
}

/*
* Dump all aliasses in keystore of given 'type'.
* Example: ListAliasesType('AndroidKeyStore');
*/
function ListAliasesType(type) {
	var result = [];
	Java.perform(function () {
		var keyStoreCls = Java.use('java.security.KeyStore');
		var keyStoreObj = keyStoreCls.getInstance(type);
		keyStoreObj.load(null);
		var aliases = keyStoreObj.aliases();
		//console.log("aliases: " + aliases.getClass());
		while (aliases.hasMoreElements()) {
			result.push("'" + aliases.nextElement() + "'");
		}
	});
	return result;
}

/*
* Dump all aliasses for a given keystore object.
* Example: ListAliasesObj(keystoreObj);
*/
function ListAliasesObj(obj) {
	var result = [];
	Java.perform(function () {
		var aliases = obj.aliases();
		while (aliases.hasMoreElements()) {
			result.push(aliases.nextElement() + "");
		}
	});
	return result;
}

/*
* Retrieve keystore instance from keystoreList
* Example: GetKeyStore("KeyStore...@af102a");
*/
function GetKeyStore(keystoreName) {
	var result = null;
	Java.perform(function () {
		for (var i = 0; i < keystoreList.length; i++) {
			if (keystoreName.localeCompare("" + keystoreList[i]) == 0)
				result = keystoreList[i];
		}
	});
	return result;
}

/*
* Dump keystore key properties in JSON object
* Example: AliasInfo('secret');
*/
function AliasInfo(keyAlias) {
	var result = {};
	Java.perform(function () {
		var keyStoreCls = Java.use('java.security.KeyStore');
		var keyFactoryCls = Java.use('java.security.KeyFactory');
		var keyInfoCls = Java.use('android.security.keystore.KeyInfo');
		var keySecretKeyFactoryCls = Java.use('javax.crypto.SecretKeyFactory');
		var keyFactoryObj = null;

		var keyStoreObj = keyStoreCls.getInstance('AndroidKeyStore');
		keyStoreObj.load(null);
		var key = keyStoreObj.getKey(keyAlias, null);
		if (key == null) {
			console.log('key does not exist');
			return null;
		}
		try {
			keyFactoryObj = keyFactoryCls.getInstance(key.getAlgorithm(), 'AndroidKeyStore');
		} catch (err) {
			keyFactoryObj = keySecretKeyFactoryCls.getInstance(key.getAlgorithm(), 'AndroidKeyStore');
		}
		var keyInfo = keyFactoryObj.getKeySpec(key, keyInfoCls.class);
		result.keyAlgorithm = key.getAlgorithm();
		result.keySize = keyInfoCls['getKeySize'].call(keyInfo);
		result.blockModes = keyInfoCls['getBlockModes'].call(keyInfo);
		result.digests = keyInfoCls['getDigests'].call(keyInfo);
		result.encryptionPaddings = keyInfoCls['getEncryptionPaddings'].call(keyInfo);
		result.keyValidityForConsumptionEnd = keyInfoCls['getKeyValidityForConsumptionEnd'].call(keyInfo);
		if (result.keyValidityForConsumptionEnd != null) result.keyValidityForConsumptionEnd = result.keyValidityForConsumptionEnd.toString();
		result.keyValidityForOriginationEnd = keyInfoCls['getKeyValidityForOriginationEnd'].call(keyInfo);
		if (result.keyValidityForOriginationEnd != null) result.keyValidityForOriginationEnd = result.keyValidityForOriginationEnd.toString();
		result.keyValidityStart = keyInfoCls['getKeyValidityStart'].call(keyInfo);
		if (result.keyValidityStart != null) result.keyValidityStart = result.keyValidityStart.toString();
		result.keystoreAlias = keyInfoCls['getKeystoreAlias'].call(keyInfo);
		result.origin = keyInfoCls['getOrigin'].call(keyInfo);
		result.purposes = keyInfoCls['getPurposes'].call(keyInfo);
		result.signaturePaddings = keyInfoCls['getSignaturePaddings'].call(keyInfo);
		result.userAuthenticationValidityDurationSeconds = keyInfoCls['getUserAuthenticationValidityDurationSeconds'].call(keyInfo);
		result.isInsideSecureHardware = keyInfoCls['isInsideSecureHardware'].call(keyInfo);
		result.isInvalidatedByBiometricEnrollment = keyInfoCls['isInvalidatedByBiometricEnrollment'].call(keyInfo);
		try { result.isTrustedUserPresenceRequired = keyInfoCls['isTrustedUserPresenceRequired'].call(keyInfo); } catch (err) { }
		result.isUserAuthenticationRequired = keyInfoCls['isUserAuthenticationRequired'].call(keyInfo);
		result.isUserAuthenticationRequirementEnforcedBySecureHardware = keyInfoCls['isUserAuthenticationRequirementEnforcedBySecureHardware'].call(keyInfo);
		result.isUserAuthenticationValidWhileOnBody = keyInfoCls['isUserAuthenticationValidWhileOnBody'].call(keyInfo);
		try { result.isUserConfirmationRequired = keyInfoCls['isUserConfirmationRequired'].call(keyInfo); } catch (err) { }
		//console.log(" result: " + JSON.stringify(result));

		//console.log("aliases: " + aliases.getClass());


	});
	return result;
}

/* following function reads an InputStream and returns an ASCII char representation of it */
function readStreamToHex(stream) {
	var data = [];
	var byteRead = stream.read();
	while (byteRead != -1) {
		data.push(('0' + (byteRead & 0xFF).toString(16)).slice(-2));
		/* <---------------- binary to hex ---------------> */
		byteRead = stream.read();
	}
	stream.close();
	return data.join('');
}

function charArrayToString(charArray) {
	if (charArray == null)
		return '(null)';
	else
		return StringCls.$new(charArray);
}

启动效果
在这里插入图片描述
然后手机点击第五题,可以发现证书密码被吐了出来
在这里插入图片描述
拿到证书密码后,就可以直接快乐的去找证书文件了,直接先拖到jadx去反编译看看,证书一般是在资源文件的assets里面的,根据上面的截图,可以知道证书密码就是这个BKS文件的密码。在这里我们就需要把这个证书文件给取出来,然后转换成.p12格式的证书,提供给Charles抓包。
在这里插入图片描述
那怎么拿出来呢,可以直接使用解压这个功能,然后找到对应的文件,拿出来就可以了。
这个证书拿出来后,要怎么才能变成.p12格式呢,这里我使用的是keystore-explorer,进行bks到p12的转换(但是这个工具最新版是打不开这个证书文件的)所以我们要回退到旧版本,去安装使用。
找到v5.5.1版本,按照自己的系统和需求去下载。
安装好了之后,把BKS证书文件拖进去,密码就是我们上面frida吐出来的密码—MZ4cozY8Qu32UzGe在这里插入图片描述
然后点击Tools->Change KeyStore Type->PKC#12,转换后还会要求输入一次密码
O
然后右键选择Export导出证书。
在这里插入图片描述
这个就是我导出的1.p12证书
在这里插入图片描述
既然有了证书文件和证书密码,那就可以拿去Charles配置一下证书,让它伪装成携带证书的Client。

在这里插入图片描述
这里如果没有创建过的会要求输入一个密码,这里直接回车就行,不用去设置。
在这里插入图片描述
把刚刚访问失败的ip和端口填上,然后点击Import P12文件
在这里插入图片描述
找到刚刚导出的1.p12证书文件
在这里插入图片描述
输入一下刚刚得到的证书密码—MZ4cozY8Qu32UzGe
在这里插入图片描述
然后一路点OK
在这里插入图片描述
然后我们回到SSL Proxying标签,新增加一个*:18443(刚刚抓失败的端口)
在这里插入图片描述
最后点一下OK,按照道理说这么干已经成功了,但是很可惜,我做到这一步还是失败了。直接告诉我证书是损坏的。这也就是我开头说的无法解决的一个小问题,希望读者如果有解决方法请告知,感激不尽。
在这里插入图片描述
但是这并不代表,我们的证书文件不能用了。我们直接用吾爱破解网darbra大佬写的文章的python代码来进行携带证书请求我们刚刚访问失败的网站。
先安装requests_pkcs12

pip install requests_pkcs12
import requests_pkcs12

def get_page(page):
    url = 'https://180.76.60.244:18443/api/app5'
    hd = {
        'Content-Type':'application/x-www-form-urlencoded',
        'user-agent': 'okhttp/3.14.9'
    }
    data = {
        'page': page
    }
    resp = requests_pkcs12.post(url,
                headers=hd, data=data, pkcs12_filename='1.p12',
                pkcs12_password='MZ4cozY8Qu32UzGe', verify=False)
    print(resp.json())

get_page(1)

可以看到数据出来了!
在这里插入图片描述
当然这个请求不是无缘无故写出来的,是可以通过r0capture抓包工具抓到分析出来的

python r0capture.py -H 127.0.0.1:8881 猿人学2022

在这里插入图片描述
POST请求

url是…:https://180.76.60.244:18443/api/app5

Content-Type是application/x-www-form-urlencoded

User-Agent是okhttp/3.14.9

data是page=1

到此我们的第一种方法已经结束。

第二种方法:

第二种证书和密钥的获取方法,就比较简单了,使用我们刚刚说的r0capture抓包工具就可以做到自动将证书给下载保存到我们的手机的/sdcard/Download/目录下,密码是r0ysue
启动一下r0captue。

python r0capture.py -H 127.0.0.1:8881 猿人学2022

然后启动成功后,去点击第五题,就会自动保存证书了。

在这里插入图片描述
剩下的步骤我们只需要用下面代码就可以把证书导出来

adb pull /sdcard/Download/证书文件.p12 ./ 

如果显示没有该文件就到/sdcard/Download/看看是什么情况。会发现很多大小一样的证书文件,但是名字都和上面截图不一样,不用慌,我们只需要选择其中一个,名字改一下(方便pull),我这里是改了一个为client

在这里插入图片描述
再执行adb pull语句就成功了。
然后重复第一方法的步骤,将证书导入,不过这次的证书密码就得是r0ysue了(这里就不演示怎么导入的了,除了导出证书和密码不一样,其他的都一模一样,就连python使用都一样)

import requests_pkcs12

def get_page(page):
    url = 'https://180.76.60.244:18443/api/app5'
    hd = {
        'Content-Type':'application/x-www-form-urlencoded',
        'user-agent': 'okhttp/3.14.9'
    }
    data = {
        'page': page
    }
    resp = requests_pkcs12.post(url,
                headers=hd, data=data, pkcs12_filename='client.p12',
                pkcs12_password='r0ysue', verify=False)
    print(resp.json())

get_page(1)

可以看到结果都是一样的
在这里插入图片描述

借鉴

猿人学比赛第五题-双向认证分享
猿人学

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/595894.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C Primer Plus第十五章编程练习答案

学完C语言之后&#xff0c;我就去阅读《C Primer Plus》这本经典的C语言书籍&#xff0c;对每一章的编程练习题都做了相关的解答&#xff0c;仅仅代表着我个人的解答思路&#xff0c;如有错误&#xff0c;请各位大佬帮忙点出&#xff01; 由于使用的是命令行参数常用于linux系…

串口通信简介

1. 数据通信的基础概念 1.1 数据通信方式 按数据通信方式分类&#xff0c;可分为串行通信和并行通信两种。串行和并行的对比如下图所示&#xff1a; 串行通信的基本特征是数据逐位顺序依次传输&#xff0c;优点是传输线少、 布线成本低 、 灵活度高等优点&#xff0c;一般用…

docker架构速看(2)-镜像

docker架构细看(2)-镜像 ​ 上一章讲了Docker服务端的启动&#xff0c;这一章我们来看Docker中的镜像,需要对容器镜像分层存储&#xff0c;容器存储驱动有一定了解&#xff0c;参考 容器技术原理(一)&#xff1a;从根本上认识容器镜像 ​ Docker篇之镜像存储-OverlayFS和联合…

JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO

JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO 这是一篇总结文章。 文章目录 JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO1、学习技术的梳理1.1、jdbc的引入1.2、ResultSet[结果集]1.3API小结 2、数据库连接池3、Apache公司的DBUtils工具-BasicDAO…

在页面上画一个三角形然后点击内部触发事件

在HTML页面上创建一个canvas元素。使用JavaScript绘制三角形并将其填充。您可以使用canvas的API来绘制形状&#xff0c;例如beginPath&#xff08;&#xff09;和lineTo&#xff08;&#xff09;等。将一个事件监听器绑定到canvas元素上&#xff0c;以便在单击三角形时触发事件…

Rust每日一练(Leetday0017) 字母异位词分组、幂函数、N皇后

目录 49. 字母异位词分组 Group Anagrams &#x1f31f;&#x1f31f; 50. 幂函数 Pow(x, n) &#x1f31f;&#x1f31f; 51. N 皇后 N-Queens &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日…

chatgpt赋能python:Python代码输出666——让你快速入门Python编程

Python代码输出666——让你快速入门Python编程 引言 Python是一种面向对象、解释型计算机程序设计语言。Python广泛应用于Web开发、科学计算、数据分析及人工智能等领域&#xff0c;具有优雅、明确、简单的特点&#xff0c;而且学习起来十分容易。如果你是一名初学者&#xf…

chatgpt赋能python:Python修改信息的方法和应用

Python修改信息的方法和应用 Python是一种简单易学、高效、功能强大的编程语言&#xff0c;被广泛应用于各种领域。其中&#xff0c;修改信息是Python最常用的功能之一。无论是在数据分析、网络爬虫还是Web开发中&#xff0c;我们都需要通过Python对数据进行修改、更新和删除等…

Jetson nano之ROS入门 - - 机器人建模与仿真

文章目录 前言一、URDF建模1. URDF语法详解a. robotb. linkc. joint 2. URDF机器人建模实操 二、Xacro宏优化1、 Xacro宏语法详解2、 Xacro建模实操 三、Rviz与Gazebo仿真1、Gazebo集成URDF建模语法基础2、Gazebo集成URDF实操 总结 前言 在ROS中&#xff0c;机器人建模和仿真是…

Spring(四)基于xml的自动装配

自动装配&#xff1a;根据指定的策略&#xff0c;在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值。 首先我们来熟悉三层架构的创建过程&#xff1a; 三层架构为controller层&#xff0c;service层&#xff0c;dao层。 在service层里面创建ser…

ShareX_一款好用的截图工具安装- Window

择心】向大家介绍and安装ShareX ShareX 免费、开源、轻量多区域截图无缝处理截图屏幕录制、文件共享各种实用工具&#xff08;如拾色器&#xff0c;屏幕拾色器&#xff0c;尺子&#xff0c;图像编辑器&#xff0c;图像合并&#xff0c;图像分割器&#xff0c;生成图像缩略图&am…

mysql学+练

从开始到放弃&#xff01; 开始 mysql -uroot -p123456退出命令 exit 或者 quit注释 # show databases; 单行注释 -- show databases; 单行注释 /* 多行注释 show databases; */DDL操作数据库 创建 /* 方式1 直接指定数据库名进行创建 */ CREATE DATABASE db1; /* 方式2 …

TerminalWorks TSPrint/TSScan/TSWebCam Crack

/ 远程桌面打印软件&#xff0c;TerminalWorks TSPrint Server/Client 从远程服务器打印到本地打印机的 简单方法 TSPrint 为您提供了一个简单的远程桌面打印软件&#xff0c;以及使 Windows 终端服务操作更容易的附加工具。有选择地启用或禁用功能&#xff0c;以便您可以完全…

Unity刚体

1、Dynamic&#xff1a;动态类型 受重力和力的影响移动和旋转 Material&#xff1a; 物理材质&#xff0c;在刚体上设置了物理材质&#xff0c;如果子物体有碰撞器但是没有设置材质则会通用刚体的物理材质 如果不设置&#xff0c;将使用在Physics 2D窗口中设置的默认材质(Physi…

python day1 函数

文章目录 前言一、python函数二、定义函数三、函数返回值四、实例五、变量作用域六、参数扩展1、默认值参数2、顺序参数&#xff0c;关键词参数3、可变参数4、可变关键词参数5、解包操作 七、函数类型的参数八、高阶函数九、匿名函数十、递归 前言 看深度学习的代码时&#xff…

SO21434 持续进行的网络安全(五)

目录 一、概要 二、目标 三、网络安全监控 3.1 输入 3.1.1 先决条件 3.1.2 进一步支持信息 3.2 要求和建议 3.3 输出 四、网络安全事件评估 4.1 输入 4.1.1 先决条件 4.1.2 进一步支持信息 4.2 要求和建议 4.3 输出 五、漏洞分析 5.1 输入 5.1.1 先决条件 5.…

Sentinel如何使用滑动窗口进行限流和降级,请看这篇文章分享

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章将详细介绍如何Sentinel如何使用滑动窗口进行限流和降级&#xff0c;后续文章将详细介绍其他知识。 如果文章有什么需要改进的地方还请大佬不吝赐教&#x1f44f;…

环境感知算法——3.PSMNet基于Kitti数据集训练

1. 前言 PSMNet的核心思想是通过金字塔结构来捕捉不同尺度的特征信息&#xff0c;从而提高视差估计的精度。其亮点在于&#xff1a;&#xff08;1&#xff09;使用了金字塔形的卷积神经&#xff08;SPP module&#xff09;网络来提取不同尺度的特征信息&#xff1b;&#xff0…

【UR机械臂ros通讯前的示教器网络配置】

1. 前言 欢迎大家阅读2345VOR的博客【D435i深度相机YOLO V5结合实现目标检测】&#x1f973;&#x1f973;&#x1f973;2345VOR鹏鹏主页&#xff1a; 已获得CSDN《嵌入式领域优质创作者》称号&#x1f47b;&#x1f47b;&#x1f47b;&#xff0c;座右铭&#xff1a;脚踏实地&…

《新程序员005:开源深度指南新金融背后的科技力量》

各位CSDN的uu们你们好呀&#xff0c;今天&#xff0c;小雅兰来写书评啦&#xff0c;尽管再忙&#xff0c;也不能不读书&#xff0c;下面&#xff0c;就让小雅兰来带你们走进《新程序员005&#xff1a;开源深度指南&新金融背后的科技力量》这本书的世界吧&#xff01;&#…