docs: Add documentation (#278)

This commit is contained in:
oSumAtrIX 2024-02-25 03:30:08 +01:00 committed by GitHub
parent 41257ee87e
commit fe616beb22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 1041 additions and 3 deletions

View File

@ -0,0 +1,19 @@
name: Update documentation
on:
push:
paths:
- docs/**
jobs:
trigger:
runs-on: ubuntu-latest
name: Dispatch event to documentation repository
if: github.ref == 'refs/heads/main'
steps:
- uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN }}
repository: revanced/revanced-documentation
event-type: update-documentation
client-payload: '{"repo": "${{ github.event.repository.name }}", "ref": "${{ github.ref }}"}'

View File

@ -64,7 +64,7 @@ This document describes how to contribute to ReVanced Patcher.
## πŸ“– Resources to help you get started
- The [documentation](https://github.com/ReVanced/revanced-patcher/tree/docs) contains the fundamentals
- The [documentation](https://github.com/ReVanced/revanced-patcher/tree/docs/docs) contains the fundamentals
of ReVanced Patcher and how to use ReVanced Patcher to create patches
- [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
- [Issues](https://github.com/ReVanced/revanced-patcher/issues) are where we keep track of bugs and feature requests

View File

@ -91,7 +91,7 @@ To use ReVanced Patcher in your project, follow these steps:
to your project
2. Add the dependency to your project:
```kotlin
```kt
dependencies {
implementation("app.revanced:revanced-patcher:{$version}")
}
@ -115,7 +115,7 @@ you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced
### πŸ“ƒ Documentation
The documentation contains the fundamentals of ReVanced Patcher and how to use ReVanced Patcher to create patches.
You can find it [here](https://github.com/ReVanced/revanced-patcher/tree/docs).
You can find it [here](https://github.com/ReVanced/revanced-patcher/tree/docs/docs).
## πŸ“œ Licence

108
docs/1_patcher_intro.md Normal file
View File

@ -0,0 +1,108 @@
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# πŸ’‰ Introduction to ReVanced Patcher
In order to create patches for Android applications, you first need to understand the fundamentals of ReVanced Patcher.
## πŸ“™ How it works
ReVanced Patcher is a library that allows you to modify Android applications by applying patches to their APKs. It is built on top of [Smali](https://github.com/google/smali) for bytecode manipulation and [Androlib (Apktool)](https://github.com/iBotPeaches/Apktool) for resource decoding and encoding.
ReVanced Patcher accepts a list of patches and integrations, and applies them to a given APK file. It then returns the modified components of the APK file, such as modified dex files and resources, that can be repackaged into a new APK file.
ReVanced Patcher has a simple API that allows you to load patches and integrations from JAR files and apply them to an APK file.
Later on, you will learn how to create patches.
```kt
// Executed patches do not necessarily reset their state.
// For that reason it is important to create a new instance of the PatchBundleLoader
// once the patches are executed instead of reusing the same instance of patches loaded by PatchBundleLoader.
val patches: PatchSet /* = Set<Patch<*>> */ = PatchBundleLoader.Jar(File("revanced-patches.jar"))
val integrations = setOf(File("integrations.apk"))
// Instantiating the patcher will decode the manifest of the APK file to read the package and version name.
val patcherConfig = PatcherConfig(apkFile = File("some.apk"))
val patcherResult = Patcher(patcherConfig).use { patcher ->
patcher.apply {
acceptIntegrations(integrations)
acceptPatches(patches)
// Execute patches.
runBlocking {
patcher.apply(returnOnError = false).collect { patchResult ->
if (patchResult.exception != null)
println("${patchResult.patchName} failed:\n${patchResult.exception}")
else
println("${patchResult.patchName} succeeded")
}
}
}.get()
}
// The result of the patcher contains the modified components of the APK file that can be repackaged into a new APK file.
val dexFiles = patcherResult.dexFiles
val resources = patcherResult.resources
```
## ⏭️ What's next
The next page teaches the fundamentals of ReVanced Patches.
Continue: [🧩 Introduction to ReVanced Patches](2_patches_intro.md)

107
docs/2_1_setup.md Normal file
View File

@ -0,0 +1,107 @@
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# πŸ‘Ά Setting up a development environment
To get started developing patches with ReVanced Patcher, you need to prepare a development environment.
## πŸ“ Prerequisites
- A Java IDE with Kotlin support, such as [IntelliJ IDEA](https://www.jetbrains.com/idea/)
- Knowledge of Java, [Kotlin](https://kotlinlang.org), and [Dalvik bytecode](https://source.android.com/docs/core/runtime/dalvik-bytecode)
- Android reverse engineering skills and tools such as [jadx](https://github.com/skylot/jadx)
## πŸƒ Prepare the environment
Throughout the documentation, [ReVanced Patches](https://github.com/revanced/revanced-patches) will be used as an example project.
1. Clone the repository
```bash
git clone https://github.com/revanced/revanced-patches && cd revanced-patches
```
2. Build the project
```bash
./gradlew build
```
> [!NOTE]
> If the build fails due to authentication, you may need to authenticate to GitHub Packages.
> Create a PAT with the scope `read:packages` [here](https://github.com/settings/tokens/new?scopes=read:packages&description=ReVanced) and add your token to ~/.gradle/gradle.properties.
>
> Example `gradle.properties` file:
>
> ```properties
> gpr.user = user
> gpr.key = key
> ```
3. Open the project in your IDE
> [!TIP]
> It is a good idea to set up a complete development environment for ReVanced, so that you can also test your patches by following the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
## ⏭️ What's next
The next page will go into details about a ReVanced patch.
Continue: [🧩 Anatomy of a patch](2_2_patch_anatomy.md)

View File

@ -0,0 +1,275 @@
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# πŸ”Ž Fingerprinting
In the context of ReVanced, fingerprinting is primarily used to resolve methods with a limited amount of known information.
Methods with obfuscated names that change with each update are primary candidates for fingerprinting.
The goal of fingerprinting is to uniquely identify a method by capturing various attributes, such as the return type, access flags, an opcode pattern, strings, and more.
## ⛳️ Example fingerprint
Throughout the documentation, the following example will be used to demonstrate the concepts of fingerprints:
```kt
package app.revanced.patches.ads.fingerprints
object ShowAdsFingerprint : MethodFingerprint(
returnType = "Z",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Z"),
opcodes = listOf(Opcode.RETURN),
strings = listOf("pro"),
customFingerprint = { (methodDef, classDef) -> methodDef.definingClass == "Lcom/some/app/ads/AdsLoader;" }
)
```
## πŸ”Ž Reconstructing the original code from a fingerprint
The following code is reconstructed from the fingerprint to understand how a fingerprint is created.
The fingerprint contains the following information:
- Method signature:
```kt
returnType = "Z",
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Z"),
```
- Method implementation:
```kt
opcodes = listOf(Opcode.RETURN)
strings = listOf("pro"),
```
- Package and class name:
```kt
customFingerprint = { (methodDef, classDef) -> methodDef.definingClass == "Lcom/some/app/ads/AdsLoader;"}
```
With this information, the original code can be reconstructed:
```java
package com.some.app.ads;
<accessFlags> class AdsLoader {
public final boolean <methodName>(boolean <parameter>) {
// ...
var userStatus = "pro";
// ...
return <returnValue>;
}
}
```
> [!TIP]
> A fingerprint should contain information about a method likely to remain the same across updates.
> A method's name is not included in the fingerprint because it is likely to change with each update in an obfuscated app. In contrast, the return type, access flags, parameters, patterns of opcodes, and strings are likely to remain the same.
## πŸ”¨ How to use fingerprints
After creating a fingerprint, add it to the constructor of a `BytecodePatch`:
```kt
object DisableAdsPatch : BytecodePatch(
setOf(ShowAdsFingerprint)
) {
// ...
}
```
> [!NOTE]
> Fingerprints passed to the constructor of `BytecodePatch` are resolved by ReVanced Patcher before the patch is executed.
> [!TIP]
> Multiple patches can share fingerprints. If a fingerprint is resolved once, it will not be resolved again.
> [!TIP]
> If a fingerprint has an opcode pattern, you can use the `FuzzyPatternScanMethod` annotation to fuzzy match the pattern.
> Opcode pattern arrays can contain `null` values to indicate that the opcode at the index is unknown.
> Any opcode will match to a `null` value.
> [!WARNING]
> If the fingerprint can not be resolved because it does not match any method, the result of a fingerprint is `null`.
Once the fingerprint is resolved, the result can be used in the patch:
```kt
object DisableAdsPatch : BytecodePatch(
setOf(ShowAdsFingerprint)
) {
override fun execute(context: BytecodeContext) {
val result = ShowAdsFingerprint.result
?: throw PatchException("ShowAdsFingerprint not found")
// ...
}
}
```
The result of a fingerprint that resolved successfully contains mutable and immutable references to the method and the class it is defined in.
```kt
class MethodFingerprintResult(
val method: Method,
val classDef: ClassDef,
val scanResult: MethodFingerprintScanResult,
// ...
) {
val mutableClass by lazy { /* ... */ }
val mutableMethod by lazy { /* ... */ }
// ...
}
class MethodFingerprintScanResult(
val patternScanResult: PatternScanResult?,
val stringsScanResult: StringsScanResult?,
) {
class StringsScanResult(val matches: List<StringMatch>) {
class StringMatch(val string: String, val index: Int)
}
class PatternScanResult(
val startIndex: Int,
val endIndex: Int,
// ...
) {
// ...
}
}
```
## 🏹 Manual resolution of fingerprints
Unless a fingerprint is added to the constructor of `BytecodePatch`, the fingerprint will not be resolved automatically by ReVanced Patcher before the patch is executed.
Instead, the fingerprint can be resolved manually using various overloads of the `resolve` function of a fingerprint.
You can resolve a fingerprint in the following ways:
- On a **list of classes**, if the fingerprint can resolve on a known subset of classes
If you have a known list of classes you know the fingerprint can resolve on, you can resolve the fingerprint on the list of classes:
```kt
override fun execute(context: BytecodeContext) {
val result = ShowAdsFingerprint.also { it.resolve(context, context.classes) }.result
?: throw PatchException("ShowAdsFingerprint not found")
// ...
}
```
- On a **single class**, if the fingerprint can resolve on a single known class
If you know the fingerprint can resolve to a method in a specific class, you can resolve the fingerprint on the class:
```kt
override fun execute(context: BytecodeContext) {
val adsLoaderClass = context.classes.single { it.name == "Lcom/some/app/ads/Loader;" }
val result = ShowAdsFingerprint.also { it.resolve(context, adsLoaderClass) }.result
?: throw PatchException("ShowAdsFingerprint not found")
// ...
}
```
- On a **single method**, to extract certain information about a method
The result of a fingerprint contains useful information about the method, such as the start and end index of an opcode pattern or the indices of the instructions with certain string references.
A fingerprint can be leveraged to extract such information from a method instead of manually figuring it out:
```kt
override fun execute(context: BytecodeContext) {
val adsFingerprintResult = ShowAdsFingerprint.result
?: throw PatchException("ShowAdsFingerprint not found")
val proStringsFingerprint = object : MethodFingerprint(
strings = listOf("free", "trial")
) {}
proStringsFingerprint.also {
it.resolve(context, adsFingerprintResult.method)
}.result?.let { result ->
result.scanResult.stringsScanResult!!.matches.forEach { match ->
println("The index of the string '${match.string}' is ${match.index}")
}
} ?: throw PatchException("pro strings fingerprint not found")
}
```
> [!TIP]
> To see real-world examples of fingerprints, check out the [ReVanced Patches](https://github.com/revanced/revanced-patches) repository.
## ⏭️ What's next
The next page discusses the structure and conventions of patches.
Continue: [πŸ“œ Project structure and conventions](3_structure_and_conventions.md)

211
docs/2_2_patch_anatomy.md Normal file
View File

@ -0,0 +1,211 @@
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# 🧩 Anatomy of a ReVanced patch
Learn the API to create patches using ReVanced Patcher.
## ⛳️ Example patch
Throughout the documentation, the following example will be used to demonstrate the concepts of patches:
```kt
package app.revanced.patches.ads
@Patch(
name = "Disable ads",
description = "Disable ads in the app.",
dependencies = [DisableAdsResourcePatch::class],
compatiblePackages = [CompatiblePackage("com.some.app", ["1.3.0"])]
)
object DisableAdsPatch : BytecodePatch(
setOf(ShowAdsFingerprint)
) {
override fun execute(context: BytecodeContext) {
ShowAdsFingerprint.result?.let { result ->
result.mutableMethod.addInstructions(
0,
"""
# Return false.
const/4 v0, 0x0
return v0
"""
)
} ?: throw PatchException("ShowAdsFingerprint not found")
}
}
```
## πŸ”Ž Breakdown
The example patch consists of the following parts:
### πŸ“ Patch annotation
```kt
@Patch(
name = "Disable ads",
description = "Disable ads in the app.",
dependencies = [DisableAdsResourcePatch::class],
compatiblePackages = [CompatiblePackage("com.some.app", ["1.3.0"])]
)
```
The `@Patch` annotation is used to provide metadata about the patch.
Notable annotation parameters are:
- `name`: The name of the patch. This is used as an identifier for the patch.
If this parameter is not set, `PatchBundleLoader` will not load the patch.
Other patches can still use this patch as a dependency
- `description`: A description of the patch. Can be unset if the name is descriptive enough
- `dependencies`: A set of patches which the patch depends on. The patches in this set will be executed before this patch. If a dependency patch raises an exception, this patch will not be executed; subsquently, other patches that depend on this patch will not be executed.
- `compatiblePackages`: A set of `CompatiblePackage` objects. Each `CompatiblePackage` object contains the package name and a set of compatible version names. This parameter can specify the packages and versions the patch is compatible with. Patches can still execute on incompatible packages, but it is recommended to use this parameter to list known compatible packages
- If unset, it is implied that the patch is compatible with all packages
- If the set of versions is unset, it is implied that the patch is compatible with all versions of the package
- If the set of versions is empty, it is implied that the patch is not compatible with any version of the package. This can be useful, for example, to prevent a patch from executing on specific packages that are known to be incompatible
> [!WARNING]
> Circular dependencies are not allowed. If a patch depends on another patch, the other patch cannot depend on the first patch.
> [!NOTE]
> The `@Patch` annotation is optional. If the patch does not require any metadata, it can be omitted.
> If the patch is only used as a dependency, the metadata, such as the `compatiblePackages` parameter, has no effect, as every dependency patch inherits the compatible packages of the patches that depend on it.
> [!TIP]
> An abstract patch class can be annotated with `@Patch`.
> Patches extending off the abstract patch class will inherit the metadata of the abstract patch class.
> [!TIP]
> Instead of the `@Patch` annotation, the superclass's constructor can be used. This is useful in the example scenario where you want to create an abstract patch class.
>
> Example:
>
> ```kt
> abstract class AbstractDisableAdsPatch(
> fingerprints: Set<Fingerprint>
> ) : BytecodePatch(
> name = "Disable ads",
> description = "Disable ads in the app.",
> fingerprints
> ) {
> // ...
> }
> ```
>
> Remember that this constructor has precedence over the `@Patch` annotation.
### πŸ—οΈ Patch class
```kt
object DisableAdsPatch : BytecodePatch( /* Parameters */ ) {
// ...
}
```
Each patch class extends off a base class that implements the `Patch` interface.
The interface requires the `execute` method to be implemented.
Depending on which base class is extended, the patch can modify different parts of the APK as described in [🧩 Introduction to ReVanced Patches](2_introduction_to_patches.md).
> [!TIP]
> A patch is usually a singleton object, meaning only one patch instance exists in the JVM.
> Because dependencies are executed before the patch itself, a patch can rely on the state of the dependency patch.
> This is useful in the example scenario, where the `DisableAdsPatch` depends on the `DisableAdsResourcePatch`.
> The `DisableAdsResourcePatch` can, for example, be used to read the decoded resources of the app and provide the `DisableAdsPatch` with the necessary information to disable ads because the `DisableAdsResourcePatch` is executed before the `DisableAdsPatch` and is a singleton object.
### 🏁 The `execute` function
The `execute` function is declared in the `Patch` interface and needs to be implemented.
The `execute` function receives an instance of a context object that provides access to the APK. The patch can use this context to modify the APK as described in [🧩 Introduction to ReVanced Patches](2_introduction_to_patches.md).
In the current example, the patch adds instructions at the beginning of a method implementation in the Dalvik VM bytecode. The added instructions return `false` to disable ads in the current example:
```kt
val result = LoadAdsFingerprint.result
?: throw PatchException("LoadAdsFingerprint not found")
result.mutableMethod.addInstructions(
0,
"""
# Return false.
const/4 v0, 0x0
return v0
"""
)
```
> [!NOTE]
> This patch uses a fingerprint to find the method and replaces the method's instructions with new instructions.
> The fingerprint is resolved on the classes present in `BytecodeContext`.
> Fingerprints will be explained in more detail on the next page.
> [!TIP]
> The patch can also raise any `Exception` or `Throwable` at any time to indicate that the patch failed to execute. A `PatchException` is recommended to be raised if the patch fails to execute.
> If any patch depends on this patch, the dependent patch will not be executed, whereas other patches that do not depend on this patch can still be executed.
> ReVanced Patcher will handle any exception raised by a patch.
> [!TIP]
> To see real-world examples of patches, check out the [ReVanced Patches](https://github.com/revanced/revanced-patches) repository.
## ⏭️ What's next
The next page explains the concept of fingerprinting in ReVanced Patcher.
Continue: [πŸ”Ž Fingerprinting](2_2_1_fingerprinting.md)

125
docs/2_patches_intro.md Normal file
View File

@ -0,0 +1,125 @@
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# 🧩 Introduction to ReVanced Patches
Learn the basic concepts of ReVanced Patcher and how to create patches.
## πŸ“™ Fundamentals
A patch is a piece of code that modifies an Android application.
There are multiple types of patches. Each type can modify a different part of the APK, such as the Dalvik VM bytecode, the APK resources, or arbitrary files in the APK:
- A `BytecodePatch` modifies the Dalvik VM bytecode
- A `ResourcePatch` modifies (decoded) resources
- A `RawResourcePatch` modifies arbitrary files
Each patch can declare a set of dependencies on other patches. ReVanced Patcher will first execute dependencies before executing the patch itself. This way, multiple patches can work together for abstract purposes in a modular way.
A patch class can be annotated with `@Patch` to provide metadata about and dependencies of the patch.
Alternatively, a constructor of the superclass can be used. This is useful in the example scenario where you want to create an abstract patch class.
The entry point of a patch is the `execute` function. This function is called by ReVanced Patcher when the patch is executed. The `execute` function receives an instance of the context object that provides access to the APK. The patch can use this context to modify the APK.
Each type of context provides different APIs to modify the APK. For example, the `BytecodeContext` provides APIs to modify the Dalvik VM bytecode, while the `ResourceContext` provides APIs to modify resources.
The difference between `ResourcePatch` and `RawResourcePatch` is that ReVanced Patcher will decode the resources if it is supplied a `ResourcePatch` for execution or if any kind of patch depends on a `ResourcePatch` and will not decode the resources before executing `RawResourcePatch`. Both, `ResourcePatch` and `RawResourcePatch` can modify arbitrary files in the APK, whereas only `ResourcePatch` can modify decoded resources. The choice of which type to use depends on the use case. Decoding and building resources is a time- and resource-consuming process, so if the patch does not need to modify decoded resources, it is better to use `RawResourcePatch` or `BytecodePatch`.
Example of a `BytecodePatch`:
```kt
@Surpress("unused")
object MyPatch : BytecodePatch() {
override fun execute(context: BytecodeContext) {
// Your patch code here
}
}
```
Example of a `ResourcePatch`:
```kt
@Surpress("unused")
object MyPatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
// Your patch code here
}
}
```
Example of a `RawResourcePatch`:
```kt
@Surpress("unused")
object MyPatch : RawResourcePatch() {
override fun execute(context: ResourceContext) {
// Your patch code here
}
}
```
> [!TIP]
> To see real-world examples of patches, check out the [ReVanced Patches](https://github.com/revanced/revanced-patches) repository.
## ⏭️ Whats next
The next page will guide you through setting up a development environment for creating patches.
Continue: [πŸ‘Ά Setting up a development environment](2_1_setup.md)

View File

@ -0,0 +1,97 @@
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# πŸ“œ Project structure and conventions
Over time, a specific project structure and conventions have been established.
## πŸ“ File structure
Patches are organized in a specific file structure. The file structure is as follows:
```text
πŸ“¦your.patches.app.category
β”œ πŸ“‚fingerprints
β”œ β”œ πŸ”SomeFingerprintA.kt
β”œ β”” πŸ”SomeFingerprintB.kt
β”” 🧩SomePatch.kt
```
## πŸ“™ Conventions
- πŸ”₯ Name a patch after what it does. For example, if a patch removes ads, name it `RemoveAdsPatch`.
If a patch changes the color of a button, name it `ChangeButtonColorPatch`
- πŸ”₯ Write the patch description in the third person, present tense, and end it with a period.
If a patch removes ads, the description can be omitted because of redundancy, but if a patch changes the color of a button, the description can be _Changes the color of the resume button to red._
- πŸ”₯ Write patches with modularity and reusability in mind. Patches can depend on each other, so it is important to write patches in a way that can be used in different contexts.
- πŸ”₯πŸ”₯ Keep patches as minimal as possible. This reduces the risk of failing patches.
Instead of involving many abstract changes in one patch or writing entire methods or classes in a patch,
you can write code in integrations. Integrations are compiled classes that are merged into the app before patches are executed as described in [πŸ’‰ Introduction to ReVanced Patcher](1_patcher_intro).
Patches can then reference methods and classes from integrations.
A real-world example of integrations can be found in the [ReVanced Integrations](https://github.com/ReVanced/revanced-integrations) repository
- πŸ”₯πŸ”₯πŸ”₯ Do not overload a fingerprint with information about a method that's likely to change.
In the example of an obfuscated method, it's better to fingerprint the method by its return type and parameters rather than its name because the name is likely to change. An intelligent selection of an opcode pattern or strings in a method can result in a strong fingerprint dynamic to app updates.
- πŸ”₯πŸ”₯πŸ”₯ Document your patches. Patches are abstract by nature, so it is important to document parts of the code that are not self-explanatory. For example, explain why and how a certain method is patched or large blocks of instructions that are modified or added to a method
## ⏭️ What's next
The next page discusses useful APIs for patch development.
Continue: [πŸ’ͺ Advanced APIs](4_apis.md)

23
docs/4_apis.md Normal file
View File

@ -0,0 +1,23 @@
# πŸ’ͺ Advanced APIs
A handful of APIs are available to make patch development easier and more efficient.
## πŸ“™ Overview
1. πŸ‘Ή Create new mutable classes with `context.proxy(ClassDef)`
2. πŸ” Find and proxy existing classes with `BytecodeContext.findClass(Predicate)`
3. πŸƒβ€ Easily access referenced methods recursively by index with `BytecodeContext.toMethodWalker(Method)`
4. πŸ”¨ Make use of extension functions from `BytecodeUtils` and `ResourceUtils` with certain applications (Available in ReVanced Patches)
5. πŸ’Ύ Read and write (decoded) resources with `ResourceContext.get(Path, Boolean) `
6. πŸ“ƒ Read and write DOM files using `ResourceContext.document`
7. πŸ”§ Equip patches with configurable options using `Patch.options`
### 🧰 APIs
> [!WARNING]
> This section is still under construction and may be incomplete.
## πŸŽ‰ Afterword
ReVanced Patcher is a powerful library to patch Android applications, offering a rich set of APIs to develop patches that outlive app updates. Patches make up ReVanced; without you, the community of patch developers, ReVanced would not be what it is today. We hope that this documentation has been helpful to you and are excited to see what you will create with ReVanced Patcher. If you have any questions or need help, talk to us on one of our platforms linked on [revanced.app](https://revanced.app) or open an issue in case of a bug or feature request,
ReVanced

73
docs/README.md Normal file
View File

@ -0,0 +1,73 @@
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# πŸ’‰ Documentation of ReVanced Patcher
This documentation contains the fundamentals of ReVanced Patcher and how to use ReVanced Patcher to create patches
## πŸ“– Table of content
1. [πŸ’‰ Introduction to ReVanced Patcher](1_patcher_intro.md)
2. [🧩 Introduction to ReVanced Patches](2_patches_intro.md)
1. [πŸ‘Ά Setting up a development environment](2_1_setup.md)
2. [🧩 Anatomy of a ReVanced patch](2_2_patch_anatomy.md)
1. [πŸ”Ž Fingerprinting](2_2_1_fingerprinting.md)
3. [πŸ“œ Project structure and conventions](3_structure_and_conventions.md)
4. [πŸ’ͺ Advanced APIs](4_apis.md)