1
mirror of https://github.com/revanced/revanced-cli synced 2025-11-19 15:33:27 +01:00

Compare commits

...

170 Commits

Author SHA1 Message Date
semantic-release-bot
5af2f10e14 chore(release): 2.6.0 [skip ci]
# [2.6.0](https://github.com/revanced/revanced-cli/compare/v2.5.3...v2.6.0) (2022-07-18)

### Features

* more efficient zipalign ([a942a57](a942a57364))
2022-07-18 22:53:38 +00:00
oSumAtrIX
40c8500e38 refactor: create a variable for duplicate code 2022-07-19 00:50:26 +02:00
oSumAtrIX
9add2f9e82 refactor: fix spelling mistake & add private attribute to field 2022-07-19 00:48:01 +02:00
bogadana
a942a57364 feat: more efficient zipalign 2022-07-19 00:19:47 +02:00
oSumAtrIX
718df54bdc docs: change size of headings [skip ci] 2022-07-12 01:13:42 +02:00
Aunali321
507b6f3409 docs: fix feature request issue template (#94) 2022-07-11 20:36:14 +02:00
semantic-release-bot
c183c38f2a chore(release): 2.5.3 [skip ci]
## [2.5.3](https://github.com/revanced/revanced-cli/compare/v2.5.2...v2.5.3) (2022-07-11)

### Bug Fixes

* Log not showing in CLI  ([#80](https://github.com/revanced/revanced-cli/issues/80)) ([d9c5a17](d9c5a179c5)), closes [#79](https://github.com/revanced/revanced-cli/issues/79)
2022-07-11 12:26:54 +00:00
EdgE790
d9c5a179c5 fix: Log not showing in CLI (#80)
* Fix #79. Changed from default StreamHandler to FlushingStreamHandler which flushes after every log statement

* Added removal of handlers, so they will not be duplicated.

* Replaced removal of handlers with addition only in case if there are no handlers already.
Changed errorLogger name from hardcoded to reusing previous logger name, so it will have the same name if only first parameter is used.

* Replaced calls ::javaClass.name to ::class.java.name to have proper class names in loggers
2022-07-11 14:25:17 +02:00
semantic-release-bot
6447311b0a chore(release): 2.5.2 [skip ci]
## [2.5.2](https://github.com/revanced/revanced-cli/compare/v2.5.1...v2.5.2) (2022-07-10)

### Bug Fixes

* `defaultExclude` unused ([2015c2a](2015c2a1dc))
2022-07-10 23:18:18 +00:00
oSumAtrIX
2015c2a1dc fix: defaultExclude unused 2022-07-11 01:16:44 +02:00
Sculas
99f123d1ca chore: remove assignees [skip ci] 2022-07-10 20:30:05 +02:00
Sculas
6ccbec6c95 chore: remove assignees [skip ci] 2022-07-10 20:29:40 +02:00
semantic-release-bot
9c30eac7c1 chore(release): 2.5.1 [skip ci]
## [2.5.1](https://github.com/revanced/revanced-cli/compare/v2.5.0...v2.5.1) (2022-07-10)

### Bug Fixes

* Make clear what the --exclusive command actually does ([a26b0ea](a26b0ea64d))
2022-07-10 18:06:07 +00:00
TheJeterLP
a26b0ea64d fix: Make clear what the --exclusive command actually does 2022-07-10 20:04:47 +02:00
semantic-release-bot
ab4c81965e chore(release): 2.5.0 [skip ci]
# [2.5.0](https://github.com/revanced/revanced-cli/compare/v2.4.0...v2.5.0) (2022-07-10)

### Bug Fixes

* null exception when resource patching is disabled ([#85](https://github.com/revanced/revanced-cli/issues/85)) ([125fa06](125fa06ca6))
* remove `excludePatches` check ([eb83cab](eb83cabfff))

### Features

* `--exclusive` switch ([#78](https://github.com/revanced/revanced-cli/issues/78)) ([8e91c12](8e91c12c5e))
* `--uninstall` switch ([#84](https://github.com/revanced/revanced-cli/issues/84)) ([131100e](131100ef00))
2022-07-10 11:59:57 +00:00
oSumAtrIX
eb83cabfff fix: remove excludePatches check 2022-07-10 13:58:21 +02:00
bogadana
125fa06ca6 fix: null exception when resource patching is disabled (#85) 2022-07-10 12:19:09 +02:00
Itroublve
131100ef00 feat: --uninstall switch (#84)
This moves the move unmount script to a command
2022-07-10 12:17:47 +02:00
bogadana
8e91c12c5e feat: --exclusive switch (#78) 2022-07-10 12:15:28 +02:00
semantic-release-bot
93a4787780 chore(release): 2.4.0 [skip ci]
# [2.4.0](https://github.com/revanced/revanced-cli/compare/v2.3.3...v2.4.0) (2022-07-10)

### Bug Fixes

* wrong label in additional items [skip ci] ([cd3ded1](cd3ded1fbd))

### Features

* better output for excluded patches ([#77](https://github.com/revanced/revanced-cli/issues/77)) ([ac7c7a9](ac7c7a9a1a))
* issue templates [skip ci] ([bac8c67](bac8c67d6f))
2022-07-10 10:15:16 +00:00
bogadana
ac7c7a9a1a feat: better output for excluded patches (#77) 2022-07-10 12:13:26 +02:00
oSumAtrIX
cd3ded1fbd fix: wrong label in additional items [skip ci] 2022-07-10 00:35:41 +02:00
oSumAtrIX
bac8c67d6f feat: issue templates [skip ci] 2022-07-10 00:34:16 +02:00
semantic-release-bot
387afd6bd5 chore(release): 2.3.3 [skip ci]
## [2.3.3](https://github.com/revanced/revanced-cli/compare/v2.3.2...v2.3.3) (2022-07-09)
2022-07-09 13:54:19 +00:00
TheJeterLP
ef2019ceba build: bump patcher dependency version 2022-07-09 15:52:28 +02:00
semantic-release-bot
c1333d87f4 chore(release): 2.3.2 [skip ci]
## [2.3.2](https://github.com/revanced/revanced-cli/compare/v2.3.1...v2.3.2) (2022-07-05)

### Bug Fixes

* fix noSuchMethodError ([00fec25](00fec2508a))
2022-07-05 18:32:05 +00:00
Joey Peter
00fec2508a fix: fix noSuchMethodError 2022-07-05 20:30:37 +02:00
semantic-release-bot
387b6bd016 chore(release): 2.3.1 [skip ci]
## [2.3.1](https://github.com/revanced/revanced-cli/compare/v2.3.0...v2.3.1) (2022-07-04)
2022-07-04 23:28:57 +00:00
oSumAtrIX
5e7785fd8f build: bump patcher dependency version 2022-07-05 01:27:18 +02:00
semantic-release-bot
19dfa2e2f1 chore(release): 2.3.0 [skip ci]
# [2.3.0](https://github.com/revanced/revanced-cli/compare/v2.2.0...v2.3.0) (2022-07-03)

### Features

* separate logger to stdout & stderr ([#63](https://github.com/revanced/revanced-cli/issues/63)) ([0ddc2b5](0ddc2b54b7))
2022-07-03 14:19:50 +00:00
EdgE790
0ddc2b54b7 feat: separate logger to stdout & stderr (#63) 2022-07-03 16:18:13 +02:00
semantic-release-bot
3a51ce71a5 chore(release): 2.2.0 [skip ci]
# [2.2.0](https://github.com/revanced/revanced-cli/compare/v2.1.0...v2.2.0) (2022-07-03)

### Features

* separate options for `--list` ([#60](https://github.com/revanced/revanced-cli/issues/60)) ([52b3161](52b316150d))
2022-07-03 13:58:57 +00:00
EdgE790
52b316150d feat: separate options for --list (#60) 2022-07-03 15:57:25 +02:00
semantic-release-bot
75e9c49b72 chore(release): 2.1.0 [skip ci]
# [2.1.0](https://github.com/revanced/revanced-cli/compare/v2.0.5...v2.1.0) (2022-07-03)

### Features

* `--include` option ([#76](https://github.com/revanced/revanced-cli/issues/76)) ([57a1e7c](57a1e7c27f))
2022-07-03 13:52:05 +00:00
Aunali321
31a05b6768 refactor: comment for unmount step (#74) 2022-07-03 15:50:12 +02:00
bogadana
57a1e7c27f feat: --include option (#76) 2022-07-03 15:49:25 +02:00
semantic-release-bot
585d77ce80 chore(release): 2.0.5 [skip ci]
## [2.0.5](https://github.com/revanced/revanced-cli/compare/v2.0.4...v2.0.5) (2022-06-29)
2022-06-29 23:43:45 +00:00
oSumAtrIX
b4d0ce52ea build: bump patcher dependency version 2022-06-30 01:41:39 +02:00
semantic-release-bot
ee26a8d233 chore(release): 2.0.4 [skip ci]
## [2.0.4](https://github.com/revanced/revanced-cli/compare/v2.0.3...v2.0.4) (2022-06-28)
2022-06-28 01:29:41 +00:00
oSumAtrIX
be3abdda30 build: bump patcher dependency version 2022-06-28 03:28:02 +02:00
semantic-release-bot
1849d570f8 chore(release): 2.0.3 [skip ci]
## [2.0.3](https://github.com/revanced/revanced-cli/compare/v2.0.2...v2.0.3) (2022-06-27)

### Bug Fixes

* wrong keystore output path ([20fa179](20fa17957e))
2022-06-27 22:45:43 +00:00
oSumAtrIX
20fa17957e fix: wrong keystore output path 2022-06-28 00:44:12 +02:00
semantic-release-bot
0d58ef14ae chore(release): 2.0.2 [skip ci]
## [2.0.2](https://github.com/revanced/revanced-cli/compare/v2.0.1...v2.0.2) (2022-06-27)

### Bug Fixes

* wrong separator when using `ZipFileSystemUtils` ([20e15de](20e15defc2))
2022-06-27 22:13:52 +00:00
oSumAtrIX
20e15defc2 fix: wrong separator when using ZipFileSystemUtils 2022-06-28 00:12:22 +02:00
oSumAtrIX
9f91f63220 refactor: ZipFileSystemUtils 2022-06-28 00:12:22 +02:00
semantic-release-bot
8782cdef67 chore(release): 2.0.1 [skip ci]
## [2.0.1](https://github.com/revanced/revanced-cli/compare/v2.0.0...v2.0.1) (2022-06-26)
2022-06-26 20:17:25 +00:00
j4k0xb
58fa0774c4 ci: trigger release on build commits (#67) 2022-06-26 22:16:02 +02:00
oSumAtrIX
dc5ff36058 build: bump patcher dependency version 2022-06-26 18:03:39 +02:00
semantic-release-bot
27c28fab5e chore(release): 2.0.0 [skip ci]
# [2.0.0](https://github.com/revanced/revanced-cli/compare/v1.11.1...v2.0.0) (2022-06-26)

### Code Refactoring

* migrate from `Signature` to `Fingerprint` ([88852a4](88852a45ac))

### BREAKING CHANGES

* Not backwards compatible, since a lot of classes where renamed.
2022-06-26 14:35:32 +00:00
oSumAtrIX
88852a45ac refactor: migrate from Signature to Fingerprint
BREAKING CHANGE: Not backwards compatible, since a lot of classes where renamed.
2022-06-26 16:34:08 +02:00
semantic-release-bot
8dd9293cb9 chore(release): 1.11.1 [skip ci]
## [1.11.1](https://github.com/revanced/revanced-cli/compare/v1.11.0...v1.11.1) (2022-06-25)

### Bug Fixes

* update patcher version ([499ce0a](499ce0a6fb))
2022-06-25 15:41:22 +00:00
Sculas
499ce0a6fb fix: update patcher version 2022-06-25 17:39:52 +02:00
semantic-release-bot
27457e0c7d chore(release): 1.11.0 [skip ci]
# [1.11.0](https://github.com/revanced/revanced-cli/compare/v1.10.2...v1.11.0) (2022-06-23)

### Features

* improve logging ([df85fa3](df85fa37ef))
2022-06-23 00:14:39 +00:00
oSumAtrIX
7418573c6c build: bump patcher dependency version 2022-06-23 02:12:51 +02:00
oSumAtrIX
df85fa37ef feat: improve logging 2022-06-23 02:10:11 +02:00
oSumAtrIX
04805e45fe refactor: logging and exception strings 2022-06-22 19:36:23 +02:00
semantic-release-bot
61d3b99313 chore(release): 1.10.2 [skip ci]
## [1.10.2](https://github.com/revanced/revanced-cli/compare/v1.10.1...v1.10.2) (2022-06-22)

### Bug Fixes

* keystore file not found exception ([#57](https://github.com/revanced/revanced-cli/issues/57)) ([5b8537e](5b8537e6b7))
2022-06-22 17:23:50 +00:00
Itroublve
5b8537e6b7 fix: keystore file not found exception (#57)
* fix: keystore file not found exception

* the fix

* fix oopsies
2022-06-22 19:22:19 +02:00
semantic-release-bot
7d8a61c3ba chore(release): 1.10.1 [skip ci]
## [1.10.1](https://github.com/revanced/revanced-cli/compare/v1.10.0...v1.10.1) (2022-06-22)

### Bug Fixes

* show actual version in CLI ([1dcdbc9](1dcdbc9fe9))
2022-06-22 14:52:53 +00:00
Lucaskyy
1dcdbc9fe9 fix: show actual version in CLI 2022-06-22 16:51:29 +02:00
Lucaskyy
4cc2f5269f Merge remote-tracking branch 'origin/main' into main 2022-06-22 16:44:17 +02:00
Lucaskyy
46056956fe refactor: fix consistency in logging 2022-06-22 16:44:07 +02:00
semantic-release-bot
8e3d147690 chore(release): 1.10.0 [skip ci]
# [1.10.0](https://github.com/revanced/revanced-cli/compare/v1.9.3...v1.10.0) (2022-06-22)

### Bug Fixes

* add callback for addFiles ([87ffaa4](87ffaa4bdb))

### Features

* add logging back ([4a23cb6](4a23cb69bc))
2022-06-22 14:43:38 +00:00
Lucaskyy
87ffaa4bdb fix: add callback for addFiles 2022-06-22 16:42:02 +02:00
Lucaskyy
8a49dcc110 Merge remote-tracking branch 'origin/main' into main
# Conflicts:
#	build.gradle.kts
2022-06-22 16:25:53 +02:00
Lucaskyy
4a23cb69bc feat: add logging back 2022-06-22 16:25:04 +02:00
Lucaskyy
8c325af0f9 build: update patcher version 2022-06-22 16:24:35 +02:00
semantic-release-bot
a26ab2a2c3 chore(release): 1.9.3 [skip ci]
## [1.9.3](https://github.com/revanced/revanced-cli/compare/v1.9.2...v1.9.3) (2022-06-22)

### Bug Fixes

* use absolute file path for key store ([d335846](d335846202))
2022-06-22 13:59:04 +00:00
oSumAtrIX
d335846202 fix: use absolute file path for key store 2022-06-22 15:57:15 +02:00
semantic-release-bot
6fc3ae67c8 chore(release): 1.9.2 [skip ci]
## [1.9.2](https://github.com/revanced/revanced-cli/compare/v1.9.1...v1.9.2) (2022-06-22)

### Bug Fixes

* update patcher version ([0df936e](0df936e99b))
2022-06-22 13:10:27 +00:00
Sculas
0df936e99b fix: update patcher version 2022-06-22 15:08:57 +02:00
semantic-release-bot
83743929c8 chore(release): 1.9.1 [skip ci]
## [1.9.1](https://github.com/revanced/revanced-cli/compare/v1.9.0...v1.9.1) (2022-06-22)

### Bug Fixes

* add back in: option to specify keystore file path ([c94471f](c94471f464))
* remove logger from Signer.kt ([51e091c](51e091ce40))

### Reverts

* "feat: use of `java.util.logging.Logger`" ([2c8a106](2c8a106151))
2022-06-22 13:03:27 +00:00
Lucaskyy
51e091ce40 fix: remove logger from Signer.kt 2022-06-22 15:01:48 +02:00
Lucaskyy
e5a37e0a5f refactor: move signing logs 2022-06-22 15:00:24 +02:00
Lucaskyy
c94471f464 fix: add back in: option to specify keystore file path 2022-06-22 14:58:12 +02:00
Lucaskyy
bfd50a43b9 build: update patcher version 2022-06-22 14:57:33 +02:00
Lucaskyy
2c8a106151 revert: "feat: use of java.util.logging.Logger"
This reverts commit 07f6bdf330.
This reverts commit 6c4c1924ee.
2022-06-22 14:56:25 +02:00
semantic-release-bot
ea7efd2afc chore(release): 1.9.0 [skip ci]
# [1.9.0](https://github.com/revanced/revanced-cli/compare/v1.8.0...v1.9.0) (2022-06-22)

### Features

* migrate logger to `slf4j` ([6c4c192](6c4c1924ee))
2022-06-22 12:20:50 +00:00
oSumAtrIX
6c4c1924ee feat: migrate logger to slf4j 2022-06-22 14:19:06 +02:00
semantic-release-bot
ce78b245d1 chore(release): 1.8.0 [skip ci]
# [1.8.0](https://github.com/revanced/revanced-cli/compare/v1.7.1...v1.8.0) (2022-06-22)

### Features

* add option to specify keystore file path ([9331594](9331594706))
* use of `java.util.logging.Logger` ([07f6bdf](07f6bdf330))
2022-06-22 11:52:35 +00:00
oSumAtrIX
d7cffea99c build: bump patcher dependency version 2022-06-22 13:50:55 +02:00
oSumAtrIX
9331594706 feat: add option to specify keystore file path 2022-06-22 13:50:54 +02:00
oSumAtrIX
07f6bdf330 feat: use of java.util.logging.Logger 2022-06-22 13:50:54 +02:00
oSumAtrIX
a48c0860e3 refactor: simply if condition 2022-06-22 13:50:54 +02:00
semantic-release-bot
4d5efab8bd chore(release): 1.7.1 [skip ci]
## [1.7.1](https://github.com/revanced/revanced-cli/compare/v1.7.0...v1.7.1) (2022-06-22)

### Bug Fixes

* migrate to changes of patcher ([b30c737](b30c7375a7))
* wrong variable inverted ([f694542](f694542d64))
2022-06-22 10:08:28 +00:00
oSumAtrIX
b30c7375a7 fix: migrate to changes of patcher 2022-06-22 12:06:53 +02:00
Sculas
f694542d64 fix: wrong variable inverted 2022-06-22 11:49:15 +02:00
oSumAtrIX
6f54af5963 build: bump patcher dependency version 2022-06-22 02:57:43 +02:00
semantic-release-bot
0a52180431 chore(release): 1.7.0 [skip ci]
# [1.7.0](https://github.com/revanced/revanced-cli/compare/v1.6.3...v1.7.0) (2022-06-21)

### Features

* show description when listing patches ([af32572](af32572f29))
2022-06-21 22:05:10 +00:00
bogadana
af32572f29 feat: show description when listing patches 2022-06-22 00:03:48 +02:00
semantic-release-bot
e126436f3d chore(release): 1.6.3 [skip ci]
## [1.6.3](https://github.com/revanced/revanced-cli/compare/v1.6.2...v1.6.3) (2022-06-21)

### Bug Fixes

* update patcher version ([80c11fe](80c11fef73))
2022-06-21 22:02:17 +00:00
Sculas
80c11fef73 fix: update patcher version 2022-06-22 00:00:51 +02:00
semantic-release-bot
debf0116fb chore(release): 1.6.2 [skip ci]
## [1.6.2](https://github.com/revanced/revanced-cli/compare/v1.6.1...v1.6.2) (2022-06-21)

### Bug Fixes

* CLI not working ([29105ba](29105bab3d))
* improper use of mount variable ([31853fe](31853fe539))
2022-06-21 20:21:41 +00:00
Lucaskyy
29105bab3d fix: CLI not working 2022-06-21 22:20:08 +02:00
Lucaskyy
31853fe539 fix: improper use of mount variable 2022-06-21 22:19:34 +02:00
Lucaskyy
21747d5552 build: update patcher version 2022-06-21 22:18:35 +02:00
Lucaskyy
ee6aff8fe7 chore: add comment 2022-06-21 19:47:03 +02:00
Lucaskyy
f3a3e935a2 refactor: prevent any future regressions in zipfs 2022-06-21 19:31:49 +02:00
Lucaskyy
c272d55e2d chore: cleanup code 2022-06-21 19:30:24 +02:00
semantic-release-bot
1781612789 chore(release): 1.6.1 [skip ci]
## [1.6.1](https://github.com/revanced/revanced-cli/compare/v1.6.0...v1.6.1) (2022-06-21)

### Bug Fixes

* remove `-e` from `experimental` option ([3829136](3829136c49))
2022-06-21 17:06:20 +00:00
oSumAtrIX
3829136c49 fix: remove -e from experimental option 2022-06-21 19:04:32 +02:00
oSumAtrIX
00145f2bb6 chore: merge nested if blocks 2022-06-21 19:00:11 +02:00
semantic-release-bot
7dabd53109 chore(release): 1.6.0 [skip ci]
# [1.6.0](https://github.com/revanced/revanced-cli/compare/v1.5.1...v1.6.0) (2022-06-21)

### Features

* rename `debugging` option to `experimental` ([98bd6f3](98bd6f3f4b))
* use `install` mode by default ([1a3db77](1a3db77c21))
2022-06-21 16:44:30 +00:00
oSumAtrIX
98bd6f3f4b feat: rename debugging option to experimental 2022-06-21 18:42:30 +02:00
oSumAtrIX
1a3db77c21 feat: use install mode by default 2022-06-21 18:42:29 +02:00
oSumAtrIX
430de23856 refactor: replace try catch block with null check 2022-06-21 18:42:29 +02:00
semantic-release-bot
c7d72c4d1c chore(release): 1.5.1 [skip ci]
## [1.5.1](https://github.com/revanced/revanced-cli/compare/v1.5.0...v1.5.1) (2022-06-21)

### Bug Fixes

* update patcher version ([09b9027](09b9027e5e)), closes [#45](https://github.com/revanced/revanced-cli/issues/45)
2022-06-21 16:42:24 +00:00
Sculas
09b9027e5e fix: update patcher version
Fixes #45
2022-06-21 18:40:54 +02:00
oSumAtrIX
3cc98efaa6 refactor: apply formatting 2022-06-21 01:02:50 +02:00
semantic-release-bot
76da6c1fa6 chore(release): 1.5.0 [skip ci]
# [1.5.0](https://github.com/revanced/revanced-cli/compare/v1.4.5...v1.5.0) (2022-06-20)

### Features

* allow listing patches without other parameters ([#42](https://github.com/revanced/revanced-cli/issues/42)) ([b977d70](b977d7039f))
2022-06-20 22:55:01 +00:00
bogadana
b977d7039f feat: allow listing patches without other parameters (#42)
* feat: allow listing patches without other parameters

* make `-b` required
2022-06-21 00:53:36 +02:00
oSumAtrIX
75c3776498 chore: bump patcher dependency version 2022-06-21 00:37:24 +02:00
semantic-release-bot
6b9751448c chore(release): 1.4.5 [skip ci]
## [1.4.5](https://github.com/revanced/revanced-cli/compare/v1.4.4...v1.4.5) (2022-06-20)

### Bug Fixes

* update patcher version (fix apktool) ([496f821](496f821218))
2022-06-20 15:11:34 +00:00
Sculas
496f821218 fix: update patcher version (fix apktool) 2022-06-20 17:09:51 +02:00
bogadana
755f2b5afc update patcher version (#38) 2022-06-19 03:50:03 +02:00
semantic-release-bot
d7353cd27a chore(release): 1.4.4 [skip ci]
## [1.4.4](https://github.com/revanced/revanced-cli/compare/v1.4.3...v1.4.4) (2022-06-18)

### Bug Fixes

* add execute permission to `./gradlew` file ([#36](https://github.com/revanced/revanced-cli/issues/36)) ([072d9e1](072d9e15d7))
2022-06-18 21:51:40 +00:00
Oskar
072d9e15d7 fix: add execute permission to ./gradlew file (#36) 2022-06-18 23:50:19 +02:00
semantic-release-bot
b768c06d90 chore(release): 1.4.3 [skip ci]
## [1.4.3](https://github.com/revanced/revanced-cli/compare/v1.4.2...v1.4.3) (2022-06-18)

### Bug Fixes

* update patcher to 1.2.5 ([055c282](055c282dd3))
2022-06-18 19:42:36 +00:00
Sculas
055c282dd3 fix: update patcher to 1.2.5 2022-06-18 21:40:47 +02:00
Sculas
d1400548ef ci: clean old build 2022-06-16 12:15:18 +02:00
semantic-release-bot
b25236c072 chore(release): 1.4.2 [skip ci]
## [1.4.2](https://github.com/revanced/revanced-cli/compare/v1.4.1...v1.4.2) (2022-06-16)

### Bug Fixes

* dummy publish task (1/2) [skip ci] ([afff4c8](afff4c8418))
* releases (2/2) ([227d8d9](227d8d94c8))
2022-06-16 10:11:56 +00:00
Sculas
227d8d94c8 fix: releases (2/2) 2022-06-16 12:09:54 +02:00
Sculas
afff4c8418 fix: dummy publish task (1/2) [skip ci] 2022-06-16 12:09:12 +02:00
semantic-release-bot
c9716be205 chore(release): 1.4.1 [skip ci]
## [1.4.1](https://github.com/revanced/revanced-cli/compare/v1.4.0...v1.4.1) (2022-06-14)

### Bug Fixes

* move the keystore to the output directory ([6ceb449](6ceb449cf8))
2022-06-14 22:01:33 +00:00
oSumAtrIX
6ceb449cf8 fix: move the keystore to the output directory 2022-06-14 23:59:59 +02:00
semantic-release-bot
6c6abafe95 chore(release): 1.4.0 [skip ci]
# [1.4.0](https://github.com/revanced/revanced-cli/compare/v1.3.3...v1.4.0) (2022-06-14)

### Features

* chcon on mount ([e1c7d10](e1c7d1082a))
2022-06-14 13:31:37 +00:00
Kinsteen
e1c7d1082a feat: chcon on mount
Co-authored-by: PaulF <paul.francon@pi.esisar.grenoble-inp.fr>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-06-14 15:30:14 +02:00
semantic-release-bot
7444e4e67d chore(release): 1.3.3 [skip ci]
## [1.3.3](https://github.com/revanced/revanced-cli/compare/v1.3.2...v1.3.3) (2022-06-13)

### Bug Fixes

* missing implementation ([48102c6](48102c6607))
2022-06-13 23:24:05 +00:00
Sculas
48102c6607 fix: missing implementation 2022-06-14 01:22:48 +02:00
Sculas
70258e251c chore: use Kotlin plugin for dependencies 2022-06-14 01:21:29 +02:00
semantic-release-bot
836892df7b chore(release): 1.3.2 [skip ci]
## [1.3.2](https://github.com/revanced/revanced-cli/compare/v1.3.1...v1.3.2) (2022-06-13)

### Bug Fixes

* only upload `-all.jar` asset ([ca8e1ba](ca8e1ba6af))
2022-06-13 23:19:28 +00:00
oSumAtrIX
ca8e1ba6af fix: only upload -all.jar asset 2022-06-14 01:18:15 +02:00
semantic-release-bot
00e9e53df6 chore(release): 1.3.1 [skip ci]
## [1.3.1](https://github.com/revanced/revanced-cli/compare/v1.3.0...v1.3.1) (2022-06-13)

### Bug Fixes

* check if `packageVersion` is compatible with any from `compatiblePackages` ([32589c8](32589c88e4))
2022-06-13 23:14:04 +00:00
oSumAtrIX
cdc4e9c8ac chore: publish releases instead of packages 2022-06-14 01:11:54 +02:00
oSumAtrIX
32589c88e4 fix: check if packageVersion is compatible with any from compatiblePackages 2022-06-13 01:59:39 +02:00
semantic-release-bot
3878532688 chore(release): 1.3.0 [skip ci]
# [1.3.0](https://github.com/revanced/revanced-cli/compare/v1.2.0...v1.3.0) (2022-06-11)

### Bug Fixes

* `Main-Class` attribute pointing to wrong method ([6e82418](6e82418958))
* `ZipAligner` not correctly calculating the file offset ([2975a47](2975a47d0f))
* broken control flow of `includeFilter` ([a0644c7](a0644c7045))
* check for root even though when not needed ([0d7581a](0d7581ad75))
* overwrite output file ([2bfbbc2](2bfbbc2eb9))
* resource patcher ([9da4f70](9da4f707ac))
* sign the aligned file instead of the input file ([22d2535](22d2535af8))

### Features

* support for `--install` ([d1ceab4](d1ceab45c8))
2022-06-11 23:41:37 +00:00
oSumAtrIX
ee4a83bef9 Merge pull request #24 from revanced/non-root
feat: support `non-root` installation
2022-06-12 01:39:45 +02:00
oSumAtrIX
775e2fd906 chore: bump patcher dependency version 2022-06-12 01:38:58 +02:00
oSumAtrIX
2975a47d0f fix: ZipAligner not correctly calculating the file offset 2022-06-12 00:52:14 +02:00
oSumAtrIX
0d7581ad75 fix: check for root even though when not needed 2022-06-11 22:03:55 +02:00
oSumAtrIX
d55ca86e5c refactor: add more logging 2022-06-11 20:29:54 +02:00
oSumAtrIX
2bfbbc2eb9 fix: overwrite output file 2022-06-11 20:29:18 +02:00
oSumAtrIX
d1ceab45c8 feat: support for --install 2022-06-11 20:02:37 +02:00
oSumAtrIX
cf1d512f4b refactor: write cache files with proper names 2022-06-11 20:02:03 +02:00
oSumAtrIX
22d2535af8 fix: sign the aligned file instead of the input file 2022-06-11 19:56:45 +02:00
oSumAtrIX
a0644c7045 fix: broken control flow of includeFilter 2022-06-11 19:44:33 +02:00
oSumAtrIX
ef01bb2016 chore: update to fork for jadb dependency 2022-06-11 19:15:40 +02:00
oSumAtrIX
6e82418958 fix: Main-Class attribute pointing to wrong method 2022-06-11 07:45:58 +02:00
oSumAtrIX
9da4f707ac fix: resource patcher 2022-06-11 06:40:59 +02:00
oSumAtrIX
45171dd4b0 chore: bump patcher dependency 2022-06-05 08:14:59 +02:00
semantic-release-bot
bd3773798d chore(release): 1.2.0 [skip ci]
# [1.2.0](https://github.com/revanced/revanced-cli/compare/v1.1.5...v1.2.0) (2022-06-05)

### Bug Fixes

* migrate to latest patcher api changes ([ace70e4](ace70e417f))

### Features

* add path for `cacheDirectory` and enable resource patching by default ([54c0a03](54c0a03d44))
* debugging option ([1b645c6](1b645c67db))
2022-06-05 06:02:17 +00:00
oSumAtrIX
8665661ed7 Merge branch 'dev'
# Conflicts:
#	build.gradle.kts
2022-06-05 08:00:00 +02:00
semantic-release-bot
1c5e5e249d chore(release): 1.2.0-dev.2 [skip ci]
# [1.2.0-dev.2](https://github.com/revanced/revanced-cli/compare/v1.2.0-dev.1...v1.2.0-dev.2) (2022-06-05)

### Features

* debugging option ([1b645c6](1b645c67db))
2022-06-05 05:58:42 +00:00
oSumAtrIX
1b645c67db feat: debugging option 2022-06-05 07:56:53 +02:00
oSumAtrIX
4758289d68 chore: bump patcher dependency 2022-06-04 02:34:03 +02:00
semantic-release-bot
87c0e05d9c chore(release): 1.2.0-dev.1 [skip ci]
# [1.2.0-dev.1](https://github.com/revanced/revanced-cli/compare/v1.1.6-dev.1...v1.2.0-dev.1) (2022-06-04)

### Features

* add path for `cacheDirectory` and enable resource patching by default ([54c0a03](54c0a03d44))
2022-06-04 00:32:25 +00:00
oSumAtrIX
54c0a03d44 feat: add path for cacheDirectory and enable resource patching by default 2022-06-03 17:51:13 +02:00
semantic-release-bot
bd185133bc chore(release): 1.1.6-dev.1 [skip ci]
## [1.1.6-dev.1](https://github.com/revanced/revanced-cli/compare/v1.1.5...v1.1.6-dev.1) (2022-05-31)

### Bug Fixes

* migrate to latest patcher api changes ([ace70e4](ace70e417f))
2022-05-31 23:55:55 +00:00
oSumAtrIX
ace70e417f fix: migrate to latest patcher api changes 2022-06-01 01:41:24 +02:00
tillnelown
d6f2609cfa Removes the uneeded revanced-patches dependency 2022-05-31 11:48:45 +02:00
oSumAtrIX
735dbc9149 Merge remote-tracking branch 'origin/dev' into dev 2022-05-29 02:14:44 +02:00
tillnelown
e756ac9920 Removes the uneeded revanced-patches dependency 2022-05-28 18:22:51 +02:00
oSumAtrIX
a4ef47a285 Merge pull request #19 from revanced/dev
chore: bump patcher version
2022-05-27 17:46:15 +02:00
oSumAtrIX
c69170535e chore: bump patcher version 2022-05-27 14:33:22 +02:00
semantic-release-bot
a1bcd1fdaf chore(release): 1.1.5 [skip ci]
## [1.1.5](https://github.com/revanced/revanced-cli/compare/v1.1.4...v1.1.5) (2022-05-27)

### Bug Fixes

* invalid code flow when adding patches ([206f202](206f2029d7))
2022-05-27 10:27:42 +00:00
oSumAtrIX
206f2029d7 fix: invalid code flow when adding patches 2022-05-27 12:25:44 +02:00
semantic-release-bot
04e0027c08 chore(release): 1.1.4 [skip ci]
## [1.1.4](https://github.com/revanced/revanced-cli/compare/v1.1.3...v1.1.4) (2022-05-26)

### Bug Fixes

* migrate from `PatchLoader.load(...)` to `JarPatchBundle(...).loadPatches()` ([cabd32f](cabd32fda4))
2022-05-26 02:26:10 +00:00
oSumAtrIX
e76983e01c chore: update gradlew wrapper 2022-05-26 04:08:29 +02:00
oSumAtrIX
11d67bc1ea chore: update dependencies 2022-05-26 04:08:15 +02:00
oSumAtrIX
cabd32fda4 fix: migrate from PatchLoader.load(...) to JarPatchBundle(...).loadPatches()
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-05-26 01:02:42 +02:00
33 changed files with 1500 additions and 587 deletions

24
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,24 @@
---
name: Bug report
about: Create a bug report on the CLI. Do not submit suggestions for patches here.
title: 'problem: some problem'
labels: bug
assignees: ''
---
## 🐞 Issue
<!-- Describe your issue in detail here -->
## ⚙ Reproduce
<!-- Include your environment and steps to reproduce the issue as detailed as possible -->
## 🛠 Solution
<!-- If applicable, add a possible solution -->
## ⚠ Additional context
<!-- Add any other context about the problem here -->

View File

@@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest a change to the CLI. Do not submit suggestions for patches here.
title: 'feat: some feature'
labels: feature-request
assignees: ''
---
## 🐞 Issue
<!-- Describe your issue in detail here -->
## ⚙ Reproduce
<!-- Include your environment and steps to reproduce the issue as detailed as possible -->
## 🛠 Solution
<!-- If applicable, add a possible solution -->
## ⚠ Additional context
<!-- Add any other context about the problem here -->

View File

@@ -1,5 +1,6 @@
name: Release
on:
workflow_dispatch:
push:
branches:
- main
@@ -27,12 +28,10 @@ jobs:
uses: actions/setup-node@v2
with:
node-version: "lts/*"
- name: Make gradlew executable
run: chmod +x gradlew
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew build
run: ./gradlew build clean
- name: Setup semantic-release
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
- name: Release

View File

@@ -7,7 +7,11 @@
}
],
"plugins": [
"@semantic-release/commit-analyzer",
["@semantic-release/commit-analyzer", {
"releaseRules": [
{"type": "build", "release": "patch"}
]
}],
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"gradle-semantic-release-plugin",
@@ -20,6 +24,15 @@
]
}
],
"@semantic-release/github"
[
"@semantic-release/github",
{
"assets": [
{
"path": "build/libs/*all.jar"
}
]
}
]
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +1,36 @@
plugins {
kotlin("jvm") version "1.6.21"
kotlin("jvm") version "1.7.0"
id("com.github.johnrengelman.shadow") version "7.1.2"
java
`maven-publish`
}
group = "app.revanced"
val githubUsername: String = project.findProperty("gpr.user") as? String ?: System.getenv("GITHUB_ACTOR")
val githubPassword: String = project.findProperty("gpr.key") as? String ?: System.getenv("GITHUB_TOKEN")
repositories {
mavenCentral()
mavenLocal()
maven {
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") // DO NOT CHANGE!
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") // DO NOT CHANGE!
username = githubUsername
password = githubPassword
}
}
maven {
url = uri("https://jitpack.io")
}
maven { url = uri("https://jitpack.io") }
google()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21")
implementation("app.revanced:revanced-patcher:+")
implementation("app.revanced:revanced-patches:+")
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))
implementation("app.revanced:revanced-patcher:2.4.0")
implementation("info.picocli:picocli:4.6.3")
implementation("com.github.li-wjohnson:jadb:master-SNAPSHOT") // using a fork instead.
implementation("com.android.tools.build:apksig:7.2.1")
implementation("com.github.revanced:jadb:master-SNAPSHOT") // updated fork
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.21")
}
java {
withSourcesJar()
withJavadocJar()
}
tasks {
@@ -45,27 +39,17 @@ tasks {
}
shadowJar {
manifest {
attributes("Main-Class" to "app.revanced.cli.MainKt")
attributes("Main-Class" to "app.revanced.cli.main.MainKt")
attributes("Implementation-Title" to project.name)
attributes("Implementation-Version" to project.version)
}
}
// Dummy task to fix the Gradle semantic-release plugin.
// Remove this if you forked it to support building only.
// Tracking issue: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
register<DefaultTask>("publish") {
group = "publish"
description = "Dummy task"
dependsOn(build)
}
}
publishing {
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/revanced/revanced-cli")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
publications {
register<MavenPublication>("gpr") {
from(components["java"])
}
}
}

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official
version = 1.1.3
version = 2.6.0

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

0
gradlew vendored Normal file → Executable file
View File

View File

@@ -1,96 +0,0 @@
package app.revanced.cli
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.extensions.findAnnotationRecursively
import app.revanced.patcher.util.patch.PatchLoader
import app.revanced.utils.adb.Adb
import app.revanced.utils.patcher.addPatchesFiltered
import app.revanced.utils.signature.Signature
import picocli.CommandLine.*
import java.io.File
@Command(
name = "ReVanced-CLI", version = ["1.0.0"], mixinStandardHelpOptions = true
)
internal object MainCommand : Runnable {
@Parameters(
paramLabel = "INCLUDE",
description = ["Which patches to include. If none is specified, all compatible patches will be included"]
)
internal var includedPatches = arrayOf<String>()
@Option(names = ["-p", "--patches"], description = ["One or more bundles of patches"])
internal var patchBundles = arrayOf<File>()
@Option(names = ["-t", "--temp-dir"], description = ["Temporal resource cache directory"], required = true)
internal lateinit var cacheDirectory: String
@Option(names = ["-r", "--resource-patcher"], description = ["Enable patching resources"])
internal var patchResources: Boolean = false
@Option(
names = ["-c", "--clean"],
description = ["Clean the temporal resource cache directory. This will be done anyways when running the patcher"]
)
internal var clean: Boolean = false
@Option(names = ["-l", "--list"], description = ["List patches only"])
internal var listOnly: Boolean = false
@Option(names = ["-s", "--signature-checker"], description = ["Check signatures of all patches"])
internal var signatureCheck: Boolean = false
@Option(names = ["-m", "--merge"], description = ["One or more dex file containers to merge"])
internal var mergeFiles = listOf<File>()
@Option(names = ["-a", "--apk"], description = ["Input file to be patched"], required = true)
internal lateinit var inputFile: File
@Option(names = ["-o", "--out"], description = ["Output file path"], required = true)
internal lateinit var outputPath: String
@Option(names = ["-d", "--deploy-on"], description = ["If specified, deploy to adb device with given name"])
internal var deploy: String? = null
override fun run() {
if (listOnly) {
for (patchBundle in patchBundles)
for (it in PatchLoader.loadFromFile(patchBundle))
println(
"[available] ${
it.javaClass.findAnnotationRecursively(
Name::class.java
)?.name ?: Name::class.java.name
}"
)
return
}
val patcher = app.revanced.patcher.Patcher(
inputFile, cacheDirectory, patchResources
)
if (signatureCheck) {
patcher.addPatchesFiltered()
Signature.checkSignatures(patcher)
return
}
val outputFile = File(outputPath)
var adb: Adb? = null
deploy?.let {
adb = Adb(
outputFile, patcher.packageName, deploy!!
)
}
Patcher.start(patcher)
if (clean) File(cacheDirectory).deleteRecursively()
adb?.deploy()
if (clean) outputFile.delete()
}
}

View File

@@ -1,54 +0,0 @@
package app.revanced.cli
import app.revanced.utils.filesystem.FileSystemUtils
import app.revanced.utils.patcher.addPatchesFiltered
import app.revanced.utils.patcher.applyPatchesPrint
import app.revanced.utils.patcher.mergeFiles
import app.revanced.utils.signing.Signer
import java.io.File
import java.io.FileFilter
internal class Patcher {
internal companion object {
internal fun start(patcher: app.revanced.patcher.Patcher) {
// merge files like necessary integrations
patcher.mergeFiles()
// add patches, but filter incompatible or excluded patches
patcher.addPatchesFiltered(includeFilter = MainCommand.includedPatches.isNotEmpty())
// apply patches
patcher.applyPatchesPrint()
// write output file
val outFile = File(MainCommand.outputPath)
if (outFile.exists()) outFile.delete()
MainCommand.inputFile.copyTo(outFile)
val zipFileSystem = FileSystemUtils(outFile)
// replace all dex files
for ((name, data) in patcher.save()) {
zipFileSystem.replaceFile(name, data.data)
}
if (MainCommand.patchResources) {
for (file in File(MainCommand.cacheDirectory).resolve("build/").listFiles(FileFilter { it.isDirectory })?.first()?.listFiles()!!) {
if (!file.isDirectory) {
zipFileSystem.replaceFile(file.name, file.readBytes())
continue
}
zipFileSystem.replaceDirectory(file)
}
}
// finally close the stream
zipFileSystem.close()
// and sign the apk file
Signer.signApk(outFile)
println("[done]")
}
}
}

View File

@@ -0,0 +1,219 @@
package app.revanced.cli.command
import app.revanced.cli.logging.impl.DefaultCliLogger
import app.revanced.cli.patcher.Patcher
import app.revanced.cli.patcher.logging.impl.PatcherLogger
import app.revanced.cli.signing.Signing
import app.revanced.cli.signing.SigningOptions
import app.revanced.patcher.PatcherOptions
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import app.revanced.patcher.extensions.PatchExtensions.description
import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.util.patch.implementation.JarPatchBundle
import app.revanced.utils.adb.Adb
import picocli.CommandLine.*
import java.io.File
import java.nio.file.Files
private class CLIVersionProvider : IVersionProvider {
override fun getVersion() = arrayOf(
MainCommand::class.java.`package`.implementationVersion ?: "unknown"
)
}
@Command(
name = "ReVanced-CLI",
mixinStandardHelpOptions = true,
versionProvider = CLIVersionProvider::class
)
internal object MainCommand : Runnable {
val logger = DefaultCliLogger()
@ArgGroup(exclusive = false, multiplicity = "1")
lateinit var args: Args
class Args {
@Option(names = ["-a", "--apk"], description = ["Input file to be patched"], required = true)
lateinit var inputFile: File
@Option(names = ["--uninstall"], description = ["Uninstall the mount variant"])
var uninstall: Boolean = false
@Option(names = ["-d", "--deploy-on"], description = ["If specified, deploy to adb device with given name"])
var deploy: String? = null
@ArgGroup(exclusive = false)
var sArgs: StartPatcherArgs? = null
}
class StartPatcherArgs {
@Option(names = ["-b", "--bundles"], description = ["One or more bundles of patches"], required = true)
var patchBundles = arrayOf<String>()
@ArgGroup(exclusive = false)
var lArgs: ListingArgs? = null
@ArgGroup(exclusive = false)
var pArgs: PatchingArgs? = null
}
class ListingArgs {
@Option(names = ["-l", "--list"], description = ["List patches only"], required = true)
var listOnly: Boolean = false
@Option(names = ["--with-versions"], description = ["List patches with compatible versions"])
var withVersions: Boolean = false
@Option(names = ["--with-packages"], description = ["List patches with compatible packages"])
var withPackages: Boolean = false
@Option(names = ["--with-descriptions"], description = ["List patches with their descriptions"])
var withDescriptions: Boolean = true
}
class PatchingArgs {
@Option(names = ["-o", "--out"], description = ["Output file path"], required = true)
lateinit var outputPath: String
@Option(names = ["-e", "--exclude"], description = ["Explicitly exclude patches"])
var excludedPatches = arrayOf<String>()
@Option(names = ["--exclusive"], description = ["Only installs the patches you include, not including any patch by default"])
var defaultExclude = false
@Option(names = ["-i", "--include"], description = ["Include patches"])
var includedPatches = arrayOf<String>()
@Option(names = ["-r", "--resource-patcher"], description = ["Disable patching resources"])
var disableResourcePatching: Boolean = false
@Option(names = ["--experimental"], description = ["Disable patch version compatibility patch"])
var experimental: Boolean = false
@Option(names = ["-m", "--merge"], description = ["One or more dex file containers to merge"])
var mergeFiles = listOf<File>()
@Option(names = ["--mount"], description = ["If specified, instead of installing, mount"])
var mount: Boolean = false
@Option(names = ["--cn"], description = ["Overwrite the default CN for the signed file"])
var cn = "ReVanced"
@Option(names = ["--keystore"], description = ["File path to your keystore"])
var keystorePath: String? = null
@Option(names = ["-p", "--password"], description = ["Overwrite the default password for the signed file"])
var password = "ReVanced"
@Option(names = ["-t", "--temp-dir"], description = ["Temporal resource cache directory"])
var cacheDirectory = "revanced-cache"
@Option(
names = ["-c", "--clean"],
description = ["Clean the temporal resource cache directory. This will be done anyways when running the patcher"]
)
var clean: Boolean = false
}
override fun run() {
if (args.sArgs?.lArgs?.listOnly == true) {
printListOfPatches()
return
}
if (args.uninstall) {
// temporarily get package name using Patcher method
// fix: abstract options in patcher
val patcher = app.revanced.patcher.Patcher(
PatcherOptions(
args.inputFile,
"uninstaller-cache",
false
)
)
File("uninstaller-cache").deleteRecursively()
val adb: Adb? = args.deploy?.let {
Adb(File("placeholder_file"), patcher.data.packageMetadata.packageName, args.deploy!!, false)
}
adb?.uninstall()
return
}
val _args = args
val args = args.sArgs?.pArgs ?: return
val patcher = app.revanced.patcher.Patcher(
PatcherOptions(
_args.inputFile,
args.cacheDirectory,
!args.disableResourcePatching,
logger = PatcherLogger
)
)
val outputFile = File(args.outputPath)
val adb: Adb? = _args.deploy?.let {
Adb(outputFile, patcher.data.packageMetadata.packageName, _args.deploy!!, !args.mount)
}
val patchedFile = if (args.mount) outputFile
else File(args.cacheDirectory).resolve("${outputFile.nameWithoutExtension}_raw.apk")
Patcher.start(patcher, patchedFile)
if (!args.mount) {
Signing.start(
patchedFile,
outputFile,
SigningOptions(
args.cn,
args.password,
args.keystorePath ?: outputFile.absoluteFile.parentFile
.resolve("${outputFile.nameWithoutExtension}.keystore")
.canonicalPath
)
)
}
if (args.clean) File(args.cacheDirectory).deleteRecursively()
adb?.deploy()
if (args.clean && _args.deploy != null) Files.delete(outputFile.toPath())
logger.info("Finished")
}
private fun printListOfPatches() {
for (patchBundlePath in args.sArgs?.patchBundles!!) for (patch in JarPatchBundle(patchBundlePath).loadPatches()) {
for (compatiblePackage in patch.compatiblePackages!!) {
val packageEntryStr = buildString {
// Add package if flag is set
if (args.sArgs?.lArgs?.withPackages == true) {
val packageName = compatiblePackage.name.substringAfterLast(".").padStart(10)
append(packageName)
append("\t")
}
// Add patch name
val patchName = patch.patchName.padStart(25)
append(patchName)
// Add description if flag is set.
if (args.sArgs?.lArgs?.withDescriptions == true) {
append("\t")
append(patch.description)
}
// Add compatible versions, if flag is set
if (args.sArgs?.lArgs?.withVersions == true) {
val compatibleVersions = compatiblePackage.versions.joinToString(separator = ", ")
append("\t")
append(compatibleVersions)
}
}
logger.info(packageEntryStr)
}
}
}
}

View File

@@ -0,0 +1,8 @@
package app.revanced.cli.logging
internal interface CliLogger {
fun error(msg: String)
fun info(msg: String)
fun trace(msg: String)
fun warn(msg: String)
}

View File

@@ -0,0 +1,30 @@
package app.revanced.cli.logging.impl
import app.revanced.cli.command.MainCommand
import app.revanced.cli.logging.CliLogger
import java.util.logging.Logger
import java.util.logging.SimpleFormatter
internal class DefaultCliLogger(
private val logger: Logger = Logger.getLogger(MainCommand::class.java.name),
private val errorLogger: Logger = Logger.getLogger(logger.name + "Err")
) : CliLogger {
init {
logger.useParentHandlers = false
if (logger.handlers.isEmpty()) {
logger.addHandler(FlushingStreamHandler(System.out, SimpleFormatter()))
}
}
companion object {
init {
System.setProperty("java.util.logging.SimpleFormatter.format", "%4\$s: %5\$s %n")
}
}
override fun error(msg: String) = errorLogger.severe(msg)
override fun info(msg: String) = logger.info(msg)
override fun trace(msg: String) = logger.finest(msg)
override fun warn(msg: String) = errorLogger.warning(msg)
}

View File

@@ -0,0 +1,13 @@
package app.revanced.cli.logging.impl
import java.io.OutputStream
import java.util.logging.Formatter
import java.util.logging.LogRecord
import java.util.logging.StreamHandler
internal class FlushingStreamHandler(out: OutputStream, format: Formatter) : StreamHandler(out, format) {
override fun publish(record: LogRecord) {
super.publish(record)
flush()
}
}

View File

@@ -1,5 +1,6 @@
package app.revanced.cli
package app.revanced.cli.main
import app.revanced.cli.command.MainCommand
import picocli.CommandLine
internal fun main(args: Array<String>) {

View File

@@ -0,0 +1,48 @@
package app.revanced.cli.patcher
import app.revanced.cli.command.MainCommand.args
import app.revanced.cli.command.MainCommand.logger
import app.revanced.utils.filesystem.ZipFileSystemUtils
import app.revanced.utils.patcher.addPatchesFiltered
import app.revanced.utils.patcher.applyPatchesVerbose
import app.revanced.utils.patcher.mergeFiles
import java.io.File
import java.nio.file.Files
internal object Patcher {
internal fun start(patcher: app.revanced.patcher.Patcher, output: File) {
val inputFile = args.inputFile
val args = args.sArgs?.pArgs!!
// merge files like necessary integrations
patcher.mergeFiles()
// add patches, but filter incompatible or excluded patches
patcher.addPatchesFiltered()
// apply patches
patcher.applyPatchesVerbose()
// write output file
if (output.exists()) Files.delete(output.toPath())
inputFile.copyTo(output)
val result = patcher.save()
ZipFileSystemUtils(output).use { outputFileSystem ->
// replace all dex files
result.dexFiles.forEach {
logger.info("Writing dex file ${it.name}")
outputFileSystem.write(it.name, it.dexFileInputStream.readAllBytes())
}
if (!args.disableResourcePatching) {
logger.info("Writing resources...")
ZipFileSystemUtils(result.resourceFile!!).use { resourceFileSystem ->
val resourceFiles = resourceFileSystem.getFile(File.separator)
outputFileSystem.writePathRecursively(resourceFiles)
}
}
result.doNotCompress?.let { outputFileSystem.uncompress(*it.toTypedArray()) }
}
}
}

View File

@@ -0,0 +1,13 @@
package app.revanced.cli.patcher.logging.impl
import app.revanced.cli.logging.impl.DefaultCliLogger
import java.util.logging.Logger
internal object PatcherLogger : app.revanced.patcher.logging.Logger{
private val logger = DefaultCliLogger(Logger.getLogger(app.revanced.patcher.Patcher::class.java.name))
override fun error(msg: String) = logger.error(msg)
override fun info(msg: String) = logger.info(msg)
override fun warn(msg: String)= logger.warn(msg)
override fun trace(msg: String)= logger.trace(msg)
}

View File

@@ -0,0 +1,28 @@
package app.revanced.cli.signing
import app.revanced.cli.command.MainCommand.args
import app.revanced.cli.command.MainCommand.logger
import app.revanced.utils.signing.Signer
import app.revanced.utils.signing.align.ZipAligner
import java.io.File
object Signing {
fun start(inputFile: File, outputFile: File, signingOptions: SigningOptions) {
val cacheDirectory = File(args.sArgs?.pArgs?.cacheDirectory)
val alignedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk")
val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk")
// align the inputFile and write to alignedOutput
logger.info("Aligning ${inputFile.name}")
ZipAligner.align(inputFile, alignedOutput)
// sign the alignedOutput and write to signedOutput
// the reason is, in case the signer fails
// it does not damage the output file
logger.info("Signing ${alignedOutput.name}")
Signer(signingOptions).signApk(alignedOutput, signedOutput)
// afterwards copy over the file to the output
logger.info("Copying ${signedOutput.name} to ${outputFile.name}")
signedOutput.copyTo(outputFile, true)
}
}

View File

@@ -0,0 +1,7 @@
package app.revanced.cli.signing
data class SigningOptions(
val cn: String,
val password: String,
val keyStoreFilePath: String
)

View File

@@ -1,14 +1,17 @@
package app.revanced.utils.adb
import app.revanced.cli.command.MainCommand.logger
import se.vidstige.jadb.JadbConnection
import se.vidstige.jadb.JadbDevice
import se.vidstige.jadb.managers.PackageManager
import java.io.File
import java.util.concurrent.Executors
internal class Adb(
private val apk: File,
private val file: File,
private val packageName: String,
deviceName: String,
private val modeInstall: Boolean = false,
private val logging: Boolean = true
) {
private val device: JadbDevice
@@ -17,49 +20,65 @@ internal class Adb(
device = JadbConnection().devices.find { it.serial == deviceName }
?: throw IllegalArgumentException("No such device with name $deviceName")
if (device.run("su -h", false) != 0)
throw IllegalArgumentException("Root required on $deviceName. Deploying failed.")
if (!modeInstall && device.run("su -h", false) != 0)
throw IllegalArgumentException("Root required on $deviceName. Task failed")
}
private fun String.replacePlaceholder(): String {
return this.replace(Constants.PLACEHOLDER, packageName)
private fun String.replacePlaceholder(with: String? = null): String {
return this.replace(Constants.PLACEHOLDER, with ?: packageName)
}
internal fun deploy() {
// create revanced path
device.run("${Constants.COMMAND_CREATE_DIR} ${Constants.PATH_REVANCED}")
if (modeInstall) {
logger.info("Installing without mounting")
// push patched file
device.copy(Constants.PATH_INIT_PUSH, apk)
// install apk
device.run(Constants.COMMAND_INSTALL_APK.replacePlaceholder())
PackageManager(device).install(file)
} else {
logger.info("Installing by mounting")
// push mount script
device.createFile(
Constants.PATH_INIT_PUSH,
Constants.CONTENT_MOUNT_SCRIPT.replacePlaceholder()
)
// install mount script
device.run(Constants.COMMAND_INSTALL_MOUNT.replacePlaceholder())
// push patched file
device.copy(Constants.PATH_INIT_PUSH, file)
// push umount script
device.createFile(
Constants.PATH_INIT_PUSH,
Constants.CONTENT_UMOUNT_SCRIPT.replacePlaceholder()
)
// install mount script
device.run(Constants.COMMAND_INSTALL_UMOUNT.replacePlaceholder())
// create revanced folder path
device.run("${Constants.COMMAND_CREATE_DIR} ${Constants.PATH_REVANCED}")
// unmount the apk for sanity
device.run(Constants.PATH_UMOUNT.replacePlaceholder())
// mount the apk
device.run(Constants.PATH_MOUNT.replacePlaceholder())
// prepare mounting the apk
device.run(Constants.COMMAND_PREPARE_MOUNT_APK.replacePlaceholder())
// relaunch app
device.run(Constants.COMMAND_RESTART.replacePlaceholder())
// push mount script
device.createFile(
Constants.PATH_INIT_PUSH,
Constants.CONTENT_MOUNT_SCRIPT.replacePlaceholder()
)
// install mount script
device.run(Constants.COMMAND_INSTALL_MOUNT.replacePlaceholder())
// log the app
log()
// unmount the apk for sanity
device.run(Constants.COMMAND_UMOUNT.replacePlaceholder())
// mount the apk
device.run(Constants.PATH_MOUNT.replacePlaceholder())
// relaunch app
device.run(Constants.COMMAND_RESTART.replacePlaceholder())
// log the app
log()
}
}
internal fun uninstall() {
logger.info("Uninstalling by unmounting")
// unmount the apk
device.run(Constants.COMMAND_UMOUNT.replacePlaceholder())
// delete revanced app
device.run(Constants.COMMAND_DELETE.replacePlaceholder(Constants.PATH_REVANCED_APP).replacePlaceholder())
// delete mount script
device.run(Constants.COMMAND_DELETE.replacePlaceholder(Constants.PATH_MOUNT).replacePlaceholder())
logger.info("Finished uninstalling")
}
private fun log() {
@@ -84,11 +103,11 @@ internal class Adb(
}
break
} catch (e: Exception) {
throw RuntimeException("An error occurred while monitoring state of app", e)
throw RuntimeException("An error occurred while monitoring the state of app", e)
}
}
println("App closed, continuing.")
logger.info("Stopped logging because the app was closed")
process.destroy()
executor.shutdown()
}
}
}

View File

@@ -15,37 +15,31 @@ internal object Constants {
private const val NAME_MOUNT_SCRIPT = "mount_revanced_$PLACEHOLDER.sh"
// initial directory to push files to via adb push
internal const val PATH_INIT_PUSH = "/sdcard/revanced.delete"
internal const val PATH_INIT_PUSH = "/data/local/tmp/revanced.delete"
// revanced path
internal const val PATH_REVANCED = "/data/adb/revanced/"
// revanced apk path
private const val PATH_REVANCED_APP = "$PATH_REVANCED$PLACEHOLDER.apk"
internal const val PATH_REVANCED_APP = "$PATH_REVANCED$PLACEHOLDER.apk"
// (un)mount script paths
// delete command
internal const val COMMAND_DELETE = "rm -rf $PLACEHOLDER"
// mount script path
internal const val PATH_MOUNT = "/data/adb/service.d/$NAME_MOUNT_SCRIPT"
internal const val PATH_UMOUNT = "/data/adb/post-fs-data.d/un$NAME_MOUNT_SCRIPT"
// move to revanced apk path & set permissions
internal const val COMMAND_INSTALL_APK =
internal const val COMMAND_PREPARE_MOUNT_APK =
"base_path=\"$PATH_REVANCED_APP\" && mv $PATH_INIT_PUSH ${'$'}base_path && chmod 644 ${'$'}base_path && chown system:system ${'$'}base_path && chcon u:object_r:apk_data_file:s0 ${'$'}base_path"
// unmount command
internal const val COMMAND_UMOUNT =
"stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' ) && umount -l ${'$'}stock_path"
// install mount script & set permissions
internal const val COMMAND_INSTALL_MOUNT = "mv $PATH_INIT_PUSH $PATH_MOUNT && $COMMAND_CHMOD_MOUNT $PATH_MOUNT"
// install umount script & set permissions
internal const val COMMAND_INSTALL_UMOUNT = "mv $PATH_INIT_PUSH $PATH_UMOUNT && $COMMAND_CHMOD_MOUNT $PATH_UMOUNT"
// unmount script
internal val CONTENT_UMOUNT_SCRIPT =
"""
#!/system/bin/sh
stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
umount -l ${'$'}stock_path
""".trimIndent()
// mount script
internal val CONTENT_MOUNT_SCRIPT =
"""
@@ -54,6 +48,8 @@ internal object Constants {
base_path="$PATH_REVANCED_APP"
stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
mount -o bind ${'$'}base_path ${'$'}stock_path
""".trimIndent()
}

View File

@@ -1,63 +0,0 @@
package app.revanced.utils.filesystem
import java.io.Closeable
import java.io.File
import java.nio.file.FileSystem
import java.nio.file.FileSystems
import java.nio.file.Files
internal class FileSystemUtils(
file: File
) : Closeable {
private var fileSystem: FileSystem
init {
fileSystem = FileSystems.newFileSystem(file.toPath(), null as ClassLoader?)
}
private fun deleteDirectory(dirPath: String) {
val files = Files.walk(fileSystem.getPath("$dirPath/"))
files
.sorted(Comparator.reverseOrder())
.forEach {
Files.delete(it)
}
files.close()
}
internal fun replaceDirectory(replacement: File) {
if (!replacement.isDirectory) throw Exception("${replacement.name} is not a directory.")
// FIXME: make this delete the directory recursively
//deleteDirectory(replacement.name)
//val path = Files.createDirectory(fileSystem.getPath(replacement.name))
val excludeFromPath = replacement.path.removeSuffix(replacement.name)
for (path in Files.walk(replacement.toPath())) {
val file = path.toFile()
if (file.isDirectory) {
val relativePath = path.toString().removePrefix(excludeFromPath)
val fileSystemPath = fileSystem.getPath(relativePath)
if (!Files.exists(fileSystemPath)) Files.createDirectory(fileSystemPath)
continue
}
replaceFile(path.toString().removePrefix(excludeFromPath), file.readBytes())
}
}
internal fun replaceFile(sourceFile: String, content: ByteArray) {
val path = fileSystem.getPath(sourceFile)
Files.deleteIfExists(path)
Files.write(path, content)
}
override fun close() {
fileSystem.close()
}
}

View File

@@ -0,0 +1,64 @@
package app.revanced.utils.filesystem
import java.io.Closeable
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.util.zip.ZipEntry
internal class ZipFileSystemUtils(
file: File
) : Closeable {
private var zipFileSystem = FileSystems.newFileSystem(file.toPath(), mapOf("noCompression" to true))
private fun Path.deleteRecursively() {
if (!Files.exists(this)) {
throw IllegalStateException("File exists in real folder but not in zip file system")
}
if (Files.isDirectory(this)) {
Files.list(this).forEach { path ->
path.deleteRecursively()
}
}
Files.delete(this)
}
internal fun getFile(path: String) = zipFileSystem.getPath(path)
internal fun writePathRecursively(path: Path) {
Files.list(path).use { fileStream ->
fileStream.forEach { filePath ->
val fileSystemPath = filePath.getRelativePath(path)
fileSystemPath.deleteRecursively()
}
}
Files.walk(path).use { fileStream ->
// don't include build directory
// by skipping the root node.
fileStream.skip(1).forEach { filePath ->
val relativePath = filePath.getRelativePath(path)
if (Files.isDirectory(filePath)) {
Files.createDirectory(relativePath)
return@forEach
}
Files.copy(filePath, relativePath)
}
}
}
internal fun write(path: String, content: ByteArray) = Files.write(zipFileSystem.getPath(path), content)
private fun Path.getRelativePath(path: Path): Path = zipFileSystem.getPath(path.relativize(this).toString())
// TODO: figure out why the file system is uncompressed by default and how to fix it
internal fun uncompress(vararg paths: String) =
paths.forEach { Files.setAttribute(zipFileSystem.getPath(it), "zip:method", ZipEntry.STORED) }
override fun close() = zipFileSystem.close()
}

View File

@@ -1,71 +1,78 @@
package app.revanced.utils.patcher
import app.revanced.cli.MainCommand
import app.revanced.cli.command.MainCommand
import app.revanced.cli.command.MainCommand.args
import app.revanced.cli.command.MainCommand.logger
import app.revanced.patcher.Patcher
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.data.base.Data
import app.revanced.patcher.extensions.findAnnotationRecursively
import app.revanced.patcher.patch.base.Patch
import app.revanced.patcher.util.patch.PatchLoader
import app.revanced.patcher.data.Data
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import app.revanced.patcher.extensions.PatchExtensions.include
import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.patch.implementation.JarPatchBundle
fun Patcher.addPatchesFiltered(
packageCompatibilityFilter: Boolean = true,
packageVersionCompatibilityFilter: Boolean = true,
includeFilter: Boolean = false
) {
val packageName = this.packageName
val packageVersion = this.packageVersion
fun Patcher.addPatchesFiltered() {
val packageName = this.data.packageMetadata.packageName
val packageVersion = this.data.packageMetadata.packageVersion
MainCommand.patchBundles.forEach { bundle ->
val includedPatches = mutableListOf<Patch<Data>>()
PatchLoader.loadFromFile(bundle).forEach patch@{ p ->
val patch = p.getDeclaredConstructor().newInstance()
args.sArgs?.patchBundles!!.forEach { bundle ->
val includedPatches = mutableListOf<Class<out Patch<Data>>>()
JarPatchBundle(bundle).loadPatches().forEach patch@{ patch ->
val compatiblePackages = patch.compatiblePackages
val patchName = patch.patchName
val compatibilityAnnotation = patch.javaClass.findAnnotationRecursively(Compatibility::class.java)
val prefix = "Skipping $patchName"
val patchName = patch.javaClass.findAnnotationRecursively(Name::class.java)?.name ?: Name::class.java.name
val args = MainCommand.args.sArgs?.pArgs!!
val prefix = "[skipped] $patchName"
if (includeFilter && !MainCommand.includedPatches.contains(patchName)) {
println(prefix)
if (args.excludedPatches.contains(patchName)) {
logger.info("$prefix: Explicitely excluded")
return@patch
} else if ((!patch.include || args.defaultExclude) && !args.includedPatches.contains(patchName)) {
logger.info("$prefix: Not explicitly included")
return@patch
}
if (packageVersionCompatibilityFilter || packageCompatibilityFilter) {
if (compatibilityAnnotation == null) {
println("$prefix: Missing compatibility annotation.")
if (compatiblePackages == null) logger.warn("$prefix: Missing compatibility annotation. Continuing.")
else {
if (!compatiblePackages.any { it.name == packageName }) {
logger.warn("$prefix: Incompatible with $packageName. This patch is only compatible with ${
compatiblePackages.joinToString(
", "
) { it.name }
}")
return@patch
}
for (compatiblePackage in compatibilityAnnotation.compatiblePackages) {
if (packageCompatibilityFilter && compatiblePackage.name != packageName) {
println("$prefix: Package name not matching ${compatiblePackage.name}.")
return@patch
}
if (!packageVersionCompatibilityFilter || compatiblePackage.versions.any { it == packageVersion }) continue
println("$prefix: Unsupported version.")
if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) {
val compatibleWith = compatiblePackages.map { _package ->
"${_package.name}: ${_package.versions.joinToString(", ")}"
}.joinToString(";")
logger.warn("$prefix: Incompatible with version $packageVersion. This patch is only compatible with version $compatibleWith")
return@patch
}
}
logger.trace("Adding $patchName")
includedPatches.add(patch)
println("[added] $patchName")
}
this.addPatches(includedPatches)
}
}
fun Patcher.applyPatchesPrint() {
for ((patch, result) in this.applyPatches()) {
println("[${if (result.isFailure) "error" else "success"}] $patch")
fun Patcher.applyPatchesVerbose() {
this.applyPatches().forEach { (patch, result) ->
if (result.isSuccess) {
logger.info("$patch succeeded")
return@forEach
}
logger.error("$patch failed:")
result.exceptionOrNull()!!.printStackTrace()
}
}
fun Patcher.mergeFiles() {
this.addFiles(MainCommand.mergeFiles)
this.addFiles(args.sArgs?.pArgs!!.mergeFiles) { file ->
logger.info("Merging $file")
}
}

View File

@@ -1,58 +0,0 @@
package app.revanced.utils.signature
import app.revanced.patcher.Patcher
import app.revanced.patcher.extensions.findAnnotationRecursively
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
import org.jf.dexlib2.iface.Method
object Signature {
fun checkSignatures(patcher: Patcher) {
val failed = mutableListOf<String>()
for (signature in patcher.resolveSignatures()) {
val signatureClass = signature::class.java
val signatureName =
signatureClass.findAnnotationRecursively(app.revanced.patcher.annotation.Name::class.java)?.name
?: signatureClass.name
if (!signature.resolved) {
failed.add(signatureName)
continue
}
val method = signature.result!!.method
val matchingMethod =
signatureClass.findAnnotationRecursively(MatchingMethod::class.java) ?: MatchingMethod()
println(
"""
[Signature] $signatureName
[Method] ${matchingMethod.definingClass}->${matchingMethod.name}
[Match] ${method.definingClass}->${method.toStr()}
""".trimIndent()
)
signatureClass.findAnnotationRecursively(FuzzyPatternScanMethod::class.java)?.let {
val warnings = signature.result!!.scanResult.warnings!!
println(
"""
[Warnings: ${warnings.count()}]
${warnings.joinToString(separator = "\n") { warning -> "${warning.instructionIndex} / ${warning.patternIndex}: ${warning.wrongOpcode} (expected: ${warning.correctOpcode})" }}
""".trimIndent()
)
}
}
println(
"""
${"=".repeat(50)}
[Failed signatures: ${failed.size}]
${failed.joinToString(separator = "\n") { it }}
""".trimIndent()
)
}
private fun Method.toStr(): String {
return "${this.name}(${this.parameterTypes.joinToString("")})${this.returnType}"
}
}

View File

@@ -1,9 +0,0 @@
package app.revanced.utils.signing
import java.security.PrivateKey
import java.security.cert.X509Certificate
data class KeySet(
val publicKey: X509Certificate,
val privateKey: PrivateKey
)

View File

@@ -1,65 +1,42 @@
/*
* Copyright (c) 2021 Juby210 & Vendicated
* Licensed under the Open Software License version 3.0
*/
package app.revanced.utils.signing
import app.revanced.cli.command.MainCommand.logger
import app.revanced.cli.signing.SigningOptions
import com.android.apksig.ApkSigner
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.cert.X509v3CertificateBuilder
import org.bouncycastle.cert.jcajce.JcaCertStore
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cms.*
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.operator.ContentSigner
import org.bouncycastle.operator.DigestCalculatorProvider
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder
import org.bouncycastle.util.encoders.Base64
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.math.BigInteger
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.security.*
import java.security.cert.X509Certificate
import java.util.*
import java.util.jar.Attributes
import java.util.jar.JarFile
import java.util.jar.Manifest
import java.util.regex.Pattern
const val CN = "ReVanced"
val PASSWORD = "revanced".toCharArray() // TODO: make it secure; random password should be enough
/**
* APK Signer.
* @author Aliucord authors
* @author ReVanced team
*/
object Signer {
internal class Signer(
private val signingOptions: SigningOptions
) {
private val passwordCharArray = signingOptions.password.toCharArray()
private fun newKeystore(out: File) {
val key = createKey()
val (publicKey, privateKey) = createKey()
val privateKS = KeyStore.getInstance("BKS", "BC")
privateKS.load(null, PASSWORD)
privateKS.setKeyEntry("alias", key.privateKey, PASSWORD, arrayOf(key.publicKey))
privateKS.store(FileOutputStream(out), PASSWORD)
privateKS.load(null, passwordCharArray)
privateKS.setKeyEntry("alias", privateKey, passwordCharArray, arrayOf(publicKey))
privateKS.store(FileOutputStream(out), passwordCharArray)
}
private fun createKey(): KeySet {
private fun createKey(): Pair<X509Certificate, PrivateKey> {
val gen = KeyPairGenerator.getInstance("RSA")
gen.initialize(2048)
val pair = gen.generateKeyPair()
var serialNumber: BigInteger
do serialNumber =
BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
val x500Name = X500Name("CN=$CN")
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
val x500Name = X500Name("CN=${signingOptions.cn}")
val builder = X509v3CertificateBuilder(
x500Name,
serialNumber,
@@ -69,143 +46,34 @@ object Signer {
x500Name,
SubjectPublicKeyInfo.getInstance(pair.public.encoded)
)
val signer: ContentSigner = JcaContentSignerBuilder("SHA1withRSA").build(pair.private)
return KeySet(JcaX509CertificateConverter().getCertificate(builder.build(signer)), pair.private)
val signer: ContentSigner = JcaContentSignerBuilder("SHA256withRSA").build(pair.private)
return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private
}
private val stripPattern: Pattern = Pattern.compile("^META-INF/(.*)[.](MF|SF|RSA|DSA)$")
// based on https://gist.github.com/mmuszkow/10288441
// and https://github.com/fornwall/apksigner/blob/master/src/main/java/net/fornwall/apksigner/ZipSigner.java
fun signApk(apkFile: File) {
fun signApk(input: File, output: File) {
Security.addProvider(BouncyCastleProvider())
val ks = File(apkFile.parent, "revanced-cli.keystore")
if (!ks.exists()) newKeystore(ks)
// TODO: keystore should be saved securely
val ks = File(signingOptions.keyStoreFilePath)
if (!ks.exists()) newKeystore(ks) else {
logger.info("Found existing keystore: ${ks.nameWithoutExtension}")
}
val keyStore = KeyStore.getInstance("BKS", "BC")
FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
val alias = keyStore.aliases().nextElement()
val keySet = KeySet(
(keyStore.getCertificate(alias) as X509Certificate),
(keyStore.getKey(alias, PASSWORD) as PrivateKey)
)
val zip = FileSystems.newFileSystem(apkFile.toPath(), null as ClassLoader?)
val config = ApkSigner.SignerConfig.Builder(
signingOptions.cn,
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
listOf(keyStore.getCertificate(alias) as X509Certificate)
).build()
val dig = MessageDigest.getInstance("SHA1")
val digests: MutableMap<String, String> = LinkedHashMap()
val signer = ApkSigner.Builder(listOf(config))
signer.setCreatedBy(signingOptions.cn)
signer.setInputApk(input)
signer.setOutputApk(output)
for (entry in zip.allEntries) {
val name = entry.toString()
if (stripPattern.matcher(name).matches()) {
Files.delete(entry)
} else {
digests[name] = toBase64(dig.digest(Files.readAllBytes(entry)))
}
}
val sectionDigests: MutableMap<String, String> = LinkedHashMap()
var manifest = Manifest()
var attrs = manifest.mainAttributes
attrs[Attributes.Name.MANIFEST_VERSION] = "1.0"
attrs[Attributes.Name("Created-By")] = CN
val digestAttr = Attributes.Name("SHA1-Digest")
for ((name, value) in digests) {
val attributes = Attributes()
attributes[digestAttr] = value
manifest.entries[name] = attributes
sectionDigests[name] = hashEntrySection(name, attributes, dig)
}
ByteArrayOutputStream().use { baos ->
manifest.write(baos)
zip.writeFile(JarFile.MANIFEST_NAME, baos.toByteArray())
}
val manifestHash = getManifestHash(manifest, dig)
val tmpManifest = Manifest()
tmpManifest.mainAttributes.putAll(attrs)
val manifestMainHash = getManifestHash(tmpManifest, dig)
manifest = Manifest()
attrs = manifest.mainAttributes
attrs[Attributes.Name.SIGNATURE_VERSION] = "1.0"
attrs[Attributes.Name("Created-By")] = CN
attrs[Attributes.Name("SHA1-Digest-Manifest")] = manifestHash
attrs[Attributes.Name("SHA1-Digest-Manifest-Main-Attributes")] = manifestMainHash
for ((key, value) in sectionDigests) {
val attributes = Attributes()
attributes[digestAttr] = value
manifest.entries[key] = attributes
}
var sigBytes: ByteArray
ByteArrayOutputStream().use { sigStream ->
manifest.write(sigStream)
sigBytes = sigStream.toByteArray()
zip.writeFile("META-INF/CERT.SF", sigBytes)
}
val signature = signSigFile(keySet, sigBytes)
zip.writeFile("META-INF/CERT.RSA", signature)
zip.close()
signer.build().sign()
}
private fun hashEntrySection(name: String, attrs: Attributes, dig: MessageDigest): String {
val manifest = Manifest()
manifest.mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0"
ByteArrayOutputStream().use { baos ->
manifest.write(baos)
val emptyLen = baos.toByteArray().size
manifest.entries[name] = attrs
baos.reset()
manifest.write(baos)
var ob = baos.toByteArray()
ob = Arrays.copyOfRange(ob, emptyLen, ob.size)
return toBase64(dig.digest(ob))
}
}
private fun getManifestHash(manifest: Manifest, dig: MessageDigest): String {
ByteArrayOutputStream().use { baos ->
manifest.write(baos)
return toBase64(dig.digest(baos.toByteArray()))
}
}
private fun signSigFile(keySet: KeySet, content: ByteArray): ByteArray {
val msg: CMSTypedData = CMSProcessableByteArray(content)
val certs = JcaCertStore(Collections.singletonList(keySet.publicKey))
val gen = CMSSignedDataGenerator()
val jcaContentSignerBuilder = JcaContentSignerBuilder("SHA1withRSA")
val sha1Signer: ContentSigner = jcaContentSignerBuilder.build(keySet.privateKey)
val jcaDigestCalculatorProviderBuilder = JcaDigestCalculatorProviderBuilder()
val digestCalculatorProvider: DigestCalculatorProvider = jcaDigestCalculatorProviderBuilder.build()
val jcaSignerInfoGeneratorBuilder = JcaSignerInfoGeneratorBuilder(digestCalculatorProvider)
jcaSignerInfoGeneratorBuilder.setDirectSignature(true)
val signerInfoGenerator: SignerInfoGenerator = jcaSignerInfoGeneratorBuilder.build(sha1Signer, keySet.publicKey)
gen.addSignerInfoGenerator(signerInfoGenerator)
gen.addCertificates(certs)
val sigData: CMSSignedData = gen.generate(msg, false)
return sigData.toASN1Structure().getEncoded("DER")
}
private fun toBase64(data: ByteArray): String {
return String(Base64.encode(data))
}
}
private val java.nio.file.FileSystem.allEntries: List<Path>
get() = buildList {
this@allEntries.rootDirectories.forEach { dir ->
Files.walk(dir).filter(Files::isRegularFile).forEach { file ->
this@buildList.add(file)
}
}
}
private fun java.nio.file.FileSystem.writeFile(path: String, bytes: ByteArray) {
Files.write(this.getPath("/$path"), bytes)
}

View File

@@ -0,0 +1,28 @@
package app.revanced.utils.signing.align
import app.revanced.utils.signing.align.zip.ZipFile
import java.io.File
internal object ZipAligner {
private const val DEFAULT_ALIGNMENT = 4
private const val LIBRARY_ALIGNMENT = 4096
fun align(input: File, output: File) {
val inputZip = ZipFile(input)
val outputZip = ZipFile(output)
for (entry in inputZip.entries) {
val data = inputZip.getDataForEntry(entry)
if (entry.compression == 0.toUShort()) {
val alignment = if (entry.fileName.endsWith(".so")) LIBRARY_ALIGNMENT else DEFAULT_ALIGNMENT
outputZip.addEntryAligned(entry, data, alignment)
} else {
outputZip.addEntry(entry, data)
}
}
outputZip.finish()
}
}

View File

@@ -0,0 +1,33 @@
package app.revanced.utils.signing.align.zip
import java.io.DataInput
import java.io.DataOutput
import java.nio.ByteBuffer
fun UInt.toLittleEndian() =
(((this.toInt() and 0xff000000.toInt()) shr 24) or ((this.toInt() and 0x00ff0000) shr 8) or ((this.toInt() and 0x0000ff00) shl 8) or (this.toInt() shl 24)).toUInt()
fun UShort.toLittleEndian() = (this.toUInt() shl 16).toLittleEndian().toUShort()
fun UInt.toBigEndian() = (((this.toInt() and 0xff) shl 24) or ((this.toInt() and 0xff00) shl 8)
or ((this.toInt() and 0x00ff0000) ushr 8) or (this.toInt() ushr 24)).toUInt()
fun UShort.toBigEndian() = (this.toUInt() shl 16).toBigEndian().toUShort()
fun ByteBuffer.getUShort() = this.getShort().toUShort()
fun ByteBuffer.getUInt() = this.getInt().toUInt()
fun ByteBuffer.putUShort(ushort: UShort) = this.putShort(ushort.toShort())
fun ByteBuffer.putUInt(uint: UInt) = this.putInt(uint.toInt())
fun DataInput.readUShort() = this.readShort().toUShort()
fun DataInput.readUInt() = this.readInt().toUInt()
fun DataOutput.writeUShort(ushort: UShort) = this.writeShort(ushort.toInt())
fun DataOutput.writeUInt(uint: UInt) = this.writeInt(uint.toInt())
fun DataInput.readUShortLE() = this.readUShort().toBigEndian()
fun DataInput.readUIntLE() = this.readUInt().toBigEndian()
fun DataOutput.writeUShortLE(ushort: UShort) = this.writeUShort(ushort.toLittleEndian())
fun DataOutput.writeUIntLE(uint: UInt) = this.writeUInt(uint.toLittleEndian())

View File

@@ -0,0 +1,130 @@
package app.revanced.utils.signing.align.zip
import app.revanced.utils.signing.align.zip.structures.ZipEndRecord
import app.revanced.utils.signing.align.zip.structures.ZipEntry
import java.io.File
import java.io.RandomAccessFile
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
class ZipFile(val file: File) {
var entries: MutableList<ZipEntry> = mutableListOf()
private val filePointer: RandomAccessFile = RandomAccessFile(file, "rw")
init {
//if file isn't empty try to load entries
if (file.length() > 0) {
val endRecord = findEndRecord()
if (endRecord.diskNumber > 0u || endRecord.totalEntries != endRecord.diskEntries)
throw IllegalArgumentException("Multi-file archives are not supported")
entries = readEntries(endRecord).toMutableList()
}
//seek back to start for writing
filePointer.seek(0)
}
private fun findEndRecord(): ZipEndRecord {
//look from end to start since end record is at the end
for (i in filePointer.length() - 1 downTo 0) {
filePointer.seek(i)
//possible beginning of signature
if (filePointer.readByte() == 0x50.toByte()) {
//seek back to get the full int
filePointer.seek(i)
val possibleSignature = filePointer.readUIntLE()
if (possibleSignature == ZipEndRecord.ECD_SIGNATURE) {
filePointer.seek(i)
return ZipEndRecord.fromECD(filePointer)
}
}
}
throw Exception("Couldn't find end record")
}
private fun readEntries(endRecord: ZipEndRecord): List<ZipEntry> {
filePointer.seek(endRecord.centralDirectoryStartOffset.toLong())
val numberOfEntries = endRecord.diskEntries.toInt()
return buildList(numberOfEntries) {
for (i in 1..numberOfEntries) {
add(ZipEntry.fromCDE(filePointer).also
{
//for some reason the local extra field can be different from the central one
it.readLocalExtra(
filePointer.channel.map(
FileChannel.MapMode.READ_ONLY,
it.localHeaderOffset.toLong() + 28,
2
)
)
})
}
}
}
private fun writeCDE() {
val CDEStart = filePointer.channel.position().toUInt()
entries.forEach {
filePointer.channel.write(it.toCDE())
}
val entriesCount = entries.size.toUShort()
val endRecord = ZipEndRecord(
0u,
0u,
entriesCount,
entriesCount,
filePointer.channel.position().toUInt() - CDEStart,
CDEStart,
""
)
filePointer.channel.write(endRecord.toECD())
}
fun addEntry(entry: ZipEntry, data: ByteBuffer) {
entry.localHeaderOffset = filePointer.channel.position().toUInt()
filePointer.channel.write(entry.toLFH())
filePointer.channel.write(data)
entries.add(entry)
}
fun addEntryAligned(entry: ZipEntry, data: ByteBuffer, alignment: Int) {
//calculate where data would end up
val dataOffset = filePointer.filePointer + entry.LFHSize
val mod = dataOffset % alignment
//wrong alignment
if (mod != 0L) {
//add padding at end of extra field
entry.localExtraField =
entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt())
}
addEntry(entry, data)
}
fun getDataForEntry(entry: ZipEntry): ByteBuffer {
return filePointer.channel.map(
FileChannel.MapMode.READ_ONLY,
entry.dataOffset.toLong(),
entry.compressedSize.toLong()
)
}
fun finish() {
writeCDE()
filePointer.close()
}
}

View File

@@ -0,0 +1,77 @@
package app.revanced.utils.signing.align.zip.structures
import app.revanced.utils.signing.align.zip.putUInt
import app.revanced.utils.signing.align.zip.putUShort
import app.revanced.utils.signing.align.zip.readUIntLE
import app.revanced.utils.signing.align.zip.readUShortLE
import java.io.DataInput
import java.nio.ByteBuffer
import java.nio.ByteOrder
data class ZipEndRecord(
val diskNumber: UShort,
val startingDiskNumber: UShort,
val diskEntries: UShort,
val totalEntries: UShort,
val centralDirectorySize: UInt,
val centralDirectoryStartOffset: UInt,
val fileComment: String,
) {
companion object {
const val ECD_HEADER_SIZE = 22
const val ECD_SIGNATURE = 0x06054b50u
fun fromECD(input: DataInput): ZipEndRecord {
val signature = input.readUIntLE()
if (signature != ECD_SIGNATURE)
throw IllegalArgumentException("Input doesn't start with end record signature")
val diskNumber = input.readUShortLE()
val startingDiskNumber = input.readUShortLE()
val diskEntries = input.readUShortLE()
val totalEntries = input.readUShortLE()
val centralDirectorySize = input.readUIntLE()
val centralDirectoryStartOffset = input.readUIntLE()
val fileCommentLength = input.readUShortLE()
var fileComment = ""
if (fileCommentLength > 0u) {
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
input.readFully(fileCommentBytes)
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
}
return ZipEndRecord(
diskNumber,
startingDiskNumber,
diskEntries,
totalEntries,
centralDirectorySize,
centralDirectoryStartOffset,
fileComment
)
}
}
fun toECD(): ByteBuffer {
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
val buffer = ByteBuffer.allocate(ECD_HEADER_SIZE + commentBytes.size).also { it.order(ByteOrder.LITTLE_ENDIAN) }
buffer.putUInt(ECD_SIGNATURE)
buffer.putUShort(diskNumber)
buffer.putUShort(startingDiskNumber)
buffer.putUShort(diskEntries)
buffer.putUShort(totalEntries)
buffer.putUInt(centralDirectorySize)
buffer.putUInt(centralDirectoryStartOffset)
buffer.putUShort(commentBytes.size.toUShort())
buffer.put(commentBytes)
buffer.flip()
return buffer
}
}

Some files were not shown because too many files have changed in this diff Show More