commit 9a3553140918f091c3fdae7aeeb3cb53cbbf3c31 Author: 方块君 Date: Wed May 11 14:11:32 2022 +0800 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f1e0164 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +.idea +.DS_Store +/build +/captures +/app/debug +/app/build +.externalNativeBuild +.cxx +local.properties diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..672cf62 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 方块君 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..588c84f --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# GenshinProxy + diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..eed701c --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' +} + +android { + compileSdk 32 + + defaultConfig { + applicationId "xfk233.workwx" + minSdk 27 + targetSdk 32 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = '11' + } +} + +dependencies { + //API + compileOnly 'de.robv.android.xposed:api:82' + //带源码Api + compileOnly 'de.robv.android.xposed:api:82:sources' + implementation 'com.github.kyuubiran:EzXHelper:0.9.2' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b784702 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/xposed_init b/app/src/main/assets/xposed_init new file mode 100644 index 0000000..a4f11bb --- /dev/null +++ b/app/src/main/assets/xposed_init @@ -0,0 +1 @@ +xfk233.GenshinProxy.MainHook \ No newline at end of file diff --git a/app/src/main/java/xfk233/GenshinProxy/JustTrustMe.java b/app/src/main/java/xfk233/GenshinProxy/JustTrustMe.java new file mode 100644 index 0000000..5728afc --- /dev/null +++ b/app/src/main/java/xfk233/GenshinProxy/JustTrustMe.java @@ -0,0 +1,469 @@ +package xfk233.GenshinProxy; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.http.SslError; +import android.net.http.X509TrustManagerExtensions; +import android.util.Log; +import android.webkit.SslErrorHandler; +import android.webkit.WebView; +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XC_MethodReplacement; +import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; +import org.apache.http.conn.scheme.HostNameResolver; +import org.apache.http.conn.ssl.SSLSocketFactory; + +import javax.net.ssl.*; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; + +import static de.robv.android.xposed.XposedHelpers.*; + +@SuppressWarnings("deprecation") +public class JustTrustMe { + + private static final String TAG = "JustTrustMe"; + String currentPackageName = ""; + + public void hook(LoadPackageParam lpparam) { + + currentPackageName = lpparam.packageName; + + findAndHookMethod(X509TrustManagerExtensions.class, "checkServerTrusted", X509Certificate[].class, String.class, String.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return param.args[0]; + } + }); + + findAndHookMethod("android.security.net.config.NetworkSecurityTrustManager", lpparam.classLoader, "checkPins", List.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return null; + } + }); + + /* external/apache-http/src/org/apache/http/conn/ssl/SSLSocketFactory.java */ + /* public SSLSocketFactory( ... ) */ + Log.d(TAG, "Hooking SSLSocketFactory(String, KeyStore, String, KeyStore) for: " + currentPackageName); + findAndHookConstructor(SSLSocketFactory.class, String.class, KeyStore.class, String.class, KeyStore.class, + SecureRandom.class, HostNameResolver.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws NoSuchAlgorithmException { + + String algorithm = (String) param.args[0]; + KeyStore keystore = (KeyStore) param.args[1]; + String keystorePassword = (String) param.args[2]; + SecureRandom random = (SecureRandom) param.args[4]; + + KeyManager[] keymanagers = null; + TrustManager[] trustmanagers; + + if (keystore != null) { + keymanagers = (KeyManager[]) callStaticMethod(SSLSocketFactory.class, "createKeyManagers", keystore, keystorePassword); + } + + trustmanagers = new TrustManager[]{new ImSureItsLegitTrustManager()}; + + setObjectField(param.thisObject, "sslcontext", SSLContext.getInstance(algorithm)); + callMethod(getObjectField(param.thisObject, "sslcontext"), "init", keymanagers, trustmanagers, random); + setObjectField(param.thisObject, "socketfactory", + callMethod(getObjectField(param.thisObject, "sslcontext"), "getSocketFactory")); + } + + }); + + + /* external/apache-http/src/org/apache/http/conn/ssl/SSLSocketFactory.java */ + /* public static SSLSocketFactory getSocketFactory() */ + Log.d(TAG, "Hooking static SSLSocketFactory(String, KeyStore, String, KeyStore) for: " + currentPackageName); + findAndHookMethod("org.apache.http.conn.ssl.SSLSocketFactory", lpparam.classLoader, "getSocketFactory", new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return newInstance(SSLSocketFactory.class); + } + }); + + /* external/apache-http/src/org/apache/http/conn/ssl/SSLSocketFactory.java */ + /* public boolean isSecure(Socket) */ + Log.d(TAG, "Hooking SSLSocketFactory(Socket) for: " + currentPackageName); + findAndHookMethod("org.apache.http.conn.ssl.SSLSocketFactory", lpparam.classLoader, "isSecure", Socket.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return true; + } + }); + + /* JSSE Hooks */ + /* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */ + /* public final TrustManager[] getTrustManager() */ + Log.d(TAG, "Hooking TrustManagerFactory.getTrustManagers() for: " + currentPackageName); + findAndHookMethod("javax.net.ssl.TrustManagerFactory", lpparam.classLoader, "getTrustManagers", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) { + + if (hasTrustManagerImpl()) { + Class cls = findClass("com.android.org.conscrypt.TrustManagerImpl", lpparam.classLoader); + + TrustManager[] managers = (TrustManager[]) param.getResult(); + if (managers.length > 0 && cls.isInstance(managers[0])) + return; + } + + param.setResult(new TrustManager[]{new ImSureItsLegitTrustManager()}); + } + }); + + /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */ + /* public void setDefaultHostnameVerifier(HostnameVerifier) */ + Log.d(TAG, "Hooking HttpsURLConnection.setDefaultHostnameVerifier for: " + currentPackageName); + findAndHookMethod("javax.net.ssl.HttpsURLConnection", lpparam.classLoader, "setDefaultHostnameVerifier", + HostnameVerifier.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return null; + } + }); + + /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */ + /* public void setSSLSocketFactory(SSLSocketFactory) */ + Log.d(TAG, "Hooking HttpsURLConnection.setSSLSocketFactory for: " + currentPackageName); + findAndHookMethod("javax.net.ssl.HttpsURLConnection", lpparam.classLoader, "setSSLSocketFactory", javax.net.ssl.SSLSocketFactory.class, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return null; + } + }); + + /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */ + /* public void setHostnameVerifier(HostNameVerifier) */ + Log.d(TAG, "Hooking HttpsURLConnection.setHostnameVerifier for: " + currentPackageName); + findAndHookMethod("javax.net.ssl.HttpsURLConnection", lpparam.classLoader, "setHostnameVerifier", HostnameVerifier.class, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return null; + } + }); + + + /* WebView Hooks */ + /* frameworks/base/core/java/android/webkit/WebViewClient.java */ + /* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */ + Log.d(TAG, "Hooking WebViewClient.onReceivedSslError(WebView, SslErrorHandler, SslError) for: " + currentPackageName); + + findAndHookMethod("android.webkit.WebViewClient", lpparam.classLoader, "onReceivedSslError", + WebView.class, SslErrorHandler.class, SslError.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + ((android.webkit.SslErrorHandler) param.args[1]).proceed(); + return null; + } + }); + + /* frameworks/base/core/java/android/webkit/WebViewClient.java */ + /* public void onReceivedError(WebView, int, String, String) */ + Log.d(TAG, "Hooking WebViewClient.onReceivedSslError(WebView, int, string, string) for: " + currentPackageName); + + findAndHookMethod("android.webkit.WebViewClient", lpparam.classLoader, "onReceivedError", + WebView.class, int.class, String.class, String.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return null; + } + }); + + //SSLContext.init >> (null,ImSureItsLegitTrustManager,null) + findAndHookMethod("javax.net.ssl.SSLContext", lpparam.classLoader, "init", KeyManager[].class, TrustManager[].class, SecureRandom.class, new XC_MethodHook() { + + @Override + protected void beforeHookedMethod(MethodHookParam param) { + + param.args[0] = null; + param.args[1] = new TrustManager[]{new ImSureItsLegitTrustManager()}; + param.args[2] = null; + + } + }); + + // Multi-dex support: https://github.com/rovo89/XposedBridge/issues/30#issuecomment-68486449 + findAndHookMethod("android.app.Application", + lpparam.classLoader, + "attach", + Context.class, + new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) { + // Hook OkHttp or third party libraries. + Context context = (Context) param.args[0]; + processOkHttp(context.getClassLoader()); + processHttpClientAndroidLib(context.getClassLoader()); + processXutils(context.getClassLoader()); + } + } + ); + + /* Only for newer devices should we try to hook TrustManagerImpl */ + if (hasTrustManagerImpl()) { + /* TrustManagerImpl Hooks */ + /* external/conscrypt/src/platform/java/org/conscrypt/TrustManagerImpl.java */ + Log.d(TAG, "Hooking com.android.org.conscrypt.TrustManagerImpl for: " + currentPackageName); + + /* public void checkServerTrusted(X509Certificate[] chain, String authType) */ + findAndHookMethod("com.android.org.conscrypt.TrustManagerImpl", lpparam.classLoader, + "checkServerTrusted", X509Certificate[].class, String.class, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return 0; + } + }); + + /* public List checkServerTrusted(X509Certificate[] chain, + String authType, String host) throws CertificateException */ + findAndHookMethod("com.android.org.conscrypt.TrustManagerImpl", lpparam.classLoader, + "checkServerTrusted", X509Certificate[].class, String.class, + String.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return new ArrayList(); + } + }); + + + /* public List checkServerTrusted(X509Certificate[] chain, + String authType, SSLSession session) throws CertificateException */ + findAndHookMethod("com.android.org.conscrypt.TrustManagerImpl", lpparam.classLoader, + "checkServerTrusted", X509Certificate[].class, String.class, + SSLSession.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return new ArrayList(); + } + }); + + findAndHookMethod("com.android.org.conscrypt.TrustManagerImpl", lpparam.classLoader, "checkTrusted", X509Certificate[].class, String.class, SSLSession.class, SSLParameters.class, boolean.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return new ArrayList(); + } + }); + + + findAndHookMethod("com.android.org.conscrypt.TrustManagerImpl", lpparam.classLoader, "checkTrusted", X509Certificate[].class, byte[].class, byte[].class, String.class, String.class, boolean.class, new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) { + return new ArrayList(); + } + }); + } + + } // End Hooks + + /* Helpers */ + // Check for TrustManagerImpl class + @SuppressLint("PrivateApi") + public boolean hasTrustManagerImpl() { + try { + Class.forName("com.android.org.conscrypt.TrustManagerImpl"); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } + + private javax.net.ssl.SSLSocketFactory getEmptySSLFactory() { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[]{new ImSureItsLegitTrustManager()}, null); + return sslContext.getSocketFactory(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + return null; + } + } + + private void processXutils(ClassLoader classLoader) { + Log.d(TAG, "Hooking org.xutils.http.RequestParams.setSslSocketFactory(SSLSocketFactory) (3) for: " + currentPackageName); + try { + classLoader.loadClass("org.xutils.http.RequestParams"); + findAndHookMethod("org.xutils.http.RequestParams", classLoader, "setSslSocketFactory", javax.net.ssl.SSLSocketFactory.class, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) { + param.args[0] = getEmptySSLFactory(); + } + }); + findAndHookMethod("org.xutils.http.RequestParams", classLoader, "setHostnameVerifier", HostnameVerifier.class, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) { + param.args[0] = new ImSureItsLegitHostnameVerifier(); + } + }); + } catch (Exception e) { + Log.d(TAG, "org.xutils.http.RequestParams not found in " + currentPackageName + "-- not hooking"); + } + } + + void processOkHttp(ClassLoader classLoader) { + /* hooking OKHTTP by SQUAREUP */ + /* com/squareup/okhttp/CertificatePinner.java available online @ https://github.com/square/okhttp/blob/master/okhttp/src/main/java/com/squareup/okhttp/CertificatePinner.java */ + /* public void check(String hostname, List peerCertificates) throws SSLPeerUnverifiedException{}*/ + /* Either returns true or a exception so blanket return true */ + /* Tested against version 2.5 */ + Log.d(TAG, "Hooking com.squareup.okhttp.CertificatePinner.check(String,List) (2.5) for: " + currentPackageName); + + try { + classLoader.loadClass("com.squareup.okhttp.CertificatePinner"); + findAndHookMethod("com.squareup.okhttp.CertificatePinner", + classLoader, + "check", + String.class, + List.class, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam methodHookParam) { + return true; + } + }); + } catch (ClassNotFoundException e) { + // pass + Log.d(TAG, "OKHTTP 2.5 not found in " + currentPackageName + "-- not hooking"); + } + + //https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/CertificatePinner.java#L144 + Log.d(TAG, "Hooking okhttp3.CertificatePinner.check(String,List) (3.x) for: " + currentPackageName); + + try { + classLoader.loadClass("okhttp3.CertificatePinner"); + findAndHookMethod("okhttp3.CertificatePinner", + classLoader, + "check", + String.class, + List.class, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam methodHookParam) { + return null; + } + }); + } catch (ClassNotFoundException e) { + Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking"); + // pass + } + + //https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java + try { + classLoader.loadClass("okhttp3.internal.tls.OkHostnameVerifier"); + findAndHookMethod("okhttp3.internal.tls.OkHostnameVerifier", + classLoader, + "verify", + String.class, + javax.net.ssl.SSLSession.class, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam methodHookParam) { + return true; + } + }); + } catch (ClassNotFoundException e) { + Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking OkHostnameVerifier.verify(String, SSLSession)"); + // pass + } + + //https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java + try { + classLoader.loadClass("okhttp3.internal.tls.OkHostnameVerifier"); + findAndHookMethod("okhttp3.internal.tls.OkHostnameVerifier", + classLoader, + "verify", + String.class, + java.security.cert.X509Certificate.class, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam methodHookParam) { + return true; + } + }); + } catch (ClassNotFoundException e) { + Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking OkHostnameVerifier.verify(String, X509)("); + // pass + } + + //https://github.com/square/okhttp/blob/okhttp_4.2.x/okhttp/src/main/java/okhttp3/CertificatePinner.kt + Log.d(TAG, "Hooking okhttp3.CertificatePinner.check(String,List) (4.2.0+) for: " + currentPackageName); + + try { + classLoader.loadClass("okhttp3.CertificatePinner"); + findAndHookMethod("okhttp3.CertificatePinner", + classLoader, + "check$okhttp", + String.class, + "kotlin.jvm.functions.Function0", + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam methodHookParam) { + return null; + } + }); + } catch (ClassNotFoundException e) { + Log.d(TAG, "OKHTTP 4.2.0+ not found in " + currentPackageName + " -- not hooking"); + // pass + } + + } + + void processHttpClientAndroidLib(ClassLoader classLoader) { + /* httpclientandroidlib Hooks */ + /* public final void verify(String host, String[] cns, String[] subjectAlts, boolean strictWithSubDomains) throws SSLException */ + Log.d(TAG, "Hooking AbstractVerifier.verify(String, String[], String[], boolean) for: " + currentPackageName); + + try { + classLoader.loadClass("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + findAndHookMethod("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier", classLoader, "verify", + String.class, String[].class, String[].class, boolean.class, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam methodHookParam) { + return null; + } + }); + } catch (ClassNotFoundException e) { + // pass + Log.d(TAG, "httpclientandroidlib not found in " + currentPackageName + "-- not hooking"); + } + } + + @SuppressLint("CustomX509TrustManager") + private static class ImSureItsLegitTrustManager implements X509TrustManager { + @SuppressLint("TrustAllX509TrustManager") + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @SuppressLint("TrustAllX509TrustManager") + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } + + private static class ImSureItsLegitHostnameVerifier implements HostnameVerifier { + + @SuppressLint("BadHostnameVerifier") + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } + +} diff --git a/app/src/main/java/xfk233/GenshinProxy/MainHook.kt b/app/src/main/java/xfk233/GenshinProxy/MainHook.kt new file mode 100644 index 0000000..e64ac52 --- /dev/null +++ b/app/src/main/java/xfk233/GenshinProxy/MainHook.kt @@ -0,0 +1,140 @@ +package xfk233.GenshinProxy + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.AlertDialog +import android.text.Editable +import android.text.TextWatcher +import android.widget.EditText +import android.widget.Toast +import com.github.kyuubiran.ezxhelper.init.EzXHelperInit +import com.github.kyuubiran.ezxhelper.init.InitFields +import com.github.kyuubiran.ezxhelper.utils.findConstructor +import com.github.kyuubiran.ezxhelper.utils.findMethod +import com.github.kyuubiran.ezxhelper.utils.findMethodOrNull +import com.github.kyuubiran.ezxhelper.utils.hookBefore +import de.robv.android.xposed.IXposedHookLoadPackage +import de.robv.android.xposed.XposedBridge +import de.robv.android.xposed.callbacks.XC_LoadPackage +import java.util.regex.Pattern +import kotlin.system.exitProcess + + +class MainHook : IXposedHookLoadPackage { + private val regex = Pattern.compile("http(s|)://.*?\\.(hoyoverse|mihoyo|yuanshen|mob)\\.com") + private lateinit var server: String + + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { + if (lpparam.packageName != "com.miHoYo.GenshinImpact") return + EzXHelperInit.initHandleLoadPackage(lpparam) + findMethod("com.miHoYo.GetMobileInfo.MainActivity") { name == "onCreate" }.hookBefore { + val context = it.thisObject as Activity + val sp = context.getPreferences(0) + AlertDialog.Builder(context).apply { + setTitle("Select server / 选择服务器") + setMessage("Input server address / 请输入服务器地址: ") + setView(EditText(context).apply { + hint = "http(s)://server.com:1234" + val str = sp.getString("serverip", "") ?: "" + setText(str.toCharArray(), 0, str.length) + addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(p0: CharSequence, p1: Int, p2: Int, p3: Int) {} + override fun onTextChanged(p0: CharSequence, p1: Int, p2: Int, p3: Int) {} + + @SuppressLint("CommitPrefEdits") + override fun afterTextChanged(p0: Editable) { + sp.edit().run { + putString("serverip", p0.toString()) + apply() + } + } + }) + }) + setNegativeButton("Custom server / 自定义服务器") { _, _ -> + val ip = sp.getString("serverip", "") ?: "" + if (ip == "") { + Toast.makeText(context, "Server address error.", Toast.LENGTH_LONG).show() + exitProcess(1) + } else { + server = ip + } + sslHook(lpparam) + hook() + } + setNeutralButton("Official server / 官方服务器") { _, _ -> + } + }.show() + } + } + + private fun sslHook(lpparam: XC_LoadPackage.LoadPackageParam) { + findMethodOrNull("com.combosdk.lib.third.okhttp3.internal.tls.OkHostnameVerifier") { name == "verify" }?.hookBefore { + it.result = true + } + findMethodOrNull("com.combosdk.lib.third.okhttp3.CertificatePinner") { name == "check" && parameterTypes[0] == String::class.java && parameterTypes[1] == List::class.java }?.hookBefore { + it.result = null + } + JustTrustMe().hook(lpparam) + } + + private fun hook() { + findMethod("com.miHoYo.sdk.webview.MiHoYoWebview") { name == "load" && parameterTypes[0] == String::class.java && parameterTypes[1] == String::class.java }.hookBefore { + XposedBridge.log("old: " + it.args[1].toString()) + val m = regex.matcher(it.args[1].toString()) + if (m.find()) { + it.args[1] = m.replaceAll(server) + } + XposedBridge.log("new: " + it.args[1].toString()) + } + + findMethod("okhttp3.HttpUrl") { name == "parse" && parameterTypes[0] == String::class.java }.hookBefore { + XposedBridge.log("old: " + it.args[0].toString()) + val m = regex.matcher(it.args[0].toString()) + if (m.find()) { + it.args[0] = m.replaceAll(server) + } + XposedBridge.log("new: " + it.args[0].toString()) + } + findMethod("com.combosdk.lib.third.okhttp3.HttpUrl") { name == "parse" && parameterTypes[0] == String::class.java }.hookBefore { + XposedBridge.log("old: " + it.args[0].toString()) + val m = regex.matcher(it.args[0].toString()) + if (m.find()) { + it.args[0] = m.replaceAll(server) + } + XposedBridge.log("new: " + it.args[0].toString()) + } + + findMethod("com.google.gson.Gson") { name == "fromJson" && parameterTypes[0] == String::class.java && parameterTypes[1] == java.lang.reflect.Type::class.java }.hookBefore { + XposedBridge.log("old: " + it.args[0].toString()) + val m = regex.matcher(it.args[0].toString()) + if (m.find()) { + it.args[0] = m.replaceAll(server) + } + XposedBridge.log("new: " + it.args[0].toString()) + } + findConstructor("java.net.URL") { parameterTypes[0] == String::class.java }.hookBefore { + XposedBridge.log("old: " + it.args[0].toString()) + val m = regex.matcher(it.args[0].toString()) + if (m.find()) { + it.args[0] = m.replaceAll(server) + } + XposedBridge.log("new: " + it.args[0].toString()) + } + findMethod("com.combosdk.lib.third.okhttp3.Request\$Builder") { name == "url" && parameterTypes[0] == String::class.java }.hookBefore { + XposedBridge.log("old: " + it.args[0].toString()) + val m = regex.matcher(it.args[0].toString()) + if (m.find()) { + it.args[0] = m.replaceAll(server) + } + XposedBridge.log("new: " + it.args[0].toString()) + } + findMethod("okhttp3.Request\$Builder") { name == "url" && parameterTypes[0] == String::class.java }.hookBefore { + XposedBridge.log("old: " + it.args[0].toString()) + val m = regex.matcher(it.args[0].toString()) + if (m.find()) { + it.args[0] = m.replaceAll(server) + } + XposedBridge.log("new: " + it.args[0].toString()) + } + } +} diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..f24e813 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,6 @@ + + + + com.miHoYo.GenshinImpact + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..fac8215 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Genshin Impact Proxy + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..53b0d6b --- /dev/null +++ b/build.gradle @@ -0,0 +1,18 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.4" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..98bed16 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c3b8f0e --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Apr 29 17:30:47 CST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f147441 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + jcenter() // Warning: this repository is going to shut down soon + } +} +rootProject.name = "GenshinProxy" +include ':app'