Compare commits

...

57 Commits

Author SHA1 Message Date
semantic-release-bot
a123026f46 chore(release): 1.6.0 [skip ci]
# [1.6.0](https://github.com/revanced/revanced-patcher/compare/v1.5.0...v1.6.0) (2022-06-22)

### Features

* use of `java.util.logging.Logger` ([9c39c9e](9c39c9efdb))
2022-06-22 11:48:43 +00:00
oSumAtrIX
9c39c9efdb feat: use of java.util.logging.Logger 2022-06-22 13:45:13 +02:00
semantic-release-bot
3ee1c01430 chore(release): 1.5.0 [skip ci]
# [1.5.0](https://github.com/revanced/revanced-patcher/compare/v1.4.0...v1.5.0) (2022-06-22)

### Features

* use streams to write the dex files ([64bae88](64bae884dc))
2022-06-22 01:21:33 +00:00
oSumAtrIX
64bae884dc feat: use streams to write the dex files 2022-06-22 03:19:39 +02:00
semantic-release-bot
e94a706949 chore(release): 1.4.0 [skip ci]
# [1.4.0](https://github.com/revanced/revanced-patcher/compare/v1.3.4...v1.4.0) (2022-06-22)

### Features

* return a `File` instance instead of `ExtFile` ([68174bb](68174bbd6b))
2022-06-22 00:55:56 +00:00
oSumAtrIX
89bb43066b build: use dependencies as implementations instead of apis 2022-06-22 02:54:23 +02:00
oSumAtrIX
68174bbd6b feat: return a File instance instead of ExtFile 2022-06-22 02:53:37 +02:00
semantic-release-bot
d05c9416d6 chore(release): 1.3.4 [skip ci]
## [1.3.4](https://github.com/revanced/revanced-patcher/compare/v1.3.3...v1.3.4) (2022-06-21)

### Bug Fixes

