diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index d14260d..5c56ad7 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -12,6 +12,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 9075e3e..cbbe561 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,8 +4,10 @@
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 8978d23..a8a2b16 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index f914e84..5ae08f0 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -38,4 +38,6 @@ android {
dependencies {
compileOnly("de.robv.android.xposed:api:82")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
+ implementation("io.ktor:ktor-client-core:2.3.0")
+ implementation("io.ktor:ktor-client-cio:2.3.0")
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/dev/beefers/vendetta/xposed/Main.kt b/app/src/main/kotlin/dev/beefers/vendetta/xposed/Main.kt
index f2b6ed5..e4d4ce8 100644
--- a/app/src/main/kotlin/dev/beefers/vendetta/xposed/Main.kt
+++ b/app/src/main/kotlin/dev/beefers/vendetta/xposed/Main.kt
@@ -1,5 +1,11 @@
package dev.beefers.vendetta.xposed
+// Java
+import java.io.File
+
+// Kotlin
+import kotlinx.coroutines.runBlocking
+
// Android
import android.content.res.AssetManager
import android.content.res.XModuleResources
@@ -12,6 +18,19 @@ import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.callbacks.XC_LoadPackage
+// Ktor
+import io.ktor.client.*
+import io.ktor.client.call.body
+import io.ktor.client.engine.cio.*
+import io.ktor.client.request.*
+import io.ktor.client.statement.*
+import io.ktor.client.plugins.*
+import io.ktor.http.HttpHeaders
+import io.ktor.http.HttpStatusCode
+import io.ktor.http.isSuccess
+import io.ktor.utils.io.reader
+import java.lang.Exception
+
class Main : IXposedHookZygoteInit, IXposedHookLoadPackage {
private lateinit var resources: XModuleResources
@@ -28,16 +47,31 @@ class Main : IXposedHookZygoteInit, IXposedHookLoadPackage {
val loadScriptFromAssets = catalystInstance.getDeclaredMethod("jniLoadScriptFromAssets", AssetManager::class.java, String::class.java, Boolean::class.javaPrimitiveType)
val loadScriptFromFile = catalystInstance.getDeclaredMethod("jniLoadScriptFromFile", String::class.java, String::class.java, Boolean::class.javaPrimitiveType)
- // TODO: bundle (down)loading with etag
- // val cache = File(appInfo.dataDir, "cache").also { it.mkdirs() }
- // val bundle = File(cache, "vendetta.js")
+ val cache = File(appInfo.dataDir, "cache").also { it.mkdirs() }
+ val bundle = File(cache, "vendetta.js")
+ val etag = File(cache, "vendetta_etag.txt")
// TODO: loader config
+ // TODO: make fast.
val hook = object : XC_MethodHook() {
- override fun beforeHookedMethod(param: MethodHookParam) {
+ override fun beforeHookedMethod(param: MethodHookParam): Unit = runBlocking {
Log.i("Vendetta", "beforeHookedMethod called")
+ try {
+ val client = HttpClient(CIO) { install(HttpTimeout) { requestTimeoutMillis = 3000 } }
+ val response: HttpResponse = client.get("https://raw.githubusercontent.com/vendetta-mod/builds/master/vendetta.js") {
+ headers { if (etag.exists() && bundle.exists()) append(HttpHeaders.IfNoneMatch, etag.readText()) }
+ }
+
+ if (response.status.isSuccess()) {
+ bundle.writeText(response.bodyAsText())
+ if (response.headers.contains("Etag")) etag.writeText(response.headers["Etag"] as String)
+ }
+ } catch (e: Exception) {
+ Log.e("Vendetta", "Failed to download Vendetta")
+ }
+
Log.i("Vendetta", "Exfiltrating Metro modules")
XposedBridge.invokeOriginalMethod(loadScriptFromAssets,param.thisObject, arrayOf(resources.assets, "assets://js/modules.js", true))
Log.i("Vendetta", "Executing identity snippet")
@@ -46,6 +80,11 @@ class Main : IXposedHookZygoteInit, IXposedHookLoadPackage {
override fun afterHookedMethod(param: MethodHookParam) {
Log.i("Vendetta", "afterHookedMethod called")
+
+ try {
+ Log.i("Vendetta", "Executing Vendetta")
+ XposedBridge.invokeOriginalMethod(loadScriptFromFile, param.thisObject, arrayOf(bundle.absolutePath, bundle.absolutePath, param.args[2]))
+ } catch(_: Exception) {}
}
}
diff --git a/build.gradle.kts b/build.gradle.kts
index 9ef8cdc..b7441e1 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,6 +2,8 @@
buildscript {
repositories {
google()
+ mavenCentral()
+ // We need jcenter for the Xposed API...
jcenter()
}
dependencies {
@@ -16,6 +18,8 @@ buildscript {
allprojects {
repositories {
google()
+ mavenCentral()
+ // See comment in buildscript repos
jcenter()
}
}