You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-10 05:30:49 +02:00
Compare commits
57 Commits
v7.0.0-dev
...
v11.0.2-de
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b615ed6aab | ||
![]() |
d718134ab2 | ||
![]() |
5e681ed381 | ||
![]() |
6e1b6479b6 | ||
![]() |
f3c9e28a62 | ||
![]() |
d5d6f85084 | ||
![]() |
b8151ebccb | ||
![]() |
5650e34432 | ||
![]() |
c893d16d52 | ||
![]() |
34f08bf206 | ||
![]() |
f02a42610b | ||
![]() |
c95e6fa92f | ||
![]() |
fd738e723b | ||
![]() |
b1d1956323 | ||
![]() |
725a8012ac | ||
![]() |
bb9a73e53b | ||
![]() |
ef2de35a74 | ||
![]() |
2a453d51a8 | ||
![]() |
43d6868d1f | ||
![]() |
cea9379b32 | ||
![]() |
a12fe7dd9e | ||
![]() |
efdd01a988 | ||
![]() |
eafe1c631f | ||
![]() |
aacf900764 | ||
![]() |
f82494e9bb | ||
![]() |
1e0ffa176e | ||
![]() |
b7eb2d2249 | ||
![]() |
b6d6a7591b | ||
![]() |
8f1c835299 | ||
![]() |
a188c16a99 | ||
![]() |
3e6804f06c | ||
![]() |
526a3d7c35 | ||
![]() |
28fc6a2ddd | ||
![]() |
d4f08d7bff | ||
![]() |
ca9fe322eb | ||
![]() |
239ea0bcaa | ||
![]() |
7f02b8df48 | ||
![]() |
a2052202b2 | ||
![]() |
223cea7021 | ||
![]() |
ac9337f694 | ||
![]() |
549651d04a | ||
![]() |
966bbd902e | ||
![]() |
81e6f8784e | ||
![]() |
9c53877888 | ||
![]() |
98f8eedecd | ||
![]() |
4ed429d25c | ||
![]() |
119d05f469 | ||
![]() |
2432fde6bf | ||
![]() |
49c173dc14 | ||
![]() |
d83e9372bb | ||
![]() |
7e8cd3bede | ||
![]() |
d67436271d | ||
![]() |
aa07f35f06 | ||
![]() |
77e0536838 | ||
![]() |
a49e78234b | ||
![]() |
a3ae825e48 | ||
![]() |
146c8504ed |
2
.github/config.yml
vendored
Normal file
2
.github/config.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
firstPRMergeComment: >
|
||||||
|
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.
|
23
.github/workflows/release.yml
vendored
23
.github/workflows/release.yml
vendored
@@ -23,17 +23,20 @@ jobs:
|
|||||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup JDK
|
- name: Cache
|
||||||
uses: actions/setup-java@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
path: |
|
||||||
distribution: 'zulu'
|
${{ runner.home }}/.gradle/caches
|
||||||
cache: gradle
|
${{ runner.home }}/.gradle/wrapper
|
||||||
- name: Setup Node.js
|
.gradle
|
||||||
uses: actions/setup-node@v3
|
build
|
||||||
with:
|
node_modules
|
||||||
node-version: "18"
|
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
|
||||||
cache: 'npm'
|
- name: Build with Gradle
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: ./gradlew clean --no-daemon
|
||||||
- name: Setup semantic-release
|
- name: Setup semantic-release
|
||||||
run: npm install
|
run: npm install
|
||||||
- name: Release
|
- name: Release
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -74,6 +74,7 @@ cmake-build-*/
|
|||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
out/
|
out/
|
||||||
|
.idea/
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin
|
# mpeltonen/sbt-idea plugin
|
||||||
.idea_modules/
|
.idea_modules/
|
||||||
|
9
.idea/.gitignore
generated
vendored
9
.idea/.gitignore
generated
vendored
@@ -1,9 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
/kotlinc.xml
|
|
10
.idea/codeStyles/Project.xml
generated
10
.idea/codeStyles/Project.xml
generated
@@ -1,10 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<code_scheme name="Project" version="173">
|
|
||||||
<JetCodeStyleSettings>
|
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
|
||||||
</JetCodeStyleSettings>
|
|
||||||
<codeStyleSettings language="kotlin">
|
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
|
||||||
</codeStyleSettings>
|
|
||||||
</code_scheme>
|
|
||||||
</component>
|
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
7
.idea/discord.xml
generated
7
.idea/discord.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="DiscordProjectSettings">
|
|
||||||
<option name="show" value="PROJECT_FILES" />
|
|
||||||
<option name="description" value="" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
15
.idea/git_toolbox_prj.xml
generated
15
.idea/git_toolbox_prj.xml
generated
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="GitToolBoxProjectSettings">
|
|
||||||
<option name="commitMessageIssueKeyValidationOverride">
|
|
||||||
<BoolValueOverride>
|
|
||||||
<option name="enabled" value="true" />
|
|
||||||
</BoolValueOverride>
|
|
||||||
</option>
|
|
||||||
<option name="commitMessageValidationEnabledOverride">
|
|
||||||
<BoolValueOverride>
|
|
||||||
<option name="enabled" value="true" />
|
|
||||||
</BoolValueOverride>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
6
.idea/inspectionProfiles/Project_Default.xml
generated
6
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="UnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
10
.idea/misc.xml
generated
10
.idea/misc.xml
generated
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="azul-17" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
12
.idea/vcs.xml
generated
12
.idea/vcs.xml
generated
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CommitMessageInspectionProfile">
|
|
||||||
<profile version="1.0">
|
|
||||||
<inspection_tool class="CommitFormat" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="CommitNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
184
CHANGELOG.md
184
CHANGELOG.md
@@ -1,3 +1,187 @@
|
|||||||
|
## [11.0.2-dev.3](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.2...v11.0.2-dev.3) (2023-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* resolve fingerprints using method maps ([#185](https://github.com/revanced/revanced-patcher/issues/185)) ([d718134](https://github.com/revanced/revanced-patcher/commit/d718134ab26423e02708e01eba711737f9260ba0))
|
||||||
|
|
||||||
|
## [11.0.2-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.1...v11.0.2-dev.2) (2023-06-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* use `versionCode` if `versionName` is unavailable ([6e1b647](https://github.com/revanced/revanced-patcher/commit/6e1b6479b677657c226693e9cc6b63f4ef2ee060))
|
||||||
|
|
||||||
|
## [11.0.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.1...v11.0.2-dev.1) (2023-06-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* catch exceptions from closing patches ([d5d6f85](https://github.com/revanced/revanced-patcher/commit/d5d6f85084c03ed9c776632823ca12394a716167))
|
||||||
|
* only close succeeded patches ([b8151eb](https://github.com/revanced/revanced-patcher/commit/b8151ebccb5b27dd9e06fa63235cf9baeef1c0ee))
|
||||||
|
|
||||||
|
## [11.0.1](https://github.com/revanced/revanced-patcher/compare/v11.0.0...v11.0.1) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* revert using `OutputStream.nullOutputStream` ([f02a426](https://github.com/revanced/revanced-patcher/commit/f02a42610b7698fc8cc6bc5183c9ccba2ed96cda))
|
||||||
|
|
||||||
|
## [11.0.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.0...v11.0.1-dev.1) (2023-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* revert using `OutputStream.nullOutputStream` ([f02a426](https://github.com/revanced/revanced-patcher/commit/f02a42610b7698fc8cc6bc5183c9ccba2ed96cda))
|
||||||
|
|
||||||
|
# [11.0.0](https://github.com/revanced/revanced-patcher/compare/v10.0.0...v11.0.0) (2023-06-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add imports to fix failing tests ([43d6868](https://github.com/revanced/revanced-patcher/commit/43d6868d1f59922f9812f3e4a2a890f3b331def6))
|
||||||
|
|
||||||
|
|
||||||
|
* refactor!: move extension functions to their corresponding classes ([a12fe7d](https://github.com/revanced/revanced-patcher/commit/a12fe7dd9e976c38a0a82fe35e6650f58f815de4))
|
||||||
|
* refactor!: use proper extension function names ([efdd01a](https://github.com/revanced/revanced-patcher/commit/efdd01a9886b6f06af213731824621964367b2a3))
|
||||||
|
* fix!: implement extension functions consistently ([aacf900](https://github.com/revanced/revanced-patcher/commit/aacf9007647b1cc11bc40053625802573efda6ef))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes the import paths for extension functions.
|
||||||
|
* This changes the names of extension functions
|
||||||
|
* This changes the name of functions
|
||||||
|
|
||||||
|
# [11.0.0-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.0-dev.1...v11.0.0-dev.2) (2023-06-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add imports to fix failing tests ([43d6868](https://github.com/revanced/revanced-patcher/commit/43d6868d1f59922f9812f3e4a2a890f3b331def6))
|
||||||
|
|
||||||
|
# [11.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v10.0.0...v11.0.0-dev.1) (2023-06-07)
|
||||||
|
|
||||||
|
|
||||||
|
* refactor!: move extension functions to their corresponding classes ([a12fe7d](https://github.com/revanced/revanced-patcher/commit/a12fe7dd9e976c38a0a82fe35e6650f58f815de4))
|
||||||
|
* refactor!: use proper extension function names ([efdd01a](https://github.com/revanced/revanced-patcher/commit/efdd01a9886b6f06af213731824621964367b2a3))
|
||||||
|
* fix!: implement extension functions consistently ([aacf900](https://github.com/revanced/revanced-patcher/commit/aacf9007647b1cc11bc40053625802573efda6ef))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes the import paths for extension functions.
|
||||||
|
* This changes the names of extension functions
|
||||||
|
* This changes the name of functions
|
||||||
|
|
||||||
|
# [10.0.0](https://github.com/revanced/revanced-patcher/compare/v9.0.0...v10.0.0) (2023-06-07)
|
||||||
|
|
||||||
|
|
||||||
|
* fix!: check for two methods parameters orders (#183) ([b6d6a75](https://github.com/revanced/revanced-patcher/commit/b6d6a7591ba1c0b48ffdef52352709564da8d9be)), closes [#183](https://github.com/revanced/revanced-patcher/issues/183)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This requires changes to `MethodFingerprint`
|
||||||
|
|
||||||
|
# [10.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v9.0.0...v10.0.0-dev.1) (2023-06-07)
|
||||||
|
|
||||||
|
|
||||||
|
* fix!: check for two methods parameters orders (#183) ([b6d6a75](https://github.com/revanced/revanced-patcher/commit/b6d6a7591ba1c0b48ffdef52352709564da8d9be)), closes [#183](https://github.com/revanced/revanced-patcher/issues/183)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This requires changes to `MethodFingerprint`
|
||||||
|
|
||||||
|
# [9.0.0](https://github.com/revanced/revanced-patcher/compare/v8.0.0...v9.0.0) (2023-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
* refactor!: rename parameter ([526a3d7](https://github.com/revanced/revanced-patcher/commit/526a3d7c359e2d95d26756da0f88d5ce975f5d9b))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes named parameters.
|
||||||
|
|
||||||
|
# [9.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v8.0.0...v9.0.0-dev.1) (2023-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
* refactor!: rename parameter ([526a3d7](https://github.com/revanced/revanced-patcher/commit/526a3d7c359e2d95d26756da0f88d5ce975f5d9b))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes named parameters.
|
||||||
|
|
||||||
|
# [8.0.0](https://github.com/revanced/revanced-patcher/compare/v7.1.1...v8.0.0) (2023-05-13)
|
||||||
|
|
||||||
|
|
||||||
|
* feat!: add `classDef` parameter to `MethodFingerprint` (#175) ([a205220](https://github.com/revanced/revanced-patcher/commit/a2052202b23037150df6aadc47f6e91efcd481cf)), closes [#175](https://github.com/revanced/revanced-patcher/issues/175)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes the signature of the `customFingerprint` function.
|
||||||
|
|
||||||
|
# [8.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.1.1...v8.0.0-dev.1) (2023-05-10)
|
||||||
|
|
||||||
|
|
||||||
|
* feat!: add `classDef` parameter to `MethodFingerprint` (#175) ([a205220](https://github.com/revanced/revanced-patcher/commit/a2052202b23037150df6aadc47f6e91efcd481cf)), closes [#175](https://github.com/revanced/revanced-patcher/issues/175)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes the signature of the `customFingerprint` function.
|
||||||
|
|
||||||
|
## [7.1.1](https://github.com/revanced/revanced-patcher/compare/v7.1.0...v7.1.1) (2023-05-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* remove `count` instead of `count + 1` instructions with `removeInstructions` ([#167](https://github.com/revanced/revanced-patcher/issues/167)) ([98f8eed](https://github.com/revanced/revanced-patcher/commit/98f8eedecd72b0afe6a0f099a3641a1cc6be2698))
|
||||||
|
|
||||||
|
## [7.1.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.1.0...v7.1.1-dev.1) (2023-05-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* remove `count` instead of `count + 1` instructions with `removeInstructions` ([#167](https://github.com/revanced/revanced-patcher/issues/167)) ([98f8eed](https://github.com/revanced/revanced-patcher/commit/98f8eedecd72b0afe6a0f099a3641a1cc6be2698))
|
||||||
|
|
||||||
|
# [7.1.0](https://github.com/revanced/revanced-patcher/compare/v7.0.0...v7.1.0) (2023-05-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add appreciation message for new contributors ([d674362](https://github.com/revanced/revanced-patcher/commit/d67436271ddca9ccfe008272c1ca82c6123ae7ee))
|
||||||
|
* add overload to get instruction as type ([49c173d](https://github.com/revanced/revanced-patcher/commit/49c173dc14137ddd198a611e9882dc71300831ee))
|
||||||
|
|
||||||
|
# [7.1.0-dev.2](https://github.com/revanced/revanced-patcher/compare/v7.1.0-dev.1...v7.1.0-dev.2) (2023-05-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add overload to get instruction as type ([49c173d](https://github.com/revanced/revanced-patcher/commit/49c173dc14137ddd198a611e9882dc71300831ee))
|
||||||
|
|
||||||
|
# [7.1.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.0.0...v7.1.0-dev.1) (2023-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add appreciation message for new contributors ([d674362](https://github.com/revanced/revanced-patcher/commit/d67436271ddca9ccfe008272c1ca82c6123ae7ee))
|
||||||
|
|
||||||
|
# [7.0.0](https://github.com/revanced/revanced-patcher/compare/v6.4.3...v7.0.0) (2023-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
* feat!: merge integrations only when necessary ([6e24a85](https://github.com/revanced/revanced-patcher/commit/6e24a85eabd1e7a1484fad229d5ba55c3ba1f1b4))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* `Patcher.addFiles` is now renamed to `Patcher.addIntegrations`
|
||||||
|
|
||||||
|
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
|
||||||
|
|
||||||
# [7.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.3...v7.0.0-dev.1) (2023-02-26)
|
# [7.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.3...v7.0.0-dev.1) (2023-02-26)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
# Patcher
|
# 💉 ReVanced Patcher
|
||||||
Patcher framework used in the ReVanced project.
|
|
||||||
|
ReVanced Patcher used to patch Android applications.
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.7.0"
|
kotlin("jvm") version "1.8.10"
|
||||||
java
|
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,8 +25,8 @@ dependencies {
|
|||||||
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
|
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
|
||||||
implementation("app.revanced:apktool-lib:2.7.0")
|
implementation("app.revanced:apktool-lib:2.7.0")
|
||||||
|
|
||||||
implementation(kotlin("reflect"))
|
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20-RC")
|
||||||
testImplementation(kotlin("test"))
|
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
@@ -46,6 +45,10 @@ java {
|
|||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(11)
|
||||||
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
if (System.getenv("GITHUB_ACTOR") != null)
|
if (System.getenv("GITHUB_ACTOR") != null)
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 7.0.0-dev.1
|
version = 11.0.2-dev.3
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@@ -4,8 +4,8 @@ import app.revanced.patcher.data.Context
|
|||||||
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.requiresIntegrations
|
import app.revanced.patcher.extensions.PatchExtensions.requiresIntegrations
|
||||||
import app.revanced.patcher.extensions.nullOutputStream
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolveUsingLookupMap
|
||||||
import app.revanced.patcher.patch.*
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.util.VersionReader
|
import app.revanced.patcher.util.VersionReader
|
||||||
import brut.androlib.Androlib
|
import brut.androlib.Androlib
|
||||||
@@ -24,7 +24,9 @@ import lanchon.multidexlib2.MultiDexIO
|
|||||||
import org.jf.dexlib2.Opcodes
|
import org.jf.dexlib2.Opcodes
|
||||||
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.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
internal val NAMER = BasicDexFileNamer()
|
internal val NAMER = BasicDexFileNamer()
|
||||||
@@ -247,7 +249,13 @@ class Patcher(private val options: PatcherOptions) {
|
|||||||
XmlPullStreamDecoder(
|
XmlPullStreamDecoder(
|
||||||
axmlParser, AndrolibResources().resXmlSerializer
|
axmlParser, AndrolibResources().resXmlSerializer
|
||||||
).decodeManifest(
|
).decodeManifest(
|
||||||
extInputFile.directory.getFileInput("AndroidManifest.xml"), nullOutputStream
|
extInputFile.directory.getFileInput("AndroidManifest.xml"),
|
||||||
|
// Older Android versions do not support OutputStream.nullOutputStream()
|
||||||
|
object : OutputStream() {
|
||||||
|
override fun write(b: Int) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,7 +263,7 @@ class Patcher(private val options: PatcherOptions) {
|
|||||||
// read of the resourceTable which is created by reading the manifest file
|
// read of the resourceTable which is created by reading the manifest file
|
||||||
context.packageMetadata.let { metadata ->
|
context.packageMetadata.let { metadata ->
|
||||||
metadata.packageName = resourceTable.currentResPackage.name
|
metadata.packageName = resourceTable.currentResPackage.name
|
||||||
metadata.packageVersion = resourceTable.versionInfo.versionName
|
metadata.packageVersion = resourceTable.versionInfo.versionName ?: resourceTable.versionInfo.versionCode
|
||||||
metadata.metaInfo.versionInfo = resourceTable.versionInfo
|
metadata.metaInfo.versionInfo = resourceTable.versionInfo
|
||||||
metadata.metaInfo.sdkInfo = resourceTable.sdkInfo
|
metadata.metaInfo.sdkInfo = resourceTable.sdkInfo
|
||||||
}
|
}
|
||||||
@@ -315,10 +323,7 @@ class Patcher(private val options: PatcherOptions) {
|
|||||||
context.resourceContext
|
context.resourceContext
|
||||||
} else {
|
} else {
|
||||||
context.bytecodeContext.also { context ->
|
context.bytecodeContext.also { context ->
|
||||||
(patchInstance as BytecodePatch).fingerprints?.resolve(
|
(patchInstance as BytecodePatch).fingerprints?.resolveUsingLookupMap(context)
|
||||||
context,
|
|
||||||
context.classes.classes
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,31 +343,52 @@ class Patcher(private val options: PatcherOptions) {
|
|||||||
return sequence {
|
return sequence {
|
||||||
if (mergeIntegrations) context.integrations.merge(logger)
|
if (mergeIntegrations) context.integrations.merge(logger)
|
||||||
|
|
||||||
|
logger.trace("Initialize lookup maps for method MethodFingerprint resolution")
|
||||||
|
|
||||||
|
MethodFingerprint.initializeFingerprintResolutionLookupMaps(context.bytecodeContext)
|
||||||
|
|
||||||
// prevent from decoding the manifest twice if it is not needed
|
// prevent from decoding the manifest twice if it is not needed
|
||||||
if (resourceDecodingMode == ResourceDecodingMode.FULL) decodeResources(ResourceDecodingMode.FULL)
|
if (resourceDecodingMode == ResourceDecodingMode.FULL) decodeResources(ResourceDecodingMode.FULL)
|
||||||
|
|
||||||
logger.trace("Executing all patches")
|
logger.info("Executing patches")
|
||||||
|
|
||||||
val executedPatches = LinkedHashMap<String, ExecutedPatch>() // first is name
|
val executedPatches = LinkedHashMap<String, ExecutedPatch>() // first is name
|
||||||
|
|
||||||
try {
|
context.patches.forEach { patch ->
|
||||||
context.patches.forEach { patch ->
|
val patchResult = executePatch(patch, executedPatches)
|
||||||
val patchResult = executePatch(patch, executedPatches)
|
|
||||||
|
|
||||||
val result = if (patchResult.isSuccess()) {
|
val result = if (patchResult.isSuccess()) {
|
||||||
Result.success(patchResult.success()!!)
|
Result.success(patchResult.success()!!)
|
||||||
} else {
|
} else {
|
||||||
Result.failure(patchResult.error()!!)
|
Result.failure(patchResult.error()!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
yield(patch.patchName to result)
|
// TODO: This prints before the patch really finishes in case it is a Closeable
|
||||||
if (stopOnError && patchResult.isError()) return@sequence
|
// because the Closeable is closed after all patches are executed.
|
||||||
}
|
yield(patch.patchName to result)
|
||||||
} finally {
|
|
||||||
executedPatches.values.reversed().forEach { (patch, _) ->
|
if (stopOnError && patchResult.isError()) return@sequence
|
||||||
patch.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executedPatches.values
|
||||||
|
.filter(ExecutedPatch::success)
|
||||||
|
.map(ExecutedPatch::patchInstance)
|
||||||
|
.filterIsInstance(Closeable::class.java)
|
||||||
|
.asReversed().forEach {
|
||||||
|
try {
|
||||||
|
it.close()
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
val patchName = (it as Patch<Context>).javaClass.patchName
|
||||||
|
|
||||||
|
logger.error("Failed to close '$patchName': ${exception.stackTraceToString()}")
|
||||||
|
|
||||||
|
yield(patchName to Result.failure(exception))
|
||||||
|
|
||||||
|
// This is not failsafe. If a patch throws an exception while closing,
|
||||||
|
// the other patches that depend on it may fail.
|
||||||
|
if (stopOnError) return@sequence
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -54,17 +54,6 @@ class BytecodeContext internal constructor(classes: MutableList<ClassDef>) : Con
|
|||||||
}
|
}
|
||||||
return proxy
|
return proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
|
||||||
inline fun <reified T> Iterable<T>.find(predicate: (T) -> Boolean): T? {
|
|
||||||
for (element in this) {
|
|
||||||
if (predicate(element)) {
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,83 +1,33 @@
|
|||||||
package app.revanced.patcher.extensions
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.*
|
|
||||||
import app.revanced.patcher.data.Context
|
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import app.revanced.patcher.patch.OptionsContainer
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchOptions
|
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
|
||||||
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KVisibility
|
|
||||||
import kotlin.reflect.full.companionObject
|
|
||||||
import kotlin.reflect.full.companionObjectInstance
|
|
||||||
|
|
||||||
/**
|
internal object AnnotationExtensions {
|
||||||
* Recursively find a given annotation on a class.
|
/**
|
||||||
* @param targetAnnotation The annotation to find.
|
* Recursively find a given annotation on a class.
|
||||||
* @return The annotation.
|
*
|
||||||
*/
|
* @param targetAnnotation The annotation to find.
|
||||||
private fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>) =
|
* @return The annotation.
|
||||||
this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf())
|
*/
|
||||||
|
fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>): T? {
|
||||||
|
fun <T : Annotation> Class<*>.findAnnotationRecursively(
|
||||||
|
targetAnnotation: Class<T>, traversed: MutableSet<Annotation>
|
||||||
|
): T? {
|
||||||
|
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST") if (found != null) return found as T
|
||||||
|
|
||||||
private fun <T : Annotation> Class<*>.findAnnotationRecursively(
|
for (annotation in this.annotations) {
|
||||||
targetAnnotation: Class<T>, traversed: MutableSet<Annotation>
|
if (traversed.contains(annotation)) continue
|
||||||
): T? {
|
traversed.add(annotation)
|
||||||
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST") if (found != null) return found as T
|
return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed))
|
||||||
|
?: continue
|
||||||
for (annotation in this.annotations) {
|
|
||||||
if (traversed.contains(annotation)) continue
|
|
||||||
traversed.add(annotation)
|
|
||||||
|
|
||||||
return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed)) ?: continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
object PatchExtensions {
|
|
||||||
val Class<out Patch<Context>>.patchName: String
|
|
||||||
get() = findAnnotationRecursively(Name::class)?.name ?: this.simpleName
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.version
|
|
||||||
get() = findAnnotationRecursively(Version::class)?.version
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.include
|
|
||||||
get() = findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.description
|
|
||||||
get() = findAnnotationRecursively(Description::class)?.description
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.dependencies
|
|
||||||
get() = findAnnotationRecursively(DependsOn::class)?.dependencies
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.compatiblePackages
|
|
||||||
get() = findAnnotationRecursively(Compatibility::class)?.compatiblePackages
|
|
||||||
|
|
||||||
internal val Class<out Patch<Context>>.requiresIntegrations
|
|
||||||
get() = findAnnotationRecursively(RequiresIntegrations::class) != null
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.options: PatchOptions?
|
|
||||||
get() = kotlin.companionObject?.let { cl ->
|
|
||||||
if (cl.visibility != KVisibility.PUBLIC) return null
|
|
||||||
kotlin.companionObjectInstance?.let {
|
|
||||||
(it as? OptionsContainer)?.options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
object MethodFingerprintExtensions {
|
return this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf())
|
||||||
val MethodFingerprint.name: String
|
}
|
||||||
get() = this.javaClass.simpleName
|
|
||||||
|
|
||||||
val MethodFingerprint.fuzzyPatternScanMethod
|
|
||||||
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)
|
|
||||||
|
|
||||||
val MethodFingerprint.fuzzyScanThreshold
|
|
||||||
get() = fuzzyPatternScanMethod?.threshold ?: 0
|
|
||||||
}
|
}
|
@@ -1,230 +1,33 @@
|
|||||||
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
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
|
||||||
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.BuilderOffsetInstruction
|
|
||||||
import org.jf.dexlib2.builder.Label
|
|
||||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
|
||||||
import org.jf.dexlib2.builder.instruction.*
|
|
||||||
import org.jf.dexlib2.iface.Method
|
|
||||||
import org.jf.dexlib2.iface.instruction.Instruction
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
infix fun AccessFlags.or(other: AccessFlags) = this.value or other.value
|
|
||||||
infix fun Int.or(other: AccessFlags) = this or other.value
|
|
||||||
|
|
||||||
fun MutableMethodImplementation.addInstructions(index: Int, instructions: List<BuilderInstruction>) {
|
|
||||||
for (i in instructions.lastIndex downTo 0) {
|
|
||||||
this.addInstruction(index, instructions[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) {
|
|
||||||
for (instruction in instructions) {
|
|
||||||
this.addInstruction(instruction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) {
|
|
||||||
for (i in instructions.lastIndex downTo 0) {
|
|
||||||
this.replaceInstruction(index + i, instructions[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) {
|
|
||||||
for (i in count downTo 0) {
|
|
||||||
this.removeInstruction(index + i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clones the method.
|
* Create a label for the instruction at given index.
|
||||||
* @param registerCount This parameter allows you to change the register count of the method.
|
*
|
||||||
* This may be a positive or negative number.
|
|
||||||
* @return The **immutable** cloned method. Call [toMutable] or [cloneMutable] to get a **mutable** copy.
|
|
||||||
*/
|
|
||||||
internal fun Method.clone(registerCount: Int = 0): ImmutableMethod {
|
|
||||||
val clonedImplementation = implementation?.let {
|
|
||||||
ImmutableMethodImplementation(
|
|
||||||
it.registerCount + registerCount,
|
|
||||||
it.instructions,
|
|
||||||
it.tryBlocks,
|
|
||||||
it.debugItems,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return ImmutableMethod(
|
|
||||||
returnType, name, parameters, returnType, accessFlags, annotations, hiddenApiRestrictions, clonedImplementation
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a smali instruction to the method.
|
|
||||||
* @param instruction The smali instruction to add.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.addInstruction(instruction: String) =
|
|
||||||
this.implementation!!.addInstruction(instruction.toInstruction(this))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a smali instruction to the method.
|
|
||||||
* @param index The index to insert the instruction at.
|
|
||||||
* @param instruction The smali instruction to add.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.addInstruction(index: Int, instruction: String) =
|
|
||||||
this.implementation!!.addInstruction(index, instruction.toInstruction(this))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace a smali instruction within the method.
|
|
||||||
* @param index The index to replace the instruction at.
|
|
||||||
* @param instruction The smali instruction to place.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.replaceInstruction(index: Int, instruction: String) =
|
|
||||||
this.implementation!!.replaceInstruction(index, instruction.toInstruction(this))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a smali instruction within the method.
|
|
||||||
* @param index The index to delete the instruction at.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.removeInstruction(index: Int) = this.implementation!!.removeInstruction(index)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a label for the instruction at given index in the method's implementation.
|
|
||||||
* @param index The index to create the label for the instruction at.
|
* @param index The index to create the label for the instruction at.
|
||||||
* @return The label.
|
* @return The label.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.label(index: Int) = this.implementation!!.newLabelForIndex(index)
|
fun MutableMethod.newLabel(index: Int) = implementation!!.newLabelForIndex(index)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the instruction at the given index in the method's implementation.
|
* Perform a bitwise OR operation between two [AccessFlags].
|
||||||
* @param index The index to get the instruction at.
|
*
|
||||||
* @return The instruction.
|
* @param other The other [AccessFlags] to perform the operation with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.instruction(index: Int): BuilderInstruction = this.implementation!!.instructions[index]
|
infix fun AccessFlags.or(other: AccessFlags) = value or other.value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add smali instructions to the method.
|
* Perform a bitwise OR operation between an [AccessFlags] and an [Int].
|
||||||
* @param index The index to insert the instructions at.
|
*
|
||||||
* @param smali The smali instructions to add.
|
* @param other The [Int] to perform the operation with.
|
||||||
* @param externalLabels A list of [ExternalLabel] representing a list of labels for instructions which are not in the method to compile.
|
|
||||||
*/
|
*/
|
||||||
|
infix fun Int.or(other: AccessFlags) = this or other.value
|
||||||
fun MutableMethod.addInstructions(index: Int, smali: String, externalLabels: List<ExternalLabel> = emptyList()) {
|
|
||||||
// Create reference dummy instructions for the instructions.
|
|
||||||
val nopedSmali = StringBuilder(smali).also { builder ->
|
|
||||||
externalLabels.forEach { (name, _) ->
|
|
||||||
builder.append("\n:$name\nnop")
|
|
||||||
}
|
|
||||||
}.toString()
|
|
||||||
|
|
||||||
// Compile the instructions with the dummy labels
|
|
||||||
val compiledInstructions = nopedSmali.toInstructions(this)
|
|
||||||
|
|
||||||
// Add the compiled list of instructions to the method.
|
|
||||||
val methodImplementation = this.implementation!!
|
|
||||||
methodImplementation.addInstructions(index, compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size))
|
|
||||||
|
|
||||||
val methodInstructions = methodImplementation.instructions
|
|
||||||
methodInstructions.subList(index, index + compiledInstructions.size - externalLabels.size)
|
|
||||||
.forEachIndexed { compiledInstructionIndex, compiledInstruction ->
|
|
||||||
// If the compiled instruction is not an offset instruction, skip it.
|
|
||||||
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new label for the instruction and replaces it with the label of the [compiledInstruction] at [compiledInstructionIndex].
|
|
||||||
*/
|
|
||||||
fun Instruction.makeNewLabel() {
|
|
||||||
// Create the final label.
|
|
||||||
val label = methodImplementation.newLabelForIndex(methodInstructions.indexOf(this))
|
|
||||||
// Create the final instruction with the new label.
|
|
||||||
val newInstruction = replaceOffset(
|
|
||||||
compiledInstruction, label
|
|
||||||
)
|
|
||||||
// Replace the instruction pointing to the dummy label with the new instruction pointing to the real instruction.
|
|
||||||
methodImplementation.replaceInstruction(index + compiledInstructionIndex, newInstruction)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the compiled instruction targets its own instruction,
|
|
||||||
// which means it points to some of its own, simply an offset has to be applied.
|
|
||||||
val labelIndex = compiledInstruction.target.location.index
|
|
||||||
if (labelIndex < compiledInstructions.size - externalLabels.size) {
|
|
||||||
// Get the targets index (insertion index + the index of the dummy instruction).
|
|
||||||
methodInstructions[index + labelIndex].makeNewLabel()
|
|
||||||
return@forEachIndexed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the compiled instruction points to a dummy instruction,
|
|
||||||
// we can find the real instruction which it was created for by calculation.
|
|
||||||
|
|
||||||
// Get the index of the instruction in the externalLabels list which the dummy instruction was created for.
|
|
||||||
// this line works because we created the dummy instructions in the same order as the externalLabels list.
|
|
||||||
val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex]
|
|
||||||
instruction.makeNewLabel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add smali instructions to the end of the method.
|
* Perform a bitwise OR operation between an [Int] and an [AccessFlags].
|
||||||
* @param instructions The smali instructions to add.
|
*
|
||||||
|
* @param other The [AccessFlags] to perform the operation with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(instructions: String, labels: List<ExternalLabel> = emptyList()) =
|
infix fun AccessFlags.or(other: Int) = value or other
|
||||||
this.addInstructions(this.implementation!!.instructions.size, instructions, labels)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace smali instructions within the method.
|
|
||||||
* @param index The index to replace the instructions at.
|
|
||||||
* @param instructions The smali instructions to place.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.replaceInstructions(index: Int, instructions: String) =
|
|
||||||
this.implementation!!.replaceInstructions(index, instructions.toInstructions(this))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove smali instructions from the method.
|
|
||||||
* @param index The index to remove the instructions at.
|
|
||||||
* @param count The amount of instructions to remove.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.removeInstructions(index: Int, count: Int) = this.implementation!!.removeInstructions(index, count)
|
|
||||||
|
|
||||||
private fun replaceOffset(
|
|
||||||
i: BuilderOffsetInstruction, label: Label
|
|
||||||
): BuilderOffsetInstruction {
|
|
||||||
return when (i) {
|
|
||||||
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
|
||||||
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
|
||||||
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
|
||||||
is BuilderInstruction22t -> BuilderInstruction22t(i.opcode, i.registerA, i.registerB, label)
|
|
||||||
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
|
||||||
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
|
||||||
else -> throw IllegalStateException("A non-offset instruction was given, this should never happen!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones the method.
|
|
||||||
* @param registerCount This parameter allows you to change the register count of the method.
|
|
||||||
* This may be a positive or negative number.
|
|
||||||
* @return The **mutable** cloned method. Call [clone] to get an **immutable** copy.
|
|
||||||
*/
|
|
||||||
internal fun Method.cloneMutable(registerCount: Int = 0) = clone(registerCount).toMutable()
|
|
||||||
|
|
||||||
// FIXME: also check the order of parameters as different order equals different method overload
|
|
||||||
internal fun parametersEqual(
|
|
||||||
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
|
|
||||||
): Boolean {
|
|
||||||
return parameters1.count() == parameters2.count() && parameters1.all { parameter ->
|
|
||||||
parameters2.any {
|
|
||||||
it.startsWith(
|
|
||||||
parameter
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val nullOutputStream = object : OutputStream() {
|
|
||||||
override fun write(b: Int) {}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
object MethodFingerprintExtensions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
val MethodFingerprint.name: String
|
||||||
|
get() = this.javaClass.simpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [FuzzyPatternScanMethod] annotation of a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
val MethodFingerprint.fuzzyPatternScanMethod
|
||||||
|
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.Context
|
||||||
|
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
||||||
|
import app.revanced.patcher.patch.OptionsContainer
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.PatchOptions
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
||||||
|
import kotlin.reflect.KVisibility
|
||||||
|
import kotlin.reflect.full.companionObject
|
||||||
|
import kotlin.reflect.full.companionObjectInstance
|
||||||
|
|
||||||
|
object PatchExtensions {
|
||||||
|
/**
|
||||||
|
* The name of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.patchName: String
|
||||||
|
get() = findAnnotationRecursively(Name::class)?.name ?: this.simpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.version
|
||||||
|
get() = findAnnotationRecursively(Version::class)?.version
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather or not a [Patch] should be included.
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.include
|
||||||
|
get() = findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.description
|
||||||
|
get() = findAnnotationRecursively(Description::class)?.description
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dependencies of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.dependencies
|
||||||
|
get() = findAnnotationRecursively(DependsOn::class)?.dependencies
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packages a [Patch] is compatible with.
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.compatiblePackages
|
||||||
|
get() = findAnnotationRecursively(Compatibility::class)?.compatiblePackages
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather or not a [Patch] requires integrations.
|
||||||
|
*/
|
||||||
|
internal val Class<out Patch<Context>>.requiresIntegrations
|
||||||
|
get() = findAnnotationRecursively(RequiresIntegrations::class) != null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.options: PatchOptions?
|
||||||
|
get() = kotlin.companionObject?.let { cl ->
|
||||||
|
if (cl.visibility != KVisibility.PUBLIC) return null
|
||||||
|
kotlin.companionObjectInstance?.let {
|
||||||
|
(it as? OptionsContainer)?.options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ import java.io.Closeable
|
|||||||
* If it implements [Closeable], it will be closed after all patches have been executed.
|
* If it implements [Closeable], it will be closed after all patches have been executed.
|
||||||
* Closing will be done in reverse execution order.
|
* Closing will be done in reverse execution order.
|
||||||
*/
|
*/
|
||||||
sealed interface Patch<out T : Context> : Closeable {
|
sealed interface Patch<out T : Context> {
|
||||||
/**
|
/**
|
||||||
* The main function of the [Patch] which the patcher will call.
|
* The main function of the [Patch] which the patcher will call.
|
||||||
*
|
*
|
||||||
@@ -20,13 +20,6 @@ sealed interface Patch<out T : Context> : Closeable {
|
|||||||
* @return The result of executing the patch.
|
* @return The result of executing the patch.
|
||||||
*/
|
*/
|
||||||
fun execute(context: @UnsafeVariance T): PatchResult
|
fun execute(context: @UnsafeVariance T): PatchResult
|
||||||
|
|
||||||
/**
|
|
||||||
* The closing function for this patch.
|
|
||||||
*
|
|
||||||
* This can be treated like popping the patch from the current patch stack.
|
|
||||||
*/
|
|
||||||
override fun close() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -41,18 +41,13 @@ sealed class PatchBundle(path: String) : File(path) {
|
|||||||
arrayOf(this.toURI().toURL()),
|
arrayOf(this.toURI().toURL()),
|
||||||
Thread.currentThread().contextClassLoader // TODO: find out why this is required
|
Thread.currentThread().contextClassLoader // TODO: find out why this is required
|
||||||
),
|
),
|
||||||
StringIterator(
|
JarFile(this)
|
||||||
JarFile(this)
|
.stream()
|
||||||
.entries()
|
.filter {it.name.endsWith(".class") && !it.name.contains("$")}
|
||||||
.toList() // TODO: find a cleaner solution than that to filter non class files
|
.map({it -> it.realName.replace('/', '.').replace(".class", "")}).iterator()
|
||||||
.filter {
|
)
|
||||||
it.name.endsWith(".class") && !it.name.contains("$")
|
|
||||||
}
|
|
||||||
.iterator()
|
|
||||||
) {
|
|
||||||
it.realName.replace('/', '.').replace(".class", "")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,8 +63,8 @@ sealed class PatchBundle(path: String) : File(path) {
|
|||||||
* Patches will be loaded to the provided [dexClassLoader].
|
* Patches will be loaded to the provided [dexClassLoader].
|
||||||
*/
|
*/
|
||||||
fun loadPatches() = loadPatches(dexClassLoader,
|
fun loadPatches() = loadPatches(dexClassLoader,
|
||||||
StringIterator(DexFileFactory.loadDexFile(path, null).classes.iterator()) { classDef ->
|
DexFileFactory.loadDexFile(path, null).classes.asSequence().map({ classDef ->
|
||||||
classDef.type.substring(1, classDef.length - 1).replace('/', '.')
|
classDef.type.substring(1, classDef.length - 1).replace('/', '.')
|
||||||
})
|
}).iterator())
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,10 +0,0 @@
|
|||||||
package app.revanced.patcher.util.patch
|
|
||||||
|
|
||||||
internal class StringIterator<T, I : Iterator<T>>(
|
|
||||||
private val iterator: I,
|
|
||||||
private val _next: (T) -> String
|
|
||||||
) : Iterator<String> {
|
|
||||||
override fun hasNext() = iterator.hasNext()
|
|
||||||
|
|
||||||
override fun next() = _next(iterator.next())
|
|
||||||
}
|
|
@@ -0,0 +1,233 @@
|
|||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.builder.BuilderOffsetInstruction
|
||||||
|
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction21s
|
||||||
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
private object InstructionExtensionsTest {
|
||||||
|
private lateinit var testMethod: MutableMethod
|
||||||
|
private lateinit var testMethodImplementation: MutableMethodImplementation
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun createTestMethod() = ImmutableMethod(
|
||||||
|
"TestClass;",
|
||||||
|
"testMethod",
|
||||||
|
null,
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC.value,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
MutableMethodImplementation(16).also { testMethodImplementation = it }.apply {
|
||||||
|
repeat(10) { i -> this.addInstruction(TestInstruction(i)) }
|
||||||
|
},
|
||||||
|
).let { testMethod = it.toMutable() }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionsToImplementationIndexed() = applyToImplementation {
|
||||||
|
addInstructions(5, getTestInstructions(5..6)).also {
|
||||||
|
assertRegisterIs(5, 5)
|
||||||
|
assertRegisterIs(6, 6)
|
||||||
|
|
||||||
|
assertRegisterIs(5, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionsToImplementation() = applyToImplementation {
|
||||||
|
addInstructions(getTestInstructions(10..11)).also {
|
||||||
|
assertRegisterIs(10, 10)
|
||||||
|
assertRegisterIs(11, 11)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionsFromImplementationIndexed() = applyToImplementation {
|
||||||
|
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionsFromImplementation() = applyToImplementation {
|
||||||
|
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
||||||
|
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
||||||
|
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replaceInstructionsInImplementationIndexed() = applyToImplementation {
|
||||||
|
replaceInstructions(5, getTestInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
assertRegisterIs(7, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionToMethodIndexed() = applyToMethod {
|
||||||
|
addInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionToMethod() = applyToMethod {
|
||||||
|
addInstruction(TestInstruction(0)).also { assertRegisterIs(0, 10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionToMethodIndexed() = applyToMethod {
|
||||||
|
addInstruction(5, getTestSmaliInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionToMethod() = applyToMethod {
|
||||||
|
addInstruction(getTestSmaliInstruction(0)).also { assertRegisterIs(0, 10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionsToMethodIndexed() = applyToMethod {
|
||||||
|
addInstructions(5, getTestInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
|
||||||
|
assertRegisterIs(5, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionsToMethod() = applyToMethod {
|
||||||
|
addInstructions(getTestInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 10)
|
||||||
|
assertRegisterIs(1, 11)
|
||||||
|
|
||||||
|
assertRegisterIs(9, 9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionsToMethodIndexed() = applyToMethod {
|
||||||
|
addInstructionsWithLabels(5, getTestSmaliInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
|
||||||
|
assertRegisterIs(5, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionsToMethod() = applyToMethod {
|
||||||
|
addInstructions(getTestSmaliInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 10)
|
||||||
|
assertRegisterIs(1, 11)
|
||||||
|
|
||||||
|
assertRegisterIs(9, 9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionsWithExternalLabelToMethodIndexed() = applyToMethod {
|
||||||
|
val label = ExternalLabel("testLabel", getInstruction(5))
|
||||||
|
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
5,
|
||||||
|
getTestSmaliInstructions(0..1).plus("\n").plus("goto :${label.name}"),
|
||||||
|
label
|
||||||
|
).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
assertRegisterIs(5, 8)
|
||||||
|
|
||||||
|
val gotoTarget = getInstruction<BuilderOffsetInstruction>(7)
|
||||||
|
.target.location.instruction as OneRegisterInstruction
|
||||||
|
|
||||||
|
assertEquals(5, gotoTarget.registerA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionFromMethodIndexed() = applyToMethod {
|
||||||
|
removeInstruction(5).also {
|
||||||
|
assertRegisterIs(4, 4)
|
||||||
|
assertRegisterIs(6, 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionsFromMethodIndexed() = applyToMethod {
|
||||||
|
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionsFromMethod() = applyToMethod {
|
||||||
|
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
||||||
|
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
||||||
|
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replaceInstructionInMethodIndexed() = applyToMethod {
|
||||||
|
replaceInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replaceInstructionsInMethodIndexed() = applyToMethod {
|
||||||
|
replaceInstructions(5, getTestInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
assertRegisterIs(7, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replaceSmaliInstructionsInMethodIndexed() = applyToMethod {
|
||||||
|
replaceInstructions(5, getTestSmaliInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
assertRegisterIs(7, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Helper methods
|
||||||
|
|
||||||
|
private fun applyToImplementation(block: MutableMethodImplementation.() -> Unit) {
|
||||||
|
testMethodImplementation.apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyToMethod(block: MutableMethod.() -> Unit) {
|
||||||
|
testMethod.apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableMethodImplementation.assertRegisterIs(register: Int, atIndex: Int) = assertEquals(
|
||||||
|
register, getInstruction<OneRegisterInstruction>(atIndex).registerA
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun MutableMethod.assertRegisterIs(register: Int, atIndex: Int) =
|
||||||
|
implementation!!.assertRegisterIs(register, atIndex)
|
||||||
|
|
||||||
|
private fun getTestInstructions(range: IntRange) = range.map { TestInstruction(it) }
|
||||||
|
|
||||||
|
private fun getTestSmaliInstruction(register: Int) = "const/16 v$register, 0"
|
||||||
|
|
||||||
|
private fun getTestSmaliInstructions(range: IntRange) = range.joinToString("\n") {
|
||||||
|
getTestSmaliInstruction(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
private class TestInstruction(register: Int) : BuilderInstruction21s(Opcode.CONST_16, register, 0)
|
||||||
|
}
|
@@ -4,9 +4,9 @@ import app.revanced.patcher.annotation.Description
|
|||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.extensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patcher.patch.*
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
@@ -119,7 +119,7 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
|||||||
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
||||||
//
|
//
|
||||||
// Control flow instructions are not supported as of now.
|
// Control flow instructions are not supported as of now.
|
||||||
method.addInstructions(
|
method.addInstructionsWithLabels(
|
||||||
startIndex + 2,
|
startIndex + 2,
|
||||||
"""
|
"""
|
||||||
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
package app.revanced.patcher.util.smali
|
package app.revanced.patcher.util.smali
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.instruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
import app.revanced.patcher.extensions.label
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.newLabel
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
@@ -35,7 +36,7 @@ internal class InlineSmaliCompilerTest {
|
|||||||
method.addInstructions(arrayOfNulls<String>(insnAmount).also {
|
method.addInstructions(arrayOfNulls<String>(insnAmount).also {
|
||||||
Arrays.fill(it, "const/4 v0, 0x0")
|
Arrays.fill(it, "const/4 v0, 0x0")
|
||||||
}.joinToString("\n"))
|
}.joinToString("\n"))
|
||||||
method.addInstructions(
|
method.addInstructionsWithLabels(
|
||||||
targetIndex,
|
targetIndex,
|
||||||
"""
|
"""
|
||||||
:test
|
:test
|
||||||
@@ -44,7 +45,7 @@ internal class InlineSmaliCompilerTest {
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
val insn = method.instruction(insnIndex) as BuilderInstruction21t
|
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
||||||
assertEquals(targetIndex, insn.target.location.index)
|
assertEquals(targetIndex, insn.target.location.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,19 +62,19 @@ internal class InlineSmaliCompilerTest {
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(labelIndex, method.label(labelIndex).location.index)
|
assertEquals(labelIndex, method.newLabel(labelIndex).location.index)
|
||||||
|
|
||||||
method.addInstructions(
|
method.addInstructionsWithLabels(
|
||||||
|
method.implementation!!.instructions.size,
|
||||||
"""
|
"""
|
||||||
const/4 v0, 0x1
|
const/4 v0, 0x1
|
||||||
if-eqz v0, :test
|
if-eqz v0, :test
|
||||||
return-void
|
return-void
|
||||||
""", listOf(
|
""",
|
||||||
ExternalLabel("test",method.instruction(1))
|
ExternalLabel("test", method.getInstruction(1))
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val insn = method.instruction(insnIndex) as BuilderInstruction21t
|
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
||||||
assertTrue(insn.target.isPlaced, "Label was not placed")
|
assertTrue(insn.target.isPlaced, "Label was not placed")
|
||||||
assertEquals(labelIndex, insn.target.location.index)
|
assertEquals(labelIndex, insn.target.location.index)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user