* `String.toInstructions` defaulting `forStaticMethod` to `false` ([5a2f02b](5a2f02b97d)), closes [revanced/revanced-patches#46](https://github.com/revanced/revanced-patches/issues/46)
2022-06-21 21:53:33 +00:00
oSumAtrIX
5a2f02b97d fix: String.toInstructions defaulting forStaticMethod to false
Fixes revanced/revanced-patches#46
2022-06-21 23:52:09 +02:00
semantic-release-bot
a3005fa08e chore(release): 1.3.3 [skip ci]
## [1.3.3](https://github.com/revanced/revanced-patcher/compare/v1.3.2...v1.3.3) (2022-06-21)

### Bug Fixes

* add docs (trigger release) ([6628b78](6628b7870f))

### Reverts

* propagate dependencies ([365e1d7](365e1d7a45))
2022-06-21 19:04:06 +00:00
Lucaskyy
6628b7870f fix: add docs (trigger release) 2022-06-21 21:02:50 +02:00
Lucaskyy
a6411245aa Merge remote-tracking branch 'origin/main' into main 2022-06-21 20:50:11 +02:00
Lucaskyy
365e1d7a45 revert: propagate dependencies
guess it doesn't work when not propagating them
2022-06-21 20:50:00 +02:00
semantic-release-bot
4507cd2353 chore(release): 1.3.2 [skip ci]
## [1.3.2](https://github.com/revanced/revanced-patcher/compare/v1.3.1...v1.3.2) (2022-06-21)

### Bug Fixes

* return resourceFile to caller ([1f75777](1f75777cf9))
2022-06-21 18:45:16 +00:00
Lucaskyy
1f75777cf9 fix: return resourceFile to caller 2022-06-21 20:43:47 +02:00
Lucaskyy
28d5468b07 build: do not propagate all dependencies 2022-06-21 20:29:37 +02:00
semantic-release-bot
746496125d chore(release): 1.3.1 [skip ci]
## [1.3.1](https://github.com/revanced/revanced-patcher/compare/v1.3.0...v1.3.1) (2022-06-21)

### Bug Fixes

* `InlineSmaliCompiler.compile` using 0 registers instead of 1 by default ([835a421](835a421cc0))
2022-06-21 15:48:43 +00:00
oSumAtrIX
835a421cc0 fix: InlineSmaliCompiler.compile using 0 registers instead of 1 by default 2022-06-21 17:46:43 +02:00
semantic-release-bot
99342fe033 chore(release): 1.3.0 [skip ci]
# [1.3.0](https://github.com/revanced/revanced-patcher/compare/v1.2.9...v1.3.0) (2022-06-20)

### Features

* `parametersCount` for `InlineSmaliCompiler` instead of `parameters` ([ad6c5c8](ad6c5c8273))
* simplify adding instructions ([e47b67d](e47b67d7ec))
2022-06-20 19:22:22 +00:00
oSumAtrIX
e47b67d7ec feat: simplify adding instructions 2022-06-20 21:20:51 +02:00
oSumAtrIX
ad6c5c8273 feat: parametersCount for InlineSmaliCompiler instead of parameters 2022-06-20 20:10:11 +02:00
oSumAtrIX
fd690acd61 refactor: add internal attribute to signatures field 2022-06-20 20:10:06 +02:00
semantic-release-bot
e698b02bf6 chore(release): 1.2.9 [skip ci]
## [1.2.9](https://github.com/revanced/revanced-patcher/compare/v1.2.8...v1.2.9) (2022-06-20)

### Bug Fixes

* update apktool ([ab866bb](ab866bb8ef))
2022-06-20 15:08:57 +00:00
Lucaskyy
ab866bb8ef fix: update apktool 2022-06-20 17:07:04 +02:00
semantic-release-bot
714a98422d chore(release): 1.2.8 [skip ci]
## [1.2.8](https://github.com/revanced/revanced-patcher/compare/v1.2.7...v1.2.8) (2022-06-18)

### Bug Fixes

* update apktool ([051afd9](051afd98d0))
2022-06-18 21:51:07 +00:00
Sculas
051afd98d0 fix: update apktool 2022-06-18 23:49:41 +02:00
semantic-release-bot
d38cf6a229 chore(release): 1.2.7 [skip ci]
## [1.2.7](https://github.com/revanced/revanced-patcher/compare/v1.2.6...v1.2.7) (2022-06-18)

### Bug Fixes

* version not working with apktool due to cache ([03f5ee0](03f5ee088b))
2022-06-18 21:17:22 +00:00
Sculas
03f5ee088b fix: version not working with apktool due to cache 2022-06-18 23:15:33 +02:00
semantic-release-bot
5d0fd48b15 chore(release): 1.2.6 [skip ci]
## [1.2.6](https://github.com/revanced/revanced-patcher/compare/v1.2.5...v1.2.6) (2022-06-18)

### Bug Fixes

* remove javadoc jar (also trigger release) ([56f6ca3](56f6ca3891))
2022-06-18 20:54:29 +00:00
Sculas
56f6ca3891 fix: remove javadoc jar (also trigger release) 2022-06-18 22:52:49 +02:00
semantic-release-bot
9e0a74fcfb chore(release): 1.2.5 [skip ci]
## [1.2.5](https://github.com/revanced/revanced-patcher/compare/v1.2.4...v1.2.5) (2022-06-17)

### Bug Fixes

* goodbye security ([8f3ac77](8f3ac7702a))
2022-06-17 16:13:47 +00:00
Sculas
8f3ac7702a fix: goodbye security 2022-06-17 18:12:18 +02:00
semantic-release-bot
7b65f2d02c chore(release): 1.2.4 [skip ci]
## [1.2.4](https://github.com/revanced/revanced-patcher/compare/v1.2.3...v1.2.4) (2022-06-15)

### Reverts

* "fix: enforce aapt v1" ([dfd8a24](dfd8a24512))
2022-06-15 19:41:28 +00:00
Lucaskyy
2a1b2df56b Merge remote-tracking branch 'origin/main' into main 2022-06-15 21:39:42 +02:00
Lucaskyy
dfd8a24512 revert: "fix: enforce aapt v1"
This reverts commit cff87ff077.
2022-06-15 21:39:27 +02:00
semantic-release-bot
a3efd212fc chore(release): 1.2.3 [skip ci]
## [1.2.3](https://github.com/revanced/revanced-patcher/compare/v1.2.2...v1.2.3) (2022-06-14)

### Bug Fixes

* enforce aapt v1 ([cff87ff](cff87ff077))
2022-06-14 22:26:32 +00:00
Lucaskyy
3e610f7ba9 Merge remote-tracking branch 'origin/main' into main 2022-06-15 00:25:21 +02:00
Lucaskyy
cff87ff077 fix: enforce aapt v1 2022-06-15 00:25:06 +02:00
semantic-release-bot
54aa04ca34 chore(release): 1.2.2 [skip ci]
## [1.2.2](https://github.com/revanced/revanced-patcher/compare/v1.2.1...v1.2.2) (2022-06-14)

### Bug Fixes

* enforce aapt v2 ([b68b0bf](b68b0bf3d7))
2022-06-14 20:35:38 +00:00
Lucaskyy
0eda84eaef Merge remote-tracking branch 'origin/main' into main 2022-06-14 22:34:21 +02:00
Lucaskyy
b68b0bf3d7 fix: enforce aapt v2 2022-06-14 22:34:10 +02:00
semantic-release-bot
c692202f67 chore(release): 1.2.1 [skip ci]
## [1.2.1](https://github.com/revanced/revanced-patcher/compare/v1.2.0...v1.2.1) (2022-06-14)

### Bug Fixes

* Patcher setting BuildOptions too late ([6a5c873](6a5c8735fb))
2022-06-14 16:25:08 +00:00
Lucaskyy
8ce3535427 Merge remote-tracking branch 'origin/main' into main 2022-06-14 18:23:49 +02:00
Lucaskyy
6a5c8735fb fix: Patcher setting BuildOptions too late
This causes the Manager to crash, due to a stupid bug in Apktool which is prevented by setting a valid frameworkFolderLocation.
2022-06-14 18:23:40 +02:00
semantic-release-bot
8f32bc9c08 chore(release): 1.2.0 [skip ci]
# [1.2.0](https://github.com/revanced/revanced-patcher/compare/v1.1.0...v1.2.0) (2022-06-14)

### Features

* allow custom framework path to be specified ([d3a580e](d3a580ea19))
2022-06-14 15:50:24 +00:00
Lucaskyy
d3a580ea19 feat: allow custom framework path to be specified 2022-06-14 17:49:07 +02:00
oSumAtrIX
4c8eb0e5c7 refactor: gradle build script 2022-06-14 01:34:48 +02:00
semantic-release-bot
bc92eb7fd8 chore(release): 1.1.0 [skip ci]
# [1.1.0](https://github.com/revanced/revanced-patcher/compare/v1.0.0...v1.1.0) (2022-06-11)

### Bug Fixes

* resource patcher ([31815ca](31815ca9ea))
* update apktool to fork ([566ecef](566ecefa2b))

### Features

* allow custom aapt path to be specified ([8eb4a8f](8eb4a8f87a))
2022-06-11 23:25:19 +00:00
oSumAtrIX
cf89bd4171 Merge pull request #39 from revanced/non-root
fix: resource patcher
2022-06-12 01:24:04 +02:00
Lucaskyy
566ecefa2b fix: update apktool to fork 2022-06-11 20:39:29 +02:00
Lucaskyy
8eb4a8f87a feat: allow custom aapt path to be specified 2022-06-11 20:08:00 +02:00
oSumAtrIX
afcba5c212 refactor: simplify code by removing the with block 2022-06-11 19:45:11 +02:00
oSumAtrIX
2dcbd8d079 refactor: use include annotation parameter instead of excludeByDefault 2022-06-11 19:14:19 +02:00
oSumAtrIX
81895c7d5c Merge remote-tracking branch 'origin/non-root' into non-root
# Conflicts:
#	src/main/kotlin/app/revanced/patcher/Patcher.kt
2022-06-11 18:56:38 +02:00
Lucaskyy
22267883b1 refactor: get rid of all useless let blocks 2022-06-11 16:56:05 +02:00
oSumAtrIX
26fca60b53 refactor: use also instead of let where possible 2022-06-11 16:24:49 +02:00
oSumAtrIX
31815ca9ea fix: resource patcher 2022-06-11 06:36:13 +02:00
17 changed files with 441 additions and 135 deletions

View File

@@ -1,3 +1,148 @@
# [1.6.0](https://github.com/revanced/revanced-patcher/compare/v1.5.0...v1.6.0) (2022-06-22)
### Features
* use of `java.util.logging.Logger` ([9c39c9e](https://github.com/revanced/revanced-patcher/commit/9c39c9efdb5d48ddaffce7f711c275e732b0b2d9))
# [1.5.0](https://github.com/revanced/revanced-patcher/compare/v1.4.0...v1.5.0) (2022-06-22)
### Features
* use streams to write the dex files ([64bae88](https://github.com/revanced/revanced-patcher/commit/64bae884dcb72550a3218e149f3ca0fd0ca03aaf))
# [1.4.0](https://github.com/revanced/revanced-patcher/compare/v1.3.4...v1.4.0) (2022-06-22)
### Features
* return a `File` instance instead of `ExtFile` ([68174bb](https://github.com/revanced/revanced-patcher/commit/68174bbd6b4df47a91b610c2b97dbae55b594163))
## [1.3.4](https://github.com/revanced/revanced-patcher/compare/v1.3.3...v1.3.4) (2022-06-21)
### Bug Fixes
* `String.toInstructions` defaulting `forStaticMethod` to `false` ([5a2f02b](https://github.com/revanced/revanced-patcher/commit/5a2f02b97dcde95dbe901fa68cca6c6c0219cb82)), closes [revanced/revanced-patches#46](https://github.com/revanced/revanced-patches/issues/46)
## [1.3.3](https://github.com/revanced/revanced-patcher/compare/v1.3.2...v1.3.3) (2022-06-21)
### Bug Fixes
* add docs (trigger release) ([6628b78](https://github.com/revanced/revanced-patcher/commit/6628b7870fc052da40be0d50a7e2b0b6c57743cc))
### Reverts
* propagate dependencies ([365e1d7](https://github.com/revanced/revanced-patcher/commit/365e1d7a4507b918a4c8170ce2c88f6c8ff1d474))
## [1.3.2](https://github.com/revanced/revanced-patcher/compare/v1.3.1...v1.3.2) (2022-06-21)
### Bug Fixes
* return resourceFile to caller ([1f75777](https://github.com/revanced/revanced-patcher/commit/1f75777cf985bf08483033ec541937d3e733347b))
## [1.3.1](https://github.com/revanced/revanced-patcher/compare/v1.3.0...v1.3.1) (2022-06-21)
### Bug Fixes
* `InlineSmaliCompiler.compile` using 0 registers instead of 1 by default ([835a421](https://github.com/revanced/revanced-patcher/commit/835a421cc0588b92c2995e9d74727069d14b1750))
# [1.3.0](https://github.com/revanced/revanced-patcher/compare/v1.2.9...v1.3.0) (2022-06-20)
### Features
* `parametersCount` for `InlineSmaliCompiler` instead of `parameters` ([ad6c5c8](https://github.com/revanced/revanced-patcher/commit/ad6c5c827389d10eae473dc66557a699df8c3280))
* simplify adding instructions ([e47b67d](https://github.com/revanced/revanced-patcher/commit/e47b67d7ec521f288644afb89baf4146dc9bc87d))
## [1.2.9](https://github.com/revanced/revanced-patcher/compare/v1.2.8...v1.2.9) (2022-06-20)
### Bug Fixes
* update apktool ([ab866bb](https://github.com/revanced/revanced-patcher/commit/ab866bb8ef4792d8f2a51edc79e687b5b636c621))
## [1.2.8](https://github.com/revanced/revanced-patcher/compare/v1.2.7...v1.2.8) (2022-06-18)
### Bug Fixes
* update apktool ([051afd9](https://github.com/revanced/revanced-patcher/commit/051afd98d065f71556392139d77c20b4c2dc7dd1))
## [1.2.7](https://github.com/revanced/revanced-patcher/compare/v1.2.6...v1.2.7) (2022-06-18)
### Bug Fixes
* version not working with apktool due to cache ([03f5ee0](https://github.com/revanced/revanced-patcher/commit/03f5ee088b1b96b88cb7aeb323443b6209a13950))
## [1.2.6](https://github.com/revanced/revanced-patcher/compare/v1.2.5...v1.2.6) (2022-06-18)
### Bug Fixes
* remove javadoc jar (also trigger release) ([56f6ca3](https://github.com/revanced/revanced-patcher/commit/56f6ca38919b522c0d5558eabffa4aee41cc0b0b))
## [1.2.5](https://github.com/revanced/revanced-patcher/compare/v1.2.4...v1.2.5) (2022-06-17)
### Bug Fixes
* goodbye security ([8f3ac77](https://github.com/revanced/revanced-patcher/commit/8f3ac7702a2b3ee98c55aeac6a1b9972f99664cc))
## [1.2.4](https://github.com/revanced/revanced-patcher/compare/v1.2.3...v1.2.4) (2022-06-15)
### Reverts
* "fix: enforce aapt v1" ([dfd8a24](https://github.com/revanced/revanced-patcher/commit/dfd8a245124f85b1b028bbba197c70c8dca689b6))
## [1.2.3](https://github.com/revanced/revanced-patcher/compare/v1.2.2...v1.2.3) (2022-06-14)
### Bug Fixes
* enforce aapt v1 ([cff87ff](https://github.com/revanced/revanced-patcher/commit/cff87ff0770d774d7ef79eec5a22462eadbcb9c5))
## [1.2.2](https://github.com/revanced/revanced-patcher/compare/v1.2.1...v1.2.2) (2022-06-14)
### Bug Fixes
* enforce aapt v2 ([b68b0bf](https://github.com/revanced/revanced-patcher/commit/b68b0bf3d735f54b92ad7dad8132f77e9007063f))
## [1.2.1](https://github.com/revanced/revanced-patcher/compare/v1.2.0...v1.2.1) (2022-06-14)
### Bug Fixes
* Patcher setting BuildOptions too late ([6a5c873](https://github.com/revanced/revanced-patcher/commit/6a5c8735fb8a5d6f7e9c606734b6684c7fa99e7f))
# [1.2.0](https://github.com/revanced/revanced-patcher/compare/v1.1.0...v1.2.0) (2022-06-14)
### Features
* allow custom framework path to be specified ([d3a580e](https://github.com/revanced/revanced-patcher/commit/d3a580ea19d7c2d5d8c97650b1e6396ea0a7fc25))
# [1.1.0](https://github.com/revanced/revanced-patcher/compare/v1.0.0...v1.1.0) (2022-06-11)
### Bug Fixes
* resource patcher ([31815ca](https://github.com/revanced/revanced-patcher/commit/31815ca9ea990f16b3600d61fd570c1805be1c82))
* update apktool to fork ([566ecef](https://github.com/revanced/revanced-patcher/commit/566ecefa2bd4cde5ebfb2b22dc56cd8bf9f396bd))
### Features
* allow custom aapt path to be specified ([8eb4a8f](https://github.com/revanced/revanced-patcher/commit/8eb4a8f87ae7679a272f3224273a37a31d4bb121))
# 1.0.0 (2022-06-05) # 1.0.0 (2022-06-05)

View File

@@ -1,38 +1,32 @@
plugins { plugins {
kotlin("jvm") version "1.6.21" kotlin("jvm") version "1.7.0"
java java
`maven-publish` `maven-publish`
} }
group = "app.revanced" group = "app.revanced"
val githubUsername: String = project.findProperty("gpr.user") as? String ?: System.getenv("GITHUB_ACTOR")
val githubPassword: String = project.findProperty("gpr.key") as? String ?: System.getenv("GITHUB_TOKEN")
repositories { repositories {
mavenCentral() mavenCentral()
maven { maven {
url = uri("https://maven.pkg.github.com/revanced/multidexlib2") url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
credentials { credentials {
// DO NOT set these variables in the project's gradle.properties. username = githubUsername
// Instead, you should set them in: password = githubPassword
// Windows: %homepath%\.gradle\gradle.properties
// Linux: ~/.gradle/gradle.properties
username =
project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") // DO NOT CHANGE!
password =
project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") // DO NOT CHANGE!
} }
} }
} }
dependencies { dependencies {
implementation(kotlin("stdlib")) implementation("xpp3:xpp3:1.1.4c")
implementation("app.revanced:multidexlib2:2.5.2.r2")
api("xpp3:xpp3:1.1.4c") implementation("org.smali:smali:2.5.2")
api("org.apktool:apktool-lib:2.6.1") implementation("org.apktool:apktool-lib:2.6.5-SNAPSHOT")
api("app.revanced:multidexlib2:2.5.2.r2")
api("org.smali:smali:2.5.2")
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
implementation(kotlin("reflect"))
} }
tasks { tasks {
@@ -46,14 +40,10 @@ tasks {
java { java {
withSourcesJar() withSourcesJar()
withJavadocJar()
} }
val isGitHubCI = System.getenv("GITHUB_ACTOR") != null
publishing { publishing {
repositories { repositories {
if (isGitHubCI) {
maven { maven {
name = "GitHubPackages" name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher") url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
@@ -62,9 +52,6 @@ publishing {
password = System.getenv("GITHUB_TOKEN") password = System.getenv("GITHUB_TOKEN")
} }
} }
} else {
mavenLocal()
}
} }
publications { publications {
register<MavenPublication>("gpr") { register<MavenPublication>("gpr") {

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 1.0.0 version = 1.6.0

View File

@@ -1,5 +1,6 @@
package app.revanced.patcher package app.revanced.patcher
import app.revanced.patcher.data.PackageMetadata
import app.revanced.patcher.data.PatcherData import app.revanced.patcher.data.PatcherData
import app.revanced.patcher.data.base.Data import app.revanced.patcher.data.base.Data
import app.revanced.patcher.data.implementation.findIndexed import app.revanced.patcher.data.implementation.findIndexed
@@ -16,11 +17,13 @@ import app.revanced.patcher.signature.implementation.method.resolver.MethodSigna
import app.revanced.patcher.util.ListBackedSet import app.revanced.patcher.util.ListBackedSet
import brut.androlib.Androlib import brut.androlib.Androlib
import brut.androlib.meta.UsesFramework import brut.androlib.meta.UsesFramework
import brut.androlib.options.BuildOptions
import brut.androlib.res.AndrolibResources import brut.androlib.res.AndrolibResources
import brut.androlib.res.data.ResPackage import brut.androlib.res.data.ResPackage
import brut.androlib.res.decoder.AXmlResourceParser import brut.androlib.res.decoder.AXmlResourceParser
import brut.androlib.res.decoder.ResAttrDecoder import brut.androlib.res.decoder.ResAttrDecoder
import brut.androlib.res.decoder.XmlPullStreamDecoder import brut.androlib.res.decoder.XmlPullStreamDecoder
import brut.androlib.res.xml.ResXmlPatcher
import brut.directory.ExtFile import brut.directory.ExtFile
import lanchon.multidexlib2.BasicDexFileNamer import lanchon.multidexlib2.BasicDexFileNamer
import lanchon.multidexlib2.DexIO import lanchon.multidexlib2.DexIO
@@ -30,6 +33,8 @@ import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.DexFile import org.jf.dexlib2.iface.DexFile
import org.jf.dexlib2.writer.io.MemoryDataStore import org.jf.dexlib2.writer.io.MemoryDataStore
import java.io.File import java.io.File
import java.nio.file.Files
import java.util.logging.Logger
val NAMER = BasicDexFileNamer() val NAMER = BasicDexFileNamer()
@@ -37,36 +42,46 @@ val NAMER = BasicDexFileNamer()
* The ReVanced Patcher. * The ReVanced Patcher.
* @param options The options for the patcher. * @param options The options for the patcher.
*/ */
class Patcher( class Patcher(private val options: PatcherOptions) {
private val options: PatcherOptions private val logger: Logger = Logger.getLogger(::Patcher.name)
) {
val packageVersion: String
val packageName: String
private lateinit var usesFramework: UsesFramework val data: PatcherData
private val patcherData: PatcherData
private val opcodes: Opcodes private val opcodes: Opcodes
init { init {
val extFileInput = ExtFile(options.inputFile) val extInputFile = ExtFile(options.inputFile)
val outDir = File(options.resourceCacheDirectory) val outDir = File(options.resourceCacheDirectory)
if (outDir.exists()){
logger.info("Delete previous resource cache directory")
if (outDir.exists()) outDir.deleteRecursively() outDir.deleteRecursively()
outDir.mkdir() outDir.mkdirs()
}
// load the resource table from the input file val androlib = Androlib(BuildOptions().also { it.setBuildOptions(options) })
val androlib = Androlib() val resourceTable = androlib.getResTable(extInputFile, true)
val resourceTable = androlib.getResTable(extFileInput, true)
val packageMetadata = PackageMetadata()
if (options.patchResources) { if (options.patchResources) {
// 1. decode resources to cache directory logger.info("Decode resources")
androlib.decodeManifestWithResources(extFileInput, outDir, resourceTable)
androlib.decodeResourcesFull(extFileInput, outDir, resourceTable) // decode resources to cache directory
androlib.decodeManifestWithResources(extInputFile, outDir, resourceTable)
androlib.decodeResourcesFull(extInputFile, outDir, resourceTable)
// read additional metadata from the resource table
packageMetadata.metaInfo.usesFramework = UsesFramework().also { framework ->
framework.ids = resourceTable.listFramePackages().map { it.id }.sorted()
}
packageMetadata.metaInfo.doNotCompress = buildList {
androlib.recordUncompressedFiles(extInputFile, this)
}
// 2. read framework ids from the resource table
usesFramework = UsesFramework()
usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted()
} else { } else {
logger.warning("Resource patching is disabled. Falling back to manually decode AndroidManifest.xml")
// create decoder for the resource table // create decoder for the resource table
val decoder = ResAttrDecoder() val decoder = ResAttrDecoder()
decoder.currentPackage = ResPackage(resourceTable, 0, null) decoder.currentPackage = ResPackage(resourceTable, 0, null)
@@ -80,19 +95,26 @@ class Patcher(
XmlPullStreamDecoder( XmlPullStreamDecoder(
axmlParser, AndrolibResources().resXmlSerializer axmlParser, AndrolibResources().resXmlSerializer
).decodeManifest( ).decodeManifest(
extFileInput.directory.getFileInput("AndroidManifest.xml"), nullOutputStream extInputFile.directory.getFileInput("AndroidManifest.xml"), nullOutputStream
) )
} }
// set package information packageMetadata.packageName = resourceTable.currentResPackage.name
packageVersion = resourceTable.versionInfo.versionName packageMetadata.packageVersion = resourceTable.versionInfo.versionName
packageName = resourceTable.currentResPackage.name packageMetadata.metaInfo.versionInfo = resourceTable.versionInfo
packageMetadata.metaInfo.sdkInfo = resourceTable.sdkInfo
logger.info("Read dex files")
// read dex files // read dex files
val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null) val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null)
// get the opcodes
opcodes = dexFile.opcodes opcodes = dexFile.opcodes
// save to patcher data // finally create patcher data
patcherData = PatcherData(dexFile.classes.toMutableList(), options.resourceCacheDirectory) data = PatcherData(
dexFile.classes.toMutableList(), options.resourceCacheDirectory, packageMetadata
)
} }
/** /**
@@ -102,23 +124,27 @@ class Patcher(
* @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found. * @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found.
*/ */
fun addFiles( fun addFiles(
files: Iterable<File>, allowedOverwrites: Iterable<String> = emptyList(), throwOnDuplicates: Boolean = false files: List<File>, allowedOverwrites: Iterable<String> = emptyList(), throwOnDuplicates: Boolean = false
) { ) {
for (file in files) { for (file in files) {
val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null) for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
for (classDef in dexFile.classes) { val type = classDef.type
val e = patcherData.bytecodeData.classes.internalClasses.findIndexed { it.type == classDef.type } val e = data.bytecodeData.classes.internalClasses.findIndexed { it.type == type }
if (e != null) { if (e != null) {
if (throwOnDuplicates) { if (throwOnDuplicates) {
throw Exception("Class ${classDef.type} has already been added to the patcher.") throw Exception("Class $type has already been added to the patcher.")
} }
val (_, idx) = e val (_, idx) = e
if (allowedOverwrites.contains(classDef.type)) { if (allowedOverwrites.contains(type)) {
patcherData.bytecodeData.classes.internalClasses[idx] = classDef logger.fine("Override $type")
data.bytecodeData.classes.internalClasses[idx] = classDef
continue
} }
logger.fine("Skip $type")
continue continue
} }
patcherData.bytecodeData.classes.internalClasses.add(classDef) logger.finest("Add $type")
data.bytecodeData.classes.internalClasses.add(classDef)
} }
} }
} }
@@ -126,11 +152,59 @@ class Patcher(
/** /**
* Save the patched dex file. * Save the patched dex file.
*/ */
fun save(): Map<String, MemoryDataStore> { fun save(): PatcherResult {
val packageMetadata = data.packageMetadata
val metaInfo = packageMetadata.metaInfo
var resourceFile: File? = null
if (options.patchResources) {
val cacheDirectory = ExtFile(options.resourceCacheDirectory)
val androlibResources = AndrolibResources().also { resources ->
resources.buildOptions = BuildOptions().also { buildOptions ->
buildOptions.setBuildOptions(options)
buildOptions.isFramework = metaInfo.isFrameworkApk
buildOptions.resourcesAreCompressed = metaInfo.compressionType
buildOptions.doNotCompress = metaInfo.doNotCompress
}
resources.setSdkInfo(metaInfo.sdkInfo)
resources.setVersionInfo(metaInfo.versionInfo)
resources.setSharedLibrary(metaInfo.sharedLibrary)
resources.setSparseResources(metaInfo.sparseResources)
}
val manifestFile = cacheDirectory.resolve("AndroidManifest.xml")
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(manifestFile)
val aaptFile = cacheDirectory.resolve("aapt_temp_file")
// delete if it exists
Files.deleteIfExists(aaptFile.toPath())
val resDirectory = cacheDirectory.resolve("res")
val includedFiles = metaInfo.usesFramework.ids.map { id ->
androlibResources.getFrameworkApk(
id,
metaInfo.usesFramework.tag
)
}.toTypedArray()
logger.info("Building resources. This takes some time")
androlibResources.aaptPackage(
aaptFile, manifestFile, resDirectory, null,
null, includedFiles
)
resourceFile = aaptFile
}
val newDexFile = object : DexFile { val newDexFile = object : DexFile {
override fun getClasses(): Set<ClassDef> { override fun getClasses(): Set<ClassDef> {
patcherData.bytecodeData.classes.applyProxies() data.bytecodeData.classes.applyProxies()
return ListBackedSet(patcherData.bytecodeData.classes.internalClasses) return ListBackedSet(data.bytecodeData.classes.internalClasses)
} }
override fun getOpcodes(): Opcodes { override fun getOpcodes(): Opcodes {
@@ -138,21 +212,22 @@ class Patcher(
} }
} }
// build modified resources logger.info("Write dex files")
if (options.patchResources) {
val extDir = ExtFile(options.resourceCacheDirectory)
// TODO: figure out why a new instance of Androlib is necessary here
Androlib().buildResources(extDir, usesFramework)
}
// write dex modified files // write dex modified files
val output = mutableMapOf<String, MemoryDataStore>() val dexFiles = mutableMapOf<String, MemoryDataStore>()
MultiDexIO.writeDexFile( MultiDexIO.writeDexFile(
true, -1, // core count true, -1, // core count
output, NAMER, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null dexFiles, NAMER, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null
)
return PatcherResult(
dexFiles.map {
app.revanced.patcher.util.dex.DexFile(it.key, it.value.readAt(0))
},
metaInfo.doNotCompress.toList(),
resourceFile
) )
return output
} }
/** /**
@@ -160,7 +235,7 @@ class Patcher(
* @param patches [Patch]es The patches to add. * @param patches [Patch]es The patches to add.
*/ */
fun addPatches(patches: Iterable<Class<out Patch<Data>>>) { fun addPatches(patches: Iterable<Class<out Patch<Data>>>) {
patcherData.patches.addAll(patches) data.patches.addAll(patches)
} }
/** /**
@@ -175,7 +250,11 @@ class Patcher(
val patchName = patch.patchName val patchName = patch.patchName
// if the patch has already applied silently skip it // if the patch has already applied silently skip it
if (appliedPatches.contains(patchName)) return PatchResultSuccess() if (appliedPatches.contains(patchName)){
logger.fine("Skip $patchName because it already has been applied")
return PatchResultSuccess()
}
appliedPatches.add(patchName) appliedPatches.add(patchName)
// recursively apply all dependency patches // recursively apply all dependency patches
@@ -197,14 +276,16 @@ class Patcher(
// TODO: find a solution for this // TODO: find a solution for this
val data = if (isResourcePatch) { val data = if (isResourcePatch) {
patcherData.resourceData data.resourceData
} else { } else {
MethodSignatureResolver( MethodSignatureResolver(
patcherData.bytecodeData.classes.internalClasses, (patchInstance as BytecodePatch).signatures data.bytecodeData.classes.internalClasses, (patchInstance as BytecodePatch).signatures
).resolve(patcherData) ).resolve(data)
patcherData.bytecodeData data.bytecodeData
} }
logger.fine("Execute $patchName")
return try { return try {
patchInstance.execute(data) patchInstance.execute(data)
} catch (e: Exception) { } catch (e: Exception) {
@@ -225,7 +306,7 @@ class Patcher(
val appliedPatches = mutableListOf<String>() val appliedPatches = mutableListOf<String>()
return buildMap { return buildMap {
for (patch in patcherData.patches) { for (patch in data.patches) {
val result = applyPatch(patch, appliedPatches) val result = applyPatch(patch, appliedPatches)
val name = patch.patchName val name = patch.patchName
@@ -242,3 +323,9 @@ class Patcher(
} }
} }
} }
private fun BuildOptions.setBuildOptions(options: PatcherOptions) {
this.aaptPath = options.aaptPath
this.useAapt2 = true
this.frameworkFolderLocation = options.frameworkFolderLocation
}

View File

@@ -3,13 +3,17 @@ package app.revanced.patcher
import java.io.File import java.io.File
/** /**
* Options for a patcher.
* @param inputFile The input file (usually an apk file). * @param inputFile The input file (usually an apk file).
* @param resourceCacheDirectory Directory to cache resources. * @param resourceCacheDirectory Directory to cache resources.
* @param patchResources Weather to use the resource patcher. Resources will still need to be decoded. * @param patchResources Weather to use the resource patcher. Resources will still need to be decoded.
* @param aaptPath Optional path to a custom aapt binary.
* @param frameworkFolderLocation Optional path to a custom framework folder.
*/ */
data class PatcherOptions( data class PatcherOptions(
internal val inputFile: File, internal val inputFile: File,
// TODO: maybe a file system in memory is better. Could cause high memory usage.
internal val resourceCacheDirectory: String, internal val resourceCacheDirectory: String,
internal val patchResources: Boolean = false internal val patchResources: Boolean = false,
internal val aaptPath: String = "",
internal val frameworkFolderLocation: String? = null
) )

View File

@@ -0,0 +1,17 @@
package app.revanced.patcher
import app.revanced.patcher.util.dex.DexFile
import brut.directory.ExtFile
import java.io.File
/**
* The result of a patcher.
* @param dexFiles The patched dex files.
* @param doNotCompress List of relative paths to files to exclude from compressing.
* @param resourceFile File containing resources that need to be extracted into the APK.
*/
data class PatcherResult(
val dexFiles: List<DexFile>,
val doNotCompress: List<String>? = null,
val resourceFile: File?
)

View File

@@ -0,0 +1,13 @@
package app.revanced.patcher.data
import brut.androlib.meta.MetaInfo
/**
* Metadata about a package.
*/
class PackageMetadata {
lateinit var packageName: String
lateinit var packageVersion: String
internal val metaInfo: MetaInfo = MetaInfo()
}

View File

@@ -7,10 +7,12 @@ import app.revanced.patcher.patch.base.Patch
import org.jf.dexlib2.iface.ClassDef import org.jf.dexlib2.iface.ClassDef
import java.io.File import java.io.File
internal data class PatcherData( data class PatcherData(
val internalClasses: MutableList<ClassDef>, internal val internalClasses: MutableList<ClassDef>,
val resourceCacheDirectory: String internal val resourceCacheDirectory: String,
val packageMetadata: PackageMetadata
) { ) {
internal val patches = mutableListOf<Class<out Patch<Data>>>() internal val patches = mutableListOf<Class<out Patch<Data>>>()
internal val bytecodeData = BytecodeData(internalClasses) internal val bytecodeData = BytecodeData(internalClasses)

View File

@@ -34,7 +34,6 @@ class DomFileEditor internal constructor(private val domFile: File) : Closeable
init { init {
val factory = DocumentBuilderFactory.newInstance() val factory = DocumentBuilderFactory.newInstance()
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
val builder = factory.newDocumentBuilder() val builder = factory.newDocumentBuilder()

View File

@@ -41,13 +41,15 @@ object PatchExtensions {
val Class<out Patch<Data>>.patchName: String val Class<out Patch<Data>>.patchName: String
get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
val Class<out Patch<Data>>.version get() = recursiveAnnotation(Version::class)?.version val Class<out Patch<Data>>.version get() = recursiveAnnotation(Version::class)?.version
val Class<out Patch<Data>>.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Dependencies::class)?.dependencies val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Dependencies::class)?.dependencies
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
} }
object MethodSignatureExtensions { object MethodSignatureExtensions {
val MethodSignature.name: String get() = javaClass.recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName val MethodSignature.name: String
get() = javaClass.recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
val MethodSignature.version get() = javaClass.recursiveAnnotation(Version::class)?.version ?: "0.0.1" val MethodSignature.version get() = javaClass.recursiveAnnotation(Version::class)?.version ?: "0.0.1"
val MethodSignature.description get() = javaClass.recursiveAnnotation(Description::class)?.description val MethodSignature.description get() = javaClass.recursiveAnnotation(Description::class)?.description
val MethodSignature.compatiblePackages get() = javaClass.recursiveAnnotation(Compatibility::class)?.compatiblePackages val MethodSignature.compatiblePackages get() = javaClass.recursiveAnnotation(Compatibility::class)?.compatiblePackages

View File

@@ -1,6 +1,9 @@
package app.revanced.patcher.extensions package app.revanced.patcher.extensions
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patcher.util.smali.toInstructions
import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.builder.BuilderInstruction import org.jf.dexlib2.builder.BuilderInstruction
import org.jf.dexlib2.builder.MutableMethodImplementation import org.jf.dexlib2.builder.MutableMethodImplementation
@@ -62,6 +65,22 @@ internal fun Method.clone(
) )
} }
/**
* Add smali instructions to the method.
* @param index The index to insert the instructions at.
* @param instruction The smali instruction to add.
*/
fun MutableMethod.addInstruction(index: Int, instruction: String) =
this.implementation!!.addInstruction(index, instruction.toInstruction(this))
/**
* Add smali instructions to the method.
* @param index The index to insert the instructions at.
* @param instructions The smali instructions to add.
*/
fun MutableMethod.addInstructions(index: Int, instructions: String) =
this.implementation!!.addInstructions(index, instructions.toInstructions(this))
/** /**
* Clones the method. * Clones the method.
* @param registerCount This parameter allows you to change the register count of the method. * @param registerCount This parameter allows you to change the register count of the method.

View File

@@ -6,11 +6,12 @@ import kotlin.reflect.KClass
/** /**
* Annotation to mark a Class as a patch. * Annotation to mark a Class as a patch.
* @param include If false, the patch should be treated as optional by default.
*/ */
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented @MustBeDocumented
annotation class Patch annotation class Patch(val include: Boolean = true)
/** /**
* Annotation for dependencies of [Patch]es . * Annotation for dependencies of [Patch]es .

View File

@@ -9,5 +9,5 @@ import app.revanced.patcher.signature.implementation.method.MethodSignature
* @param signatures A list of [MethodSignature] this patch relies on. * @param signatures A list of [MethodSignature] this patch relies on.
*/ */
abstract class BytecodePatch( abstract class BytecodePatch(
val signatures: Iterable<MethodSignature> internal val signatures: Iterable<MethodSignature>
) : Patch<BytecodeData>() ) : Patch<BytecodeData>()

View File

@@ -3,6 +3,7 @@ package app.revanced.patcher.signature.implementation.method.resolver
import app.revanced.patcher.data.PatcherData import app.revanced.patcher.data.PatcherData
import app.revanced.patcher.data.implementation.proxy import app.revanced.patcher.data.implementation.proxy
import app.revanced.patcher.extensions.MethodSignatureExtensions.fuzzyThreshold import app.revanced.patcher.extensions.MethodSignatureExtensions.fuzzyThreshold
import app.revanced.patcher.extensions.MethodSignatureExtensions.name
import app.revanced.patcher.extensions.parametersEqual import app.revanced.patcher.extensions.parametersEqual
import app.revanced.patcher.signature.implementation.method.MethodSignature import app.revanced.patcher.signature.implementation.method.MethodSignature
import org.jf.dexlib2.Opcode import org.jf.dexlib2.Opcode
@@ -11,37 +12,25 @@ import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.Instruction import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.formats.Instruction21c import org.jf.dexlib2.iface.instruction.formats.Instruction21c
import org.jf.dexlib2.iface.reference.StringReference import org.jf.dexlib2.iface.reference.StringReference
import java.util.logging.Logger
internal class MethodSignatureResolver( internal class MethodSignatureResolver(
private val classes: List<ClassDef>, private val classes: List<ClassDef>,
private val methodSignatures: Iterable<MethodSignature> private val methodSignatures: Iterable<MethodSignature>
) { ) {
fun resolve(patcherData: PatcherData) {
for (signature in methodSignatures) {
for (classDef in classes) {
for (method in classDef.methods) {
val patternScanData = compareSignatureToMethod(signature, method) ?: continue
// create class proxy, in case a patch needs mutability
val classProxy = patcherData.bytecodeData.proxy(classDef)
signature.result = SignatureResolverResult(
classProxy,
patternScanData,
method,
)
}
}
}
}
// These functions do not require the constructor values, so they can be static. // These functions do not require the constructor values, so they can be static.
companion object { companion object {
private val LOGGER: Logger = Logger.getLogger(::MethodSignatureResolver.name)
fun resolveFromProxy( fun resolveFromProxy(
classProxy: app.revanced.patcher.util.proxy.ClassProxy, classProxy: app.revanced.patcher.util.proxy.ClassProxy,
signature: MethodSignature signature: MethodSignature
): SignatureResolverResult? { ): SignatureResolverResult? {
for (method in classProxy.immutableClass.methods) { for (method in classProxy.immutableClass.methods) {
val result = compareSignatureToMethod(signature, method) ?: continue val result = compareSignatureToMethod(signature, method) ?: continue
LOGGER.fine("${signature.name} match to ${method.definingClass}->${method.name}")
return SignatureResolverResult( return SignatureResolverResult(
classProxy, classProxy,
result, result,
@@ -76,19 +65,17 @@ internal class MethodSignatureResolver(
signature.strings?.let { strings -> signature.strings?.let { strings ->
method.implementation ?: return null method.implementation ?: return null
method.implementation!!.instructions.let { instructions -> val stringsList = strings.toMutableList()
val stringsList = strings.toMutableList()
for (instruction in instructions) { for (instruction in method.implementation!!.instructions) {
if (instruction.opcode != Opcode.CONST_STRING) continue if (instruction.opcode != Opcode.CONST_STRING) continue
val string = ((instruction as Instruction21c).reference as StringReference).string val string = ((instruction as Instruction21c).reference as StringReference).string
val i = stringsList.indexOfFirst { it == string } val i = stringsList.indexOfFirst { it == string }
if (i != -1) stringsList.removeAt(i) if (i != -1) stringsList.removeAt(i)
}
if (stringsList.isNotEmpty()) return null
} }
if (stringsList.isNotEmpty()) return null
} }
return if (signature.opcodes == null) { return if (signature.opcodes == null) {
@@ -158,6 +145,28 @@ internal class MethodSignatureResolver(
} }
} }
} }
fun resolve(patcherData: PatcherData) {
for (signature in methodSignatures) {
val signatureName = signature.name
LOGGER.fine("Resolve $signatureName")
for (classDef in classes) {
for (method in classDef.methods) {
val patternScanData = compareSignatureToMethod(signature, method) ?: continue
LOGGER.fine("$signatureName match to ${method.definingClass}->${method.name}")
// create class proxy, in case a patch needs mutability
val classProxy = patcherData.bytecodeData.proxy(classDef)
signature.result = SignatureResolverResult(
classProxy,
patternScanData,
method,
)
}
}
}
}
} }
private operator fun ClassDef.component1() = this private operator fun ClassDef.component1() = this

View File

@@ -0,0 +1,10 @@
package app.revanced.patcher.util.dex
import java.io.InputStream
/**
* Wrapper for dex files.
* @param name The original name of the dex file.
* @param dexFileInputStream The dex file as [InputStream].
*/
data class DexFile(val name: String, val dexFileInputStream: InputStream)

View File

@@ -3,8 +3,10 @@ package app.revanced.patcher.util.smali
import org.antlr.runtime.CommonTokenStream import org.antlr.runtime.CommonTokenStream
import org.antlr.runtime.TokenSource import org.antlr.runtime.TokenSource
import org.antlr.runtime.tree.CommonTreeNodeStream import org.antlr.runtime.tree.CommonTreeNodeStream
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcodes import org.jf.dexlib2.Opcodes
import org.jf.dexlib2.builder.BuilderInstruction import org.jf.dexlib2.builder.BuilderInstruction
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.writer.builder.DexBuilder import org.jf.dexlib2.writer.builder.DexBuilder
import org.jf.smali.LexerErrorInterface import org.jf.smali.LexerErrorInterface
import org.jf.smali.smaliFlexLexer import org.jf.smali.smaliFlexLexer
@@ -13,12 +15,12 @@ import org.jf.smali.smaliTreeWalker
import java.io.InputStreamReader import java.io.InputStreamReader
private const val METHOD_TEMPLATE = """ private const val METHOD_TEMPLATE = """
.class LInlineCompiler; .class LInlineCompiler;
.super Ljava/lang/Object; .super Ljava/lang/Object;
.method %s dummyMethod(%s)V .method %s dummyMethod(%s)V
.registers %d .registers %d
%s %s
.end method .end method
""" """
class InlineSmaliCompiler { class InlineSmaliCompiler {
@@ -32,13 +34,11 @@ class InlineSmaliCompiler {
* be messed up and results in broken Dalvik bytecode. * be messed up and results in broken Dalvik bytecode.
* FIXME: Fix the above issue. When this is fixed, add the proper conversions in [InstructionConverter]. * FIXME: Fix the above issue. When this is fixed, add the proper conversions in [InstructionConverter].
*/ */
fun compileMethodInstructions( fun compile(
instructions: String, instructions: String, parameters: String, registers: Int, forStaticMethod: Boolean
parameters: String,
registers: Int,
forStaticMethod: Boolean
): List<BuilderInstruction> { ): List<BuilderInstruction> {
val input = METHOD_TEMPLATE.format(if (forStaticMethod) "static" else "", parameters, registers, instructions) val input =
METHOD_TEMPLATE.format(if (forStaticMethod) "static" else "", parameters, registers, instructions)
val reader = InputStreamReader(input.byteInputStream()) val reader = InputStreamReader(input.byteInputStream())
val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15) val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15)
val tokens = CommonTokenStream(lexer as TokenSource) val tokens = CommonTokenStream(lexer as TokenSource)
@@ -59,8 +59,20 @@ class InlineSmaliCompiler {
} }
} }
fun String.toInstructions(parameters: String = "", registers: Int = 1, forStaticMethod: Boolean = true) = /**
InlineSmaliCompiler.compileMethodInstructions(this, parameters, registers, forStaticMethod) * Compile lines of Smali code to a list of instructions.
* @param templateMethod The method to compile the instructions against.
* @returns A list of instructions.
*/
fun String.toInstructions(templateMethod: Method? = null) = InlineSmaliCompiler.compile(this,
templateMethod?.parameters?.joinToString("") { it } ?: "",
templateMethod?.implementation?.registerCount ?: 1,
templateMethod?.let { AccessFlags.STATIC.isSet(it.accessFlags) } ?: true
)
fun String.toInstruction(parameters: String = "", registers: Int = 1, forStaticMethod: Boolean = true) = /**
this.toInstructions(parameters, registers, forStaticMethod).first() * Compile a line of Smali code to an instruction.
* @param templateMethod The method to compile the instructions against.
* @return The instruction.
*/
fun String.toInstruction(templateMethod: Method? = null) = this.toInstructions(templateMethod).first()

View File

@@ -15,7 +15,6 @@ import app.revanced.patcher.usage.resource.annotation.ExampleResourceCompatibili
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.toInstruction import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patcher.util.smali.toInstructions
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Format import org.jf.dexlib2.Format
@@ -47,7 +46,7 @@ class ExampleBytecodePatch : BytecodePatch(
// You can treat it as a constructor // You can treat it as a constructor
override fun execute(data: BytecodeData): PatchResult { override fun execute(data: BytecodeData): PatchResult {
// Get the resolved method for the signature from the resolver cache // Get the resolved method for the signature from the resolver cache
val result = signatures.first().result!! val result = ExampleSignature.result!!
// Get the implementation for the resolved method // Get the implementation for the resolved method
val implementation = result.method.implementation!! val implementation = result.method.implementation!!
@@ -126,8 +125,8 @@ class ExampleBytecodePatch : BytecodePatch(
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String; invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
move-result-object v1 move-result-object v1
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
""".trimIndent().toInstructions() """
implementation.addInstructions(startIndex + 2, instructions) result.method.addInstructions(startIndex + 2, instructions)
// Finally, tell the patcher that this patch was a success. // Finally, tell the patcher that this patch was a success.
// You can also return PatchResultError with a message. // You can also return PatchResultError with a message.