Skip to content

Commit

Permalink
Add kotlin formatter and format old codes
Browse files Browse the repository at this point in the history
  • Loading branch information
JingMatrix committed Dec 12, 2022
1 parent 44a7f4c commit c3fd130
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 164 deletions.
3 changes: 2 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.ncorti.ktfmt.gradle") version "0.11.0"
}

android {
Expand All @@ -9,7 +10,7 @@ android {

defaultConfig {
applicationId = "org.matrix.chromext"
minSdk = 24
minSdk = 26
targetSdk = 33
versionCode = 1
versionName = "1.0.0"
Expand Down
49 changes: 25 additions & 24 deletions app/src/main/java/org/matrix/chromext/MainHook.kt
Original file line number Diff line number Diff line change
@@ -1,42 +1,43 @@
package org.matrix.chromext

import org.matrix.chromext.hook.BaseHook
import org.matrix.chromext.hook.ChromeHook
import com.github.kyuubiran.ezxhelper.init.EzXHelperInit
import com.github.kyuubiran.ezxhelper.utils.Log
import com.github.kyuubiran.ezxhelper.utils.Log.logexIfThrow
import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.IXposedHookZygoteInit
import de.robv.android.xposed.callbacks.XC_LoadPackage
import org.matrix.chromext.hook.BaseHook
import org.matrix.chromext.hook.ChromeHook

private const val PACKAGE_NAME_HOOKED = "com.android.chrome"
private const val TAG = "ChromeXt"

class MainHook : IXposedHookLoadPackage, IXposedHookZygoteInit /* Optional */ {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
if (lpparam.packageName == PACKAGE_NAME_HOOKED ) {
// Init EzXHelper
EzXHelperInit.initHandleLoadPackage(lpparam)
EzXHelperInit.setLogTag(TAG)
EzXHelperInit.setToastTag(TAG)
// Init hooks
initHooks(ChromeHook)
}
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
if (lpparam.packageName == PACKAGE_NAME_HOOKED) {
// Init EzXHelper
EzXHelperInit.initHandleLoadPackage(lpparam)
EzXHelperInit.setLogTag(TAG)
EzXHelperInit.setToastTag(TAG)
// Init hooks
initHooks(ChromeHook)
}
}

// Optional
override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
EzXHelperInit.initZygote(startupParam)
}
// Optional
override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
EzXHelperInit.initZygote(startupParam)
}

private fun initHooks(vararg hook: BaseHook) {
hook.forEach {
runCatching {
if (it.isInit) return@forEach
it.init()
it.isInit = true
Log.i("Inited hook: ${it.javaClass.simpleName}")
}.logexIfThrow("Failed init hook: ${it.javaClass.simpleName}")
}
private fun initHooks(vararg hook: BaseHook) {
hook.forEach {
runCatching {
if (it.isInit) return@forEach
it.init()
it.isInit = true
Log.i("Inited hook: ${it.javaClass.simpleName}")
}
.logexIfThrow("Failed init hook: ${it.javaClass.simpleName}")
}
}
}
22 changes: 12 additions & 10 deletions app/src/main/java/org/matrix/chromext/hook/BaseHook.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package org.matrix.chromext.hook

abstract class BaseHook {
var isInit: Boolean = false
abstract fun init()
companion object {
// Use frida-trace to find common method
const val SHOW_URL = "j"
// grep smali code with Tab.loadUrl to get the loadUrl function
const val LOAD_URL = "h"
// get TabImpl field Lorg/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl
const val TAB_FIELD = "a"
}
var isInit: Boolean = false
abstract fun init()
companion object {
// Use frida-trace to find method for casting GURL to String in org/chromium/url/GURL.smali
const val SHOW_URL = "j"
// grep smali code with Tab.loadUrl to get the loadUrl function in
// org/chromium/chrome/browser/tab/TabImpl.smali
const val LOAD_URL = "h"
// get TabImpl field in
// org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroidImpl.smali
const val TAB_FIELD = "a"
}
}
176 changes: 92 additions & 84 deletions app/src/main/java/org/matrix/chromext/hook/ChromeHook.kt
Original file line number Diff line number Diff line change
@@ -1,100 +1,108 @@
package org.matrix.chromext.hook

import com.github.kyuubiran.ezxhelper.utils.findAllMethods
import android.content.Context
import com.github.kyuubiran.ezxhelper.utils.Log
import com.github.kyuubiran.ezxhelper.utils.findMethod
import com.github.kyuubiran.ezxhelper.utils.hookBefore
import com.github.kyuubiran.ezxhelper.utils.hookAfter
import com.github.kyuubiran.ezxhelper.utils.hookBefore
import com.github.kyuubiran.ezxhelper.utils.invokeMethod
import com.github.kyuubiran.ezxhelper.utils.paramCount
import com.github.kyuubiran.ezxhelper.utils.Log
import com.github.kyuubiran.ezxhelper.utils.Log.logexIfThrow
import android.content.Context
import dalvik.system.PathClassLoader
import java.lang.reflect.Field
import java.net.URL

const val youtubeScript = """
const val youtubeScript =
"""
setTimeout(()=>{
const skipAds = () => {
let btn = document
.getElementsByClassName("ytp-ad-skip-button ytp-button")
.item(0);
if (btn) {
btn.click();
}
const skipAds = () => {
let btn = document
.getElementsByClassName("ytp-ad-skip-button ytp-button")
.item(0);
if (btn) {
btn.click();
}
const ad = [...document.querySelectorAll(".ad-showing")][0];
const vid = document.querySelector("video");
if (ad) {
vid.muted = true;
vid.currentTime = vid.duration;
} else {
if (vid != undefined) {
vid.muted = false;
}
}
};
const main = new MutationObserver(() => {
let adComponent = document.querySelector("ytd-ad-slot-renderer");
if (adComponent) {
const node = adComponent.closest('ytd-rich-item-renderer')
|| adComponent.closest('ytd-search-pyv-renderer') || adComponent;
node.remove();
}
let shortsNav = document.querySelector("div.pivot-bar-item-tab.pivot-shorts");
if (shortsNav) {
const node = shortsNav.closest('ytm-pivot-bar-item-renderer') || shortsNav;
node.remove();
}
const ad = [...document.querySelectorAll(".ad-showing")][0];
const vid = document.querySelector("video");
if (ad) {
vid.muted = true;
vid.currentTime = vid.duration;
} else {
if (vid != undefined) {
vid.muted = false;
}
}
};
const main = new MutationObserver(() => {
let adComponent = document.querySelector("ytd-ad-slot-renderer");
if (adComponent) {
const node = adComponent.closest('ytd-rich-item-renderer')
|| adComponent.closest('ytd-search-pyv-renderer') || adComponent;
node.remove();
}
let shortsNav = document.querySelector("div.pivot-bar-item-tab.pivot-shorts");
if (shortsNav) {
const node = shortsNav.closest('ytm-pivot-bar-item-renderer') || shortsNav;
node.remove();
}
const adContainer = document
.getElementsByClassName("video-ads ytp-ad-module")
.item(0);
if (adContainer) {
new MutationObserver(skipAds).observe(adContainer, {
attributes: true,
characterData: true,
childList: true,
});
}
});
const adContainer = document
.getElementsByClassName("video-ads ytp-ad-module")
.item(0);
if (adContainer) {
new MutationObserver(skipAds).observe(adContainer, {
attributes: true,
characterData: true,
childList: true,
});
}
});
main.observe(document.body, {
attributes: false,
characterData: false,
childList: true,
subtree: true,
});
main.observe(document.body, {
attributes: false,
characterData: false,
childList: true,
subtree: true,
});
}, 50);
"""

object ChromeHook : BaseHook() {
override fun init() {
findMethod("org.chromium.chrome.browser.base.SplitChromeApplication") {
name == "attachBaseContext"
}.hookAfter{
val ctx: Context = (it.args[0] as Context).createContextForSplit("chrome")
val UrlParams = ctx.getClassLoader().loadClass("org.chromium.content_public.browser.LoadUrlParams").getDeclaredConstructor(String::class.java)
val tabDelegateImpl = ctx.getClassLoader().loadClass(
"org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroidImpl")
val tabImpl: Field = tabDelegateImpl.getDeclaredField(TAB_FIELD)
tabImpl.setAccessible(true);
findMethod(tabDelegateImpl) { name == "onUpdateUrl" }.hookBefore {
val url = it.args[0].invokeMethod() {name == SHOW_URL} as String
Log.d("Load ${url}")
if (url.startsWith("chrome://xt")) {
// Reserve chrome://xt for futur usage
it.thisObject.invokeMethod() { name == "closeContents"}
} else if (url.startsWith("https://m.youtube.com")) {
Log.d("Inject userscript for m.youtube.com")
tabImpl.get(it.thisObject).invokeMethod(UrlParams.newInstance("javascript: ${youtubeScript}")) { name == LOAD_URL }
}
}
override fun init() {
findMethod("org.chromium.chrome.browser.base.SplitChromeApplication") {
name == "attachBaseContext"
}
.hookAfter {
val ctx: Context = (it.args[0] as Context).createContextForSplit("chrome")
val UrlParams =
ctx.getClassLoader()
.loadClass("org.chromium.content_public.browser.LoadUrlParams")
.getDeclaredConstructor(String::class.java)
val tabDelegateImpl =
ctx.getClassLoader()
.loadClass("org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroidImpl")
val tabImpl: Field = tabDelegateImpl.getDeclaredField(TAB_FIELD)
tabImpl.setAccessible(true)
findMethod(tabDelegateImpl) { name == "onUpdateUrl" }
.hookBefore {
val url = it.args[0].invokeMethod() { name == SHOW_URL } as String
Log.d("Load ${url}")
if (url.startsWith("chrome://xt")) {
// Reserve chrome://xt for futur usage
it.thisObject.invokeMethod() { name == "closeContents" }
} else if (url.startsWith("https://m.youtube.com")) {
Log.d("Inject userscript for m.youtube.com")
if (it.thisObject != null) {
tabImpl.get(it.thisObject).invokeMethod(
UrlParams.newInstance("javascript: ${youtubeScript}")) {
name == LOAD_URL
}
}
}
}

findMethod(tabDelegateImpl) { name == "addMessageToConsole" }.hookAfter {
// addMessageToConsole(int level, String message, int lineNumber, String sourceId) {
Log.i("[${it.args[0]}] ${it.args[1]} @${it.args[3]}:${it.args[2]}")
}
}
}
}
findMethod(tabDelegateImpl) { name == "addMessageToConsole" }
.hookAfter {
// addMessageToConsole(int level, String message, int lineNumber, String sourceId) {
Log.i("[${it.args[0]}] ${it.args[1]} @${it.args[3]}:${it.args[2]}")
}
}
}
}
83 changes: 38 additions & 45 deletions app/src/main/java/org/matrix/chromext/hook/InfoHook.kt
Original file line number Diff line number Diff line change
@@ -1,54 +1,47 @@
package org.matrix.chromext.hook

import com.github.kyuubiran.ezxhelper.utils.*
import com.github.kyuubiran.ezxhelper.utils.Log.logexIfThrow
import android.content.Context
import dalvik.system.PathClassLoader

object InfoHook : BaseHook() {
override fun init() {
findAllMethods("J.N") {
paramCount == 3
}.hookBefore {
if (
it.args[0]::class.simpleName == "Long" &&
it.args[1]::class.simpleName == "String"
) {
Log.d("${Thread.currentThread().getStackTrace()[9].getClassName()} > ${Thread.currentThread().getStackTrace()[8].getClassName()} > ${Thread.currentThread().getStackTrace()[7].getClassName()} call with ${it.args[0]} ${it.args[1]} ${it.args[2]}")
if (it.args[2]::class.simpleName == "JavaScriptCallback") {
Log.d("Call ${it.method.getName()} with ${it.args[2]::class.simpleName}")
}
}
}
override fun init() {
findAllMethods("J.N") { paramCount == 3 }
.hookBefore {
if (it.args[0]::class.simpleName == "Long" && it.args[1]::class.simpleName == "String") {
Log.d(
"${Thread.currentThread().getStackTrace()[9].getClassName()} > ${Thread.currentThread().getStackTrace()[8].getClassName()} > ${Thread.currentThread().getStackTrace()[7].getClassName()} call with ${it.args[0]} ${it.args[1]} ${it.args[2]}")
if (it.args[2]::class.simpleName == "JavaScriptCallback") {
Log.d("Call ${it.method.getName()} with ${it.args[2]::class.simpleName}")
}
}
}

// One can use signature to identify JNI, for example
// Mi3V1mlO(JLjava/lang/Object;ZIZLjava/lang/Object;)I must be
// int downloadImage(long nativeWebContentsAndroid, GURL url, boolean isFavicon, int maxBitmapSize, boolean bypassCache, ImageDownloadCallback callback);
// One can use signature to identify JNI, for example
// Mi3V1mlO(JLjava/lang/Object;ZIZLjava/lang/Object;)I must be
// int downloadImage(long nativeWebContentsAndroid, GURL url, boolean isFavicon, int
// maxBitmapSize, boolean bypassCache, ImageDownloadCallback callback);

// We want to test out which one is
// void evaluateJavaScriptForTests(long nativeWebContentsAndroid, String script, JavaScriptCallback callback);
// Currently, there are 11 options to test with: (JLjava/lang/String;Ljava/lang/Object;)V
// We want to test out which one is
// void evaluateJavaScriptForTests(long nativeWebContentsAndroid, String script,
// JavaScriptCallback callback);
// Currently, there are 11 options to test with: (JLjava/lang/String;Ljava/lang/Object;)V

findMethod("J.N") {
name == "Mi3V1mlO"
}.hookBefore {
Log.d("Call downloadImage with url: ${it.args[1].invokeMethod() {name == SHOW_URL}}")
}
findMethod("J.N") { name == "Mi3V1mlO" }
.hookBefore {
Log.d("Call downloadImage with url: ${it.args[1].invokeMethod() {name == SHOW_URL}}")
}

findMethod("java.lang.Runtime") {
name == "loadLibrary0" }.hookAfter {
if ("${it.args[1]}".startsWith("class org.chromium.base.library_loader", false)) {
Log.i("${it.args[0]} load ${it.args[1]}")
}
}
findAllMethods("org.chromium.chrome.browser.base.SplitChromeApplication") {
true
}.forEach {
var info: String = it.getName() + ": "
it.getParameterTypes().forEach {
info = info + it.getName() + " "
}
Log.i(info)
}
}
}
findMethod("java.lang.Runtime") { name == "loadLibrary0" }
.hookAfter {
if ("${it.args[1]}".startsWith("class org.chromium.base.library_loader", false)) {
Log.i("${it.args[0]} load ${it.args[1]}")
}
}

findAllMethods("org.chromium.chrome.browser.base.SplitChromeApplication") { true }
.forEach {
var info: String = it.getName() + ": "
it.getParameterTypes().forEach { info = info + it.getName() + " " }
Log.i(info)
}
}
}

0 comments on commit c3fd130

Please sign in to comment.