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

Compare commits

...

127 Commits

Author SHA1 Message Date
semantic-release-bot
63a8aa315d chore(release): 2.8.3 [skip ci]
## [2.8.3](https://github.com/revanced/revanced-cli/compare/v2.8.2...v2.8.3) (2022-08-03)
2022-08-03 01:56:48 +00:00
oSumAtrIX
491874bb51 build: bump patcher dependency version 2022-08-03 03:55:07 +02:00
semantic-release-bot
cb887ead2c chore(release): 2.8.2 [skip ci]
## [2.8.2](https://github.com/revanced/revanced-cli/compare/v2.8.1...v2.8.2) (2022-08-02)
2022-08-02 22:47:46 +00:00
oSumAtrIX
cd36e710d6 build: bump patcher dependency version 2022-08-03 00:46:04 +02:00
semantic-release-bot
1d096c3e92 chore(release): 2.8.1 [skip ci]
## [2.8.1](https://github.com/revanced/revanced-cli/compare/v2.8.0...v2.8.1) (2022-08-02)

### Bug Fixes

* remove requirement for solution [skip ci] ([#108](https://github.com/revanced/revanced-cli/issues/108)) ([0ce680a](0ce680a6f1))
2022-08-02 01:13:28 +00:00
oSumAtrIX
8d29b8cd47 build: bump patcher dependency version 2022-08-02 03:11:39 +02:00
Robert
0ce680a6f1 fix: remove requirement for solution [skip ci] (#108) 2022-08-01 13:08:22 +02:00
semantic-release-bot
ddebe4d43c chore(release): 2.8.0 [skip ci]
# [2.8.0](https://github.com/revanced/revanced-cli/compare/v2.7.1...v2.8.0) (2022-07-31)

### Features

* `frameworkFolderLocation` patcher option ([bc17298](bc17298a80))
2022-07-31 03:58:28 +00:00
oSumAtrIX
bc17298a80 feat: frameworkFolderLocation patcher option 2022-07-31 05:56:18 +02:00
oSumAtrIX
536f6f90b1 docs: add missing . to feature-issue.yml description 2022-07-26 19:38:01 +02:00
oSumAtrIX
ccf5206da8 docs: add missing . to issue description 2022-07-26 19:34:41 +02:00
oSumAtrIX
07714abe49 docs: GitHub issue forms 2022-07-26 19:28:34 +02:00
semantic-release-bot
d954424b30 chore(release): 2.7.1 [skip ci]
## [2.7.1](https://github.com/revanced/revanced-cli/compare/v2.7.0...v2.7.1) (2022-07-21)

### Bug Fixes

* align every file ([96ec6a0](96ec6a0384))
2022-07-21 21:52:18 +00:00
oSumAtrIX
168c003a31 refactor: create methods in MainCommand class 2022-07-21 23:50:26 +02:00
Canny
96ec6a0384 fix: align every file 2022-07-21 23:49:25 +02:00
semantic-release-bot
1cd0976415 chore(release): 2.7.0 [skip ci]
# [2.7.0](https://github.com/revanced/revanced-cli/compare/v2.6.0...v2.7.0) (2022-07-21)

### Features

* `--custom-aapt2-binary` option ([#104](https://github.com/revanced/revanced-cli/issues/104)) ([d8dbffd](d8dbffd7a7))
2022-07-21 12:13:10 +00:00
Robert
d8dbffd7a7 feat: --custom-aapt2-binary option (#104) 2022-07-21 14:11:26 +02:00
n0k0m3
5f2bccc4e5 refactor: fix typo (#101) 2022-07-19 17:30:31 +02:00
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
29 changed files with 1239 additions and 336 deletions

61
.github/ISSUE_TEMPLATE/bug-issue.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: 🐞 Bug report
description: Report a very clearly broken issue.
title: 'bug: <title>'
labels: [bug]
body:
- type: markdown
attributes:
value: |
# ReVanced bug report
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-cli/labels/bug).
- type: dropdown
attributes:
label: Type
options:
- Error while running the CLI
- Error at runtime
- Cosmetic
- Other
validations:
required: true
- type: textarea
attributes:
label: Bug description
description: How did you find the bug? Any additional details that might help?
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Add the steps to reproduce this bug including your environment.
placeholder: Step 1. Download some files. Step 2. ...
validations:
required: true
- type: textarea
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
validations:
required: true
- type: textarea
attributes:
label: Screenshots or videos
description: Add screenshots or videos that show the bug here.
placeholder: Drag and drop the screenshots/videos into this box.
validations:
required: false
- type: textarea
attributes:
label: Solution
description: If applicable, add a possible solution.
validations:
required: false
- type: textarea
attributes:
label: Additional context
description: Add additional context here.
validations:
required: false

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 📃 Documentation
url: https://github.com/revanced/revanced-documentation/
about: Don't know how or where to start? Check out our documentation!
- name: 🗨 Discussions
url: https://github.com/revanced/revanced-suggestions/discussions
about: Got something you think should change or be added? Search for or start a new discussion!

View File

@@ -0,0 +1,46 @@
name: ⭐ Feature request
description: Create a detailed feature request.
title: 'feat: <title>'
labels: [feature-request]
body:
- type: markdown
attributes:
value: |
# ReVanced feature request
Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-cli/labels/feature-request).
- type: dropdown
attributes:
label: Type
options:
- Functionality
- Cosmetic
- Other
validations:
required: true
- type: textarea
attributes:
label: Issue
description: What is the current problem. Why does it require a feature request?
validations:
required: true
- type: textarea
attributes:
label: Feature
description: Describe your feature in detail. How does it solve the issue?
validations:
required: true
- type: textarea
attributes:
label: Motivation
description: Why should your feature should be considered?
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add additional context here.
validations:
required: false

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",

File diff suppressed because it is too large Load Diff

View File

@@ -10,13 +10,7 @@ val githubPassword: String = project.findProperty("gpr.key") as? String ?: Syste
repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
credentials {
username = githubUsername
password = githubPassword
}
}
mavenLocal()
maven {
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
credentials {
@@ -31,7 +25,8 @@ repositories {
dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))
implementation("app.revanced:revanced-patcher:1.3.0")
implementation("app.revanced:revanced-patcher:3.3.1")
implementation("info.picocli:picocli:4.6.3")
implementation("com.android.tools.build:apksig:7.2.1")
implementation("com.github.revanced:jadb:master-SNAPSHOT") // updated fork

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official
version = 1.5.0
version = 2.8.3

View File

@@ -0,0 +1,12 @@
package app.revanced.cli.aligning
import app.revanced.cli.command.MainCommand.logger
import app.revanced.utils.signing.align.ZipAligner
import java.io.File
object Aligning {
fun align(inputFile: File, outputFile: File) {
logger.info("Aligning ${inputFile.name} to ${outputFile.name}")
ZipAligner.align(inputFile, outputFile)
}
}

File diff suppressed because it is too large Load Diff

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,6 +1,7 @@
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
@@ -10,31 +11,38 @@ import java.nio.file.Files
internal object Patcher {
internal fun start(patcher: app.revanced.patcher.Patcher, output: File) {
val args = args.pArgs;
val inputFile = args.inputFile
val args = args.patchArgs?.patchingArgs!!
// merge files like necessary integrations
patcher.mergeFiles()
// add patches, but filter incompatible or excluded patches
patcher.addPatchesFiltered(includeFilter = args.includedPatches.isNotEmpty())
patcher.addPatchesFiltered()
// apply patches
patcher.applyPatchesVerbose()
// write output file
if (output.exists()) Files.delete(output.toPath())
args.inputFile.copyTo(output)
inputFile.copyTo(output)
ZipFileSystemUtils(output).use { fileSystem ->
val result = patcher.save()
ZipFileSystemUtils(output).use { outputFileSystem ->
// replace all dex files
val result = patcher.save()
result.dexFiles.forEach {
fileSystem.write(it.name, it.memoryDataStore.data)
logger.info("Writing dex file ${it.name}")
outputFileSystem.write(it.name, it.dexFileInputStream.readAllBytes())
}
// write resources
if (!args.disableResourcePatching) {
fileSystem.writePathRecursively(File(args.cacheDirectory).resolve("build").toPath())
fileSystem.uncompress(*result.doNotCompress!!.toTypedArray())
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

@@ -1,25 +1,12 @@
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, cn: String, password: String) {
val cacheDirectory = File(args.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
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
val keyStore = Signer(cn, password).signApk(alignedOutput, signedOutput)
// afterwards copy over the file and the keystore to the output
signedOutput.copyTo(outputFile, true)
keyStore.copyTo(outputFile.resolveSibling(keyStore.name), true)
fun sign(alignedFile: File, signedOutput: File, signingOptions: SigningOptions) {
logger.info("Signing ${alignedFile.name} to ${signedOutput.name}")
Signer(signingOptions).signApk(alignedFile, signedOutput)
}
}

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,5 +1,6 @@
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
@@ -20,7 +21,7 @@ internal class Adb(
?: throw IllegalArgumentException("No such device with name $deviceName")
if (!modeInstall && device.run("su -h", false) != 0)
throw IllegalArgumentException("Root required on $deviceName. Deploying failed.")
throw IllegalArgumentException("Root required on $deviceName. Task failed")
}
private fun String.replacePlaceholder(with: String? = null): String {
@@ -29,12 +30,16 @@ internal class Adb(
internal fun deploy() {
if (modeInstall) {
logger.info("Installing without mounting")
PackageManager(device).install(file)
} else {
logger.info("Installing by mounting")
// push patched file
device.copy(Constants.PATH_INIT_PUSH, file)
// create revanced path
// create revanced folder path
device.run("${Constants.COMMAND_CREATE_DIR} ${Constants.PATH_REVANCED}")
// prepare mounting the apk
@@ -48,16 +53,8 @@ internal class Adb(
// install mount script
device.run(Constants.COMMAND_INSTALL_MOUNT.replacePlaceholder())
// push umount script
device.createFile(
Constants.PATH_INIT_PUSH,
Constants.CONTENT_UMOUNT_SCRIPT.replacePlaceholder()
)
// install mount script
device.run(Constants.COMMAND_INSTALL_UMOUNT.replacePlaceholder())
// unmount the apk for sanity
device.run(Constants.PATH_UMOUNT.replacePlaceholder())
device.run(Constants.COMMAND_UMOUNT.replacePlaceholder())
// mount the apk
device.run(Constants.PATH_MOUNT.replacePlaceholder())
@@ -69,6 +66,21 @@ internal class Adb(
}
}
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() {
val executor = Executors.newSingleThreadExecutor()
val pipe = if (logging) {
@@ -91,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

@@ -21,31 +21,25 @@ internal object Constants {
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_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 =
"""

View File

@@ -13,6 +13,10 @@ internal class ZipFileSystemUtils(
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()
@@ -22,17 +26,19 @@ internal class ZipFileSystemUtils(
Files.delete(this)
}
internal fun getFile(path: String) = zipFileSystem.getPath(path)
internal fun writePathRecursively(path: Path) {
Files.list(path).let { fileStream ->
Files.list(path).use { fileStream ->
fileStream.forEach { filePath ->
val fileSystemPath = filePath.getRelativePath(path)
fileSystemPath.deleteRecursively()
}
}
fileStream
}.close()
Files.walk(path).let { fileStream ->
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)
@@ -43,15 +49,14 @@ internal class ZipFileSystemUtils(
Files.copy(filePath, relativePath)
}
fileStream
}.close()
}
}
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) }

View File

@@ -2,55 +2,59 @@ package app.revanced.utils.patcher
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.data.base.Data
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.base.Patch
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.patch.implementation.JarPatchBundle
fun Patcher.addPatchesFiltered(
includeFilter: Boolean = false
) {
fun Patcher.addPatchesFiltered() {
val packageName = this.data.packageMetadata.packageName
val packageVersion = this.data.packageMetadata.packageVersion
MainCommand.args.patchBundles.forEach { bundle ->
args.patchArgs?.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 prefix = "[skipped] $patchName"
val prefix = "Skipping $patchName"
val args = MainCommand.args.pArgs
val args = MainCommand.args.patchArgs?.patchingArgs!!
if (includeFilter) {
if (!args.includedPatches.contains(patchName)) {
println("$prefix: Explicitly excluded.")
return@patch
}
} else if (!patch.include) {
println("$prefix: Implicitly excluded.")
if (args.excludedPatches.contains(patchName)) {
logger.info("$prefix: Explicitly excluded")
return@patch
} else if ((!patch.include || args.defaultExclude) && !args.includedPatches.contains(patchName)) {
logger.info("$prefix: Not explicitly included")
return@patch
}
if (compatiblePackages == null) println("$prefix: Missing compatibility annotation. Continuing.")
if (compatiblePackages == null) logger.warn("$prefix: Missing compatibility annotation. Continuing.")
else {
if (!compatiblePackages.any { it.name == packageName }) {
println("$prefix: Incompatible package.")
logger.warn("$prefix: Incompatible with $packageName. This patch is only compatible with ${
compatiblePackages.joinToString(
", "
) { it.name }
}")
return@patch
}
if (!(args.debugging || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion }})) {
println("$prefix: The package version is $packageVersion and is incompatible.")
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)
}
@@ -59,14 +63,16 @@ fun Patcher.addPatchesFiltered(
fun Patcher.applyPatchesVerbose() {
this.applyPatches().forEach { (patch, result) ->
if (result.isSuccess) {
println("[success] $patch")
logger.info("$patch succeeded")
return@forEach
}
println("[error] $patch:")
logger.error("$patch failed:")
result.exceptionOrNull()!!.printStackTrace()
}
}
fun Patcher.mergeFiles() {
this.addFiles(MainCommand.args.pArgs.mergeFiles)
this.addFiles(args.patchArgs?.patchingArgs!!.mergeFiles) { file ->
logger.info("Merging $file")
}
}

View File

@@ -1,55 +0,0 @@
package app.revanced.utils.signature
import app.revanced.patcher.Patcher
import org.jf.dexlib2.iface.Method
object Signature {
fun checkSignatures(patcher: Patcher) {
TODO()
/**
val failed = mutableListOf<String>()
for (signature in patcher.resolveSignatures()) {
val signatureClass = signature::class.java
val signatureName = signature.name ?: signatureClass.simpleName
if (!signature.resolved) {
failed.add(signatureName)
continue
}
val method = signature.result!!.method
val matchingMethod = signature.matchingMethod ?: MatchingMethod()
println(
"""
[Signature] $signatureName
[Method] ${matchingMethod.definingClass}->${matchingMethod.name}
[Match] ${method.definingClass}->${method.toStr()}
""".trimIndent()
)
signature.fuzzyThreshold.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,5 +1,7 @@
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
@@ -17,9 +19,9 @@ import java.security.cert.X509Certificate
import java.util.*
internal class Signer(
private val cn: String, password: String
private val signingOptions: SigningOptions
) {
private val passwordCharArray = password.toCharArray()
private val passwordCharArray = signingOptions.password.toCharArray()
private fun newKeystore(out: File) {
val (publicKey, privateKey) = createKey()
val privateKS = KeyStore.getInstance("BKS", "BC")
@@ -34,7 +36,7 @@ internal class Signer(
val pair = gen.generateKeyPair()
var serialNumber: BigInteger
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
val x500Name = X500Name("CN=$cn")
val x500Name = X500Name("CN=${signingOptions.cn}")
val builder = X509v3CertificateBuilder(
x500Name,
serialNumber,
@@ -48,30 +50,30 @@ internal class Signer(
return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private
}
fun signApk(input: File, output: File): File {
fun signApk(input: File, output: File) {
Security.addProvider(BouncyCastleProvider())
// TODO: keystore should be saved securely
val ks = File(input.parent, "${output.nameWithoutExtension}.keystore")
if (!ks.exists()) newKeystore(ks)
val ks = File(signingOptions.keyStoreFilePath)
if (!ks.exists()) newKeystore(ks) else {
logger.info("Found existing keystore: ${ks.name}")
}
val keyStore = KeyStore.getInstance("BKS", "BC")
FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
val alias = keyStore.aliases().nextElement()
val config = ApkSigner.SignerConfig.Builder(
cn,
signingOptions.cn,
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
listOf(keyStore.getCertificate(alias) as X509Certificate)
).build()
val signer = ApkSigner.Builder(listOf(config))
signer.setCreatedBy(cn)
signer.setCreatedBy(signingOptions.cn)
signer.setInputApk(input)
signer.setOutputApk(output)
signer.build().sign()
return ks
}
}

View File

@@ -1,63 +1,28 @@
package app.revanced.utils.signing.align
import app.revanced.utils.signing.align.stream.MultiOutputStream
import app.revanced.utils.signing.align.stream.PeekingFakeStream
import java.io.BufferedOutputStream
import app.revanced.utils.signing.align.zip.ZipFile
import java.io.File
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
internal object ZipAligner {
fun align(input: File, output: File, alignment: Int = 4) {
val zipFile = ZipFile(input)
private const val DEFAULT_ALIGNMENT = 4
private const val LIBRARY_ALIGNMENT = 4096
val entries: Enumeration<out ZipEntry?> = zipFile.entries()
fun align(input: File, output: File) {
val inputZip = ZipFile(input)
val outputZip = ZipFile(output)
// fake
val peekingFakeStream = PeekingFakeStream()
val fakeOutputStream = ZipOutputStream(peekingFakeStream)
// real
val zipOutputStream = ZipOutputStream(BufferedOutputStream(output.outputStream()))
for (entry in inputZip.entries) {
val data = inputZip.getDataForEntry(entry)
val multiOutputStream = MultiOutputStream(
listOf(
fakeOutputStream, // fake, used to add the data to the fake stream
zipOutputStream // real
)
)
if (entry.compression == 0.toUShort()) {
val alignment = if (entry.fileName.endsWith(".so")) LIBRARY_ALIGNMENT else DEFAULT_ALIGNMENT
var bias = 0
while (entries.hasMoreElements()) {
var padding = 0
val entry: ZipEntry = entries.nextElement()!!
// fake, used to calculate the file offset of the entry
fakeOutputStream.putNextEntry(entry)
if (entry.size == entry.compressedSize) {
val fileOffset = peekingFakeStream.peek()
val newOffset = fileOffset + bias
padding = ((alignment - (newOffset % alignment)) % alignment).toInt()
// real
entry.extra = if (entry.extra == null) ByteArray(padding)
else Arrays.copyOf(entry.extra, entry.extra.size + padding)
outputZip.addEntryAligned(entry, data, alignment)
} else {
outputZip.addEntry(entry, data)
}
zipOutputStream.putNextEntry(entry)
zipFile.getInputStream(entry).copyTo(multiOutputStream)
// fake, used to add remaining bytes
fakeOutputStream.closeEntry()
// real
zipOutputStream.closeEntry()
bias += padding
}
zipFile.close()
zipOutputStream.close()
outputZip.finish()
}
}
}

View File

@@ -1,20 +0,0 @@
package app.revanced.utils.signing.align.stream
import java.io.OutputStream
internal class MultiOutputStream(
private val streams: Iterable<OutputStream>,
) : OutputStream() {
override fun write(b: ByteArray, off: Int, len: Int) = streams.forEach {
it.write(b, off, len)
}
override fun write(b: ByteArray) = streams.forEach {
it.write(b)
}
override fun write(b: Int) = streams.forEach {
it.write(b)
}
}

View File

@@ -1,21 +0,0 @@
package app.revanced.utils.signing.align.stream
import java.io.OutputStream
internal class PeekingFakeStream : OutputStream() {
private var numberOfBytes: Long = 0
fun peek() = numberOfBytes
override fun write(b: Int) {
numberOfBytes++
}
override fun write(b: ByteArray) {
numberOfBytes += b.size
}
override fun write(b: ByteArray, offset: Int, len: Int) {
numberOfBytes += len
}
}

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
}
}

View File

@@ -0,0 +1,170 @@
package app.revanced.utils.signing.align.zip.structures
import app.revanced.utils.signing.align.zip.getUShort
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 ZipEntry(
val version: UShort,
val versionNeeded: UShort,
val flags: UShort,
val compression: UShort,
val modificationTime: UShort,
val modificationDate: UShort,
val crc32: UInt,
val compressedSize: UInt,
val uncompressedSize: UInt,
val diskNumber: UShort,
val internalAttributes: UShort,
val externalAttributes: UInt,
var localHeaderOffset: UInt,
val fileName: String,
val extraField: ByteArray,
val fileComment: String,
var localExtraField: ByteArray = ByteArray(0), //seperate for alignment
) {
val LFHSize: Int
get() = LFH_HEADER_SIZE + fileName.toByteArray(Charsets.UTF_8).size + localExtraField.size
val dataOffset: UInt
get() = localHeaderOffset + LFHSize.toUInt()
companion object {
const val CDE_HEADER_SIZE = 46
const val CDE_SIGNATURE = 0x02014b50u
const val LFH_HEADER_SIZE = 30
const val LFH_SIGNATURE = 0x04034b50u
fun fromCDE(input: DataInput): ZipEntry {
val signature = input.readUIntLE()
if (signature != CDE_SIGNATURE)
throw IllegalArgumentException("Input doesn't start with central directory entry signature")
val version = input.readUShortLE()
val versionNeeded = input.readUShortLE()
var flags = input.readUShortLE()
val compression = input.readUShortLE()
val modificationTime = input.readUShortLE()
val modificationDate = input.readUShortLE()
val crc32 = input.readUIntLE()
val compressedSize = input.readUIntLE()
val uncompressedSize = input.readUIntLE()
val fileNameLength = input.readUShortLE()
var fileName = ""
val extraFieldLength = input.readUShortLE()
var extraField = ByteArray(extraFieldLength.toInt())
val fileCommentLength = input.readUShortLE()
var fileComment = ""
val diskNumber = input.readUShortLE()
val internalAttributes = input.readUShortLE()
val externalAttributes = input.readUIntLE()
val localHeaderOffset = input.readUIntLE()
val variableFieldsLength = fileNameLength.toInt() + extraFieldLength.toInt() + fileCommentLength.toInt()
if (variableFieldsLength > 0) {
val fileNameBytes = ByteArray(fileNameLength.toInt())
input.readFully(fileNameBytes)
fileName = fileNameBytes.toString(Charsets.UTF_8)
input.readFully(extraField)
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
input.readFully(fileCommentBytes)
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
}
flags = (flags and 0b1000u.inv().toUShort()) //disable data descriptor flag as they are not used
return ZipEntry(
version,
versionNeeded,
flags,
compression,
modificationTime,
modificationDate,
crc32,
compressedSize,
uncompressedSize,
diskNumber,
internalAttributes,
externalAttributes,
localHeaderOffset,
fileName,
extraField,
fileComment,
)
}
}
fun readLocalExtra(buffer: ByteBuffer) {
buffer.order(ByteOrder.LITTLE_ENDIAN)
localExtraField = ByteArray(buffer.getUShort().toInt())
}
fun toLFH(): ByteBuffer {
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
val buffer = ByteBuffer.allocate(LFH_HEADER_SIZE + nameBytes.size + localExtraField.size)
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
buffer.putUInt(LFH_SIGNATURE)
buffer.putUShort(versionNeeded)
buffer.putUShort(flags)
buffer.putUShort(compression)
buffer.putUShort(modificationTime)
buffer.putUShort(modificationDate)
buffer.putUInt(crc32)
buffer.putUInt(compressedSize)
buffer.putUInt(uncompressedSize)
buffer.putUShort(nameBytes.size.toUShort())
buffer.putUShort(localExtraField.size.toUShort())
buffer.put(nameBytes)
buffer.put(localExtraField)
buffer.flip()
return buffer
}
fun toCDE(): ByteBuffer {
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
val buffer = ByteBuffer.allocate(CDE_HEADER_SIZE + nameBytes.size + extraField.size + commentBytes.size)
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
buffer.putUInt(CDE_SIGNATURE)
buffer.putUShort(version)
buffer.putUShort(versionNeeded)
buffer.putUShort(flags)
buffer.putUShort(compression)
buffer.putUShort(modificationTime)
buffer.putUShort(modificationDate)
buffer.putUInt(crc32)
buffer.putUInt(compressedSize)
buffer.putUInt(uncompressedSize)
buffer.putUShort(nameBytes.size.toUShort())
buffer.putUShort(extraField.size.toUShort())
buffer.putUShort(commentBytes.size.toUShort())
buffer.putUShort(diskNumber)
buffer.putUShort(internalAttributes)
buffer.putUInt(externalAttributes)
buffer.putUInt(localHeaderOffset)
buffer.put(nameBytes)
buffer.put(extraField)
buffer.put(commentBytes)
buffer.flip()
return buffer
}
}