(TrustAllManager()),
+ SecureRandom()
+ )
+ sSLSocketFactory = sc.socketFactory
+ } catch (e: Exception) {
+ }
+
+ return sSLSocketFactory
+ }
+
+
+ private class TrustAllManager : X509TrustManager {
+ @Throws(CertificateException::class)
+ override fun checkClientTrusted(chain: Array
, authType: String) {
+ }
+
+ @Throws(CertificateException::class)
+ override fun checkServerTrusted(chain: Array, authType: String) {
+ }
+
+ override fun getAcceptedIssuers(): Array {
+ return arrayOf()
+ }
+ }
+
+ private class TrustAllHostnameVerifier : HostnameVerifier {
+ override fun verify(hostname: String, session: SSLSession): Boolean {
+ return true
+ }
+ }
+}
diff --git a/app/src/main/java/com/wrbug/developerhelper/util/ShaUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShaUtils.kt
similarity index 86%
rename from app/src/main/java/com/wrbug/developerhelper/util/ShaUtils.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShaUtils.kt
index e86896b..3429762 100644
--- a/app/src/main/java/com/wrbug/developerhelper/util/ShaUtils.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShaUtils.kt
@@ -1,8 +1,6 @@
-package com.wrbug.developerhelper.util
+package com.wrbug.developerhelper.commonutil
-import android.provider.SyncStateContract.Helpers.update
import java.security.MessageDigest
-import kotlin.experimental.and
object ShaUtils {
diff --git a/app/src/main/java/com/wrbug/developerhelper/util/ShellUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShellUtils.kt
similarity index 80%
rename from app/src/main/java/com/wrbug/developerhelper/util/ShellUtils.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShellUtils.kt
index caa12b6..8c36c0f 100644
--- a/app/src/main/java/com/wrbug/developerhelper/util/ShellUtils.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShellUtils.kt
@@ -1,14 +1,11 @@
-package com.wrbug.developerhelper.util
+package com.wrbug.developerhelper.commonutil
import com.jaredrummler.android.shell.CommandResult
import com.jaredrummler.android.shell.Shell
-import com.wrbug.developerhelper.model.mmkv.ConfigKv
-import com.wrbug.developerhelper.model.mmkv.manager.MMKVManager
+import com.wrbug.developerhelper.mmkv.ConfigKv
+import com.wrbug.developerhelper.mmkv.manager.MMKVManager
import org.jetbrains.anko.doAsync
-import java.util.concurrent.Executor
-import java.util.concurrent.Executors
-
object ShellUtils {
val configKv = MMKVManager.get(ConfigKv::class.java)
fun run(cmds: Array, callback: ShellResultCallback) {
@@ -23,7 +20,7 @@ object ShellUtils {
}
fun runWithSu(cmds: Array, callback: ShellResultCallback) {
- if (!configKv.getOpenRoot()) {
+ if (!configKv.isOpenRoot()) {
callback.onError("未开启root权限")
return
}
@@ -39,12 +36,16 @@ object ShellUtils {
fun runWithSu(vararg cmds: String): CommandResult {
- if (configKv.getOpenRoot().not()) {
+ if (configKv.isOpenRoot().not()) {
return CommandResult(arrayListOf("未开启root权限"), arrayListOf("未开启root权限"), 1)
}
return Shell.SU.run(*cmds)
}
+ fun isRoot(): Boolean {
+ return Shell.SU.available()
+ }
+
abstract class ShellResultCallback(vararg args: Any) {
protected var args = args
open fun onComplete(result: CommandResult) {
diff --git a/app/src/main/java/com/wrbug/developerhelper/util/StringConvertUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringConvertUtils.kt
similarity index 76%
rename from app/src/main/java/com/wrbug/developerhelper/util/StringConvertUtils.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringConvertUtils.kt
index 72c3ea4..4aca828 100644
--- a/app/src/main/java/com/wrbug/developerhelper/util/StringConvertUtils.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringConvertUtils.kt
@@ -1,4 +1,4 @@
-package com.wrbug.developerhelper.util
+package com.wrbug.developerhelper.commonutil
import java.math.BigDecimal
import java.math.BigInteger
@@ -13,19 +13,21 @@ fun String.isInt(): Boolean {
}
fun String.isDecimal(): Boolean {
- if (this=="0") {
+ if (this == "0") {
return true
}
val pattern = Pattern.compile("-?([0-9]+\\.0*)?[1-9][0-9]*$")
return pattern.matcher(this).find()
}
+
fun String.isNumber(): Boolean {
- if (this=="0") {
+ if (this == "0") {
return true
}
val pattern = Pattern.compile("-?[1-9][0-9]*$")
return pattern.matcher(this).find()
}
+
fun String.isBoolean(): Boolean {
return this.toLowerCase() == "true" || this.toLowerCase() == "false"
}
@@ -52,3 +54,17 @@ fun String.toLong(): Long {
return 0L
}
+
+fun String.toFloat(): Float {
+ if (isDecimal()) {
+ return BigDecimal(this).toFloat()
+ }
+ return 0F
+}
+
+fun String.toDouble(): Double {
+ if (isDecimal()) {
+ return BigDecimal(this).toDouble()
+ }
+ return 0.0
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/wrbug/developerhelper/util/UiUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/UiUtils.kt
similarity index 72%
rename from app/src/main/java/com/wrbug/developerhelper/util/UiUtils.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/UiUtils.kt
index ce897c8..be487bf 100644
--- a/app/src/main/java/com/wrbug/developerhelper/util/UiUtils.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/UiUtils.kt
@@ -1,11 +1,10 @@
-package com.wrbug.developerhelper.util
+package com.wrbug.developerhelper.commonutil
import android.app.Dialog
import android.content.Context
import android.util.TypedValue
import android.view.View
import androidx.fragment.app.Fragment
-import com.wrbug.developerhelper.basecommon.BaseApp
fun Context.dp2px(dpVal: Float): Int = UiUtils.dp2px(this, dpVal)
fun Fragment.dp2px(dpVal: Float): Int = UiUtils.dp2px(activity!!, dpVal)
@@ -14,7 +13,7 @@ fun View.dp2px(dpVal: Float): Int = UiUtils.dp2px(context, dpVal)
object UiUtils {
- fun dp2px(context: Context = BaseApp.instance, dpVal: Float): Int {
+ fun dp2px(context: Context = CommonUtils.application, dpVal: Float): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dpVal,
@@ -22,23 +21,23 @@ object UiUtils {
).toInt()
}
- fun sp2px(context: Context = BaseApp.instance, spVal: Float): Int {
+ fun sp2px(context: Context = CommonUtils.application, spVal: Float): Int {
return TypedValue.applyDimension(2, spVal, context.resources?.displayMetrics).toInt()
}
- fun px2dp(context: Context = BaseApp.instance, pxVal: Float): Float {
+ fun px2dp(context: Context = CommonUtils.application, pxVal: Float): Float {
val scale = context.resources?.displayMetrics?.density!!
return pxVal / scale
}
- fun px2sp(context: Context = BaseApp.instance, pxVal: Float): Float {
+ fun px2sp(context: Context = CommonUtils.application, pxVal: Float): Float {
return pxVal / context.resources?.displayMetrics?.scaledDensity!!
}
/**
* 获取屏幕高度
*/
- fun getDeviceHeight(context: Context = BaseApp.instance): Int {
+ fun getDeviceHeight(context: Context = CommonUtils.application): Int {
val dm = context.resources?.displayMetrics
return dm?.heightPixels ?: 0
}
@@ -46,7 +45,7 @@ object UiUtils {
/**
* 获取屏幕宽度
*/
- fun getDeviceWidth(context: Context = BaseApp.instance): Int {
+ fun getDeviceWidth(context: Context = CommonUtils.application): Int {
val dm = context.resources?.displayMetrics
return dm?.widthPixels ?: 0
}
@@ -54,7 +53,7 @@ object UiUtils {
/**
* 获取状态栏的高度
*/
- fun getStatusHeight(context: Context = BaseApp.instance): Int {
+ fun getStatusHeight(context: Context = CommonUtils.application): Int {
var result: Int? = 10
val resourceId =
context.resources?.getIdentifier("status_bar_height", "dimen", "android") ?: 0
diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ZipUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ZipUtils.kt
new file mode 100644
index 0000000..eb380ef
--- /dev/null
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ZipUtils.kt
@@ -0,0 +1,193 @@
+package com.wrbug.developerhelper.commonutil
+
+import java.io.*
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
+import java.util.zip.ZipOutputStream
+
+object ZipUtils {
+ fun zip(src: File, outFile: File) {
+ //提供了一个数据项压缩成一个ZIP归档输出流
+ var out: ZipOutputStream? = null
+ try {
+ out = ZipOutputStream(FileOutputStream(outFile))
+ //如果此文件是一个文件,否则为false。
+ if (src.isFile) {
+ zipFileOrDirectory(out, src, "")
+ } else {
+ //返回一个文件或空阵列。
+ val entries = src.listFiles()
+ for (i in entries!!.indices) {
+ // 递归压缩,更新curPaths
+ zipFileOrDirectory(out, entries[i], "")
+ }
+ }
+ } catch (ex: IOException) {
+ ex.printStackTrace()
+ } finally {
+ //关闭输出流
+ if (out != null) {
+ try {
+ out.close()
+ } catch (ex: IOException) {
+ ex.printStackTrace()
+ }
+
+ }
+ }
+ }
+
+ fun zip(src: File): File? {
+ val outFile = File(src.parent, src.name + ".zip")//源文件或者目录
+ zip(src, outFile)
+ return outFile
+ }
+
+ @Throws(IOException::class)
+ private fun zipFileOrDirectory(
+ out: ZipOutputStream,
+ fileOrDirectory: File, curPath: String
+ ) {
+ //从文件中读取字节的输入流
+ var fileInputStream: FileInputStream? = null
+ try {
+ //如果此文件是一个目录,否则返回false。
+ if (!fileOrDirectory.isDirectory) {
+ // 压缩文件
+ val buffer = ByteArray(4096)
+ fileInputStream = FileInputStream(fileOrDirectory)
+ //实例代表一个条目内的ZIP归档
+ val entry = ZipEntry(curPath + fileOrDirectory.name)
+ //条目的信息写入底层流
+ out.putNextEntry(entry)
+ var bytesRead: Int = fileInputStream.read(buffer)
+ while (bytesRead != -1) {
+ out.write(buffer, 0, bytesRead)
+ bytesRead = fileInputStream.read(buffer)
+ }
+ out.closeEntry()
+ } else {
+ // 压缩目录
+ val entries = fileOrDirectory.listFiles()
+ for (i in entries!!.indices) {
+ // 递归压缩,更新curPaths
+ zipFileOrDirectory(
+ out, entries[i], curPath
+ + fileOrDirectory.name + "/"
+ )
+ }
+ }
+ } catch (ex: IOException) {
+ ex.printStackTrace()
+ // throw ex;
+ } finally {
+ if (fileInputStream != null) {
+ try {
+ fileInputStream.close()
+ } catch (ex: IOException) {
+ ex.printStackTrace()
+ }
+
+ }
+ }
+ }
+
+ @Throws(IOException::class)
+ fun unzip(zipFileName: String, outputDirectory: String) {
+ var zipFile: ZipFile? = null
+ try {
+ zipFile = ZipFile(zipFileName)
+ val e = zipFile.entries()
+ var zipEntry: ZipEntry? = null
+ val dest = File(outputDirectory)
+ dest.mkdirs()
+ while (e.hasMoreElements()) {
+ zipEntry = e.nextElement() as ZipEntry
+ val entryName = zipEntry.name
+ var inputStream: InputStream? = null
+ var out: FileOutputStream? = null
+ try {
+ if (zipEntry.isDirectory) {
+ var name = zipEntry.name
+ name = name.substring(0, name.length - 1)
+ val f = File(
+ outputDirectory + File.separator
+ + name
+ )
+ f.mkdirs()
+ } else {
+ var index = entryName.lastIndexOf("\\")
+ if (index != -1) {
+ val df = File(
+ outputDirectory + File.separator
+ + entryName.substring(0, index)
+ )
+ df.mkdirs()
+ }
+ index = entryName.lastIndexOf("/")
+ if (index != -1) {
+ val df = File(
+ outputDirectory + File.separator
+ + entryName.substring(0, index)
+ )
+ df.mkdirs()
+ }
+ val f = File(
+ outputDirectory + File.separator
+ + zipEntry.name
+ )
+ // f.createNewFile();
+ inputStream = zipFile.getInputStream(zipEntry)
+ out = FileOutputStream(f)
+ val by = ByteArray(1024)
+ var c: Int = inputStream?.read(by) ?: -1
+ while (c != -1) {
+ out.write(by, 0, c)
+ c = inputStream?.read(by) ?: -1
+ }
+ out.flush()
+ }
+ } catch (ex: IOException) {
+ ex.printStackTrace()
+ throw IOException("解压失败:" + ex.toString())
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close()
+ } catch (ex: IOException) {
+ }
+
+ }
+ if (out != null) {
+ try {
+ out.close()
+ } catch (ex: IOException) {
+ }
+
+ }
+ }
+ }
+ } catch (ex: IOException) {
+ ex.printStackTrace()
+ throw IOException("解压失败:" + ex.toString())
+ } finally {
+ if (zipFile != null) {
+ try {
+ zipFile.close()
+ } catch (ex: IOException) {
+ }
+
+ }
+ }
+ }
+}
+
+
+fun File.zip(): File? {
+ return ZipUtils.zip(this)
+}
+
+
+fun File.zip(outFile: File) {
+ return ZipUtils.zip(this, outFile)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/ApkInfo.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkInfo.kt
similarity index 79%
rename from app/src/main/java/com/wrbug/developerhelper/model/entity/ApkInfo.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkInfo.kt
index 700f235..5e8b861 100644
--- a/app/src/main/java/com/wrbug/developerhelper/model/entity/ApkInfo.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkInfo.kt
@@ -1,11 +1,11 @@
-package com.wrbug.developerhelper.model.entity
+package com.wrbug.developerhelper.commonutil.entity
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.graphics.drawable.Drawable
import android.os.Parcel
import android.os.Parcelable
-import com.wrbug.developerhelper.basecommon.BaseApp
+import com.wrbug.developerhelper.commonutil.CommonUtils
class ApkInfo(val packageInfo: PackageInfo, val applicationInfo: ApplicationInfo) : Parcelable {
@@ -15,11 +15,11 @@ class ApkInfo(val packageInfo: PackageInfo, val applicationInfo: ApplicationInfo
)
fun getIco(): Drawable {
- return applicationInfo.loadIcon(BaseApp.instance.packageManager)
+ return applicationInfo.loadIcon(CommonUtils.application.packageManager)
}
fun getAppName(): String {
- val label = BaseApp.instance.packageManager.getApplicationLabel(applicationInfo)
+ val label = CommonUtils.application.packageManager.getApplicationLabel(applicationInfo)
return if (label == null) "" else label.toString()
}
diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/ApkSignInfo.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkSignInfo.kt
similarity index 52%
rename from app/src/main/java/com/wrbug/developerhelper/model/entity/ApkSignInfo.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkSignInfo.kt
index 9aef60c..4b3f2ed 100644
--- a/app/src/main/java/com/wrbug/developerhelper/model/entity/ApkSignInfo.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkSignInfo.kt
@@ -1,4 +1,4 @@
-package com.wrbug.developerhelper.model.entity
+package com.wrbug.developerhelper.commonutil.entity
class ApkSignInfo {
var md5 = ""
diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/FragmentInfo.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/FragmentInfo.kt
similarity index 97%
rename from app/src/main/java/com/wrbug/developerhelper/model/entity/FragmentInfo.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/FragmentInfo.kt
index a134f38..ab23081 100644
--- a/app/src/main/java/com/wrbug/developerhelper/model/entity/FragmentInfo.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/FragmentInfo.kt
@@ -1,4 +1,4 @@
-package com.wrbug.developerhelper.model.entity
+package com.wrbug.developerhelper.commonutil.entity
import android.os.Parcel
import android.os.Parcelable
diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/LsFileInfo.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/LsFileInfo.kt
similarity index 66%
rename from app/src/main/java/com/wrbug/developerhelper/model/entity/LsFileInfo.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/LsFileInfo.kt
index 5a284a6..7ec45dc 100644
--- a/app/src/main/java/com/wrbug/developerhelper/model/entity/LsFileInfo.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/LsFileInfo.kt
@@ -1,4 +1,4 @@
-package com.wrbug.developerhelper.model.entity
+package com.wrbug.developerhelper.commonutil.entity
class LsFileInfo {
var type = ""
diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/TopActivityInfo.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/TopActivityInfo.kt
similarity index 94%
rename from app/src/main/java/com/wrbug/developerhelper/model/entity/TopActivityInfo.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/TopActivityInfo.kt
index 6acb4f7..d5e415a 100644
--- a/app/src/main/java/com/wrbug/developerhelper/model/entity/TopActivityInfo.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/TopActivityInfo.kt
@@ -1,4 +1,4 @@
-package com.wrbug.developerhelper.model.entity
+package com.wrbug.developerhelper.commonutil.entity
import android.os.Parcel
import android.os.Parcelable
diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Callback.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Callback.kt
new file mode 100644
index 0000000..4bc41c9
--- /dev/null
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Callback.kt
@@ -0,0 +1,6 @@
+package com.wrbug.developerhelper.commonutil.shell
+
+interface Callback {
+ fun onSuccess(data: T)
+ fun onFailed(msg: String = "") {}
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/wrbug/developerhelper/shell/ShellManager.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellManager.kt
similarity index 81%
rename from app/src/main/java/com/wrbug/developerhelper/shell/ShellManager.kt
rename to commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellManager.kt
index 1804a02..801eb3c 100644
--- a/app/src/main/java/com/wrbug/developerhelper/shell/ShellManager.kt
+++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellManager.kt
@@ -1,12 +1,11 @@
-package com.wrbug.developerhelper.shell
+package com.wrbug.developerhelper.commonutil.shell
import com.jaredrummler.android.shell.CommandResult
-import com.wrbug.developerhelper.basecommon.BaseApp
-import com.wrbug.developerhelper.model.entity.FragmentInfo
-import com.wrbug.developerhelper.model.entity.LsFileInfo
-import com.wrbug.developerhelper.model.entity.TopActivityInfo
-import com.wrbug.developerhelper.util.ShellUtils
-import com.yhao.floatwindow.FloatActivity
+import com.wrbug.developerhelper.commonutil.CommonUtils
+import com.wrbug.developerhelper.commonutil.ShellUtils
+import com.wrbug.developerhelper.commonutil.entity.FragmentInfo
+import com.wrbug.developerhelper.commonutil.entity.LsFileInfo
+import com.wrbug.developerhelper.commonutil.entity.TopActivityInfo
import java.io.File
import java.util.regex.Pattern
@@ -24,6 +23,10 @@ object ShellManager {
private const val SHELL_GET_ZIP_FILE_LIST =
"app_process -Djava.class.path=/data/local/tmp/zip.dex /data/local/tmp Zip %s"
private const val SHELL_CHECK_IS_SQLITE = "od -An -tx %1\$s |grep '694c5153'"
+ private const val SHELL_UNINSTALL_APP = "pm uninstall %1\$s"
+ private const val SHELL_CLEAR_APP_DATA = "pm clear %1\$s"
+ private const val SHELL_FORCE_STOP_APP = "am force-stop %1\$s"
+ private val SHELL_OPEN_ADB_WIFI = arrayOf("setprop service.adb.tcp.port 5555", "stop adbd", "start adbd")
fun getTopActivity(callback: Callback) {
ShellUtils.runWithSu(arrayOf(SHELL_TOP_ACTIVITY), object : ShellUtils.ShellResultCallback() {
override fun onComplete(result: CommandResult) {
@@ -196,11 +199,26 @@ object ShellManager {
return commandResult.getStdout()
}
+ fun rmFile(file: String): Boolean {
+ val commandResult = ShellUtils.runWithSu("rm -rf $file")
+ return commandResult.isSuccessful
+ }
+
fun modifyFile(filaPath: String, content: String): Boolean {
val commandResult = ShellUtils.runWithSu("echo $content>> $filaPath")
return commandResult.isSuccessful
}
+ fun cpFile(source: String, dst: String, mod: String = "666"): Boolean {
+ val dir = dst.substring(0, dst.lastIndexOf("/"))
+ var commandResult = ShellUtils.runWithSu("mkdir -p $dir")
+ if (commandResult.isSuccessful.not()) {
+ return false
+ }
+ commandResult = ShellUtils.runWithSu("cp -R $source $dst && chmod $mod $dst")
+ return commandResult.isSuccessful || commandResult.getStderr()?.contains("Operation not permitted") ?: false
+ }
+
fun catFile(source: String, dst: String, mod: String? = null): Boolean {
val cmds = arrayListOf()
cmds.add("cat $source> $dst")
@@ -212,7 +230,7 @@ object ShellManager {
}
fun getZipFileList(path: String): List {
- val file = File(BaseApp.instance.cacheDir, "zip.dex")
+ val file = File(CommonUtils.application.cacheDir, "zip.dex")
if (file.exists()) {
ShellUtils.runWithSu("cp ${file.absolutePath} /data/local/tmp", "rm -rf ${file.absolutePath}")
}
@@ -226,4 +244,29 @@ object ShellManager {
return commandResult.stdout
}
+ fun findApkDir(packageName: String): String {
+ val cmd = "ls /data/app/|grep $packageName"
+ val dir = ShellUtils.runWithSu(cmd).getStdout()
+ return "/data/app/$dir/base.apk"
+ }
+
+ fun uninstallApp(packageName: String): Boolean {
+ val commandResult = ShellUtils.runWithSu(String.format(SHELL_UNINSTALL_APP, packageName))
+ return commandResult.isSuccessful
+ }
+
+ fun clearAppData(packageName: String): Boolean {
+ val commandResult = ShellUtils.runWithSu(String.format(SHELL_CLEAR_APP_DATA, packageName))
+ return commandResult.isSuccessful
+ }
+
+ fun forceStopApp(packageName: String): Boolean {
+ val commandResult = ShellUtils.runWithSu(String.format(SHELL_FORCE_STOP_APP, packageName))
+ return commandResult.isSuccessful
+ }
+
+ fun openAdbWifi(): Boolean {
+ val commandResult = ShellUtils.runWithSu(*SHELL_OPEN_ADB_WIFI)
+ return commandResult.isSuccessful
+ }
}
diff --git a/commonutil/src/main/res/values/strings.xml b/commonutil/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a900d65
--- /dev/null
+++ b/commonutil/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ commonutil
+
diff --git a/commonutil/src/test/java/com/wrbug/developerhelper/commonutil/ExampleUnitTest.java b/commonutil/src/test/java/com/wrbug/developerhelper/commonutil/ExampleUnitTest.java
new file mode 100644
index 0000000..e4cabfb
--- /dev/null
+++ b/commonutil/src/test/java/com/wrbug/developerhelper/commonutil/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.wrbug.developerhelper.commonutil;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/commonwidget/.gitignore b/commonwidget/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/commonwidget/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/commonwidget/build.gradle b/commonwidget/build.gradle
new file mode 100644
index 0000000..9e215c4
--- /dev/null
+++ b/commonwidget/build.gradle
@@ -0,0 +1,36 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion 28
+
+
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ testImplementation 'junit:junit:4.12'
+ implementation project(':commonutil')
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
+}
diff --git a/commonwidget/proguard-rules.pro b/commonwidget/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/commonwidget/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
diff --git a/commonwidget/src/androidTest/java/com/wrbug/developerhelper/commonwidget/ExampleInstrumentedTest.java b/commonwidget/src/androidTest/java/com/wrbug/developerhelper/commonwidget/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..78375d2
--- /dev/null
+++ b/commonwidget/src/androidTest/java/com/wrbug/developerhelper/commonwidget/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.wrbug.developerhelper.commonwidget;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.wrbug.developerhelper.commonwidget.test", appContext.getPackageName());
+ }
+}
diff --git a/commonwidget/src/main/AndroidManifest.xml b/commonwidget/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1099276
--- /dev/null
+++ b/commonwidget/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/flexibletoast/FlexibleToast.kt b/commonwidget/src/main/java/com/wrbug/developerhelper/commonwidget/flexibletoast/FlexibleToast.kt
similarity index 96%
rename from app/src/main/java/com/wrbug/developerhelper/ui/widget/flexibletoast/FlexibleToast.kt
rename to commonwidget/src/main/java/com/wrbug/developerhelper/commonwidget/flexibletoast/FlexibleToast.kt
index 9fc4c1b..f492466 100644
--- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/flexibletoast/FlexibleToast.kt
+++ b/commonwidget/src/main/java/com/wrbug/developerhelper/commonwidget/flexibletoast/FlexibleToast.kt
@@ -1,4 +1,4 @@
-package com.wrbug.developerhelper.ui.widget.flexibletoast
+package com.wrbug.developerhelper.commonwidget.flexibletoast
import android.content.Context
import android.view.Gravity
@@ -7,8 +7,8 @@ import android.view.View
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
-import com.wrbug.developerhelper.R
-import com.wrbug.developerhelper.util.UiUtils
+import com.wrbug.developerhelper.commonutil.UiUtils
+import com.wrbug.developerhelper.commonwidget.R
class FlexibleToast(private val mContext: Context) {
diff --git a/app/src/main/res/drawable/toast_bg_circle_cornor_rect.xml b/commonwidget/src/main/res/drawable/toast_bg_circle_cornor_rect.xml
similarity index 100%
rename from app/src/main/res/drawable/toast_bg_circle_cornor_rect.xml
rename to commonwidget/src/main/res/drawable/toast_bg_circle_cornor_rect.xml
diff --git a/app/src/main/res/layout/layout_toast_flexible.xml b/commonwidget/src/main/res/layout/layout_toast_flexible.xml
similarity index 100%
rename from app/src/main/res/layout/layout_toast_flexible.xml
rename to commonwidget/src/main/res/layout/layout_toast_flexible.xml
diff --git a/commonwidget/src/main/res/values/strings.xml b/commonwidget/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f6c0897
--- /dev/null
+++ b/commonwidget/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ commonwidget
+
diff --git a/commonwidget/src/test/java/com/wrbug/developerhelper/commonwidget/ExampleUnitTest.java b/commonwidget/src/test/java/com/wrbug/developerhelper/commonwidget/ExampleUnitTest.java
new file mode 100644
index 0000000..0cf0c5a
--- /dev/null
+++ b/commonwidget/src/test/java/com/wrbug/developerhelper/commonwidget/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.wrbug.developerhelper.commonwidget;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/mmkv/.gitignore b/mmkv/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/mmkv/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/mmkv/build.gradle b/mmkv/build.gradle
new file mode 100644
index 0000000..1f89e36
--- /dev/null
+++ b/mmkv/build.gradle
@@ -0,0 +1,40 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion 28
+
+
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'com.tencent:mmkv:1.0.14'
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.0-alpha3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha3'
+ implementation 'com.google.code.gson:gson:2.8.5'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
+}
diff --git a/mmkv/proguard-rules.pro b/mmkv/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/mmkv/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
diff --git a/mmkv/src/androidTest/java/com/wrbug/developerhelper/mmkv/ExampleInstrumentedTest.java b/mmkv/src/androidTest/java/com/wrbug/developerhelper/mmkv/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..161e03c
--- /dev/null
+++ b/mmkv/src/androidTest/java/com/wrbug/developerhelper/mmkv/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.wrbug.developerhelper.mmkv;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.wrbug.developerhelper.mmkv.test", appContext.getPackageName());
+ }
+}
diff --git a/mmkv/src/main/AndroidManifest.xml b/mmkv/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1b6e5f2
--- /dev/null
+++ b/mmkv/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/mmkv/src/main/java/com/wrbug/developerhelper/mmkv/ConfigKv.kt b/mmkv/src/main/java/com/wrbug/developerhelper/mmkv/ConfigKv.kt
new file mode 100644
index 0000000..bb1bb5f
--- /dev/null
+++ b/mmkv/src/main/java/com/wrbug/developerhelper/mmkv/ConfigKv.kt
@@ -0,0 +1,6 @@
+package com.wrbug.developerhelper.mmkv
+
+interface ConfigKv {
+ fun setOpenRoot(openRoot: Boolean)
+ fun isOpenRoot(): Boolean
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/wrbug/developerhelper/model/mmkv/manager/MMKVInvocationHandler.kt b/mmkv/src/main/java/com/wrbug/developerhelper/mmkv/manager/MMKVInvocationHandler.kt
similarity index 76%
rename from app/src/main/java/com/wrbug/developerhelper/model/mmkv/manager/MMKVInvocationHandler.kt
rename to mmkv/src/main/java/com/wrbug/developerhelper/mmkv/manager/MMKVInvocationHandler.kt
index 18d016a..f80b2f5 100644
--- a/app/src/main/java/com/wrbug/developerhelper/model/mmkv/manager/MMKVInvocationHandler.kt
+++ b/mmkv/src/main/java/com/wrbug/developerhelper/mmkv/manager/MMKVInvocationHandler.kt
@@ -1,7 +1,7 @@
-package com.wrbug.developerhelper.model.mmkv.manager
+package com.wrbug.developerhelper.mmkv.manager
+import com.google.gson.Gson
import com.tencent.mmkv.MMKV
-import com.wrbug.developerhelper.util.JsonHelper
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
@@ -12,14 +12,16 @@ class MMKVInvocationHandler(clazz: Class<*>) : InvocationHandler {
if (it.name.startsWith("set") && args != null && args.size == 1) {
setValue(it.name, args[0])
} else if (it.name.startsWith("get")) {
- return getValue(it)
+ return getValue(it, 3)
+ } else if (it.name.startsWith("is")) {
+ return getValue(it, 2)
}
}
return null
}
- private fun getValue(method: Method): Any? = with(method) {
- val key = name.substring(3)
+ private fun getValue(method: Method, prefixLen: Int): Any? = with(method) {
+ val key = name.substring(prefixLen)
return when (returnType) {
Boolean::class.java -> mmkv.decodeBool(key)
Int::class.java -> mmkv.decodeInt(key)
@@ -28,7 +30,7 @@ class MMKVInvocationHandler(clazz: Class<*>) : InvocationHandler {
Double::class.java -> mmkv.decodeDouble(key)
String::class.java -> mmkv.decodeString(key)
ByteArray::class.java -> mmkv.decodeBytes(key)
- else -> JsonHelper.fromJson(mmkv.decodeString(key), returnType)
+ else -> Gson().fromJson(mmkv.decodeString(key), returnType)
}
}
@@ -42,7 +44,7 @@ class MMKVInvocationHandler(clazz: Class<*>) : InvocationHandler {
is Double -> mmkv.encode(key, data)
is String -> mmkv.encode(key, data)
is ByteArray -> mmkv.encode(key, data)
- else -> mmkv.encode(key, JsonHelper.toJson(data))
+ else -> mmkv.encode(key, Gson().toJson(data))
}
}
diff --git a/app/src/main/java/com/wrbug/developerhelper/model/mmkv/manager/MMKVManager.kt b/mmkv/src/main/java/com/wrbug/developerhelper/mmkv/manager/MMKVManager.kt
similarity index 50%
rename from app/src/main/java/com/wrbug/developerhelper/model/mmkv/manager/MMKVManager.kt
rename to mmkv/src/main/java/com/wrbug/developerhelper/mmkv/manager/MMKVManager.kt
index aab97f6..8fce616 100644
--- a/app/src/main/java/com/wrbug/developerhelper/model/mmkv/manager/MMKVManager.kt
+++ b/mmkv/src/main/java/com/wrbug/developerhelper/mmkv/manager/MMKVManager.kt
@@ -1,11 +1,20 @@
@file:Suppress("UNCHECKED_CAST")
-package com.wrbug.developerhelper.model.mmkv.manager
+package com.wrbug.developerhelper.mmkv.manager
+import android.app.Application
+import android.content.Context
import androidx.collection.ArrayMap
+import com.tencent.mmkv.MMKV
import java.lang.reflect.Proxy
object MMKVManager {
+ lateinit var application: Application
+ fun register(ctx: Context) {
+ application = ctx.applicationContext as Application
+ MMKV.initialize(application)
+ }
+
val map = ArrayMap, Any>()
fun get(clazz: Class): T {
if (map.containsKey(clazz)) {
@@ -17,7 +26,10 @@ object MMKVManager {
}
private fun obtainImpl(clazz: Class): T {
- val instance = Proxy.newProxyInstance(clazz.classLoader, arrayOf(clazz), MMKVInvocationHandler(clazz))
+ val instance = Proxy.newProxyInstance(
+ clazz.classLoader, arrayOf(clazz),
+ MMKVInvocationHandler(clazz)
+ )
return instance as T
}
}
diff --git a/mmkv/src/main/res/values/strings.xml b/mmkv/src/main/res/values/strings.xml
new file mode 100644
index 0000000..687a971
--- /dev/null
+++ b/mmkv/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ mmkv
+
diff --git a/mmkv/src/test/java/com/wrbug/developerhelper/mmkv/ExampleUnitTest.java b/mmkv/src/test/java/com/wrbug/developerhelper/mmkv/ExampleUnitTest.java
new file mode 100644
index 0000000..9b42f39
--- /dev/null
+++ b/mmkv/src/test/java/com/wrbug/developerhelper/mmkv/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.wrbug.developerhelper.mmkv;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 2b546fe..6daa287 100644
--- a/readme.md
+++ b/readme.md
@@ -12,6 +12,13 @@ Android 5.0+

+
+
+### 新版本功能 v1.0.3
+
+* 新增Android Pie (sdk28) [EdXposed](https://github.com/solohsu/EdXposed)支持
+* 新增脱壳应用管理
+
### 已支持功能
1. 应用基本信息查看包括:
@@ -21,10 +28,12 @@ Android 5.0+
* Uid
* 安装时间/更新时间
* Data目录
+ * 应用脱壳(xposed)
2. apk信息:
* apk目录
* MD5/Sha1
* sharedPreference查看和编辑
+ * 数据库查看
3. 布局分析
* WidgetClass
* ViewId
@@ -33,11 +42,9 @@ Android 5.0+
* View位置
### TODO
-
-1. 数据库查看与编辑
-2. root功能(adb wifi、重启应用......)
-3. xposed功能(脱壳等)
-4. 应用抓包
+
+1. root功能(adb wifi、重启应用......)
+2. 应用抓包
### 下载
diff --git a/settings.gradle b/settings.gradle
index 1a7119e..cf8020c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':basecommon'
+include ':app', ':basecommon', ':commonutil', ':mmkv', ':xposedmodule', ':commonwidget'
diff --git a/xposedmodule/.gitignore b/xposedmodule/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/xposedmodule/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/xposedmodule/CMakeLists.txt b/xposedmodule/CMakeLists.txt
new file mode 100644
index 0000000..73bac34
--- /dev/null
+++ b/xposedmodule/CMakeLists.txt
@@ -0,0 +1,59 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html
+
+# Sets the minimum version of CMake required to build the native library.
+
+cmake_minimum_required(VERSION 3.4.1)
+
+# Creates and names a library, sets it as either STATIC
+# or SHARED, and provides the relative paths to its source code.
+# You can define multiple libraries, and CMake builds them for you.
+# Gradle automatically packages shared libraries with your APK.
+
+add_library( # Sets the name of the library.
+ nativeDump
+
+ # Sets the library as a shared library.
+ SHARED
+
+ # Provides a relative path to your source file(s).
+ src/main/cpp/native.cpp
+ src/main/cpp/dlopen.c
+ src/main/cpp/inlineHook.c
+ src/main/cpp/relocate.c
+ src/main/cpp/And64InlineHook.cpp
+ src/main/cpp/util/deviceutils.cpp
+ src/main/cpp/util/fileutils.cpp)
+
+# Searches for a specified prebuilt library and stores the path as a
+# variable. Because CMake includes system libraries in the search path by
+# default, you only need to specify the name of the public NDK library
+# you want to add. CMake verifies that the library exists before
+# completing its build.
+
+find_library( # Sets the name of the path variable.
+ log-lib
+
+ # Specifies the name of the NDK library that
+ # you want CMake to locate.
+ log )
+
+# Specifies libraries CMake should link to your target library. You
+# can link multiple libraries, such as libraries you define in this
+# build script, prebuilt third-party libraries, or system libraries.
+
+target_link_libraries( # Specifies the target library.
+ nativeDump
+
+ # Links the target library to the log library
+ # included in the NDK.
+ ${log-lib} )
+
+
+if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
+ include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androidabi)
+elseif(${ANDROID_ABI} STREQUAL "arm64-v8a")
+ include_directories(${ANDROID_SYSROOT}/usr/include/aarch64-linux-android)
+else()
+ include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androidabi)
+endif()
\ No newline at end of file
diff --git a/xposedmodule/build.gradle b/xposedmodule/build.gradle
new file mode 100644
index 0000000..87277e8
--- /dev/null
+++ b/xposedmodule/build.gradle
@@ -0,0 +1,66 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion 28
+
+
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags "-std=c++14", "-fms-extensions"
+ }
+ }
+ ndk {
+ abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
+ }
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+ implementation 'org.jetbrains.anko:anko-commons:0.10.8'
+ implementation project(':commonutil')
+ implementation 'com.jaredrummler:android-shell:1.0.0'
+ implementation project(':mmkv')
+ compileOnly 'de.robv.android.xposed:api:82'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'com.google.code.gson:gson:2.8.5'
+ implementation project(':basecommon')
+}
+repositories {
+ mavenCentral()
+}
+task buildSo(dependsOn: "compileReleaseSources") {
+ doLast {
+ println "更新so"
+ exec {
+ ignoreExitValue true
+ commandLine "pwd"
+ commandLine "sh", "./script/so.sh"
+ }
+ }
+}
\ No newline at end of file
diff --git a/xposedmodule/proguard-rules.pro b/xposedmodule/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/xposedmodule/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
diff --git a/xposedmodule/script/so.sh b/xposedmodule/script/so.sh
new file mode 100644
index 0000000..5a3a845
--- /dev/null
+++ b/xposedmodule/script/so.sh
@@ -0,0 +1,11 @@
+result=$(ls build/intermediates/cmake/release/obj)|grep 'No such file or directory'
+echo $result
+if [ -z "${result}" ]; then
+ echo '目录已生成'
+ cp build/intermediates/cmake/release/obj/arm64-v8a/libnativeDump.so src/main/assets/nativeDumpV8a.so
+ cp build/intermediates/cmake/release/obj/armeabi-v7a/libnativeDump.so src/main/assets/nativeDumpV7a.so
+ cp build/intermediates/cmake/release/obj/armeabi/libnativeDump.so src/main/assets/nativeDump.so
+ echo '完成'
+else
+ echo '目录未生成'
+fi
\ No newline at end of file
diff --git a/xposedmodule/src/androidTest/java/com/wrbug/developerhelper/xposed/ExampleInstrumentedTest.java b/xposedmodule/src/androidTest/java/com/wrbug/developerhelper/xposed/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..178345d
--- /dev/null
+++ b/xposedmodule/src/androidTest/java/com/wrbug/developerhelper/xposed/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.wrbug.developerhelper.xposed;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.wrbug.developerhelper.xposed.test", appContext.getPackageName());
+ }
+}
diff --git a/xposedmodule/src/main/AndroidManifest.xml b/xposedmodule/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c249e0a
--- /dev/null
+++ b/xposedmodule/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/xposedmodule/src/main/assets/xposed_init b/xposedmodule/src/main/assets/xposed_init
new file mode 100644
index 0000000..06ffbeb
--- /dev/null
+++ b/xposedmodule/src/main/assets/xposed_init
@@ -0,0 +1 @@
+com.wrbug.developerhelper.xposed.XposedInit
\ No newline at end of file
diff --git a/xposedmodule/src/main/cpp/And64InlineHook.cpp b/xposedmodule/src/main/cpp/And64InlineHook.cpp
new file mode 100755
index 0000000..564fbeb
--- /dev/null
+++ b/xposedmodule/src/main/cpp/And64InlineHook.cpp
@@ -0,0 +1,599 @@
+/*
+ * @date : 2018年01月24日
+ * @author : rrrfff@foxmail.com
+ * https://github.com/rrrfff/And64InlineHook
+ */
+/*
+ MIT License
+
+ Copyright (c) 2017 rrrfff@foxmail.com
+
+ 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.
+ */
+#define __STDC_FORMAT_MACROS
+
+#include
+#include
+#include
+#include
+#include
+#include
+#if defined(__aarch64__)
+
+#include "And64InlineHook.hpp"
+
+#define A64_MAX_INSTRUCTIONS 5
+#define A64_MAX_REFERENCES (A64_MAX_INSTRUCTIONS * 2)
+#define A64_NOP 0xd503201fu
+#define A64_JNIEXPORT __attribute__((visibility("default")))
+#define A64_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "A64_HOOK", __VA_ARGS__))
+#ifndef NDEBUG
+# define A64_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "A64_HOOK", __VA_ARGS__))
+#else
+# define A64_LOGI(...) ((void)0)
+#endif // NDEBUG
+typedef uint32_t *__restrict *__restrict instruction;
+typedef struct {
+ struct fix_info {
+ uint32_t *bp;
+ uint32_t ls; // left-shift counts
+ uint32_t ad; // & operand
+ };
+ struct insns_info {
+ union {
+ uint64_t insu;
+ int64_t ins;
+ void *insp;
+ };
+ fix_info fmap[A64_MAX_REFERENCES];
+ };
+ int64_t basep;
+ int64_t endp;
+ insns_info dat[A64_MAX_INSTRUCTIONS];
+
+public:
+ inline bool is_in_fixing_range(const int64_t absolute_addr) {
+ return absolute_addr>= this->basep && absolute_addr < this->endp;
+ }
+
+ inline intptr_t get_ref_ins_index(const int64_t absolute_addr) {
+ return static_cast((absolute_addr - this->basep) / sizeof(uint32_t));
+ }
+
+ inline intptr_t get_and_set_current_index(uint32_t *__restrict inp, uint32_t *__restrict outp) {
+ intptr_t current_idx = this->get_ref_ins_index(reinterpret_cast(inp));
+ this->dat[current_idx].insp = outp;
+ return current_idx;
+ }
+
+ inline void reset_current_ins(const intptr_t idx, uint32_t *__restrict outp) {
+ this->dat[idx].insp = outp;
+ }
+
+ void
+ insert_fix_map(const intptr_t idx, uint32_t *bp, uint32_t ls = 0u, uint32_t ad = 0xffffffffu) {
+ for (auto &f : this->dat[idx].fmap) {
+ if (f.bp == NULL) {
+ f.bp = bp;
+ f.ls = ls;
+ f.ad = ad;
+ return;
+ } //if
+ }
+ // What? GGing..
+ }
+
+ void process_fix_map(const intptr_t idx) {
+ for (auto &f : this->dat[idx].fmap) {
+ if (f.bp == NULL) break;
+ *(f.bp) = *(f.bp) |
+ (((int32_t(this->dat[idx].ins - reinterpret_cast(f.bp))>> 2)
+ << f.ls) & f.ad); + f.bp = NULL; + } + } +} context; + +//------------------------------------------------------------------------- + +static bool __fix_branch_imm(instruction inpp, instruction outpp, context *ctxp) { + constexpr uint32_t mbits = 6u; + constexpr uint32_t mask = 0xfc000000u; // 0b11111100000000000000000000000000 + constexpr uint32_t rmask = 0x03ffffffu; // 0b00000011111111111111111111111111 + constexpr uint32_t op_b = 0x14000000u; // "b" ADDR_PCREL26 + constexpr uint32_t op_bl = 0x94000000u; // "bl" ADDR_PCREL26 + + const uint32_t ins = *(*inpp); + const uint32_t opc = ins & mask; + switch (opc) { + case op_b: + case op_bl: { + intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int64_t absolute_addr = reinterpret_cast(*inpp) +
+ (static_cast(ins << mbits) +>> (mbits - 2u)); // sign-extended
+ int64_t new_pc_offset =
+ static_cast(absolute_addr - reinterpret_cast(*outpp))
+>> 2; // shifted
+ bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr);
+ // whether the branch should be converted to absolute jump
+ if (!special_fix_type && llabs(new_pc_offset)> rmask) {
+ bool b_aligned = (reinterpret_cast(*outpp + 2) & 7u) == 0u;
+ if (opc == op_b) {
+ if (b_aligned == true) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+ (*outpp)[0] = 0x58000051u; // LDR X17, #0x8
+ (*outpp)[1] = 0xd61f0220u; // BR X17
+ memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr));
+ *outpp += 4;
+ } else {
+ if (b_aligned == false) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+ (*outpp)[0] = 0x58000071u; // LDR X17, #12
+ (*outpp)[1] = 0x1000009eu; // ADR X30, #16
+ (*outpp)[2] = 0xd61f0220u; // BR X17
+ memcpy(*outpp + 3, &absolute_addr, sizeof(absolute_addr));
+ *outpp += 5;
+ } //if
+ } else {
+ if (special_fix_type) {
+ intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr);
+ if (ref_idx <= current_idx) { + new_pc_offset = static_cast(ctxp->dat[ref_idx].ins -
+ reinterpret_cast(*outpp))
+>> 2;
+ } else {
+ ctxp->insert_fix_map(ref_idx, *outpp, 0u, rmask);
+ new_pc_offset = 0;
+ } //if
+ } //if
+
+ (*outpp)[0] = opc | (new_pc_offset & ~mask);
+ ++(*outpp);
+ } //if
+
+ ++(*inpp);
+ return ctxp->process_fix_map(current_idx), true;
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------
+
+static bool __fix_cond_comp_test_branch(instruction inpp, instruction outpp, context *ctxp) {
+ constexpr uint32_t lsb = 5u;
+ constexpr uint32_t lmask01 = 0xff00001fu; // 0b11111111000000000000000000011111
+ constexpr uint32_t mask0 = 0xff000010u; // 0b11111111000000000000000000010000
+ constexpr uint32_t op_bc = 0x54000000u; // "b.c" ADDR_PCREL19
+ constexpr uint32_t mask1 = 0x7f000000u; // 0b01111111000000000000000000000000
+ constexpr uint32_t op_cbz = 0x34000000u; // "cbz" Rt, ADDR_PCREL19
+ constexpr uint32_t op_cbnz = 0x35000000u; // "cbnz" Rt, ADDR_PCREL19
+ constexpr uint32_t lmask2 = 0xfff8001fu; // 0b11111111111110000000000000011111
+ constexpr uint32_t mask2 = 0x7f000000u; // 0b01111111000000000000000000000000
+ constexpr uint32_t op_tbz = 0x36000000u; // 0b00110110000000000000000000000000 "tbz" Rt, BIT_NUM, ADDR_PCREL14
+ constexpr uint32_t op_tbnz = 0x37000000u; // 0b00110111000000000000000000000000 "tbnz" Rt, BIT_NUM, ADDR_PCREL14
+
+ const uint32_t ins = *(*inpp);
+ uint32_t lmask = lmask01;
+ if ((ins & mask0) != op_bc) {
+ uint32_t opc = ins & mask1;
+ if (opc != op_cbz && opc != op_cbnz) {
+ opc = ins & mask2;
+ if (opc != op_tbz && opc != op_tbnz) {
+ return false;
+ } //if
+ lmask = lmask2;
+ } //if
+ } //if
+
+ intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int64_t absolute_addr = reinterpret_cast(*inpp) + ((ins & ~lmask)>> (lsb - 2u));
+ int64_t new_pc_offset =
+ static_cast(absolute_addr - reinterpret_cast(*outpp))>> 2; // shifted
+ bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr);
+ if (!special_fix_type && llabs(new_pc_offset)> (~lmask>> lsb)) {
+ if ((reinterpret_cast(*outpp + 4) & 7u) != 0u) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+ (*outpp)[0] = (((8u>> 2u) << lsb) & ~lmask) | (ins & lmask); // B.C #0x8 + (*outpp)[1] = 0x14000005u; // B #0x14 + (*outpp)[2] = 0x58000051u; // LDR X17, #0x8 + (*outpp)[3] = 0xd61f0220u; // BR X17 + memcpy(*outpp + 4, &absolute_addr, sizeof(absolute_addr)); + *outpp += 6; + } else { + if (special_fix_type) { + intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr);
+ if (ref_idx <= current_idx) { + new_pc_offset = static_cast(ctxp->dat[ref_idx].ins -
+ reinterpret_cast(*outpp))>> 2;
+ } else {
+ ctxp->insert_fix_map(ref_idx, *outpp, lsb, ~lmask);
+ new_pc_offset = 0;
+ } //if
+ } //if
+
+ (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~lmask) | (ins & lmask); + ++(*outpp); + } //if + + ++(*inpp); + return ctxp->process_fix_map(current_idx), true;
+}
+
+//-------------------------------------------------------------------------
+
+static bool __fix_loadlit(instruction inpp, instruction outpp, context *ctxp) {
+ const uint32_t ins = *(*inpp);
+
+ // memory prefetch("prfm"), just skip it
+ // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897420050.html
+ if ((ins & 0xff000000u) == 0xd8000000u) {
+ ctxp->process_fix_map(ctxp->get_and_set_current_index(*inpp, *outpp));
+ ++(*inpp);
+ return true;
+ } //if
+
+ constexpr uint32_t msb = 8u;
+ constexpr uint32_t lsb = 5u;
+ constexpr uint32_t mask_30 = 0x40000000u; // 0b01000000000000000000000000000000
+ constexpr uint32_t mask_31 = 0x80000000u; // 0b10000000000000000000000000000000
+ constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111
+ constexpr uint32_t mask_ldr = 0xbf000000u; // 0b10111111000000000000000000000000
+ constexpr uint32_t op_ldr = 0x18000000u; // 0b00011000000000000000000000000000 "LDR Wt/Xt, label" | ADDR_PCREL19
+ constexpr uint32_t mask_ldrv = 0x3f000000u; // 0b00111111000000000000000000000000
+ constexpr uint32_t op_ldrv = 0x1c000000u; // 0b00011100000000000000000000000000 "LDR St/Dt/Qt, label" | ADDR_PCREL19
+ constexpr uint32_t mask_ldrsw = 0xff000000u; // 0b11111111000000000000000000000000
+ constexpr uint32_t op_ldrsw = 0x98000000u; // "LDRSW Xt, label" | ADDR_PCREL19 | load register signed word
+ // LDR S0, #0 | 0b00011100000000000000000000000000 | 32-bit
+ // LDR D0, #0 | 0b01011100000000000000000000000000 | 64-bit
+ // LDR Q0, #0 | 0b10011100000000000000000000000000 | 128-bit
+ // INVALID | 0b11011100000000000000000000000000 | may be 256-bit
+
+ uint32_t mask = mask_ldr;
+ uintptr_t faligned = (ins & mask_30) ? 7u : 3u;
+ if ((ins & mask_ldr) != op_ldr) {
+ mask = mask_ldrv;
+ if (faligned != 7u)
+ faligned = (ins & mask_31) ? 15u : 3u;
+ if ((ins & mask_ldrv) != op_ldrv) {
+ if ((ins & mask_ldrsw) != op_ldrsw) {
+ return false;
+ } //if
+ mask = mask_ldrsw;
+ faligned = 7u;
+ } //if
+ } //if
+
+ intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int64_t absolute_addr = reinterpret_cast(*inpp) +
+ ((static_cast(ins << msb)>> (msb + lsb - 2u)) & ~3u);
+ int64_t new_pc_offset =
+ static_cast(absolute_addr - reinterpret_cast(*outpp))>> 2; // shifted
+ bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr);
+ // special_fix_type may encounter issue when there are mixed data and code
+ if (special_fix_type || (llabs(new_pc_offset) + (faligned + 1u - 4u))> (~lmask>> lsb)) {
+ while ((reinterpret_cast(*outpp + 2) & faligned) != 0u) {
+ *(*outpp)++ = A64_NOP;
+ }
+ ctxp->reset_current_ins(current_idx, *outpp);
+
+ // Note that if memory at absolute_addr is writeable (non-const), we will fail to fetch it.
+ // And what's worse, we may unexpectedly overwrite something if special_fix_type is true...
+ uint32_t ns = static_cast((faligned + 1) / sizeof(uint32_t));
+ (*outpp)[0] = (((8u>> 2u) << lsb) & ~mask) | (ins & lmask); // LDR #0x8 + (*outpp)[1] = 0x14000001u + ns; // B #0xc + memcpy(*outpp + 2, reinterpret_cast(absolute_addr), faligned + 1);
+ *outpp += 2 + ns;
+ } else {
+ faligned>>= 2; // new_pc_offset is shifted and 4-byte aligned
+ while ((new_pc_offset & faligned) != 0) {
+ *(*outpp)++ = A64_NOP;
+ new_pc_offset =
+ static_cast(absolute_addr - reinterpret_cast(*outpp))>> 2;
+ }
+ ctxp->reset_current_ins(current_idx, *outpp);
+
+ (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~mask) | (ins & lmask); + ++(*outpp); + } //if + + ++(*inpp); + return ctxp->process_fix_map(current_idx), true;
+}
+
+//-------------------------------------------------------------------------
+
+static bool __fix_pcreladdr(instruction inpp, instruction outpp, context *ctxp) {
+ // Load a PC-relative address into a register
+ // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897645644.html
+ constexpr uint32_t msb = 8u;
+ constexpr uint32_t lsb = 5u;
+ constexpr uint32_t mask = 0x9f000000u; // 0b10011111000000000000000000000000
+ constexpr uint32_t rmask = 0x0000001fu; // 0b00000000000000000000000000011111
+ constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111
+ constexpr uint32_t fmask = 0x00ffffffu; // 0b00000000111111111111111111111111
+ constexpr uint32_t max_val = 0x001fffffu; // 0b00000000000111111111111111111111
+ constexpr uint32_t op_adr = 0x10000000u; // "adr" Rd, ADDR_PCREL21
+ constexpr uint32_t op_adrp = 0x90000000u; // "adrp" Rd, ADDR_ADRP
+
+ const uint32_t ins = *(*inpp);
+ intptr_t current_idx;
+ switch (ins & mask) {
+ case op_adr: {
+ current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int64_t lsb_bytes = static_cast(ins << 1u)>> 30u;
+ int64_t absolute_addr = reinterpret_cast(*inpp) +
+ (((static_cast(ins << msb)>> (msb + lsb - 2u)) &
+ ~3u) | lsb_bytes);
+ int64_t new_pc_offset = static_cast(absolute_addr -
+ reinterpret_cast(*outpp));
+ bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr);
+ if (!special_fix_type && llabs(new_pc_offset)> max_val) {
+ if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+
+ (*outpp)[0] =
+ 0x58000000u | (((8u>> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 + (*outpp)[1] = 0x14000003u; // B #0xc + memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); + *outpp += 4; + } else { + if (special_fix_type) { + intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr & ~3ull);
+ if (ref_idx <= current_idx) { + new_pc_offset = static_cast(ctxp->dat[ref_idx].ins -
+ reinterpret_cast(*outpp));
+ } else {
+ ctxp->insert_fix_map(ref_idx, *outpp, lsb, fmask);
+ new_pc_offset = 0;
+ } //if
+ } //if
+
+ // the lsb_bytes will never be changed, so we can use lmask to keep it
+ (*outpp)[0] = (static_cast(new_pc_offset << (lsb - 2u)) & fmask) | + (ins & lmask); + ++(*outpp); + } //if + } + break; + case op_adrp: { + current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int32_t lsb_bytes = static_cast(ins << 1u)>> 30u;
+ int64_t absolute_addr = (reinterpret_cast(*inpp) & ~0xfffll) +
+ ((((static_cast(ins << msb)>> (msb + lsb - 2u)) &
+ ~3u) | lsb_bytes) << 12); + A64_LOGI("ins = 0x%.8X, pc = %p, abs_addr = %p", + ins, *inpp, reinterpret_cast(absolute_addr));
+ if (ctxp->is_in_fixing_range(absolute_addr)) {
+ intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr/* & ~3ull*/);
+ if (ref_idx> current_idx) {
+ // the bottom 12 bits of absolute_addr are masked out,
+ // so ref_idx must be less than or equal to current_idx!
+ A64_LOGE("ref_idx must be less than or equal to current_idx!");
+ } //if
+
+ // *absolute_addr may be changed due to relocation fixing
+ A64_LOGI("What is the correct way to fix this?");
+ *(*outpp)++ = ins; // 0x90000000u;
+ } else {
+ if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+
+ (*outpp)[0] =
+ 0x58000000u | (((8u>> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 + (*outpp)[1] = 0x14000003u; // B #0xc + memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); // potential overflow? + *outpp += 4; + } //if + } + break; + default: + return false; + } + + ctxp->process_fix_map(current_idx);
+ ++(*inpp);
+ return true;
+}
+
+//-------------------------------------------------------------------------
+
+static void __fix_instructions(uint32_t *__restrict inp, int32_t count, uint32_t *__restrict outp) {
+ context ctx;
+ ctx.basep = reinterpret_cast(inp);
+ ctx.endp = reinterpret_cast(inp + count);
+ memset(ctx.dat, 0, sizeof(ctx.dat));
+ static_assert(sizeof(ctx.dat) / sizeof(ctx.dat[0]) == A64_MAX_INSTRUCTIONS,
+ "please use A64_MAX_INSTRUCTIONS!");
+#ifndef NDEBUG
+ if (count> A64_MAX_INSTRUCTIONS) {
+ A64_LOGE("too many fixing instructions!");
+ } //if
+#endif // NDEBUG
+
+ while (--count>= 0) {
+ if (__fix_branch_imm(&inp, &outp, &ctx)) continue;
+ if (__fix_cond_comp_test_branch(&inp, &outp, &ctx)) continue;
+ if (__fix_loadlit(&inp, &outp, &ctx)) continue;
+ if (__fix_pcreladdr(&inp, &outp, &ctx)) continue;
+
+ // without PC-relative offset
+ ctx.process_fix_map(ctx.get_and_set_current_index(inp, outp));
+ *(outp++) = *(inp++);
+ }
+
+ constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111
+ auto callback = reinterpret_cast(inp);
+ auto pc_offset = static_cast(callback - reinterpret_cast(outp))>> 2;
+ if (llabs(pc_offset)> mask) {
+ if ((reinterpret_cast(outp + 2) & 7u) != 0u) {
+ outp[0] = A64_NOP;
+ ++outp;
+ } //if
+ outp[0] = 0x58000051u; // LDR X17, #0x8
+ outp[1] = 0xd61f0220u; // BR X17
+ *reinterpret_cast(outp + 2) = callback;
+// outp += 4;
+ } else {
+ outp[0] = 0x14000000u | (pc_offset & mask); // "B" ADDR_PCREL26
+// ++outp;
+ } //if
+}
+
+//-------------------------------------------------------------------------
+
+extern "C" {
+#define __attribute __attribute__
+#define aligned(x) __aligned__(x)
+#define __intval(p) reinterpret_cast(p)
+#define __uintval(p) reinterpret_cast(p)
+#define __ptr(p) reinterpret_cast(p)
+#define __page_size 4096
+#define __page_align(n) __align_up(static_cast(n), __page_size)
+#define __ptr_align(x) __ptr(__align_down(reinterpret_cast(x), __page_size))
+#define __align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1))
+#define __align_down(x, n) ((x) & -(n))
+#define __countof(x) static_cast(sizeof(x) / sizeof((x)[0])) // must be signed
+#define __atomic_increase(p) __sync_add_and_fetch(p, 1)
+#define __sync_cmpswap(p, v, n) __sync_bool_compare_and_swap(p, v, n)
+#define __predict_true(exp) __builtin_expect((exp) != 0, 1)
+#define __flush_cache(c, n) __builtin___clear_cache(reinterpret_cast(c), reinterpret_cast(c) + n)
+#define __make_rwx(p, n) ::mprotect(__ptr_align(p), \
+ __page_align(__uintval(p) + n) != __page_align(__uintval(p)) ? __page_align(n) + __page_size : __page_align(n), \
+ PROT_READ | PROT_WRITE | PROT_EXEC)
+
+//-------------------------------------------------------------------------
+
+static __attribute((aligned(__page_size))) uint32_t __insns_pool[A64_MAX_BACKUPS][
+ A64_MAX_INSTRUCTIONS * 10];
+
+//-------------------------------------------------------------------------
+
+class A64HookInit {
+public:
+ A64HookInit() {
+ __make_rwx(__insns_pool, sizeof(__insns_pool));
+ A64_LOGI("insns pool initialized.");
+ }
+};
+static A64HookInit __init;
+
+//-------------------------------------------------------------------------
+
+static uint32_t *FastAllocateTrampoline() {
+ static_assert((A64_MAX_INSTRUCTIONS * 10 * sizeof(uint32_t)) % 8 == 0, "8-byte align");
+ static volatile int32_t __index = -1;
+
+ int32_t i = __atomic_increase(&__index);
+ if (__predict_true(i>= 0 && i < __countof(__insns_pool))) { + return __insns_pool[i]; + } //if + + A64_LOGE("failed to allocate trampoline!"); + return NULL; +} + +//------------------------------------------------------------------------- + +A64_JNIEXPORT void *A64HookFunctionV(void *const symbol, void *const replace, + void *const rwx, const uintptr_t rwx_size) { + constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111 + + uint32_t *trampoline = static_cast(rwx), *original = static_cast(symbol);
+
+ static_assert(A64_MAX_INSTRUCTIONS>= 5, "please fix A64_MAX_INSTRUCTIONS!");
+ auto pc_offset = static_cast(__intval(replace) - __intval(symbol))>> 2;
+ if (llabs(pc_offset)> mask) {
+ int32_t count = (reinterpret_cast(original + 2) & 7u) != 0u ? 5 : 4;
+ if (trampoline) {
+ if (rwx_size < count * 10u) { + return NULL; + } //if + __fix_instructions(original, count, trampoline); + } //if + + if (__make_rwx(original, 5 * sizeof(uint32_t)) == 0) { + if (count == 5) { + original[0] = A64_NOP; + ++original; + } //if + original[0] = 0x58000051u; // LDR X17, #0x8 + original[1] = 0xd61f0220u; // BR X17 + *reinterpret_cast(original + 2) = __intval(replace);
+ __flush_cache(symbol, 5 * sizeof(uint32_t));
+
+ A64_LOGI("inline hook %p->%p successfully! %zu bytes overwritten",
+ symbol, replace, 5 * sizeof(uint32_t));
+ } else {
+
+ trampoline = NULL;
+ } //if
+ } else {
+ if (trampoline) {
+ if (rwx_size < 1u * 10u) { + return NULL; + } //if + __fix_instructions(original, 1, trampoline); + } //if + + if (__make_rwx(original, 1 * sizeof(uint32_t)) == 0) { + __sync_cmpswap(original, *original, + 0x14000000u | (pc_offset & mask)); // "B" ADDR_PCREL26 + __flush_cache(symbol, 1 * sizeof(uint32_t)); + + A64_LOGI("inline hook %p->%p successfully! %zu bytes overwritten",
+ symbol, replace, 1 * sizeof(uint32_t));
+ } else {
+
+ trampoline = NULL;
+ } //if
+ } //if
+
+ return trampoline;
+}
+
+//-------------------------------------------------------------------------
+
+A64_JNIEXPORT void A64HookFunction(void *const symbol, void *const replace, void **result) {
+ void *trampoline = NULL;
+ if (result != NULL) {
+ trampoline = FastAllocateTrampoline();
+ *result = trampoline;
+ if (trampoline == NULL) return;
+ } //if
+
+ trampoline = A64HookFunctionV(symbol, replace, trampoline, A64_MAX_INSTRUCTIONS * 10u);
+ if (trampoline == NULL && result != NULL) {
+ *result = NULL;
+ } //if
+}
+}
+
+#endif // defined(__aarch64__)
\ No newline at end of file
diff --git a/xposedmodule/src/main/cpp/And64InlineHook.hpp b/xposedmodule/src/main/cpp/And64InlineHook.hpp
new file mode 100755
index 0000000..2511754
--- /dev/null
+++ b/xposedmodule/src/main/cpp/And64InlineHook.hpp
@@ -0,0 +1,42 @@
+/*
+ * @date : 2018年01月24日
+ * @author : rrrfff@foxmail.com
+ * https://github.com/rrrfff/And64InlineHook
+ */
+/*
+ MIT License
+
+ Copyright (c) 2017 rrrfff@foxmail.com
+
+ 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.
+ */
+#pragma once
+#define A64_MAX_BACKUPS 256
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ void A64HookFunction(void *const symbol, void *const replace, void **result);
+ void *A64HookFunctionV(void *const symbol, void *const replace,
+ void *const rwx, const uintptr_t rwx_size);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/xposedmodule/src/main/cpp/dlopen.c b/xposedmodule/src/main/cpp/dlopen.c
new file mode 100644
index 0000000..c2a9714
--- /dev/null
+++ b/xposedmodule/src/main/cpp/dlopen.c
@@ -0,0 +1,191 @@
+/*
+ *
+ * @author : rrrfff@foxmail.com
+ * https://github.com/rrrfff/ndk_dlopen
+ *
+ */
+#include "dlopen.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define LOG_TAG "ndk_dlopen"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+
+static volatile int SDK_INT = 0;
+static void *quick_on_stack_back;
+static union
+{
+ void *generic_stub;
+ void *(*quick_on_stack_replace)(const void *param1, const void *param2,
+ const void *fake_trampoline, const void *called);
+} STUBS;
+
+void JNIEXPORT ndk_init(JNIEnv *env)
+{
+ if (SDK_INT <= 0) { + char sdk[PROP_VALUE_MAX]; + __system_property_get("ro.build.version.sdk", sdk); + SDK_INT = atoi(sdk); + LOGI("SDK_INT = %d", SDK_INT); + if (SDK_INT>= 24) {
+ static __attribute__((__aligned__(PAGE_SIZE))) uint8_t __insns[PAGE_SIZE];
+ STUBS.generic_stub = __insns;
+ mprotect(__insns, sizeof(__insns), PROT_READ | PROT_WRITE | PROT_EXEC);
+
+ // we are currently hijacking "FatalError" as a fake system-call trampoline
+ uintptr_t pv = (uintptr_t)(*env)->FatalError;
+ uintptr_t pu = (pv | (PAGE_SIZE - 1)) + 1u;
+ uintptr_t pd = (pv & ~(PAGE_SIZE - 1));
+ mprotect((void *)pd, pv + 8u>= pu ? PAGE_SIZE * 2u : PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
+ quick_on_stack_back = (void *)pv;
+
+#if defined(__i386__)
+ /*
+ DEFINE_FUNCTION art_quick_on_stack_replace
+ movl 12(REG_VAR(esp)), REG_VAR(eax)
+ movl (REG_VAR(esp)), REG_VAR(edx)
+ movl REG_VAR(eax), (REG_VAR(esp))
+ movl REG_VAR(edx), 12(REG_VAR(esp))
+ pushl 16(REG_VAR(esp))
+ ret
+ END_FUNCTION art_quick_on_stack_replace
+ */
+ memcpy(__insns, "\x8B\x44\x24\x0C\x8B\x14\x24\x89\x04\x24\x89\x54\x24\x0C\xFF\x74\x24\x10\xC3", 19);
+ /*
+ DEFINE_FUNCTION art_quick_on_stack_back
+ push 8(REG_VAR(esp))
+ ret
+ END_FUNCTION art_quick_on_stack_back
+ */
+ memcpy(quick_on_stack_back, "\xC3\xFF\x74\x24\x08\xC3", 6);
+ quick_on_stack_back = (void *)(pv + 1); // inserts `ret` at first
+#elif defined(__x86_64__)
+ // rdi, rsi, rdx, rcx, r8, r9
+ /*
+ 0x0000000000000000: 52 push rdx
+ 0x0000000000000001: FF E1 jmp rcx
+ */
+ memcpy(__insns, "\x52\xFF\xE1", 3);
+ /*
+ 0x0000000000000000: C3 ret
+ */
+ memcpy(quick_on_stack_back, "\xC3", 1);
+#elif defined(__aarch64__)
+ // x0~x7
+ /*
+ 0x0000000000000000: FD 7B BF A9 stp x29, x30, [sp, #-0x10]!
+ 0x0000000000000004: FD 03 00 91 mov x29, sp
+ 0x0000000000000008: FE 03 02 AA mov x30, x2
+ 0x000000000000000C: 60 00 1F D6 br x3
+ */
+ memcpy(__insns, "\xFD\x7B\xBF\xA9\xFD\x03\x00\x91\xFE\x03\x02\xAA\x60\x00\x1F\xD6", 16);
+ /*
+ 0x0000000000000000: FD 7B C1 A8 ldp x29, x30, [sp], #0x10
+ 0x0000000000000004: C0 03 5F D6 ret
+ */
+ memcpy(quick_on_stack_back, "\xFD\x7B\xC1\xA8\xC0\x03\x5F\xD6", 8);
+#elif defined(__arm__)
+ // r0~r3
+ /*
+ 0x0000000000000000: 04 E0 2D E5 str lr, [sp, #-4]!
+ 0x0000000000000004: 02 E0 A0 E1 mov lr, r2
+ 0x0000000000000008: 13 FF 2F E1 bx r3
+ */
+ memcpy(__insns, "\x04\xE0\x2D\xE5\x02\xE0\xA0\xE1\x13\xFF\x2F\xE1", 12);
+ if ((pv & 1u) != 0u) { // Thumb
+ /*
+ 0x0000000000000000: 08 BC pop {r3}
+ 0x0000000000000002: 18 47 bx r3
+ */
+ memcpy((void *)(pv - 1), "\x08\xBC\x18\x47", 4);
+ } else {
+ /*
+ 0x0000000000000000: 04 30 9D E4 pop {r3}
+ 0x0000000000000004: 13 FF 2F E1 bx r3
+ */
+ memcpy(quick_on_stack_back, "\x04\x30\x9D\xE4\x13\xFF\x2F\xE1", 8);
+ } //if
+#else
+# error "not supported"
+#endif
+ LOGI("init done! quick_on_stack_replace = %p, quick_on_stack_back = %p",
+ STUBS.generic_stub, quick_on_stack_back);
+ } //if
+ } //if
+}
+
+void *JNIEXPORT ndk_dlopen(const char *filename, int flag)
+{
+ if (SDK_INT>= 24) {
+#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__)
+ return STUBS.quick_on_stack_replace(filename, (void *)flag,
+ quick_on_stack_back, dlopen);
+#else
+# error "not supported"
+#endif
+ } //if
+
+ return dlopen(filename, flag);
+}
+
+int JNIEXPORT ndk_dlclose(void *handle)
+{
+ if (SDK_INT>= 24) {
+#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__)
+ return (int)STUBS.quick_on_stack_replace(handle, NULL,
+ quick_on_stack_back, dlclose);
+#else
+# error "not supported"
+#endif
+ } //if
+
+ return dlclose(handle);
+}
+
+const char *JNIEXPORT ndk_dlerror(void)
+{
+ if (SDK_INT>= 24) {
+#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__)
+ return STUBS.quick_on_stack_replace(NULL, NULL,
+ quick_on_stack_back, dlerror);
+#else
+# error "not supported"
+#endif
+ } //if
+
+ return dlerror();
+}
+
+void *JNIEXPORT ndk_dlsym(void *handle, const char *symbol)
+{
+ if (SDK_INT>= 24) {
+#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__)
+ return STUBS.quick_on_stack_replace(handle, symbol,
+ quick_on_stack_back, dlsym);
+
+#else
+# error "not supported"
+#endif
+ } //if
+
+ return dlsym(handle, symbol);
+}
+
+int JNIEXPORT ndk_dladdr(const void *ddr, Dl_info *info)
+{
+ if (SDK_INT>= 24) {
+#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__)
+ return (int)STUBS.quick_on_stack_replace(ddr, info,
+ quick_on_stack_back, dladdr);
+#else
+# error "not supported"
+#endif
+ } //if
+
+ return dladdr(ddr, info);
+}
\ No newline at end of file
diff --git a/xposedmodule/src/main/cpp/dlopen.h b/xposedmodule/src/main/cpp/dlopen.h
new file mode 100644
index 0000000..58374ea
--- /dev/null
+++ b/xposedmodule/src/main/cpp/dlopen.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * @author : rrrfff@foxmail.com
+ * https://github.com/rrrfff/ndk_dlopen
+ *
+ */
+#pragma once
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ndk_init(JNIEnv *env);
+void *ndk_dlopen(const char *filename, int flag);
+int ndk_dlclose(void *handle);
+const char *ndk_dlerror(void);
+void *ndk_dlsym(void *handle, const char *symbol);
+int ndk_dladdr(const void *ddr, Dl_info *info);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/xposedmodule/src/main/cpp/inlineHook.c b/xposedmodule/src/main/cpp/inlineHook.c
new file mode 100755
index 0000000..004f13d
--- /dev/null
+++ b/xposedmodule/src/main/cpp/inlineHook.c
@@ -0,0 +1,416 @@
+/*
+thumb16 thumb32 arm32 inlineHook
+author: ele7enxxh
+mail: ele7enxxh@qq.com
+website: ele7enxxh.com
+modified time: 2015年01月23日
+created time: 2015年11月30日
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "relocate.h"
+#include "inlineHook.h"
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+#define TAG "developerhelper.xposed.inlineHook.native-->"
+
+#define PAGE_START(addr) (~(PAGE_SIZE - 1) & (addr))
+#define SET_BIT0(addr) (addr | 1)
+#define CLEAR_BIT0(addr) (addr & 0xFFFFFFFE)
+#define TEST_BIT0(addr) (addr & 1)
+
+#define ACTION_ENABLE 0
+#define ACTION_DISABLE 1
+
+enum hook_status {
+ REGISTERED,
+ HOOKED,
+};
+
+struct inlineHookItem {
+ uint32_t target_addr;
+ uint32_t new_addr;
+ uint32_t **proto_addr;
+ void *orig_instructions;
+ int orig_boundaries[4];
+ int trampoline_boundaries[20];
+ int count;
+ void *trampoline_instructions;
+ int length;
+ int status;
+ int mode;
+};
+
+struct inlineHookInfo {
+ struct inlineHookItem item[1024];
+ int size;
+};
+
+static struct inlineHookInfo info = {0};
+
+static int getAllTids(pid_t pid, pid_t *tids) {
+ char dir_path[32];
+ DIR *dir;
+ int i;
+ struct dirent *entry;
+ pid_t tid;
+
+ if (pid < 0) { + snprintf(dir_path, sizeof(dir_path), "/proc/self/task"); + } else { + snprintf(dir_path, sizeof(dir_path), "/proc/%d/task", pid); + } + + dir = opendir(dir_path); + if (dir == NULL) { + return 0; + } + + i = 0; + while ((entry = readdir(dir)) != NULL) { + tid = atoi(entry->d_name);
+ if (tid != 0 && tid != getpid()) {
+ tids[i++] = tid;
+ }
+ }
+ closedir(dir);
+ return i;
+}
+
+static bool doProcessThreadPC(struct inlineHookItem *item, struct pt_regs *regs, int action) {
+ int offset;
+ int i;
+
+ switch (action) {
+ case ACTION_ENABLE:
+ offset = regs->ARM_pc - CLEAR_BIT0(item->target_addr);
+ for (i = 0; i < item->count; ++i) {
+ if (offset == item->orig_boundaries[i]) {
+ regs->ARM_pc = (uint32_t) item->trampoline_instructions +
+ item->trampoline_boundaries[i];
+ return true;
+ }
+ }
+ break;
+ case ACTION_DISABLE:
+ offset = regs->ARM_pc - (int) item->trampoline_instructions;
+ for (i = 0; i < item->count; ++i) {
+ if (offset == item->trampoline_boundaries[i]) {
+ regs->ARM_pc = CLEAR_BIT0(item->target_addr) + item->orig_boundaries[i];
+ return true;
+ }
+ }
+ break;
+ }
+
+ return false;
+}
+
+static void processThreadPC(pid_t tid, struct inlineHookItem *item, int action) {
+ struct pt_regs regs;
+
+ if (ptrace(PTRACE_GETREGS, tid, NULL, ®s) == 0) {
+ if (item == NULL) {
+ int pos;
+
+ for (pos = 0; pos < info.size; ++pos) { + if (doProcessThreadPC(&info.item[pos], ®s, action) == true) { + break; + } + } + } else { + doProcessThreadPC(item, ®s, action); + } + + ptrace(PTRACE_SETREGS, tid, NULL, ®s); + } +} + +static pid_t freeze(struct inlineHookItem *item, int action) { + int count; + pid_t tids[1024]; + pid_t pid; + + pid = -1; + count = getAllTids(getpid(), tids); + if (count> 0) {
+ pid = fork();
+
+ if (pid == 0) {
+ int i;
+
+ for (i = 0; i < count; ++i) { + if (ptrace(PTRACE_ATTACH, tids[i], NULL, NULL) == 0) { + waitpid(tids[i], NULL, WUNTRACED); + processThreadPC(tids[i], item, action); + } + } + + raise(SIGSTOP); + + for (i = 0; i < count; ++i) { + ptrace(PTRACE_DETACH, tids[i], NULL, NULL); + } + + raise(SIGKILL); + } else if (pid> 0) {
+ waitpid(pid, NULL, WUNTRACED);
+ }
+ }
+
+ return pid;
+}
+
+static void unFreeze(pid_t pid) {
+ if (pid < 0) { + return; + } + + kill(pid, SIGCONT); + wait(NULL); +} + +static bool isExecutableAddr(uint32_t addr) { + FILE *fp; + char line[1024]; + uint32_t start; + uint32_t end; + + fp = fopen("/proc/self/maps", "r"); + if (fp == NULL) { + return false; + } + + while (fgets(line, sizeof(line), fp)) { + if (strstr(line, "r-xp") || strstr(line, "rwxp")) { + start = strtoul(strtok(line, "-"), NULL, 16); + end = strtoul(strtok(NULL, " "), NULL, 16); + if (addr>= start && addr <= end) { + fclose(fp); + return true; + } + } + } + + fclose(fp); + + return false; +} + +static struct inlineHookItem *findInlineHookItem(uint32_t target_addr) { + int i; + + for (i = 0; i < info.size; ++i) { + if (info.item[i].target_addr == target_addr) { + return &info.item[i]; + } + } + + return NULL; +} + +static struct inlineHookItem *addInlineHookItem() { + struct inlineHookItem *item; + + if (info.size>= 1024) {
+ return NULL;
+ }
+
+ item = &info.item[info.size];
+ ++info.size;
+
+ return item;
+}
+
+static void deleteInlineHookItem(int pos) {
+ info.item[pos] = info.item[info.size - 1];
+ --info.size;
+}
+
+enum ele7en_status
+registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr) {
+ struct inlineHookItem *item;
+
+ if (!isExecutableAddr(target_addr) || !isExecutableAddr(new_addr)) {
+ return ELE7EN_ERROR_NOT_EXECUTABLE;
+ }
+
+ item = findInlineHookItem(target_addr);
+ if (item != NULL) {
+ if (item->status == REGISTERED) {
+ return ELE7EN_ERROR_ALREADY_REGISTERED;
+ } else if (item->status == HOOKED) {
+ return ELE7EN_ERROR_ALREADY_HOOKED;
+ } else {
+ return ELE7EN_ERROR_UNKNOWN;
+ }
+ }
+
+ item = addInlineHookItem();
+
+ item->target_addr = target_addr;
+ item->new_addr = new_addr;
+ item->proto_addr = proto_addr;
+
+ item->length = TEST_BIT0(item->target_addr) ? 12 : 8;
+ item->orig_instructions = malloc(item->length);
+ __android_log_print(ANDROID_LOG_ERROR, TAG, "item->orig_instructions length=%d", item->length);
+ memcpy(item->orig_instructions, (void *) CLEAR_BIT0(item->target_addr), item->length);
+ __android_log_print(ANDROID_LOG_ERROR, TAG, "item->orig_instructions address=%p",
+ item->orig_instructions);
+ item->trampoline_instructions = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ relocateInstruction(item->target_addr, item->orig_instructions, item->length,
+ item->trampoline_instructions, item->orig_boundaries,
+ item->trampoline_boundaries, &item->count);
+
+ item->status = REGISTERED;
+
+ return ELE7EN_OK;
+}
+
+static void doInlineUnHook(struct inlineHookItem *item, int pos) {
+ mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2,
+ PROT_READ | PROT_WRITE | PROT_EXEC);
+ memcpy((void *) CLEAR_BIT0(item->target_addr), item->orig_instructions, item->length);
+ mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2,
+ PROT_READ | PROT_EXEC);
+ munmap(item->trampoline_instructions, PAGE_SIZE);
+ free(item->orig_instructions);
+
+ deleteInlineHookItem(pos);
+
+ __builtin___clear_cache(CLEAR_BIT0(item->target_addr),
+ CLEAR_BIT0(item->target_addr) + item->length);
+}
+
+enum ele7en_status inlineUnHook(uint32_t target_addr) {
+ int i;
+
+ for (i = 0; i < info.size; ++i) { + if (info.item[i].target_addr == target_addr && info.item[i].status == HOOKED) { + pid_t pid; + + pid = freeze(&info.item[i], ACTION_DISABLE); + + doInlineUnHook(&info.item[i], i); + + unFreeze(pid); + + return ELE7EN_OK; + } + } + + return ELE7EN_ERROR_NOT_HOOKED; +} + +void inlineUnHookAll() { + pid_t pid; + int i; + + pid = freeze(NULL, ACTION_DISABLE); + + for (i = 0; i < info.size; ++i) { + if (info.item[i].status == HOOKED) { + doInlineUnHook(&info.item[i], i); + --i; + } + } + + unFreeze(pid); +} + +static void doInlineHook(struct inlineHookItem *item) { + mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2,
+ PROT_READ | PROT_WRITE | PROT_EXEC);
+
+ if (item->proto_addr != NULL) {
+ *(item->proto_addr) = TEST_BIT0(item->target_addr) ? (uint32_t *) SET_BIT0(
+ (uint32_t) item->trampoline_instructions) : item->trampoline_instructions;
+ }
+
+ if (TEST_BIT0(item->target_addr)) {
+ int i;
+
+ i = 0;
+ if (CLEAR_BIT0(item->target_addr) % 4 != 0) {
+ ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00; // NOP
+ }
+ ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF;
+ ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC]
+ ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF;
+ ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr>> 16;
+ } else {
+ ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4]
+ ((uint32_t *) (item->target_addr))[1] = item->new_addr;
+ }
+
+ mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2,
+ PROT_READ | PROT_EXEC);
+
+ item->status = HOOKED;
+
+ __builtin___clear_cache(CLEAR_BIT0(item->target_addr),
+ CLEAR_BIT0(item->target_addr) + item->length);
+}
+
+enum ele7en_status inlineHook(uint32_t target_addr) {
+ int i;
+ struct inlineHookItem *item;
+
+ item = NULL;
+ for (i = 0; i < info.size; ++i) { + if (info.item[i].target_addr == target_addr) { + item = &info.item[i]; + break; + } + } + + if (item == NULL) { + return ELE7EN_ERROR_NOT_REGISTERED; + } + + if (item->status == REGISTERED) {
+ pid_t pid;
+
+ pid = freeze(item, ACTION_ENABLE);
+
+ doInlineHook(item);
+
+ unFreeze(pid);
+
+ return ELE7EN_OK;
+ } else if (item->status == HOOKED) {
+ return ELE7EN_ERROR_ALREADY_HOOKED;
+ } else {
+ return ELE7EN_ERROR_UNKNOWN;
+ }
+}
+
+void inlineHookAll() {
+ pid_t pid;
+ int i;
+
+ pid = freeze(NULL, ACTION_ENABLE);
+
+ for (i = 0; i < info.size; ++i) { + if (info.item[i].status == REGISTERED) { + doInlineHook(&info.item[i]); + } + } + + unFreeze(pid); +} diff --git a/xposedmodule/src/main/cpp/inlineHook.h b/xposedmodule/src/main/cpp/inlineHook.h new file mode 100755 index 0000000..86bc6a8 --- /dev/null +++ b/xposedmodule/src/main/cpp/inlineHook.h @@ -0,0 +1,33 @@ +#ifndef _INLINEHOOK_H +#define _INLINEHOOK_H + +#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum ele7en_status {
+ ELE7EN_ERROR_UNKNOWN = -1,
+ ELE7EN_OK = 0,
+ ELE7EN_ERROR_NOT_INITIALIZED,
+ ELE7EN_ERROR_NOT_EXECUTABLE,
+ ELE7EN_ERROR_NOT_REGISTERED,
+ ELE7EN_ERROR_NOT_HOOKED,
+ ELE7EN_ERROR_ALREADY_REGISTERED,
+ ELE7EN_ERROR_ALREADY_HOOKED,
+ ELE7EN_ERROR_SO_NOT_FOUND,
+ ELE7EN_ERROR_FUNCTION_NOT_FOUND
+};
+
+enum ele7en_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr);
+enum ele7en_status inlineUnHook(uint32_t target_addr);
+void inlineUnHookAll();
+enum ele7en_status inlineHook(uint32_t target_addr);
+void inlineHookAll();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/xposedmodule/src/main/cpp/native.cpp b/xposedmodule/src/main/cpp/native.cpp
new file mode 100644
index 0000000..1305934
--- /dev/null
+++ b/xposedmodule/src/main/cpp/native.cpp
@@ -0,0 +1,55 @@
+//
+// Created by WrBug on 2018年3月23日.
+//
+#include "native.h"
+#include "inlineHook.h"
+
+#define TAG "developerhelper.xposed.native.native-->"
+
+
+JNIEXPORT void JNICALL Java_com_wrbug_developerhelper_xposed_dumpdex_Native_dump
+ (JNIEnv *env, jclass obj, jstring packageName) {
+
+ static bool is_hook = false;
+ char *p = (char *) env->GetStringUTFChars(packageName, 0);
+ __android_log_print(ANDROID_LOG_ERROR, TAG, "%s", p);
+ if (is_hook) {
+ __android_log_print(ANDROID_LOG_INFO, TAG, "hooked ignore");
+ return;
+ }
+ init_package_name(p);
+ env->ReleaseStringChars(packageName, (const jchar *) p);
+ ndk_init(env);
+ void *handle = ndk_dlopen("libart.so", RTLD_NOW);
+ if (handle == NULL) {
+ __android_log_print(ANDROID_LOG_ERROR, TAG, "Error: unable to find the SO : libart.so");
+ return;
+ }
+ void *open_common_addr = ndk_dlsym(handle, get_open_function_flag());
+ if (open_common_addr == NULL) {
+ __android_log_print(ANDROID_LOG_ERROR, TAG,
+ "Error: unable to find the Symbol : ");
+ return;
+ }
+#if defined(__aarch64__)
+ A64HookFunction(open_common_addr, get_new_open_function_addr(), get_old_open_function_addr());
+ __android_log_print(ANDROID_LOG_DEFAULT, TAG, "loaded so: libart.so");
+#elif defined(__arm__)
+ if (registerInlineHook((uint32_t) open_common_addr, (uint32_t) get_new_open_function_addr(),
+ (uint32_t **) get_old_open_function_addr()) != ELE7EN_OK) {
+ __android_log_print(ANDROID_LOG_ERROR, TAG, "register1 hook failed!");
+ return;
+ } else {
+ __android_log_print(ANDROID_LOG_ERROR, TAG, "register1 hook success!");
+ }
+ if (inlineHook((uint32_t) open_common_addr) != ELE7EN_OK) {
+ __android_log_print(ANDROID_LOG_ERROR, TAG, "register2 hook failed!");
+ return;
+ } else {
+ __android_log_print(ANDROID_LOG_ERROR, TAG, "register2 hook success!");
+ }
+ __android_log_print(ANDROID_LOG_DEFAULT, TAG, "loaded so: libart.so");
+#endif
+ __android_log_print(ANDROID_LOG_INFO, TAG, "hook init complete");
+ is_hook = true;
+}
diff --git a/xposedmodule/src/main/cpp/native.h b/xposedmodule/src/main/cpp/native.h
new file mode 100644
index 0000000..ba727d4
--- /dev/null
+++ b/xposedmodule/src/main/cpp/native.h
@@ -0,0 +1,43 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "dlopen.h"
+#include "util/deviceutils.h"
+#include "util/fileutils.h"
+
+#if defined(__aarch64__)
+
+#include "And64InlineHook.hpp"
+
+#elif defined(__arm__)
+#include "inlineHook.h"
+#endif
+
+#ifndef _Included_com_wrbug_xposeddemo_Native
+#define _Included_com_wrbug_xposeddemo_Native
+#ifdef __cplusplus
+extern "C" {
+
+#endif
+/*
+ * Class: com_wrbug_xposeddemo_Native
+ * Method: test
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT void JNICALL Java_com_wrbug_developerhelper_xposed_dumpdex_Native_dump
+ (JNIEnv *, jclass, jstring);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/xposedmodule/src/main/cpp/readme.md b/xposedmodule/src/main/cpp/readme.md
new file mode 100644
index 0000000..ff4f8fd
--- /dev/null
+++ b/xposedmodule/src/main/cpp/readme.md
@@ -0,0 +1,10 @@
+# ndk说明
+
+ndk主要针对android8.0及以上机型使用,通过hook底层的代码,将数据dump出来,目前360加固测试通过,其他正在寻找方案
+
+
+## 感谢
+
+[https://github.com/ele7enxxh/Android-Inline-Hook](https://github.com/ele7enxxh/Android-Inline-Hook)
+
+[https://github.com/rrrfff/ndk_dlopen](https://github.com/rrrfff/ndk_dlopen)
\ No newline at end of file
diff --git a/xposedmodule/src/main/cpp/relocate.c b/xposedmodule/src/main/cpp/relocate.c
new file mode 100755
index 0000000..36fd383
--- /dev/null
+++ b/xposedmodule/src/main/cpp/relocate.c
@@ -0,0 +1,612 @@
+/*
+relocate instruction
+author: ele7enxxh
+mail: ele7enxxh@qq.com
+website: ele7enxxh.com
+modified time: 2016年10月17日
+created time: 2015年01月17日
+*/
+
+#include "relocate.h"
+
+#define ALIGN_PC(pc) (pc & 0xFFFFFFFC)
+
+enum INSTRUCTION_TYPE {
+ // B