├── .gitignore ├── .idea ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── copyright │ ├── Default.xml │ └── profiles_settings.xml ├── dictionaries │ └── rod.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── .travis.yml ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── README.txt ├── annotation ├── build.gradle └── src │ └── main │ └── java │ └── lanchon │ └── dexpatcher │ └── annotation │ ├── DexAction.java │ ├── DexAdd.java │ ├── DexAppend.java │ ├── DexEdit.java │ ├── DexIgnore.java │ ├── DexPrepend.java │ ├── DexRemove.java │ ├── DexReplace.java │ ├── DexTarget.java │ └── DexWrap.java ├── build.gradle ├── check-copyright.gradle ├── configure-artifacts.gradle ├── configure-publishing.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── test ├── build.gradle ├── compose-mapping.txt ├── encode-mapping.txt ├── mapping.txt ├── patch │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── test │ │ ├── Main.java │ │ ├── info │ │ └── package-info.java │ │ ├── rec │ │ ├── Rec.java │ │ ├── inner │ │ │ └── RecInner.java │ │ └── package-info.java │ │ └── target │ │ └── package-info.java ├── run-dex-via-adb ├── run-patched-dex-via-adb ├── shell-test ├── shell-test-diff ├── shell-test-ref.txt ├── shell-test-set-ref ├── shell-test.config ├── source │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── test │ │ ├── Main.java │ │ ├── info │ │ └── package-info.java │ │ ├── nonrec │ │ ├── NonRec.java │ │ └── inner │ │ │ └── NonRecInner.java │ │ └── rec │ │ ├── Rec.java │ │ └── inner │ │ └── RecInner.java └── template-mapping-ref.txt └── tool ├── about └── lib │ ├── commons-cli-LICENSE.txt │ ├── dexlib2-NOTICE.txt │ ├── guava-COPYING.txt │ └── multidexlib2-NOTICE.txt ├── build.gradle └── src └── main └── java └── lanchon └── dexpatcher ├── Configuration.java ├── Main.java ├── MapReader.java ├── Parser.java ├── Processor.java ├── core ├── Action.java ├── ActionParser.java ├── Context.java ├── DexPatcher.java ├── Marker.java ├── PatchException.java ├── PatcherAnnotation.java ├── logger │ ├── BasicLogger.java │ └── Logger.java ├── model │ ├── BasicClassDef.java │ ├── BasicDexFile.java │ ├── BasicField.java │ ├── BasicMethod.java │ └── BasicMethodImplementation.java ├── patcher │ ├── AbstractPatcher.java │ ├── ActionBasedPatcher.java │ ├── AnnotatableSetPatcher.java │ ├── ClassSetPatcher.java │ ├── FieldSetPatcher.java │ ├── MemberSetPatcher.java │ ├── MethodSetPatcher.java │ └── PackagePatcher.java └── util │ ├── AccessFlagLogger.java │ ├── DexUtils.java │ ├── ElementalTypeRewriter.java │ ├── Id.java │ ├── InvalidTypeDescriptorException.java │ ├── Label.java │ ├── SimpleTypeRewriter.java │ ├── Target.java │ ├── TemplateMapFileWriter.java │ └── TypeName.java └── transform ├── BaseLogger.java ├── MemberLogger.java ├── TransformLogger.java ├── anonymizer ├── DexAnonymizer.java └── TypeAnonymizer.java ├── codec ├── BinaryClassNameRewriter.java ├── DexCodec.java ├── DexCodecModule.java ├── StringCodec.java ├── decoder │ ├── DexDecoder.java │ └── StringDecoder.java └── encoder │ ├── BasicDexEncoder.java │ ├── BasicStringEncoder.java │ ├── CopyStringBuilder.java │ ├── DefaultIgnoredHintTypes.java │ ├── EncoderConfiguration.java │ ├── EncoderDexMap.java │ ├── ReservedWords.java │ ├── StringEscaper.java │ ├── StringEscaperConfiguration.java │ ├── TypeClassifier.java │ ├── TypeHintMapper.java │ └── TypeInfoMapper.java ├── mapper ├── DexMapperModule.java ├── MapFileReader.java ├── PatchRewriterModule.java └── map │ ├── DexMap.java │ ├── DexMaps.java │ ├── LoggingDexMap.java │ └── builder │ ├── CompositeMapBuilder.java │ ├── DexMapping.java │ ├── InverseMapBuilder.java │ └── MapBuilder.java └── util ├── DexVisitor.java └── wrapper ├── WrapperAnnotation.java ├── WrapperAnnotationElement.java ├── WrapperClassDef.java ├── WrapperField.java ├── WrapperFieldReference.java ├── WrapperMethod.java ├── WrapperMethodReference.java └── WrapperRewriterModule.java /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /.idea/workspace.xml 3 | /.idea/libraries 4 | 5 | /build/ 6 | /annotation/build/ 7 | /tool/build/ 8 | /test/build/ 9 | /test/source/build/ 10 | /test/patch/build/ 11 | 12 | /gradle.properties 13 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | dexpatcher-tool -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | 20 | 26 | 27 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/copyright/Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dictionaries/rod.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | annotatable 5 | anonymization 6 | anonymized 7 | anonymizer 8 | anonymizing 9 | balerdi 10 | dalvik 11 | deanon 12 | deanonymization 13 | deanonymize 14 | deanonymized 15 | deanonymizer 16 | deobfuscated 17 | dexlib 18 | dexpatcher 19 | lanchon 20 | multidexlib 21 | nano 22 | opcode 23 | opcodes 24 | precalculated 25 | reanon 26 | reanonymize 27 | reanonymized 28 | reanonymizer 29 | reanonymizing 30 | smali 31 | 32 | 33 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: xenial 3 | language: java 4 | jdk: openjdk8 5 | 6 | script: 7 | - ./gradlew clean build portableTest shellTest --stacktrace 8 | 9 | before_cache: 10 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 11 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 12 | 13 | cache: 14 | directories: 15 | - $HOME/.gradle/caches/ 16 | - $HOME/.gradle/wrapper/ 17 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | ******************************************************************** 2 | DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | (GNU General Public License version 3 or later) 4 | ******************************************************************** 5 | DexPatcher uses dexlib2 (part of smali) by Ben Gruver (JesusFreke). 6 | Many thanks to him for repeatedly helping me in #smali on freenode. 7 | 8 | DexPatcher also uses other free software libraries. Please see the 9 | LIB-LICENSE folder for details. 10 | ******************************************************************** 11 | DexPatcher is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published 13 | by the Free Software Foundation, either version 3 of the License, 14 | or (at your option) any later version. 15 | 16 | DexPatcher is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License 22 | along with DexPatcher. If not, see . 23 | ******************************************************************** 24 | LICENSING NOTE: Recent versions of DexPatcher no longer require that 25 | users bundle the DexPatcher annotations with every patch (although 26 | continuing to do so has no ill effects). The DexPatcher licensing 27 | terms no longer impose licensing restrictions on patches, as long as 28 | users refrain from bundling the DexPatcher annotations with them. 29 | In particular, DexPatcher patches are no longer considered to be 30 | derivative works of DexPatcher and thus are no longer automatically 31 | covered by the GPL. Legals aside, the DexPatcher project urges you 32 | not to use copyright laws to introduce artificial scarcity in the 33 | world. Please give back to the community: share your work. 34 | ******************************************************************** 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DexPatcher-tool 2 | ### Android Dalvik bytecode patcher. 3 | 4 | [![Build Status](https://travis-ci.org/DexPatcher/dexpatcher-tool.svg?branch=master)](https://travis-ci.org/DexPatcher/dexpatcher-tool) 5 | 6 | https://dexpatcher.github.io/ 7 | 8 | DexPatcher is free software. (GPLv3+) 9 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | =============== 2 | DexPatcher-tool 3 | =============== 4 | 5 | Android Dalvik bytecode patcher. 6 | https://dexpatcher.github.io/ 7 | 8 | DexPatcher is free software. (GPLv3+) 9 | -------------------------------------------------------------------------------- /annotation/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | plugins { 12 | id 'java-library' 13 | id 'maven-publish' 14 | id 'signing' 15 | } 16 | 17 | group = parent.group 18 | version = parent.version 19 | 20 | ext.mainArtifact = 'dexpatcher-annotation' 21 | ext.artifactName = 'DexPatcher-annotation' 22 | 23 | sourceCompatibility = '1.6' 24 | def jdk = findProperty('JDK6_HOME') ?: '/usr/lib/jvm/java-6-openjdk-amd64' 25 | def jdk_rt = new File(jdk, 'jre/lib/rt.jar') 26 | if (jdk_rt.exists()) compileJava.options.bootstrapClasspath = files(jdk_rt) 27 | 28 | apply from: '../configure-artifacts.gradle' 29 | 30 | apply from: '../configure-publishing.gradle' 31 | 32 | publishing { 33 | publications { 34 | dexpatcherAnnotation(MavenPublication) { 35 | artifactId = mainArtifact 36 | from components.java 37 | pom { 38 | name = artifactName 39 | configurePom it 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | @DexIgnore 14 | public enum DexAction { 15 | ADD, 16 | EDIT, 17 | REPLACE, 18 | REMOVE, 19 | IGNORE, 20 | WRAP, 21 | PREPEND, 22 | APPEND, 23 | 24 | NONE, 25 | UNDEFINED; 26 | } 27 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexAdd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | import java.lang.annotation.Documented; 14 | import java.lang.annotation.ElementType; 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | import java.lang.annotation.Target; 18 | 19 | @DexIgnore 20 | @Documented 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ 23 | ElementType.PACKAGE, 24 | ElementType.TYPE, 25 | ElementType.CONSTRUCTOR, 26 | ElementType.METHOD, 27 | ElementType.FIELD 28 | }) 29 | public @interface DexAdd {} 30 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexAppend.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | import java.lang.annotation.Documented; 14 | import java.lang.annotation.ElementType; 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | import java.lang.annotation.Target; 18 | 19 | @DexIgnore 20 | @Documented 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ 23 | ElementType.METHOD 24 | }) 25 | public @interface DexAppend { 26 | String target() default ""; 27 | } 28 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexEdit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | import java.lang.annotation.Documented; 14 | import java.lang.annotation.ElementType; 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | import java.lang.annotation.Target; 18 | 19 | @DexIgnore 20 | @Documented 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ 23 | ElementType.TYPE, 24 | ElementType.CONSTRUCTOR, 25 | ElementType.METHOD, 26 | ElementType.FIELD 27 | }) 28 | public @interface DexEdit { 29 | String target() default ""; 30 | Class targetClass() default void.class; 31 | DexAction staticConstructorAction() default DexAction.UNDEFINED; 32 | DexAction defaultAction() default DexAction.UNDEFINED; 33 | boolean contentOnly() default false; 34 | } 35 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexIgnore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | import java.lang.annotation.Documented; 14 | import java.lang.annotation.ElementType; 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | import java.lang.annotation.Target; 18 | 19 | @DexIgnore 20 | @Documented 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ 23 | ElementType.PACKAGE, 24 | ElementType.TYPE, 25 | ElementType.CONSTRUCTOR, 26 | ElementType.METHOD, 27 | ElementType.FIELD, 28 | ElementType.PARAMETER 29 | }) 30 | public @interface DexIgnore {} 31 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexPrepend.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | import java.lang.annotation.Documented; 14 | import java.lang.annotation.ElementType; 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | import java.lang.annotation.Target; 18 | 19 | @DexIgnore 20 | @Documented 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ 23 | ElementType.METHOD 24 | }) 25 | public @interface DexPrepend { 26 | String target() default ""; 27 | } 28 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexRemove.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | import java.lang.annotation.Documented; 14 | import java.lang.annotation.ElementType; 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | import java.lang.annotation.Target; 18 | 19 | @DexIgnore 20 | @Documented 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ 23 | ElementType.PACKAGE, 24 | ElementType.TYPE, 25 | ElementType.CONSTRUCTOR, 26 | ElementType.METHOD, 27 | ElementType.FIELD 28 | }) 29 | public @interface DexRemove { 30 | String target() default ""; 31 | Class targetClass() default void.class; 32 | boolean recursive() default false; 33 | } 34 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexReplace.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | import java.lang.annotation.Documented; 14 | import java.lang.annotation.ElementType; 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | import java.lang.annotation.Target; 18 | 19 | @DexIgnore 20 | @Documented 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ 23 | ElementType.PACKAGE, 24 | ElementType.TYPE, 25 | ElementType.CONSTRUCTOR, 26 | ElementType.METHOD 27 | }) 28 | public @interface DexReplace { 29 | String target() default ""; 30 | Class targetClass() default void.class; 31 | boolean contentOnly() default false; 32 | boolean recursive() default false; 33 | } 34 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexTarget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | @DexIgnore 14 | public final class DexTarget { 15 | public static final String STATIC_CONSTRUCTOR = ""; 16 | public static final String CONSTRUCTOR = ""; 17 | private DexTarget() {} 18 | } 19 | -------------------------------------------------------------------------------- /annotation/src/main/java/lanchon/dexpatcher/annotation/DexWrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.annotation; 12 | 13 | import java.lang.annotation.Documented; 14 | import java.lang.annotation.ElementType; 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | import java.lang.annotation.Target; 18 | 19 | @DexIgnore 20 | @Documented 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ 23 | ElementType.METHOD 24 | }) 25 | public @interface DexWrap { 26 | String target() default ""; 27 | } 28 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | plugins { 12 | id 'base' 13 | } 14 | 15 | group = 'com.github.lanchon.dexpatcher' 16 | version = '1.8.0-beta1' 17 | 18 | apply from: 'check-copyright.gradle' 19 | 20 | wrapper.distributionType = Wrapper.DistributionType.ALL 21 | -------------------------------------------------------------------------------- /check-copyright.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | task checkCopyrightNotice { 12 | dependsOn { subprojects*.tasks.build } 13 | def notice = "Copyright 2015-${new java.text.SimpleDateFormat('yyyy').format(new Date())}" 14 | def checkNotice = { noticeFile -> 15 | if (!file(noticeFile).text.contains(notice)) { 16 | System.err.println "******************************** WARNING ********************************" 17 | System.err.println "Expired copyright notice: ${noticeFile}" 18 | System.err.println "***************************************************************************" 19 | } 20 | } 21 | doLast { 22 | checkNotice 'NOTICE.txt' 23 | } 24 | } 25 | 26 | build { 27 | dependsOn checkCopyrightNotice 28 | } 29 | -------------------------------------------------------------------------------- /configure-artifacts.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | java { 12 | withJavadocJar() 13 | withSourcesJar() 14 | } 15 | 16 | processResources { 17 | from(rootDir) { 18 | include 'README.txt' 19 | include 'NOTICE.txt' 20 | include 'LICENSE.txt' 21 | into 'META-INF/about' 22 | } 23 | } 24 | 25 | def sharedManifest = manifest { 26 | attributes( 27 | 'Implementation-Title': artifactName, 28 | 'Implementation-Version': version 29 | ) 30 | } 31 | 32 | tasks.withType(Jar).configureEach { 33 | archiveBaseName.set mainArtifact 34 | manifest.from sharedManifest 35 | reproducibleFileOrder = true 36 | preserveFileTimestamps = false 37 | duplicatesStrategy = DuplicatesStrategy.FAIL 38 | } 39 | -------------------------------------------------------------------------------- /configure-publishing.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | ext.configurePom = { pom -> 12 | pom.description = 'Android Dalvik bytecode patcher.' 13 | pom.url = 'https://github.com/DexPatcher/dexpatcher-tool' 14 | pom.organization { 15 | name = 'DexPatcher' 16 | url = 'https://dexpatcher.github.io/' 17 | } 18 | pom.licenses { 19 | license { 20 | name = 'GNU General Public License (version 3 or later)' 21 | url = 'https://www.gnu.org/licenses/gpl.txt' 22 | } 23 | } 24 | pom.developers { 25 | developer { 26 | name = 'Lanchon' 27 | url = 'https://github.com/Lanchon' 28 | } 29 | } 30 | pom.scm { 31 | connection = 'scm:git:git://github.com/DexPatcher/dexpatcher-tool.git' 32 | developerConnection = 'scm:git:ssh://github.com:DexPatcher/dexpatcher-tool.git' 33 | url = 'https://github.com/DexPatcher/dexpatcher-tool' 34 | } 35 | } 36 | 37 | publishing { 38 | repositories { 39 | maven { 40 | name = 'local' 41 | url = rootProject.layout.buildDirectory.dir('repository') 42 | } 43 | if (project.hasProperty('publishing.url')) { 44 | maven { 45 | name = 'remote' 46 | url = project.getProperty('publishing.url') 47 | credentials { 48 | username = project.getProperty('publishing.username') 49 | password = project.getProperty('publishing.password') 50 | } 51 | } 52 | } 53 | } 54 | } 55 | 56 | signing { 57 | if (project.hasProperty('signing.secretKeyRingFile')) { 58 | sign publishing.publications 59 | } 60 | } 61 | 62 | assemble.dependsOn publishAllPublicationsToLocalRepository 63 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DexPatcher/dexpatcher-tool/e301fb823a857e684c1dd4503a92fee30f966785/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | include ':annotation', ':tool', ':test', ':test:source', ':test:patch' 12 | -------------------------------------------------------------------------------- /test/compose-mapping.txt: -------------------------------------------------------------------------------- 1 | test.Main$ObfuscatedDbRecord -> test.Main$DbRecord 2 | java.lang.String obfuscatedPut(test.Main$ObfuscatedDbRecord) -> put 3 | test.Main$ObfuscatedDbRecord obfuscatedGet(java.lang.String) -> get 4 | test.Main$ObfuscatedDbRecord[] obfuscatedGetAllRecords() -> getAllRecords 5 | test.Main$ObfuscatedDbRecord obfuscatedParentRecord -> parentRecord 6 | -------------------------------------------------------------------------------- /test/encode-mapping.txt: -------------------------------------------------------------------------------- 1 | test.Main$ObfuscatedClassForEncodingWithMap -> test.EncodedClass 2 | int obfuscatedField -> field 3 | void obfuscatedMethod() -> method 4 | void obfuscatedMethodWithArg(test.Main$ObfuscatedClassForEncodingWithMap) -> methodWithArg 5 | -------------------------------------------------------------------------------- /test/mapping.txt: -------------------------------------------------------------------------------- 1 | test.Main$ObfuscatedThing -> test.Main$Thing: 2 | int obfuscatedField -> field 3 | .int obfuscatedField -> weirdField ; the type of this field is the class named 'int' in the default package 4 | void obfuscatedMethod() -> someMethod 5 | float[][] obfuscatedMethod2(int, .java.lang.String, java.lang.Object[]) -> anotherMethodWithArgs 6 | void yetAnotherObfuscatedMethod(test.Main$ObfuscatedThing[]) -> makeFriendsWithOtherThings 7 | int obfuscatedFieldForTargeting -> targetedField 8 | void obfuscatedMethodForTargeting() -> targetedMethod 9 | 10 | test.Main$ObfuscatedClassForTargeting -> test.Main$TargetedClass 11 | int obfuscatedField -> field 12 | void obfuscatedMethod() -> method 13 | void obfuscatedMethodWithArg(test.Main$ObfuscatedClassForTargeting) -> methodWithArg 14 | int obfuscatedFieldForTargeting -> targetedField 15 | void obfuscatedMethodForTargeting() -> targetedMethod 16 | 17 | /test.Main$DbRecord -> test.Main$User 18 | java.lang.String /put(/test.Main$DbRecord) -> put 19 | /test.Main$DbRecord /get(java.lang.String) -> get 20 | /test.Main$DbRecord[] /getAllRecords() -> getAllUsers 21 | /test.Main$DbRecord /parentRecord -> parentUser 22 | -------------------------------------------------------------------------------- /test/patch/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | plugins { 12 | id 'java' 13 | } 14 | 15 | dependencies { 16 | compileOnly project(':annotation') 17 | } 18 | -------------------------------------------------------------------------------- /test/patch/src/main/java/test/info/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | @DexReplace 12 | package test.info; 13 | 14 | import lanchon.dexpatcher.annotation.*; 15 | -------------------------------------------------------------------------------- /test/patch/src/main/java/test/rec/Rec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package test.rec; 12 | 13 | public class Rec {} 14 | -------------------------------------------------------------------------------- /test/patch/src/main/java/test/rec/inner/RecInner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package test.rec.inner; 12 | 13 | public class RecInner {} 14 | -------------------------------------------------------------------------------- /test/patch/src/main/java/test/rec/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | @DexRemove(recursive = true) 12 | package test.rec; 13 | 14 | import lanchon.dexpatcher.annotation.*; 15 | -------------------------------------------------------------------------------- /test/patch/src/main/java/test/target/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | @DexRemove(target = "test.nonrec") 12 | package test.target; 13 | 14 | import lanchon.dexpatcher.annotation.*; 15 | -------------------------------------------------------------------------------- /test/run-dex-via-adb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | run-dex-via-adb() { 6 | local dexFile="$1" 7 | local mainClass="$2" 8 | echo "--- prepare $dexFile ---" 9 | rm -rf build/run-via-adb 10 | mkdir build/run-via-adb 11 | cp "$dexFile" build/run-via-adb/classes.dex 12 | cd build/run-via-adb 13 | zip run-via-adb.zip classes.dex 14 | cd ../.. 15 | adb push build/run-via-adb/run-via-adb.zip /sdcard/run-via-adb.zip 16 | rm -rf build/run-via-adb 17 | echo "--- run $dexFile ---" 18 | adb shell dalvikvm -cp /sdcard/run-via-adb.zip "$mainClass" 19 | echo "--- end $dexFile ---" 20 | echo 21 | } 22 | 23 | run-dex-via-adb "$@" 24 | -------------------------------------------------------------------------------- /test/run-patched-dex-via-adb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./run-dex-via-adb build/patched.dex test.Main 3 | -------------------------------------------------------------------------------- /test/shell-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source shell-test.config 6 | 7 | run() { 8 | echo -n "\$" 9 | printf " %q" "$@" 10 | echo 11 | "run-$@" 12 | local ret="$?" 13 | echo 14 | return "$ret" 15 | } 16 | 17 | dex-jar() { 18 | name="$1" 19 | jar=$name/build/libs/$name.jar 20 | dex=build/$name.dex.zip 21 | run d8 --no-desugaring --output $dex $jar 22 | #run dex2jar --force --output $name-dex2jar.jar $dex 23 | } 24 | 25 | cat-file() { 26 | file="$1" 27 | echo "--- cat $file ---" 28 | cat $file 29 | echo "--- end $file ---" 30 | echo 31 | } 32 | 33 | run-jar() { 34 | jar="$1" 35 | echo "--- run $jar ---" 36 | run-java -cp $jar test.Main 37 | echo "--- end $jar ---" 38 | echo 39 | } 40 | 41 | mkdir -p build 42 | 43 | dex-jar source 44 | dex-jar patch 45 | 46 | run dexpatcher --help 47 | run dexpatcher build/source.dex.zip build/patch.dex.zip --output build/patched.dex\ 48 | --create-map build/template-mapping.txt\ 49 | --map-source --unmap-output --map mapping.txt --compose-map compose-mapping.txt\ 50 | --encode-source --encode-map encode-mapping.txt --escape-non-ascii --encode-reserved-chars\ 51 | --encode-obfuscated-classes --obfuscated-classes '.*OBF' --encode-class-hints\ 52 | --encode-obfuscated-members --obfuscated-members '.*OBF' --encode-member-hints\ 53 | --deanon-source --reanon-output --main-plan Anon[_Level] --deanon-patches-alt --no-reanon-errors\ 54 | --decode-patches --no-decode-errors\ 55 | --debug "$@" 56 | 57 | run sha1sum build/patched.dex 58 | 59 | #cat-file build/template-mapping.txt 60 | run sha1sum build/template-mapping.txt 61 | 62 | if "$RUN_RELEASE_PATCH_TEST"; then 63 | 64 | run dexpatcher build/source.dex.zip build/patch.dex.zip --output build/patched-release.dex\ 65 | --unmap-patches --map mapping.txt --compose-map compose-mapping.txt\ 66 | --encode-source --encode-map encode-mapping.txt --escape-non-ascii --encode-reserved-chars\ 67 | --encode-obfuscated-classes --obfuscated-classes '.*OBF' --encode-class-hints\ 68 | --encode-obfuscated-members --obfuscated-members '.*OBF' --encode-member-hints\ 69 | --reanon-patches --main-plan Anon[_Level] --deanon-patches-alt --no-reanon-errors\ 70 | --decode-patches --no-decode-errors\ 71 | --quiet "$@" 72 | 73 | #run sha1sum build/patched-release.dex 74 | run cmp --quiet build/patched.dex build/patched-release.dex || { 75 | echo "error: patched-release.dex differs from patched.dex" 76 | exit 1 77 | } 78 | 79 | fi 80 | 81 | run dex2jar --force --output build/patched-dex2jar.jar build/patched.dex 82 | 83 | run-jar source/build/libs/source.jar 84 | run-jar build/patched-dex2jar.jar 85 | -------------------------------------------------------------------------------- /test/shell-test-diff: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | mkdir -p build 6 | 7 | ./shell-test "$@" &>build/shell-test-out.txt || { 8 | echo "error: shell-test: execution failed" 9 | echo 10 | echo "====== full output ======" 11 | echo 12 | cat build/shell-test-out.txt 13 | echo 14 | exit 1 15 | } 16 | 17 | diff shell-test-ref.txt build/shell-test-out.txt &>/dev/null || { 18 | echo "error: shell-test: unexpected output" 19 | #echo 20 | #echo "====== full output ======" 21 | #echo 22 | #cat build/shell-test-out.txt 23 | #echo 24 | 25 | sedcmd='s|\(Main\.java\:[0-9]+\)\:|\(Main\.java\:\<\?\>\)\:|g' 26 | sed -r "$sedcmd" shell-test-ref.txt >build/shell-test-ref-filtered.txt 27 | sed -r "$sedcmd" build/shell-test-out.txt >build/shell-test-out-filtered.txt 28 | 29 | echo 30 | echo "====== diff output ======" 31 | echo 32 | diff build/shell-test-ref-filtered.txt build/shell-test-out-filtered.txt --color=always -U8 && { 33 | echo "info: shell-test: only source code line numbers differ" 34 | } || true 35 | echo 36 | exit 2 37 | } 38 | -------------------------------------------------------------------------------- /test/shell-test-set-ref: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | mkdir -p build 6 | 7 | ./shell-test "$@" &> build/shell-test-out.txt 8 | 9 | cp build/shell-test-out.txt shell-test-ref.txt 10 | cp build/template-mapping.txt template-mapping-ref.txt 11 | -------------------------------------------------------------------------------- /test/shell-test.config: -------------------------------------------------------------------------------- 1 | # Tool Path Configuration 2 | 3 | run-java() { java "$@"; } 4 | run-d8() { run-java -cp 'build/tools/r8/*' 'com.android.tools.r8.D8' "$@"; } 5 | run-dex2jar() { run-java -cp 'build/tools/dex2jar/*' 'com.googlecode.dex2jar.tools.Dex2jarCmd' "$@"; } 6 | 7 | run-dexpatcher() { run-java -jar ../tool/build/libs/dexpatcher-[0-9]*.jar "$@"; } 8 | 9 | run-sha1sum() { sha1sum "$@"; } 10 | run-cmp() { cmp "$@"; } 11 | 12 | RUN_RELEASE_PATCH_TEST=true 13 | -------------------------------------------------------------------------------- /test/source/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | plugins { 12 | id 'java' 13 | } 14 | -------------------------------------------------------------------------------- /test/source/src/main/java/test/info/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | @Deprecated 12 | package test.info; 13 | -------------------------------------------------------------------------------- /test/source/src/main/java/test/nonrec/NonRec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package test.nonrec; 12 | 13 | public class NonRec {} 14 | -------------------------------------------------------------------------------- /test/source/src/main/java/test/nonrec/inner/NonRecInner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package test.nonrec.inner; 12 | 13 | public class NonRecInner {} 14 | -------------------------------------------------------------------------------- /test/source/src/main/java/test/rec/Rec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package test.rec; 12 | 13 | public class Rec {} 14 | -------------------------------------------------------------------------------- /test/source/src/main/java/test/rec/inner/RecInner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package test.rec.inner; 12 | 13 | public class RecInner {} 14 | -------------------------------------------------------------------------------- /tool/about/lib/dexlib2-NOTICE.txt: -------------------------------------------------------------------------------- 1 | The majority of smali/baksmali is written and copyrighted by me (Ben Gruver) 2 | and released under the following license: 3 | 4 | ******************************************************************************* 5 | Copyright (c) 2010 Ben Gruver (JesusFreke) 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions 10 | are met: 11 | 1. Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | ******************************************************************************* 30 | 31 | 32 | Unless otherwise stated in the code/commit message, any changes with the 33 | committer of bgruv@google.com is copyrighted by Google Inc. and released 34 | under the following license: 35 | 36 | ******************************************************************************* 37 | Copyright 2011, Google Inc. 38 | All rights reserved. 39 | 40 | Redistribution and use in source and binary forms, with or without 41 | modification, are permitted provided that the following conditions are 42 | met: 43 | 44 | * Redistributions of source code must retain the above copyright 45 | notice, this list of conditions and the following disclaimer. 46 | * Redistributions in binary form must reproduce the above 47 | copyright notice, this list of conditions and the following disclaimer 48 | in the documentation and/or other materials provided with the 49 | distribution. 50 | * Neither the name of Google Inc. nor the names of its 51 | contributors may be used to endorse or promote products derived from 52 | this software without specific prior written permission. 53 | 54 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 55 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 56 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 57 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 58 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 59 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 60 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 61 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 62 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 63 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 64 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 65 | ******************************************************************************* 66 | 67 | 68 | Various portions of the code are taken from the Android Open Source Project, 69 | and are used in accordance with the following license: 70 | 71 | ******************************************************************************* 72 | Copyright (C) 2007 The Android Open Source Project 73 | 74 | Licensed under the Apache License, Version 2.0 (the "License"); 75 | you may not use this file except in compliance with the License. 76 | You may obtain a copy of the License at 77 | 78 | http://www.apache.org/licenses/LICENSE-2.0 79 | 80 | Unless required by applicable law or agreed to in writing, software 81 | distributed under the License is distributed on an "AS IS" BASIS, 82 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 83 | See the License for the specific language governing permissions and 84 | limitations under the License. 85 | ******************************************************************************* -------------------------------------------------------------------------------- /tool/about/lib/multidexlib2-NOTICE.txt: -------------------------------------------------------------------------------- 1 | ******************************************************************** 2 | multidexlib2 - Copyright 2015-2020 Rodrigo Balerdi 3 | (GNU General Public License version 3 or later) 4 | ******************************************************************** 5 | multidexlib2 is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, 8 | or (at your option) any later version. 9 | 10 | multidexlib2 is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with multidexlib2. If not, see . 17 | ******************************************************************** 18 | -------------------------------------------------------------------------------- /tool/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | plugins { 12 | id 'java-library' 13 | id 'com.github.johnrengelman.shadow' version '5.2.0' 14 | id 'maven-publish' 15 | id 'signing' 16 | } 17 | 18 | group = parent.group 19 | version = parent.version 20 | 21 | ext.mainArtifact = 'dexpatcher-tool' 22 | ext.artifactName = 'DexPatcher-tool' 23 | 24 | def fatArtifact = 'dexpatcher' 25 | 26 | sourceCompatibility = '1.7' 27 | def jdk = findProperty('JDK7_HOME') ?: '/usr/lib/jvm/java-7-openjdk-amd64' 28 | def jdk_rt = new File(jdk, 'jre/lib/rt.jar') 29 | if (jdk_rt.exists()) compileJava.options.bootstrapClasspath = files(jdk_rt) 30 | 31 | repositories { 32 | jcenter() 33 | 34 | // Use local version of multidexlib2: 35 | maven { 36 | url = '../../multidexlib2/build/repository' 37 | } 38 | } 39 | 40 | def dexlib2Version = '2.3.4' 41 | def multidexlib2VersionSuffix = '.r2' 42 | 43 | def multidexlib2Version = dexlib2Version + multidexlib2VersionSuffix 44 | 45 | dependencies { 46 | implementation 'commons-cli:commons-cli:1.4' 47 | implementation 'com.github.lanchon.dexpatcher:multidexlib2:' + multidexlib2Version 48 | api 'org.smali:dexlib2:' + dexlib2Version 49 | } 50 | 51 | compileJava { 52 | options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' 53 | } 54 | 55 | apply from: '../configure-artifacts.gradle' 56 | 57 | def versionFile = layout.buildDirectory.file('version-file/version') 58 | 59 | task createVersionFile { 60 | inputs.property 'version', version 61 | outputs.file versionFile 62 | doLast { 63 | versionFile.get().asFile.text = version 64 | } 65 | } 66 | 67 | processResources { 68 | dependsOn createVersionFile 69 | from(versionFile) { 70 | into 'lanchon/dexpatcher' 71 | } 72 | } 73 | 74 | // The Shadow plugin erroneously unpacks nested jars. 75 | // The issue can be worked around by double-jarring the jars. 76 | task shadowBugWorkaround(type: Jar) { 77 | destinationDirectory.set layout.buildDirectory.dir('shadow-bug-workaround') 78 | archiveBaseName.set 'nested-content' 79 | from jar 80 | from tasks.getByPath(':annotation:jar') 81 | } 82 | 83 | shadowJar { 84 | archiveBaseName.set fatArtifact 85 | archiveClassifier.set null 86 | manifest { 87 | attributes( 88 | 'Main-Class': 'lanchon.dexpatcher.Main' 89 | ) 90 | } 91 | exclude '*.txt' 92 | exclude 'META-INF/*.txt' 93 | exclude 'META-INF/maven/**' 94 | from('about') { 95 | into 'META-INF/about' 96 | } 97 | //from jar 98 | //from tasks.getByPath(':annotation:jar') 99 | from shadowBugWorkaround 100 | } 101 | 102 | assemble.dependsOn shadowJar 103 | 104 | apply from: '../configure-publishing.gradle' 105 | 106 | publishing { 107 | publications { 108 | dexpatcherTool(MavenPublication) { 109 | artifactId = mainArtifact 110 | from components.java 111 | pom { 112 | name = artifactName 113 | configurePom it 114 | } 115 | } 116 | dexpatcher(MavenPublication) { 117 | artifactId = fatArtifact 118 | artifact shadowJar 119 | artifact javadocJar 120 | artifact sourcesJar 121 | pom { 122 | name = artifactName + ' (Fat JAR)' 123 | configurePom it 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher; 12 | 13 | import java.util.Collections; 14 | 15 | import lanchon.dexpatcher.Processor.PreTransform; 16 | import lanchon.dexpatcher.core.Context; 17 | import lanchon.dexpatcher.core.logger.Logger; 18 | import lanchon.dexpatcher.transform.anonymizer.TypeAnonymizer; 19 | import lanchon.dexpatcher.transform.codec.StringCodec; 20 | import lanchon.dexpatcher.transform.codec.encoder.EncoderConfiguration; 21 | import lanchon.multidexlib2.DexIO; 22 | 23 | public class Configuration { 24 | 25 | public String sourceFile; 26 | public Iterable patchFiles = Collections.emptyList(); 27 | 28 | public int apiLevel; 29 | 30 | public boolean multiDex; 31 | public int multiDexJobs = 1; 32 | 33 | public int maxDexPoolSize = DexIO.DEFAULT_MAX_DEX_POOL_SIZE; 34 | 35 | public String annotationPackage = Context.DEFAULT_ANNOTATION_PACKAGE; 36 | public boolean constructorAutoIgnoreDisabled; 37 | 38 | public String patchedFile; 39 | public String templateMapFile; 40 | public boolean dryRun; 41 | 42 | public Logger.Level logLevel = Context.DEFAULT_LOG_LEVEL; 43 | 44 | public String sourceCodeRoot; 45 | public boolean timingStats; 46 | 47 | // Code transform options: 48 | 49 | public boolean mapSource; 50 | public boolean deanonSource; 51 | public boolean deanonSourceAlternate; 52 | public boolean encodeSource; 53 | public boolean decodeSource; 54 | public boolean reanonSource; 55 | public boolean unmapSource; 56 | 57 | public boolean deanonPatches; 58 | public boolean deanonPatchesAlternate; 59 | public boolean decodePatches; 60 | public boolean reanonPatches; 61 | public boolean unmapPatches; 62 | 63 | public boolean decodeOutput; 64 | public boolean reanonOutput; 65 | public boolean unmapOutput; 66 | 67 | public Iterable mapFiles; 68 | public boolean invertMap; 69 | public Iterable composeMapFiles; 70 | public boolean invertComposeMap; 71 | 72 | public String mainAnonymizationPlan = TypeAnonymizer.DEFAULT_MAIN_ANONYMIZATION_PLAN; 73 | public String alternateAnonymizationPlan = TypeAnonymizer.DEFAULT_ALTERNATE_ANONYMIZATION_PLAN; 74 | public boolean treatReanonymizeErrorsAsWarnings; 75 | 76 | public String codeMarker = StringCodec.DEFAULT_CODE_MARKER; 77 | public Iterable encodeMapFiles; 78 | public boolean invertEncodeMap; 79 | public EncoderConfiguration encoderConfiguration = new EncoderConfiguration(); 80 | public boolean treatDecodeErrorsAsWarnings; 81 | 82 | public PreTransform preTransform = Processor.DEFAULT_PRE_TRANSFORM; 83 | 84 | } 85 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.InputStreamReader; 17 | import java.util.Locale; 18 | 19 | import lanchon.dexpatcher.core.logger.BasicLogger; 20 | import lanchon.dexpatcher.core.logger.Logger; 21 | 22 | import org.apache.commons.cli.ParseException; 23 | 24 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 25 | 26 | public class Main { 27 | 28 | public static void main(String[] args) { 29 | Locale.setDefault(Locale.ROOT); 30 | int value = runWithUsage(args); 31 | System.exit(value); 32 | } 33 | 34 | public static int runWithUsage(String[] args) { 35 | if (args.length == 0) { 36 | Parser.printUsage(); 37 | return 2; 38 | } else { 39 | return run(args); 40 | } 41 | } 42 | 43 | public static int run(String[] args) { 44 | return run(args, new BasicLogger()); 45 | } 46 | 47 | public static int run(String[] args, Logger logger) { 48 | try { 49 | boolean success = runWithExceptions(args, logger); 50 | return success ? 0 : 1; 51 | } catch (ParseException e) { 52 | logger.log(FATAL, e.getMessage()); 53 | return 2; 54 | } catch (Exception e) { 55 | if (logger.isLogging(DEBUG)) { 56 | logger.log(FATAL, "exception:", e); 57 | } else { 58 | logger.log(FATAL, "exception: " + e); 59 | } 60 | return 3; 61 | } finally { 62 | logger.flush(); 63 | } 64 | } 65 | 66 | public static boolean runWithExceptions(String[] args, Logger logger) throws ParseException, IOException { 67 | try { 68 | Configuration config = Parser.parseCommandLine(args); 69 | if (config == null) return true; 70 | logger.log(NONE, getHeader()); 71 | return Processor.processFiles(logger, config); 72 | } finally { 73 | logger.flush(); 74 | } 75 | } 76 | 77 | public static String getVersion() { 78 | final String FILE = "version"; 79 | try (InputStream is = Main.class.getResourceAsStream(FILE)) { 80 | return new BufferedReader(new InputStreamReader(is)).readLine().trim(); 81 | } catch (IOException e) { 82 | return ""; 83 | } 84 | } 85 | 86 | public static String getHeader() { 87 | return "DexPatcher version " + Main.getVersion() + " by Lanchon (https://dexpatcher.github.io/)"; 88 | } 89 | 90 | private Main() {} 91 | 92 | } 93 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/MapReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | 16 | import lanchon.dexpatcher.core.logger.Logger; 17 | import lanchon.dexpatcher.transform.mapper.MapFileReader; 18 | import lanchon.dexpatcher.transform.mapper.map.DexMap; 19 | import lanchon.dexpatcher.transform.mapper.map.builder.CompositeMapBuilder; 20 | import lanchon.dexpatcher.transform.mapper.map.builder.DexMapping; 21 | import lanchon.dexpatcher.transform.mapper.map.builder.InverseMapBuilder; 22 | import lanchon.dexpatcher.transform.mapper.map.builder.MapBuilder; 23 | 24 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 25 | 26 | public final class MapReader { 27 | 28 | public static boolean readMapPair(Iterable mapFiles, DexMap inverseComposeMap, boolean invertMap, 29 | DexMapping directMap, DexMapping inverseMap, Logger logger) throws IOException { 30 | return readMapPair(mapFiles, inverseComposeMap, invertMap ? inverseMap : directMap, invertMap ? directMap : inverseMap, 31 | logger); 32 | } 33 | 34 | public static boolean readMapPair(Iterable mapFiles, DexMap inverseComposeMap, DexMapping directMap, 35 | MapBuilder inverseMap, Logger logger) throws IOException { 36 | // The direct map is needed to read the inverse map. (It will be discarded if not needed further.) 37 | if (directMap == null) { 38 | if (inverseMap == null) return true; 39 | directMap = new DexMapping(); 40 | } 41 | MapBuilder directMapBuilder = CompositeMapBuilder.of(directMap, inverseComposeMap); 42 | boolean success = readMap(mapFiles, directMapBuilder, logger); 43 | // Read the inverse map only if needed. (It is more memory efficient to read it again from disk.) 44 | if (inverseMap != null && (success || !Processor.ABORT_ON_EARLY_ERRORS)) { 45 | MapBuilder inverseMapBuilder = new InverseMapBuilder(inverseMap, directMap); 46 | inverseMapBuilder = CompositeMapBuilder.of(inverseMapBuilder, inverseComposeMap); 47 | success = readMap(mapFiles, inverseMapBuilder, logger) && success; 48 | } 49 | return success; 50 | } 51 | 52 | public static boolean readMap(Iterable mapFiles, MapBuilder mapBuilder, Logger logger) throws IOException { 53 | int errors = logger.getMessageCount(FATAL) + logger.getMessageCount(ERROR); 54 | for (String mapFile : mapFiles) { 55 | MapFileReader.read(new File(mapFile), true, mapBuilder, logger); 56 | } 57 | return (errors == (logger.getMessageCount(FATAL) + logger.getMessageCount(ERROR))); 58 | } 59 | 60 | private MapReader() {} 61 | 62 | } 63 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/Action.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core; 12 | 13 | import java.util.Locale; 14 | 15 | import org.jf.dexlib2.iface.value.EnumEncodedValue; 16 | 17 | public enum Action { 18 | 19 | ADD("DexAdd", false), 20 | EDIT("DexEdit", true), 21 | REPLACE("DexReplace", false), 22 | REMOVE("DexRemove", true), 23 | IGNORE("DexIgnore", true), 24 | WRAP("DexWrap", false), 25 | PREPEND("DexPrepend", false), 26 | APPEND("DexAppend", false), 27 | 28 | NONE(null, false); 29 | 30 | public static final String NAME_UNDEFINED = "UNDEFINED"; 31 | 32 | private final String className; 33 | private final String label; 34 | private final boolean ignoresCode; 35 | 36 | Action(String className, boolean ignoresCode) { 37 | this.className = className; 38 | label = name().toLowerCase(Locale.ROOT); 39 | this.ignoresCode = ignoresCode; 40 | } 41 | 42 | public String getClassName() { 43 | return className; 44 | } 45 | 46 | public String getLabel() { 47 | return label; 48 | } 49 | 50 | public boolean ignoresCode() { 51 | return ignoresCode; 52 | } 53 | 54 | public PatchException invalidAction() { 55 | return new PatchException("invalid action (" + label + ")"); 56 | } 57 | 58 | public static Action fromEnumEncodedValue(EnumEncodedValue value) { 59 | String s = value.getValue().getName(); 60 | if (NAME_UNDEFINED.equals(s)) return null; 61 | return Action.valueOf(s); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/ActionParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core; 12 | 13 | import java.util.Collections; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | import lanchon.dexpatcher.core.util.TypeName; 18 | 19 | public class ActionParser { 20 | 21 | private final String annotationPackage; 22 | private final Map actionMap; 23 | 24 | public ActionParser(String annotationPackage) { 25 | this.annotationPackage = annotationPackage; 26 | if (annotationPackage == null) { 27 | actionMap = Collections.emptyMap(); 28 | } else { 29 | Action[] actions = Action.values(); 30 | int sizeFactor = 4; 31 | actionMap = new HashMap<>(sizeFactor * actions.length); 32 | for (Action action : actions) { 33 | String actionTypeDescriptor = getTypeDescriptor(action); 34 | if (actionTypeDescriptor != null) actionMap.put(actionTypeDescriptor, action); 35 | } 36 | } 37 | } 38 | 39 | public String getAnnotationPackage() { 40 | return annotationPackage; 41 | } 42 | 43 | public boolean isDisabled() { 44 | return annotationPackage == null; 45 | } 46 | 47 | private String getTypeDescriptor(Action action) { 48 | String className = action.getClassName(); 49 | if (className == null) return null; 50 | if (!annotationPackage.isEmpty()) className = annotationPackage + '.' + className; 51 | return TypeName.toClassDescriptor(className); 52 | } 53 | 54 | public Action parseTypeDescriptor(String typeDescriptor) { 55 | return actionMap.get(typeDescriptor); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/Context.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core; 12 | 13 | import java.io.File; 14 | 15 | import lanchon.dexpatcher.core.logger.BasicLogger; 16 | import lanchon.dexpatcher.core.logger.Logger; 17 | 18 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 19 | 20 | public class Context { 21 | 22 | public static final String DEFAULT_ANNOTATION_PACKAGE = "lanchon.dexpatcher.annotation"; 23 | public static final Logger.Level DEFAULT_LOG_LEVEL = WARN; 24 | 25 | public static class Builder { 26 | 27 | private final Logger logger; 28 | private String annotationPackage = DEFAULT_ANNOTATION_PACKAGE; 29 | private boolean constructorAutoIgnoreDisabled; 30 | private String sourceCodeRoot; 31 | 32 | public Builder() { 33 | this(DEFAULT_LOG_LEVEL); 34 | } 35 | 36 | public Builder(Logger.Level logLevel) { 37 | this(new BasicLogger()); 38 | logger.setLogLevel(logLevel); 39 | } 40 | 41 | public Builder(Logger logger) { 42 | this.logger = logger; 43 | } 44 | 45 | public Builder setAnnotationPackage(String value) { 46 | annotationPackage = value; 47 | return this; 48 | } 49 | 50 | public Builder setConstructorAutoIgnoreDisabled(boolean value) { 51 | constructorAutoIgnoreDisabled = value; 52 | return this; 53 | } 54 | 55 | public Builder setSourceCodeRoot(String value) { 56 | sourceCodeRoot = value; 57 | return this; 58 | } 59 | 60 | public Context build() { 61 | return new Context(logger, annotationPackage, constructorAutoIgnoreDisabled, sourceCodeRoot); 62 | } 63 | 64 | } 65 | 66 | private final Logger logger; 67 | private final ActionParser actionParser; 68 | private final boolean constructorAutoIgnoreDisabled; 69 | private final String sourceCodeRoot; 70 | 71 | private Context(Logger logger, String annotationPackage, boolean constructorAutoIgnoreDisabled, 72 | String sourceCodeRoot) { 73 | this.logger = logger; 74 | actionParser = new ActionParser(annotationPackage); 75 | this.constructorAutoIgnoreDisabled = constructorAutoIgnoreDisabled; 76 | if (sourceCodeRoot != null && !sourceCodeRoot.isEmpty() && !sourceCodeRoot.endsWith(File.separator)) { 77 | sourceCodeRoot += File.separator; 78 | } 79 | this.sourceCodeRoot = sourceCodeRoot; 80 | } 81 | 82 | public Logger getLogger() { 83 | return logger; 84 | } 85 | 86 | public ActionParser getActionParser() { 87 | return actionParser; 88 | } 89 | 90 | public boolean isConstructorAutoIgnoreDisabled() { 91 | return constructorAutoIgnoreDisabled; 92 | } 93 | 94 | public String getSourceCodeRoot() { 95 | return sourceCodeRoot; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/DexPatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core; 12 | 13 | import java.util.Collection; 14 | import java.util.Collections; 15 | import java.util.LinkedHashSet; 16 | import java.util.Set; 17 | 18 | import lanchon.dexpatcher.core.model.BasicDexFile; 19 | import lanchon.dexpatcher.core.patcher.PackagePatcher; 20 | 21 | import org.jf.dexlib2.Opcodes; 22 | import org.jf.dexlib2.iface.ClassDef; 23 | import org.jf.dexlib2.iface.DexFile; 24 | 25 | public class DexPatcher { 26 | 27 | public static DexFile process(Context context, DexFile sourceDex, DexFile patchDex) { 28 | return process(context, sourceDex, patchDex, sourceDex.getOpcodes()); 29 | } 30 | 31 | public static DexFile process(Context context, DexFile sourceDex, DexFile patchDex, Opcodes opcodes) { 32 | Set sourceClasses = sourceDex.getClasses(); 33 | Set patchClasses = patchDex.getClasses(); 34 | PackagePatcher patcher = new PackagePatcher(context); 35 | Collection patchedClasses = patcher.process(sourceClasses, sourceClasses.size(), 36 | patchClasses, patchClasses.size()); 37 | return new BasicDexFile(opcodes, Collections.unmodifiableSet(new LinkedHashSet<>(patchedClasses))); 38 | } 39 | 40 | private DexPatcher() {} 41 | 42 | } 43 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/Marker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core; 12 | 13 | import lanchon.dexpatcher.core.util.TypeName; 14 | 15 | public class Marker { 16 | 17 | // Annotation Elements 18 | 19 | public static final String ELEM_TARGET = "target"; 20 | public static final String ELEM_TARGET_CLASS = "targetClass"; 21 | public static final String ELEM_STATIC_CONSTRUCTOR_ACTION = "staticConstructorAction"; 22 | public static final String ELEM_DEFAULT_ACTION = "defaultAction"; 23 | public static final String ELEM_CONTENT_ONLY = "contentOnly"; 24 | public static final String ELEM_RECURSIVE = "recursive"; 25 | 26 | public static final String ELEM_ONLY_EDIT_MEMBERS = "onlyEditMembers"; // deprecated 27 | 28 | // Actions 29 | 30 | private static final String DEXPATCHER_PREFIX = "__$"; 31 | 32 | public static final String WRAP_SOURCE_SUFFIX = DEXPATCHER_PREFIX + "wrapSource"; 33 | public static final String PREPEND_SOURCE_SUFFIX = DEXPATCHER_PREFIX + "prependSource"; 34 | public static final String PREPEND_PATCH_SUFFIX = DEXPATCHER_PREFIX + "prependPatch"; 35 | public static final String APPEND_SOURCE_SUFFIX = DEXPATCHER_PREFIX + "appendSource"; 36 | public static final String APPEND_PATCH_SUFFIX = DEXPATCHER_PREFIX + "appendPatch"; 37 | 38 | public static final String SPECIAL_METHOD_PREFIX = DEXPATCHER_PREFIX; 39 | 40 | // Dalvik 41 | 42 | public static final String TYPE_VOID = "V"; 43 | public static final String TYPE_INNER_CLASS = TypeName.toClassDescriptor("dalvik.annotation.InnerClass"); 44 | public static final String ELEM_ACCESS_FLAGS = "accessFlags"; 45 | public static final String NAME_VOID = "void"; 46 | public static final String NAME_STATIC_CONSTRUCTOR = ""; 47 | public static final String NAME_INSTANCE_CONSTRUCTOR = ""; 48 | public static final String NAME_PACKAGE_INFO = "package-info"; 49 | 50 | private Marker() {} 51 | 52 | } 53 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/PatchException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core; 12 | 13 | @SuppressWarnings("serial") 14 | public class PatchException extends Exception { 15 | 16 | public PatchException(String message) { 17 | super(message); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/logger/BasicLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.logger; 12 | 13 | import java.io.PrintStream; 14 | import java.io.PrintWriter; 15 | 16 | public class BasicLogger extends Logger { 17 | 18 | private final PrintWriter out; 19 | private final PrintWriter err; 20 | 21 | public BasicLogger() { 22 | this(System.out, System.err); 23 | } 24 | 25 | public BasicLogger(PrintStream out) { 26 | this(out, null); 27 | } 28 | 29 | public BasicLogger(PrintWriter out) { 30 | this(out, null); 31 | } 32 | 33 | public BasicLogger(PrintStream out, PrintStream err) { 34 | this(new PrintWriter(out, true), err != null ? new PrintWriter(err, true) : null); 35 | } 36 | 37 | public BasicLogger(PrintWriter out, PrintWriter err) { 38 | this.out = out; 39 | this.err = err; 40 | flush(); 41 | } 42 | 43 | @Override 44 | protected void doLog(Level level, String message, Throwable throwable) { 45 | boolean isError = false; 46 | switch (level) { 47 | case ERROR: 48 | case FATAL: 49 | isError = true; 50 | case DEBUG: 51 | case INFO: 52 | case WARN: 53 | message = level.getLabel() + ": " + message; 54 | case NONE: 55 | break; 56 | default: 57 | throw new AssertionError("Unexpected log level"); 58 | } 59 | if (isError && err != null) { 60 | out.flush(); 61 | err.println(message); 62 | if (throwable != null) throwable.printStackTrace(err); 63 | err.flush(); 64 | } else { 65 | out.println(message); 66 | if (throwable != null) { 67 | throwable.printStackTrace(out); 68 | out.flush(); 69 | } 70 | } 71 | } 72 | 73 | @Override 74 | public void flush() { 75 | out.flush(); 76 | if (err != null) err.flush(); 77 | } 78 | 79 | @Override 80 | public void close() { 81 | out.close(); 82 | if (err != null) err.close(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/logger/Logger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.logger; 12 | 13 | import java.io.Closeable; 14 | import java.io.Flushable; 15 | 16 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 17 | 18 | public abstract class Logger implements Flushable, Closeable { 19 | 20 | public enum Level { 21 | 22 | DEBUG("debug"), 23 | INFO("info"), 24 | WARN("warning"), 25 | ERROR("error"), 26 | FATAL("fatal"), 27 | NONE("none"); 28 | 29 | private final String label; 30 | 31 | Level(String label) { 32 | this.label = label; 33 | } 34 | 35 | public String getLabel() { 36 | return label; 37 | } 38 | 39 | } 40 | 41 | private Level logLevel; 42 | private int logLevelOrdinal; 43 | private int isLoggingOrdinal; 44 | private int[] counts; 45 | 46 | public Logger() { 47 | setLogLevel(DEBUG); 48 | clearMessageCounts(); 49 | } 50 | 51 | public final void log(Level level, String message) { 52 | log(level, message, null); 53 | } 54 | 55 | public final void log(Level level, String message, Throwable throwable) { 56 | if (message == null) throw new NullPointerException("message"); 57 | int levelOrdinal = level.ordinal(); 58 | counts[levelOrdinal]++; 59 | if (levelOrdinal >= logLevelOrdinal) doLog(level, message, throwable); 60 | } 61 | 62 | public Level getLogLevel() { 63 | return logLevel; 64 | } 65 | 66 | public void setLogLevel(Level logLevel) { 67 | this.logLevel = logLevel; 68 | logLevelOrdinal = logLevel.ordinal(); 69 | isLoggingOrdinal = Math.min(logLevelOrdinal, WARN.ordinal()); 70 | } 71 | 72 | public final boolean isLogging(Level level) { 73 | return level.ordinal() >= isLoggingOrdinal; 74 | } 75 | 76 | public int getMessageCount(Level level) { 77 | return counts[level.ordinal()]; 78 | } 79 | 80 | public void clearMessageCounts() { 81 | counts = new int[Level.values().length]; 82 | } 83 | 84 | public boolean hasNotLoggedErrors() { 85 | int errors = getMessageCount(FATAL) + getMessageCount(ERROR); 86 | return errors == 0; 87 | } 88 | 89 | public void logErrorAndWarningCounts() { 90 | int errors = getMessageCount(FATAL) + getMessageCount(ERROR); 91 | int warnings = getMessageCount(WARN); 92 | //if (errors != 0 || warnings != 0) { 93 | log(NONE, errors + " error(s), " + warnings + " warning(s)"); 94 | //} 95 | } 96 | 97 | protected abstract void doLog(Level level, String message, Throwable throwable); 98 | 99 | public abstract void flush(); 100 | public abstract void close(); 101 | 102 | } 103 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/model/BasicClassDef.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.model; 12 | 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | import com.google.common.collect.Iterables; 17 | import org.jf.dexlib2.base.reference.BaseTypeReference; 18 | import org.jf.dexlib2.iface.Annotation; 19 | import org.jf.dexlib2.iface.ClassDef; 20 | import org.jf.dexlib2.iface.Field; 21 | import org.jf.dexlib2.iface.Method; 22 | import org.jf.dexlib2.util.FieldUtil; 23 | import org.jf.dexlib2.util.MethodUtil; 24 | 25 | public class BasicClassDef extends BaseTypeReference implements ClassDef { 26 | 27 | private final String type; 28 | private final int accessFlags; 29 | private final String superclass; 30 | private final List interfaces; 31 | private final String sourceFile; 32 | private final Set annotations; 33 | private final Iterable fields; 34 | private final Iterable staticFields; 35 | private final Iterable instanceFields; 36 | private final Iterable methods; 37 | private final Iterable directMethods; 38 | private final Iterable virtualMethods; 39 | 40 | public BasicClassDef( 41 | String type, 42 | int accessFlags, 43 | String superclass, 44 | List interfaces, 45 | String sourceFile, 46 | Set annotations, 47 | Iterable fields, 48 | Iterable methods 49 | ) { 50 | this.type = type; 51 | this.accessFlags = accessFlags; 52 | this.superclass = superclass; 53 | this.interfaces = interfaces; 54 | this.sourceFile = sourceFile; 55 | this.annotations = annotations; 56 | this.fields = fields; 57 | this.staticFields = Iterables.filter(fields, FieldUtil.FIELD_IS_STATIC); 58 | this.instanceFields = Iterables.filter(fields, FieldUtil.FIELD_IS_INSTANCE); 59 | this.methods = methods; 60 | this.directMethods = Iterables.filter(methods, MethodUtil.METHOD_IS_DIRECT); 61 | this.virtualMethods = Iterables.filter(methods, MethodUtil.METHOD_IS_VIRTUAL); 62 | } 63 | 64 | public BasicClassDef( 65 | String type, 66 | int accessFlags, 67 | String superclass, 68 | List interfaces, 69 | String sourceFile, 70 | Set annotations, 71 | Iterable staticFields, 72 | Iterable instanceFields, 73 | Iterable directMethods, 74 | Iterable virtualMethods 75 | ) { 76 | this.type = type; 77 | this.accessFlags = accessFlags; 78 | this.superclass = superclass; 79 | this.interfaces = interfaces; 80 | this.sourceFile = sourceFile; 81 | this.annotations = annotations; 82 | this.fields = Iterables.concat(staticFields, instanceFields); 83 | this.staticFields = staticFields; 84 | this.instanceFields = instanceFields; 85 | this.methods = Iterables.concat(directMethods, virtualMethods); 86 | this.directMethods = directMethods; 87 | this.virtualMethods = virtualMethods; 88 | } 89 | 90 | @Override 91 | public String getType() { 92 | return type; 93 | } 94 | 95 | @Override 96 | public int getAccessFlags() { 97 | return accessFlags; 98 | } 99 | 100 | @Override 101 | public String getSuperclass() { 102 | return superclass; 103 | } 104 | 105 | @Override 106 | public List getInterfaces() { 107 | return interfaces; 108 | } 109 | 110 | @Override 111 | public String getSourceFile() { 112 | return sourceFile; 113 | } 114 | 115 | @Override 116 | public Set getAnnotations() { 117 | return annotations; 118 | } 119 | 120 | @Override 121 | public Iterable getFields() { 122 | return fields; 123 | } 124 | 125 | @Override 126 | public Iterable getStaticFields() { 127 | return staticFields; 128 | } 129 | 130 | @Override 131 | public Iterable getInstanceFields() { 132 | return instanceFields; 133 | } 134 | 135 | @Override 136 | public Iterable getMethods() { 137 | return methods; 138 | } 139 | 140 | @Override 141 | public Iterable getDirectMethods() { 142 | return directMethods; 143 | } 144 | 145 | @Override 146 | public Iterable getVirtualMethods() { 147 | return virtualMethods; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/model/BasicDexFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.model; 12 | 13 | import java.util.Set; 14 | 15 | import org.jf.dexlib2.Opcodes; 16 | import org.jf.dexlib2.iface.ClassDef; 17 | import org.jf.dexlib2.iface.DexFile; 18 | 19 | public class BasicDexFile implements DexFile { 20 | 21 | private final Opcodes opcodes; 22 | private final Set classes; 23 | 24 | public BasicDexFile( 25 | Opcodes opcodes, 26 | Set classes 27 | ) { 28 | this.opcodes = opcodes; 29 | this.classes = classes; 30 | } 31 | 32 | @Override 33 | public Opcodes getOpcodes() { 34 | return opcodes; 35 | } 36 | 37 | @Override 38 | public Set getClasses() { 39 | return classes; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/model/BasicField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.model; 12 | 13 | import java.util.Set; 14 | 15 | import org.jf.dexlib2.base.reference.BaseFieldReference; 16 | import org.jf.dexlib2.iface.Annotation; 17 | import org.jf.dexlib2.iface.Field; 18 | import org.jf.dexlib2.iface.value.EncodedValue; 19 | 20 | public class BasicField extends BaseFieldReference implements Field { 21 | 22 | private final String definingClass; 23 | private final String name; 24 | private final String type; 25 | private final int accessFlags; 26 | private final EncodedValue initialValue; 27 | private final Set annotations; 28 | 29 | public BasicField( 30 | String definingClass, 31 | String name, 32 | String type, 33 | int accessFlags, 34 | EncodedValue initialValue, 35 | Set annotations 36 | ) { 37 | this.definingClass = definingClass; 38 | this.name = name; 39 | this.type = type; 40 | this.accessFlags = accessFlags; 41 | this.initialValue = initialValue; 42 | this.annotations = annotations; 43 | } 44 | 45 | @Override 46 | public String getDefiningClass() { 47 | return definingClass; 48 | } 49 | 50 | @Override 51 | public String getName() { 52 | return name; 53 | } 54 | 55 | @Override 56 | public String getType() { 57 | return type; 58 | } 59 | 60 | @Override 61 | public int getAccessFlags() { 62 | return accessFlags; 63 | } 64 | 65 | @Override 66 | public EncodedValue getInitialValue() { 67 | return initialValue; 68 | } 69 | 70 | @Override 71 | public Set getAnnotations() { 72 | return annotations; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/model/BasicMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.model; 12 | 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | import org.jf.dexlib2.base.reference.BaseMethodReference; 17 | import org.jf.dexlib2.iface.Annotation; 18 | import org.jf.dexlib2.iface.Method; 19 | import org.jf.dexlib2.iface.MethodImplementation; 20 | import org.jf.dexlib2.iface.MethodParameter; 21 | 22 | public class BasicMethod extends BaseMethodReference implements Method { 23 | 24 | private final String definingClass; 25 | private final String name; 26 | private final List parameters; 27 | private final String returnType; 28 | private final int accessFlags; 29 | private final Set annotations; 30 | private final MethodImplementation methodImplementation; 31 | 32 | public BasicMethod( 33 | String definingClass, 34 | String name, 35 | List parameters, 36 | String returnType, 37 | int accessFlags, 38 | Set annotations, 39 | MethodImplementation methodImplementation 40 | ) { 41 | this.definingClass = definingClass; 42 | this.name = name; 43 | this.parameters = parameters; 44 | this.returnType = returnType; 45 | this.accessFlags = accessFlags; 46 | this.annotations = annotations; 47 | this.methodImplementation = methodImplementation; 48 | } 49 | 50 | @Override 51 | public String getDefiningClass() { 52 | return definingClass; 53 | } 54 | 55 | @Override 56 | public String getName() { 57 | return name; 58 | } 59 | 60 | @Override 61 | public List getParameters() { 62 | return parameters; 63 | } 64 | 65 | @Override 66 | public String getReturnType() { 67 | return returnType; 68 | } 69 | 70 | @Override 71 | public int getAccessFlags() { 72 | return accessFlags; 73 | } 74 | 75 | @Override 76 | public Set getAnnotations() { 77 | return annotations; 78 | } 79 | 80 | @Override 81 | public MethodImplementation getImplementation() { 82 | return methodImplementation; 83 | } 84 | 85 | @Override 86 | public List getParameterTypes() { 87 | return parameters; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/model/BasicMethodImplementation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.model; 12 | 13 | import java.util.List; 14 | 15 | import org.jf.dexlib2.iface.ExceptionHandler; 16 | import org.jf.dexlib2.iface.MethodImplementation; 17 | import org.jf.dexlib2.iface.TryBlock; 18 | import org.jf.dexlib2.iface.debug.DebugItem; 19 | import org.jf.dexlib2.iface.instruction.Instruction; 20 | 21 | public class BasicMethodImplementation implements MethodImplementation { 22 | 23 | private final int registerCount; 24 | private final Iterable instructions; 25 | private final List> tryBlocks; 26 | private final Iterable debugItems; 27 | 28 | public BasicMethodImplementation( 29 | int registerCount, 30 | Iterable instructions, 31 | List> tryBlocks, 32 | Iterable debugItems 33 | ) { 34 | this.registerCount = registerCount; 35 | this.instructions = instructions; 36 | this.tryBlocks = tryBlocks; 37 | this.debugItems = debugItems; 38 | } 39 | 40 | @Override 41 | public int getRegisterCount() { 42 | return registerCount; 43 | } 44 | 45 | @Override 46 | public Iterable getInstructions() { 47 | return instructions; 48 | } 49 | 50 | @Override 51 | public List> getTryBlocks() { 52 | return tryBlocks; 53 | } 54 | 55 | @Override 56 | public Iterable getDebugItems() { 57 | return debugItems; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/patcher/ActionBasedPatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.patcher; 12 | 13 | import lanchon.dexpatcher.core.Action; 14 | import lanchon.dexpatcher.core.Context; 15 | import lanchon.dexpatcher.core.PatchException; 16 | 17 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 18 | 19 | public abstract class ActionBasedPatcher extends AbstractPatcher { 20 | 21 | public interface ActionContext { 22 | Action getAction(); 23 | } 24 | 25 | protected ActionBasedPatcher(Context context) { 26 | super(context); 27 | } 28 | 29 | protected ActionBasedPatcher(AbstractPatcher parent) { 30 | super(parent); 31 | } 32 | 33 | // Implementation 34 | 35 | @Override 36 | protected void onPatch(String patchId, T patch) throws PatchException { 37 | C actionContext = getActionContext(patchId, patch); 38 | onPrepare(patchId, patch, actionContext); 39 | Action action = actionContext.getAction(); 40 | if (isLogging(DEBUG)) log(DEBUG, action.getLabel()); 41 | switch (action) { 42 | case ADD: 43 | onAdd(patchId, patch, actionContext); 44 | break; 45 | case EDIT: 46 | onEdit(patchId, patch, actionContext); 47 | break; 48 | case REPLACE: 49 | onReplace(patchId, patch, actionContext); 50 | break; 51 | case REMOVE: 52 | onRemove(patchId, patch, actionContext); 53 | break; 54 | case IGNORE: 55 | onIgnore(patchId, patch, actionContext); 56 | break; 57 | case WRAP: 58 | onWrap(patchId, patch, actionContext); 59 | break; 60 | case PREPEND: 61 | onSplice(patchId, patch, actionContext, Action.PREPEND); 62 | break; 63 | case APPEND: 64 | onSplice(patchId, patch, actionContext, Action.APPEND); 65 | break; 66 | default: 67 | throw new AssertionError("Unexpected action"); 68 | } 69 | } 70 | 71 | // Intermediate Handlers 72 | 73 | protected void onAdd(String patchId, T patch, C actionContext) throws PatchException { 74 | T patched = onSimpleAdd(patch, actionContext); 75 | addPatched(patch, patched); 76 | } 77 | 78 | protected void onEdit(String patchId, T patch, C actionContext) throws PatchException { 79 | String targetId = getTargetId(patchId, patch, actionContext); 80 | boolean inPlace = patchId.equals(targetId); 81 | T target = findTarget(targetId, inPlace); 82 | T patched = onSimpleEdit(patch, actionContext, target, inPlace); 83 | addPatched(patch, patched); 84 | } 85 | 86 | protected void onReplace(String patchId, T patch, C actionContext) throws PatchException { 87 | String targetId = getTargetId(patchId, patch, actionContext); 88 | boolean inPlace = patchId.equals(targetId); 89 | T target = findTarget(targetId, false); 90 | T patched = onSimpleReplace(patch, actionContext, target, inPlace); 91 | addPatched(patch, patched); 92 | } 93 | 94 | protected void onRemove(String patchId, T patch, C actionContext) throws PatchException { 95 | String targetId = getTargetId(patchId, patch, actionContext); 96 | T target = findTarget(targetId, false); 97 | onSimpleRemove(patch, actionContext, target); 98 | } 99 | 100 | protected void onIgnore(String patchId, T patch, C actionContext) throws PatchException {} 101 | 102 | protected void onWrap(String patchId, T patch, C actionContext) throws PatchException { 103 | throw Action.WRAP.invalidAction(); 104 | } 105 | 106 | protected void onSplice(String patchId, T patch, C actionContext, Action action) throws PatchException { 107 | throw action.invalidAction(); 108 | } 109 | 110 | // Handlers 111 | 112 | protected abstract C getActionContext(String patchId, T patch) throws PatchException; 113 | protected void onPrepare(String patchId, T patch, C actionContext) throws PatchException {} 114 | protected abstract String getTargetId(String patchId, T patch, C actionContext) throws PatchException; 115 | 116 | protected abstract T onSimpleAdd(T patch, C actionContext); 117 | protected abstract T onSimpleEdit(T patch, C actionContext, T target, boolean inPlace); 118 | protected abstract T onSimpleReplace(T patch, C actionContext, T target, boolean inPlace); 119 | protected void onSimpleRemove(T patch, C actionContext, T target) {} 120 | 121 | } 122 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/patcher/AnnotatableSetPatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.patcher; 12 | 13 | import java.io.File; 14 | import java.util.Set; 15 | 16 | import lanchon.dexpatcher.core.Action; 17 | import lanchon.dexpatcher.core.Context; 18 | import lanchon.dexpatcher.core.PatchException; 19 | import lanchon.dexpatcher.core.PatcherAnnotation; 20 | import lanchon.dexpatcher.core.logger.Logger; 21 | import lanchon.dexpatcher.core.util.AccessFlagLogger; 22 | 23 | import org.jf.dexlib2.iface.Annotatable; 24 | import org.jf.dexlib2.iface.Annotation; 25 | import org.jf.dexlib2.iface.ClassDef; 26 | 27 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 28 | 29 | public abstract class AnnotatableSetPatcher extends ActionBasedPatcher { 30 | 31 | private ClassDef sourceFileClass; 32 | private String sourceFileName; 33 | 34 | protected AnnotatableSetPatcher(Context context) { 35 | super(context); 36 | } 37 | 38 | protected AnnotatableSetPatcher(AnnotatableSetPatcher parent) { 39 | super(parent); 40 | sourceFileClass = parent.sourceFileClass; 41 | sourceFileName = parent.sourceFileName; 42 | } 43 | 44 | // Debug Info 45 | 46 | protected void setSourceFileClass(ClassDef sourceFileClass) { 47 | this.sourceFileClass = sourceFileClass; 48 | sourceFileName = null; 49 | } 50 | 51 | protected String getSourceFileName() { 52 | // Parse debug information lazily. 53 | if (sourceFileClass != null) { 54 | sourceFileName = sourceFileClass.getSourceFile(); 55 | if (sourceFileName != null && getContext().getSourceCodeRoot() != null) { 56 | String type = sourceFileClass.getType(); 57 | int i = type.lastIndexOf('/'); 58 | if (i >= 1) { 59 | String path = type.substring(1, i + 1).replace('/', File.separatorChar); 60 | sourceFileName = path + sourceFileName; 61 | } 62 | } 63 | sourceFileClass = null; 64 | } 65 | return sourceFileName; 66 | } 67 | 68 | protected int getSourceFileLine() { 69 | return 0; 70 | } 71 | 72 | // Logging 73 | 74 | protected void log(Logger.Level level, String message) { 75 | if (isLogging(level)) { 76 | String name = getSourceFileName(); 77 | if (name != null) { 78 | int line = getSourceFileLine(); 79 | String root = getContext().getSourceCodeRoot(); 80 | if (root != null) { 81 | String ls = System.lineSeparator(); 82 | message += ls + '\t' + root + name + ":" + line; 83 | } else { 84 | message = "(" + name + ":" + line + "): " + message; 85 | } 86 | } 87 | super.log(level, message); 88 | } 89 | } 90 | 91 | protected final boolean shouldLogTarget(String patchId, String targetId) { 92 | return !patchId.equals(targetId); 93 | } 94 | 95 | protected final void extendLogPrefixWithTargetLabel(String targetLabel) { 96 | extendLogPrefix("target '" + targetLabel + "'"); 97 | } 98 | 99 | // Implementation 100 | 101 | @Override 102 | protected PatcherAnnotation getActionContext(String patchId, T patch) throws PatchException { 103 | Set rawAnnotations = patch.getAnnotations(); 104 | PatcherAnnotation annotation = PatcherAnnotation.parse(getContext().getActionParser(), rawAnnotations); 105 | if (annotation == null) annotation = new PatcherAnnotation(getDefaultAction(patchId, patch), rawAnnotations); 106 | return annotation; 107 | } 108 | 109 | // Access Flags Logging 110 | 111 | private void logAccessFlags(String item, int oldFlags, int newFlags, boolean keepInterface, 112 | boolean ensureInterface, boolean keepImplementation, Logger.Level warningLevel) { 113 | new AccessFlagLogger(item, oldFlags, newFlags) { 114 | @Override 115 | protected void log(Logger.Level level, String message) { 116 | AnnotatableSetPatcher.this.log(level, message); 117 | } 118 | }.allFlags(this, keepInterface, ensureInterface, keepImplementation, warningLevel); 119 | } 120 | 121 | @Override 122 | protected T onSimpleEdit(T patch, PatcherAnnotation annotation, T target, boolean inPlace) { 123 | int oldFlags = getAccessFlags(target); 124 | int newFlags = getAccessFlags(patch); 125 | if (inPlace) { 126 | String item = "edited " + getItemLabel(); 127 | logAccessFlags(item, oldFlags, newFlags, true, false, 128 | true, WARN); 129 | } else { 130 | String item = "renamed " + getItemLabel(); 131 | logAccessFlags(item, oldFlags, newFlags, false, false, 132 | true, WARN); 133 | } 134 | return patch; 135 | } 136 | 137 | @Override 138 | protected void onEffectiveReplacement(String id, T patch, T patched, T original, boolean inPlaceEdit) { 139 | // Avoid duplicated messages if not renaming. 140 | if (!inPlaceEdit) { 141 | int oldFlags = getAccessFlags(original); 142 | int newFlags = getAccessFlags(patched); 143 | String item = "replaced " + getItemLabel(); 144 | logAccessFlags(item, oldFlags, newFlags, true, false, 145 | false, WARN); 146 | } 147 | } 148 | 149 | // Handlers 150 | 151 | protected abstract String getItemLabel(); 152 | protected abstract int getAccessFlags(T item); 153 | protected abstract Action getDefaultAction(String patchId, T patch) throws PatchException; 154 | 155 | } 156 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/patcher/FieldSetPatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.patcher; 12 | 13 | import lanchon.dexpatcher.core.Action; 14 | import lanchon.dexpatcher.core.PatchException; 15 | import lanchon.dexpatcher.core.PatcherAnnotation; 16 | import lanchon.dexpatcher.core.model.BasicField; 17 | import lanchon.dexpatcher.core.util.Id; 18 | import lanchon.dexpatcher.core.util.Label; 19 | 20 | import org.jf.dexlib2.iface.Field; 21 | import org.jf.dexlib2.iface.value.EncodedValue; 22 | import org.jf.dexlib2.util.FieldUtil; 23 | 24 | import static lanchon.dexpatcher.core.PatcherAnnotation.*; 25 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 26 | import static org.jf.dexlib2.AccessFlags.*; 27 | 28 | public class FieldSetPatcher extends MemberSetPatcher { 29 | 30 | public FieldSetPatcher(ClassSetPatcher parent, PatcherAnnotation annotation) { 31 | super(parent, annotation); 32 | } 33 | 34 | // Logging 35 | 36 | @Override 37 | protected void setupLogPrefix(String id, Field item, Field patch, Field patched) { 38 | setupLogPrefix(getItemLabel() + " '" + Label.ofField(item) + "'"); 39 | } 40 | 41 | // Implementation 42 | 43 | @Override 44 | protected final String getId(Field item) { 45 | return Id.ofField(item); 46 | } 47 | 48 | @Override 49 | protected String getItemLabel() { 50 | return "field"; 51 | } 52 | 53 | @Override 54 | protected void onPrepare(String patchId, Field patch, PatcherAnnotation annotation) throws PatchException { 55 | if (annotation.getAction() == Action.REPLACE) throw invalidAnnotation(Action.REPLACE); 56 | super.onPrepare(patchId, patch, annotation); 57 | } 58 | 59 | @Override 60 | protected String getTargetId(String patchId, Field patch, PatcherAnnotation annotation) { 61 | String target = annotation.getTarget(); 62 | String targetId = (target != null) ? Id.ofField(patch, target) : patchId; 63 | if (shouldLogTarget(patchId, targetId)) { 64 | extendLogPrefixWithTargetLabel(Label.ofTargetMember(target)); 65 | } 66 | return targetId; 67 | } 68 | 69 | @Override 70 | protected Field onSimpleAdd(Field patch, PatcherAnnotation annotation) { 71 | 72 | EncodedValue value = filterInitialValue(patch, null); 73 | 74 | return new BasicField( 75 | patch.getDefiningClass(), 76 | patch.getName(), 77 | patch.getType(), 78 | patch.getAccessFlags(), 79 | value, 80 | annotation.getFilteredAnnotations()); 81 | 82 | } 83 | 84 | @Override 85 | protected Field onSimpleEdit(Field patch, PatcherAnnotation annotation, Field target, boolean inPlace) { 86 | 87 | // Use the static field initializer value in source only 88 | // if not renaming, given that the static constructor in 89 | // source would only initialize it if not renamed. 90 | // This makes behavior predictable across compilers. 91 | EncodedValue value = inPlace ? target.getInitialValue() : null; 92 | value = filterInitialValue(patch, value); 93 | 94 | onSimpleRemove(patch, annotation, target); 95 | 96 | Field patched = new BasicField( 97 | patch.getDefiningClass(), 98 | patch.getName(), 99 | patch.getType(), 100 | patch.getAccessFlags(), 101 | value, 102 | annotation.getFilteredAnnotations()); 103 | 104 | return super.onSimpleEdit(patched, annotation, target, inPlace); 105 | 106 | } 107 | 108 | @Override 109 | protected Field onSimpleReplace(Field patch, PatcherAnnotation annotation, Field target, boolean inPlace) { 110 | throw new AssertionError("Replace field"); 111 | } 112 | 113 | @Override 114 | protected void onSimpleRemove(Field patch, PatcherAnnotation annotation, Field target) { 115 | if (FieldUtil.isStatic(target)) { 116 | if (FINAL.isSet(target.getAccessFlags())) { 117 | log(WARN, "original value of final static field is likely to be embedded in code"); 118 | } 119 | } 120 | } 121 | 122 | // Helpers 123 | 124 | private EncodedValue filterInitialValue(Field patch, EncodedValue value){ 125 | if (FieldUtil.isStatic(patch)) { 126 | // Use the static field initializer values in patch if and 127 | // only if the static constructor code in patch is being used. 128 | // This makes behavior more predictable across compilers. 129 | Action action = resolvedStaticConstructorAction; 130 | if (/* action != Action.NONE && */ action.ignoresCode()) { 131 | log(WARN, "static field will not be initialized as specified in patch because code of static constructor of class is being discarded"); 132 | return value; 133 | } else { 134 | EncodedValue patchValue = patch.getInitialValue(); 135 | return (patchValue != null) ? patchValue : value; 136 | } 137 | } else { 138 | // Instance fields should never have initializer values. 139 | EncodedValue patchValue = patch.getInitialValue(); 140 | if (patchValue != null) { 141 | log(ERROR, "unexpected instance field initializer value in patch"); 142 | } 143 | return (patchValue != null) ? patchValue : value; 144 | } 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/patcher/MemberSetPatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.patcher; 12 | 13 | import lanchon.dexpatcher.core.Action; 14 | import lanchon.dexpatcher.core.Marker; 15 | import lanchon.dexpatcher.core.PatchException; 16 | import lanchon.dexpatcher.core.PatcherAnnotation; 17 | 18 | import org.jf.dexlib2.iface.Member; 19 | 20 | import static lanchon.dexpatcher.core.PatcherAnnotation.*; 21 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 22 | 23 | public abstract class MemberSetPatcher extends AnnotatableSetPatcher { 24 | 25 | protected final Action explicitStaticConstructorAction; 26 | protected final Action resolvedStaticConstructorAction; // not null 27 | protected final Action explicitDefaultAction; 28 | protected final Action resolvedDefaultAction; // not null 29 | 30 | public MemberSetPatcher(ClassSetPatcher parent, PatcherAnnotation annotation) { 31 | super(parent); 32 | Action defaultAction = Action.NONE; 33 | Action sca = annotation.getStaticConstructorAction(); 34 | Action da = annotation.getDefaultAction(); 35 | explicitStaticConstructorAction = sca; 36 | explicitDefaultAction = da; 37 | resolvedDefaultAction = (da != null) ? da : defaultAction; 38 | resolvedStaticConstructorAction = (sca != null) ? sca : resolvedDefaultAction; 39 | } 40 | 41 | // Implementation 42 | 43 | @Override 44 | protected int getAccessFlags(T item) { 45 | return item.getAccessFlags(); 46 | } 47 | 48 | @Override 49 | protected Action getDefaultAction(String patchId, T patch) throws PatchException { 50 | if (resolvedDefaultAction == Action.NONE) throw new PatchException("no action defined"); 51 | log(INFO, "default " + resolvedDefaultAction.getLabel()); 52 | return resolvedDefaultAction; 53 | } 54 | 55 | @Override 56 | protected void onPrepare(String patchId, T patch, PatcherAnnotation annotation) throws PatchException { 57 | if (annotation.getTargetClass() != null) throw invalidElement(Marker.ELEM_TARGET_CLASS); 58 | if (annotation.getStaticConstructorAction() != null) throw invalidElement(Marker.ELEM_STATIC_CONSTRUCTOR_ACTION); 59 | if (annotation.getDefaultAction() != null) throw invalidElement(Marker.ELEM_DEFAULT_ACTION); 60 | if (annotation.getContentOnly()) throw invalidElement(Marker.ELEM_CONTENT_ONLY); 61 | if (annotation.getRecursive()) throw invalidElement(Marker.ELEM_RECURSIVE); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/patcher/PackagePatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.patcher; 12 | 13 | import lanchon.dexpatcher.core.Context; 14 | import lanchon.dexpatcher.core.Marker; 15 | import lanchon.dexpatcher.core.PatchException; 16 | import lanchon.dexpatcher.core.PatcherAnnotation; 17 | import lanchon.dexpatcher.core.util.DexUtils; 18 | import lanchon.dexpatcher.core.util.Id; 19 | import lanchon.dexpatcher.core.util.Label; 20 | import lanchon.dexpatcher.core.util.Target; 21 | 22 | import org.jf.dexlib2.iface.ClassDef; 23 | 24 | import static lanchon.dexpatcher.core.PatcherAnnotation.*; 25 | import static lanchon.dexpatcher.core.logger.Logger.Level.*; 26 | 27 | public class PackagePatcher extends ClassSetPatcher { 28 | 29 | private boolean processingPackage; 30 | 31 | public PackagePatcher(Context context) { 32 | super(context); 33 | } 34 | 35 | // Implementation 36 | 37 | @Override 38 | protected void onPrepare(String patchId, ClassDef patch, PatcherAnnotation annotation) throws PatchException { 39 | processingPackage = DexUtils.isPackageId(patchId); 40 | if (!processingPackage) { 41 | super.onPrepare(patchId, patch, annotation); 42 | return; 43 | } 44 | if (annotation.getTargetClass() != null) throw invalidElement(Marker.ELEM_TARGET_CLASS); 45 | if (annotation.getContentOnly()) throw invalidElement(Marker.ELEM_CONTENT_ONLY); 46 | } 47 | 48 | @Override 49 | protected void onReplace(String patchId, ClassDef patch, PatcherAnnotation annotation) throws PatchException { 50 | if (!processingPackage) { 51 | super.onReplace(patchId, patch, annotation); 52 | return; 53 | } 54 | String targetId = getPackageTargetId(patchId, patch, annotation); 55 | boolean recursive = annotation.getRecursive(); 56 | if (isLogging(DEBUG)) log(DEBUG, recursive ? "replace package recursive" : "replace package non-recursive"); 57 | removePackage(targetId, recursive); 58 | ClassDef patched = onSimpleAdd(patch, annotation); 59 | addPatched(patch, patched); 60 | } 61 | 62 | @Override 63 | protected void onRemove(String patchId, ClassDef patch, PatcherAnnotation annotation) throws PatchException { 64 | if (!processingPackage) { 65 | super.onRemove(patchId, patch, annotation); 66 | return; 67 | } 68 | String targetId = getPackageTargetId(patchId, patch, annotation); 69 | boolean recursive = annotation.getRecursive(); 70 | if (isLogging(DEBUG)) log(DEBUG, recursive ? "remove package recursive" : "remove package non-recursive"); 71 | removePackage(targetId, recursive); 72 | } 73 | 74 | private String getPackageTargetId(String patchId, ClassDef patch, PatcherAnnotation annotation) throws PatchException { 75 | String target = annotation.getTarget(); 76 | String targetId = (target != null) ? Id.ofClass(Target.resolvePackageDescriptor(target)) : patchId; 77 | if (!DexUtils.isPackageId(targetId)) throw new PatchException("target is not a package"); 78 | if (shouldLogTarget(patchId, targetId)) { 79 | extendLogPrefixWithTargetLabel(Label.fromClassId(targetId)); 80 | } 81 | return targetId; 82 | } 83 | 84 | private void removePackage(String targetId, boolean recursive) throws PatchException { 85 | int prefixLength = targetId.length() - (Marker.NAME_PACKAGE_INFO.length() + 1); 86 | String prefix = targetId.substring(0, prefixLength); 87 | for (String id: getSourceMap().keySet()) { 88 | if (id.startsWith(prefix) && (recursive || id.indexOf('/', prefixLength) < 0) && id.endsWith(";")) { 89 | try { 90 | addTarget(id, false); 91 | if (isLogging(DEBUG)) log(DEBUG, "remove type '" + Label.fromClassId(id) + "'"); 92 | } catch (PatchException e) { 93 | log(ERROR, "already targeted type '" + Label.fromClassId(id) + "'"); 94 | } 95 | } 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/DexUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import java.util.Iterator; 14 | 15 | import lanchon.dexpatcher.core.Marker; 16 | 17 | import org.jf.dexlib2.Opcode; 18 | import org.jf.dexlib2.iface.Annotation; 19 | import org.jf.dexlib2.iface.AnnotationElement; 20 | import org.jf.dexlib2.iface.ClassDef; 21 | import org.jf.dexlib2.iface.Method; 22 | import org.jf.dexlib2.iface.MethodImplementation; 23 | import org.jf.dexlib2.iface.instruction.Instruction; 24 | import org.jf.dexlib2.iface.instruction.ReferenceInstruction; 25 | import org.jf.dexlib2.iface.reference.MethodReference; 26 | import org.jf.dexlib2.iface.value.EncodedValue; 27 | import org.jf.dexlib2.iface.value.IntEncodedValue; 28 | 29 | import static org.jf.dexlib2.AccessFlags.*; 30 | 31 | public class DexUtils { 32 | 33 | public static boolean isClassDescriptor(String descriptor) { 34 | int l = descriptor.length(); 35 | return l >= 2 && descriptor.charAt(0) == 'L' && descriptor.charAt(l - 1) == ';'; 36 | } 37 | 38 | private static final String PACKAGE_DESCRIPTOR_SUFFIX = '/' + Marker.NAME_PACKAGE_INFO + ';'; 39 | private static final String PACKAGE_DESCRIPTOR_DEFAULT = 'L' + Marker.NAME_PACKAGE_INFO + ';'; 40 | 41 | public static boolean isPackageDescriptor(String descriptor) { 42 | return (descriptor.endsWith(PACKAGE_DESCRIPTOR_SUFFIX) && descriptor.charAt(0) == 'L') || 43 | descriptor.equals(PACKAGE_DESCRIPTOR_DEFAULT); 44 | } 45 | 46 | public static boolean isPackageId(String id) { 47 | return DexUtils.isPackageDescriptor(Id.toClassDescriptor(id)); 48 | } 49 | 50 | // Access Flags 51 | 52 | public static int getClassAccessFlags(ClassDef classDef) { 53 | int f = classDef.getAccessFlags(); 54 | for (Annotation a : classDef.getAnnotations()) { 55 | if (Marker.TYPE_INNER_CLASS.equals(a.getType())) { 56 | for (AnnotationElement e : a.getElements()) { 57 | if (Marker.ELEM_ACCESS_FLAGS.equals(e.getName())) { 58 | EncodedValue v = e.getValue(); 59 | if (v instanceof IntEncodedValue) { 60 | f |= ((IntEncodedValue) v).getValue(); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | return f; 67 | } 68 | 69 | // Constructors 70 | 71 | public static boolean isStaticConstructor(Method method) { 72 | int flags = method.getAccessFlags(); 73 | return CONSTRUCTOR.isSet(flags) && STATIC.isSet(flags) && 74 | isStaticConstructorReference(method); 75 | } 76 | 77 | public static boolean isStaticConstructorReference(MethodReference method) { 78 | return Marker.NAME_STATIC_CONSTRUCTOR.equals(method.getName()) && 79 | Marker.TYPE_VOID.equals(method.getReturnType()) && 80 | method.getParameterTypes().isEmpty(); 81 | } 82 | 83 | public static boolean isInstanceConstructor(Method method) { 84 | int flags = method.getAccessFlags(); 85 | return CONSTRUCTOR.isSet(flags) && !STATIC.isSet(flags) && 86 | isInstanceConstructorReference(method); 87 | } 88 | 89 | public static boolean isInstanceConstructorReference(MethodReference method) { 90 | return Marker.NAME_INSTANCE_CONSTRUCTOR.equals(method.getName()) && 91 | Marker.TYPE_VOID.equals(method.getReturnType()); 92 | } 93 | 94 | public static boolean isDefaultConstructor(Method method) { 95 | return isInstanceConstructor(method) && 96 | method.getParameterTypes().isEmpty(); 97 | } 98 | 99 | public static boolean hasTrivialConstructorImplementation(Method method) { 100 | // Precondition: isDefaultConstructor(...) returns true. 101 | MethodImplementation implementation = method.getImplementation(); 102 | if (implementation.getRegisterCount() != 1 || !implementation.getTryBlocks().isEmpty()) return false; 103 | Iterator iterator = implementation.getInstructions().iterator(); 104 | if (!iterator.hasNext()) return false; 105 | { 106 | Instruction instruction = iterator.next(); 107 | if (instruction.getOpcode() != Opcode.INVOKE_DIRECT) return false; 108 | MethodReference reference = (MethodReference) ((ReferenceInstruction) instruction).getReference(); 109 | if (!Marker.NAME_INSTANCE_CONSTRUCTOR.equals(reference.getName())) return false; 110 | if (method.getDefiningClass().equals(reference.getDefiningClass())) return false; 111 | } 112 | if (!iterator.hasNext()) return false; 113 | { 114 | Instruction instruction = iterator.next(); 115 | if (instruction.getOpcode() != Opcode.RETURN_VOID) return false; 116 | } 117 | if (iterator.hasNext()) return false; 118 | return true; 119 | } 120 | 121 | private DexUtils() {} 122 | 123 | } 124 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/ElementalTypeRewriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import org.jf.dexlib2.rewriter.Rewriter; 14 | 15 | public abstract class ElementalTypeRewriter implements Rewriter { 16 | 17 | @Override 18 | public String rewrite(String value) { 19 | int length = value.length(); 20 | if (length == 0 || value.charAt(0) != '[') return rewriteElementalType(value); 21 | int start = 1; 22 | while (start < length && value.charAt(start) == '[') start++; 23 | String elementalType = value.substring(start); 24 | String rewrittenElementalType = rewriteElementalType(elementalType); 25 | if (rewrittenElementalType.equals(elementalType)) return value; 26 | StringBuilder sb = new StringBuilder(start + rewrittenElementalType.length()); 27 | sb.append(value, 0, start).append(rewrittenElementalType); 28 | return sb.toString(); 29 | } 30 | 31 | public abstract String rewriteElementalType(String elementalType); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/Id.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import lanchon.dexpatcher.core.Marker; 14 | 15 | import org.jf.dexlib2.iface.ClassDef; 16 | import org.jf.dexlib2.iface.reference.FieldReference; 17 | import org.jf.dexlib2.iface.reference.MethodReference; 18 | 19 | public class Id { 20 | 21 | public static final String STATIC_CONSTRUCTOR = Marker.NAME_STATIC_CONSTRUCTOR + "..V"; 22 | 23 | public static String ofClass(ClassDef classDef) { 24 | return ofClass(classDef.getType()); 25 | } 26 | 27 | public static String ofClass(String descriptor) { 28 | return descriptor; 29 | } 30 | 31 | public static String toClassDescriptor(String id) { 32 | return id; 33 | } 34 | 35 | public static String ofField(FieldReference field) { 36 | return ofField(field.getType(), field.getName()); 37 | } 38 | 39 | public static String ofField(FieldReference field, String name) { 40 | return ofField(field.getType(), name); 41 | } 42 | 43 | public static String ofField(String type, String name) { 44 | return name + '.' + type; 45 | } 46 | 47 | public static String ofMethod(MethodReference method) { 48 | return ofMethod(method.getParameterTypes(), method.getReturnType(), method.getName()); 49 | } 50 | 51 | public static String ofMethod(MethodReference method, String name) { 52 | return ofMethod(method.getParameterTypes(), method.getReturnType(), name); 53 | } 54 | 55 | public static String ofMethod(Iterable parameterTypes, String returnType, String name) { 56 | StringBuilder sb = new StringBuilder(); 57 | sb.append(name).append('.'); 58 | for (CharSequence parameterType : parameterTypes) sb.append(parameterType); 59 | sb.append('.').append(returnType); 60 | return sb.toString(); 61 | } 62 | 63 | private Id() {} 64 | 65 | } 66 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/InvalidTypeDescriptorException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import lanchon.dexpatcher.core.PatchException; 14 | 15 | public class InvalidTypeDescriptorException extends PatchException { 16 | 17 | protected final String descriptorType; 18 | protected final String descriptor; 19 | 20 | public InvalidTypeDescriptorException(String descriptorType, String descriptor) { 21 | super("Invalid " + descriptorType + " type descriptor (" + descriptor + ")"); 22 | this.descriptorType = descriptorType; 23 | this.descriptor = descriptor; 24 | } 25 | 26 | public String getDescriptorType() { 27 | return descriptorType; 28 | } 29 | 30 | public String getDescriptor() { 31 | return descriptor; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/Label.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import org.jf.dexlib2.iface.ClassDef; 14 | import org.jf.dexlib2.iface.reference.FieldReference; 15 | import org.jf.dexlib2.iface.reference.MethodReference; 16 | 17 | public class Label { 18 | 19 | public static String fromClassDescriptor(String descriptor) { 20 | try { 21 | return TypeName.fromClassDescriptor(descriptor); 22 | } catch (InvalidTypeDescriptorException e) { 23 | return "[class-type:" + descriptor + "]"; 24 | } 25 | } 26 | 27 | public static String fromFieldDescriptor(String descriptor) { 28 | try { 29 | return TypeName.fromFieldDescriptor(descriptor); 30 | } catch (InvalidTypeDescriptorException e) { 31 | return "[field-type:" + descriptor + "]"; 32 | } 33 | } 34 | 35 | public static String fromReturnDescriptor(String descriptor) { 36 | try { 37 | return TypeName.fromReturnDescriptor(descriptor); 38 | } catch (InvalidTypeDescriptorException e) { 39 | return "[return-type:" + descriptor + "]"; 40 | } 41 | } 42 | 43 | public static String ofClass(ClassDef classDef) { 44 | return fromClassDescriptor(classDef.getType()); 45 | } 46 | 47 | public static String fromClassId(String id) { 48 | return fromClassDescriptor(Id.toClassDescriptor(id)); 49 | } 50 | 51 | public static String ofTargetMember(String name) { 52 | return name; 53 | } 54 | 55 | public static String ofField(FieldReference field) { 56 | return ofField(field, field.getName()); 57 | } 58 | 59 | public static String ofField(FieldReference field, String name) { 60 | return name + ':' + fromFieldDescriptor(field.getType()); 61 | } 62 | 63 | public static String ofMethod(MethodReference method) { 64 | return ofMethod(method, method.getName()); 65 | } 66 | 67 | public static String ofMethod(MethodReference method, String name) { 68 | return ofMethod(method.getParameterTypes(), method.getReturnType(), name); 69 | } 70 | 71 | public static String ofMethod(Iterable parameterTypes, String returnType, String name) { 72 | StringBuilder sb = new StringBuilder(); 73 | sb.append(name).append('('); 74 | boolean first = true; 75 | for (CharSequence parameterType : parameterTypes) { 76 | if (!first) sb.append(", "); 77 | sb.append(fromFieldDescriptor(parameterType.toString())); 78 | first = false; 79 | } 80 | sb.append("):").append(fromReturnDescriptor(returnType)); 81 | return sb.toString(); 82 | } 83 | 84 | private Label() {} 85 | 86 | } 87 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/SimpleTypeRewriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import org.jf.dexlib2.iface.ClassDef; 14 | import org.jf.dexlib2.rewriter.DexRewriter; 15 | import org.jf.dexlib2.rewriter.Rewriter; 16 | import org.jf.dexlib2.rewriter.RewriterModule; 17 | import org.jf.dexlib2.rewriter.Rewriters; 18 | 19 | public class SimpleTypeRewriter { 20 | 21 | public static RewriterModule getModule(final String fromDescriptor, final String toDescriptor) { 22 | return new RewriterModule() { 23 | @Override 24 | public Rewriter getTypeRewriter(Rewriters rewriters) { 25 | return new ElementalTypeRewriter() { 26 | @Override 27 | public String rewriteElementalType(String elementalType) { 28 | return elementalType.equals(fromDescriptor) ? toDescriptor : elementalType; 29 | } 30 | }; 31 | } 32 | }; 33 | } 34 | 35 | public static ClassDef renameClass(ClassDef classDef, String toClassDescriptor) { 36 | RewriterModule rewriterModule = getModule(classDef.getType(), toClassDescriptor); 37 | return new DexRewriter(rewriterModule).getClassDefRewriter().rewrite(classDef); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/Target.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import lanchon.dexpatcher.core.Marker; 14 | 15 | public class Target { 16 | 17 | public static String resolveDescriptor(String baseDescriptor, String target) 18 | throws InvalidTypeDescriptorException { 19 | // Precondition: target is a non-zero length string. 20 | return DexUtils.isPackageDescriptor(baseDescriptor) ? 21 | Target.resolvePackageDescriptor(target) : 22 | Target.resolveClassDescriptor(baseDescriptor, target); 23 | } 24 | 25 | public static String resolveClassDescriptor(String baseDescriptor, String target) 26 | throws InvalidTypeDescriptorException { 27 | // Precondition: target is a non-zero length string. 28 | if (!DexUtils.isClassDescriptor(target)) { 29 | String baseName = TypeName.fromClassDescriptor(baseDescriptor); 30 | target = TypeName.toClassDescriptor(resolveClassName(baseName, target)); 31 | } 32 | return target; 33 | } 34 | 35 | private static String resolveClassName(String baseName, String target) { 36 | int targetDot = target.indexOf('.'); 37 | if (targetDot < 0) { 38 | // If target is not fully qualified: 39 | int baseNameEnd = baseName.lastIndexOf('.'); 40 | if (target.indexOf('$') < 0) { 41 | // If target is not a qualified nested type: 42 | baseNameEnd = Math.max(baseNameEnd, baseName.lastIndexOf('$')); 43 | } 44 | if (baseNameEnd >= 0) { 45 | target = baseName.substring(0, baseNameEnd + 1) + target; 46 | } 47 | } 48 | return target; 49 | } 50 | 51 | public static String resolvePackageDescriptor(String target) { 52 | // Precondition: target is a non-zero length string. 53 | if (!DexUtils.isClassDescriptor(target)) { 54 | if (target.startsWith(".")) target = target.substring(1); 55 | target = (target.isEmpty()) ? Marker.NAME_PACKAGE_INFO : target + '.' + Marker.NAME_PACKAGE_INFO; 56 | target = TypeName.toClassDescriptor(target); 57 | } 58 | return target; 59 | } 60 | 61 | private Target() {} 62 | 63 | } 64 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/TemplateMapFileWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import java.io.BufferedWriter; 14 | import java.io.File; 15 | import java.io.FileOutputStream; 16 | import java.io.IOException; 17 | import java.io.OutputStream; 18 | import java.io.OutputStreamWriter; 19 | import java.io.PrintWriter; 20 | import java.io.Writer; 21 | import java.nio.charset.StandardCharsets; 22 | 23 | import org.jf.dexlib2.iface.ClassDef; 24 | import org.jf.dexlib2.iface.DexFile; 25 | import org.jf.dexlib2.iface.Field; 26 | import org.jf.dexlib2.iface.Method; 27 | 28 | public class TemplateMapFileWriter { 29 | 30 | private static String MEMBER_INDENTATION = " "; 31 | 32 | public static void write(File file, DexFile dexFile, String prefix) throws IOException { 33 | try (OutputStream outputStream = new FileOutputStream(file)) { 34 | Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); 35 | write(writer, dexFile, prefix); 36 | } 37 | } 38 | 39 | public static void write(Writer writer, DexFile dexFile, String prefix) throws IOException { 40 | write(new BufferedWriter(writer), dexFile, prefix); 41 | } 42 | 43 | public static void write(BufferedWriter writer, DexFile dexFile, String prefix) throws IOException { 44 | write(new PrintWriter(writer), dexFile, prefix); 45 | } 46 | 47 | public static void write(PrintWriter writer, DexFile dexFile, String prefix) throws IOException { 48 | if (prefix == null) prefix = ""; 49 | for (ClassDef classDef : dexFile.getClasses()) { 50 | String classDescriptor = classDef.getType(); 51 | //if (DexUtils.isPackageDescriptor(classDescriptor)) continue; 52 | String className = Label.fromClassDescriptor(classDescriptor); 53 | writer.print(prefix); 54 | writer.print(className); 55 | writer.print(" -> "); 56 | writer.print(className); 57 | writer.print(":"); 58 | writer.println(); 59 | for (Field field : classDef.getFields()) { 60 | String fieldName = field.getName(); 61 | writer.print(prefix); 62 | writer.print(MEMBER_INDENTATION); 63 | writer.print(Label.fromFieldDescriptor(field.getType())); 64 | writer.print(" "); 65 | writer.print(fieldName); 66 | writer.print(" -> "); 67 | writer.print(fieldName); 68 | writer.println(); 69 | } 70 | for (Method method : classDef.getMethods()) { 71 | if (DexUtils.isStaticConstructor(method) || DexUtils.isInstanceConstructor(method)) continue; 72 | String methodName = method.getName(); 73 | writer.print(prefix); 74 | writer.print(MEMBER_INDENTATION); 75 | writer.print(Label.fromReturnDescriptor(method.getReturnType())); 76 | writer.print(" "); 77 | writer.print(methodName); 78 | writer.print("("); 79 | boolean first = true; 80 | for (CharSequence parameterType : method.getParameterTypes()) { 81 | if (!first) writer.print(", "); 82 | writer.print(Label.fromFieldDescriptor(parameterType.toString())); 83 | first = false; 84 | } 85 | writer.print(") -> "); 86 | writer.print(methodName); 87 | writer.println(); 88 | } 89 | writer.println(); 90 | } 91 | if (writer.checkError()) throw new IOException("Cannot write template map file"); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/core/util/TypeName.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.core.util; 12 | 13 | import lanchon.dexpatcher.core.Marker; 14 | 15 | import com.google.common.collect.ImmutableBiMap; 16 | 17 | public class TypeName { 18 | 19 | private static final ImmutableBiMap fieldTypeToNameMap = ImmutableBiMap.builder() 20 | .put("Z", "boolean") 21 | .put("C", "char") 22 | .put("B", "byte") 23 | .put("S", "short") 24 | .put("I", "int") 25 | .put("J", "long") 26 | .put("F", "float") 27 | .put("D", "double") 28 | .build(); 29 | 30 | private static final ImmutableBiMap returnTypeToNameMap = ImmutableBiMap.builder() 31 | .putAll(fieldTypeToNameMap) 32 | .put(Marker.TYPE_VOID, Marker.NAME_VOID) 33 | .build(); 34 | 35 | private static final ImmutableBiMap nameToFieldTypeMap = fieldTypeToNameMap.inverse(); 36 | 37 | public static String fromClassDescriptor(String descriptor) throws InvalidTypeDescriptorException { 38 | if (!DexUtils.isClassDescriptor(descriptor)) { 39 | throw new InvalidTypeDescriptorException("class", descriptor); 40 | } 41 | int length = descriptor.length(); 42 | StringBuilder sb = new StringBuilder(length - 2); 43 | length--; 44 | for (int i = 1; i < length; i++) { 45 | char c = descriptor.charAt(i); 46 | sb.append(c == '/' ? '.' : c); 47 | } 48 | String name = sb.toString(); 49 | if (returnTypeToNameMap.containsValue(name)) name = '.' + name; 50 | return name; 51 | } 52 | 53 | public static String toClassDescriptor(String name) { 54 | int length = name.length(); 55 | int start = name.startsWith(".") ? 1 : 0; 56 | StringBuilder sb = new StringBuilder(length - start + 2); 57 | sb.append('L'); 58 | for (int i = start; i < length; i++) { 59 | char c = name.charAt(i); 60 | sb.append(c == '.' ? '/' : c); 61 | } 62 | sb.append(';'); 63 | return sb.toString(); 64 | } 65 | 66 | public static String fromFieldDescriptor(String descriptor) throws InvalidTypeDescriptorException { 67 | try { 68 | if (descriptor.length() == 1) { 69 | String name = fieldTypeToNameMap.get(descriptor); 70 | if (name != null) return name; 71 | } else if (descriptor.startsWith("[")) { 72 | return fromFieldDescriptor(descriptor.substring(1)) + "[]"; 73 | } 74 | return fromClassDescriptor(descriptor); 75 | } catch (InvalidTypeDescriptorException e) { 76 | throw new InvalidTypeDescriptorException("field", descriptor); 77 | } 78 | } 79 | 80 | public static String toFieldDescriptor(String name) { 81 | if (name.endsWith("[]")) { 82 | return '[' + toFieldDescriptor(name.substring(0, name.length() - 2)); 83 | } 84 | String type = nameToFieldTypeMap.get(name); 85 | return type != null ? type : toClassDescriptor(name); 86 | } 87 | 88 | public static String fromReturnDescriptor(String descriptor) throws InvalidTypeDescriptorException { 89 | try { 90 | return Marker.TYPE_VOID.equals(descriptor) ? Marker.NAME_VOID : fromFieldDescriptor(descriptor); 91 | } catch (InvalidTypeDescriptorException e) { 92 | throw new InvalidTypeDescriptorException("return", descriptor); 93 | } 94 | } 95 | 96 | public static String toReturnDescriptor(String name) { 97 | return Marker.NAME_VOID.equals(name) ? Marker.TYPE_VOID : toFieldDescriptor(name); 98 | } 99 | 100 | private TypeName() {} 101 | 102 | } 103 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/BaseLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform; 12 | 13 | import lanchon.dexpatcher.core.util.Label; 14 | 15 | public abstract class BaseLogger { 16 | 17 | public final TransformLogger logger; 18 | private final String logPrefix; 19 | 20 | protected BaseLogger(TransformLogger logger, String logPrefix) { 21 | this.logger = logger; 22 | this.logPrefix = logPrefix; 23 | logger.markAsInUse(); 24 | } 25 | 26 | public final StringBuilder getMessageHeader() { 27 | StringBuilder sb = new StringBuilder(); 28 | if (logPrefix != null) sb.append(logPrefix).append(": "); 29 | return sb; 30 | } 31 | 32 | public final StringBuilder getMessageHeaderForClass(String descriptor) { 33 | StringBuilder sb = getMessageHeader(); 34 | sb.append("type '").append(Label.fromClassDescriptor(descriptor)).append("': "); 35 | return sb; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/MemberLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform; 12 | 13 | import lanchon.dexpatcher.core.util.Label; 14 | 15 | import org.jf.dexlib2.iface.reference.FieldReference; 16 | import org.jf.dexlib2.iface.reference.MethodReference; 17 | 18 | public abstract class MemberLogger extends BaseLogger { 19 | 20 | private static final boolean LOG_TRANSFORMED_DEFINING_CLASS = false; 21 | 22 | public MemberLogger(TransformLogger logger, String logPrefix) { 23 | super(logger, logPrefix); 24 | } 25 | 26 | public final StringBuilder getMessageHeaderForMember(String definingClass) { 27 | StringBuilder sb = getMessageHeader(); 28 | sb.append("type '").append(Label.fromClassDescriptor(definingClass)); 29 | if (LOG_TRANSFORMED_DEFINING_CLASS) { 30 | String rewrittenDefiningClass = getTransformedDefiningClass(definingClass); 31 | if (rewrittenDefiningClass != null && !rewrittenDefiningClass.equals(definingClass)) { 32 | sb.append("' -> '").append(Label.fromClassDescriptor(rewrittenDefiningClass)); 33 | } 34 | } 35 | sb.append("': "); 36 | return sb; 37 | } 38 | 39 | public final StringBuilder getMessageHeaderForField(FieldReference field) { 40 | StringBuilder sb = getMessageHeaderForMember(field.getDefiningClass()); 41 | sb.append("field '").append(Label.ofField(field)).append("': "); 42 | return sb; 43 | } 44 | 45 | public final StringBuilder getMessageHeaderForMethod(MethodReference method) { 46 | StringBuilder sb = getMessageHeaderForMember(method.getDefiningClass()); 47 | sb.append("method '").append(Label.ofMethod(method)).append("': "); 48 | return sb; 49 | } 50 | 51 | protected abstract String getTransformedDefiningClass(String definingClass); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/TransformLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform; 12 | 13 | import java.util.HashSet; 14 | 15 | import lanchon.dexpatcher.core.logger.Logger; 16 | 17 | public final class TransformLogger { 18 | 19 | private Logger logger; 20 | private boolean inUse; 21 | private boolean sync; 22 | private HashSet loggedMessages; 23 | 24 | public TransformLogger(Logger logger) { 25 | this.logger = logger; 26 | if (logger != null) loggedMessages = new HashSet<>(); 27 | } 28 | 29 | public boolean isInUse() { 30 | return inUse; 31 | } 32 | 33 | public void markAsInUse() { 34 | this.inUse = true; 35 | } 36 | 37 | public boolean getSync() { 38 | return sync; 39 | } 40 | 41 | public void setSync(boolean sync) { 42 | this.sync = sync; 43 | } 44 | 45 | public boolean isLogging() { 46 | return logger != null; 47 | } 48 | 49 | public boolean isLogging(Logger.Level level) { 50 | // NOTE: Logger level is assumed to be constant during renaming. 51 | // This is why the call to logger.isLogging() is not synchronized. 52 | // NOTE: A null value for level disables logging. 53 | return logger != null && level != null && logger.isLogging(level); 54 | } 55 | 56 | public void log(Logger.Level level, String message) { 57 | if (isLogging(level)) { 58 | if (sync) { 59 | synchronized (logger) { 60 | if (loggedMessages.add(message)) logger.log(level, message); 61 | } 62 | } else { 63 | if (loggedMessages.add(message)) logger.log(level, message); 64 | } 65 | } 66 | } 67 | 68 | public void stopLogging() { 69 | logger = null; 70 | loggedMessages = null; 71 | } 72 | 73 | public TransformLogger cloneIf(boolean condition) { 74 | return condition ? new TransformLogger(logger) : this; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/anonymizer/DexAnonymizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.anonymizer; 12 | 13 | import lanchon.dexpatcher.core.logger.Logger; 14 | import lanchon.dexpatcher.core.util.Label; 15 | import lanchon.dexpatcher.transform.BaseLogger; 16 | import lanchon.dexpatcher.transform.TransformLogger; 17 | 18 | import org.jf.dexlib2.rewriter.Rewriter; 19 | import org.jf.dexlib2.rewriter.RewriterModule; 20 | import org.jf.dexlib2.rewriter.Rewriters; 21 | 22 | public final class DexAnonymizer extends BaseLogger implements Rewriter, TypeAnonymizer.ErrorHandler { 23 | 24 | private final TypeAnonymizer typeAnonymizer; 25 | private final Logger.Level infoLevel; 26 | private final Logger.Level errorLevel; 27 | 28 | public DexAnonymizer(TypeAnonymizer typeAnonymizer, TransformLogger logger, String logPrefix, 29 | Logger.Level infoLevel, Logger.Level errorLevel) { 30 | super(logger, logPrefix); 31 | this.typeAnonymizer = typeAnonymizer; 32 | this.infoLevel = infoLevel; 33 | this.errorLevel = errorLevel; 34 | } 35 | 36 | public RewriterModule getModule() { 37 | return new RewriterModule() { 38 | @Override 39 | public Rewriter getTypeRewriter(Rewriters rewriters) { 40 | return DexAnonymizer.this; 41 | } 42 | }; 43 | } 44 | 45 | @Override 46 | public String rewrite(String type) { 47 | String anonymizedType = typeAnonymizer.anonymizeType(type, this); 48 | if (logger.isLogging(infoLevel) && !anonymizedType.equals(type)) { 49 | StringBuilder sb = getMessageHeaderForClass(type); 50 | sb.append(typeAnonymizer.isReanonymizer() ? "reanonymized to '" : "deanonymized to '") 51 | .append(Label.fromClassDescriptor(anonymizedType)).append("'"); 52 | logger.log(infoLevel, sb.toString()); 53 | } 54 | return anonymizedType; 55 | } 56 | 57 | @Override 58 | public void onError(String type, String message) { 59 | if (logger.isLogging(errorLevel)) { 60 | StringBuilder sb = getMessageHeaderForClass(type); 61 | sb.append(message); 62 | logger.log(errorLevel, sb.toString()); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/BinaryClassNameRewriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec; 12 | 13 | import org.jf.dexlib2.rewriter.Rewriter; 14 | 15 | // Binary class name '' (eg: 'java/lang/String') corresponds to type descriptor 'L;'. 16 | public abstract class BinaryClassNameRewriter implements Rewriter { 17 | 18 | @Override 19 | public String rewrite(String value) { 20 | int end = value.length() - 1; 21 | if (end < 0 || value.charAt(end) != ';') return value; 22 | int start = 0; 23 | char c; 24 | while ((c = value.charAt(start)) == '[') start++; 25 | if (c != 'L') return value; 26 | start++; 27 | String name = value.substring(start, end); 28 | String rewrittenName = rewriteBinaryClassName(name); 29 | if (rewrittenName.equals(name)) return value; 30 | StringBuilder sb = new StringBuilder(start + rewrittenName.length() + 1); 31 | sb.append(value, 0, start).append(rewrittenName).append(';'); 32 | return sb.toString(); 33 | } 34 | 35 | public abstract String rewriteBinaryClassName(String binaryClassName); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/DexCodec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec; 12 | 13 | import lanchon.dexpatcher.transform.MemberLogger; 14 | import lanchon.dexpatcher.transform.TransformLogger; 15 | import lanchon.dexpatcher.transform.codec.DexCodecModule.ItemType; 16 | 17 | import org.jf.dexlib2.rewriter.RewriterModule; 18 | 19 | public abstract class DexCodec extends MemberLogger implements DexCodecModule.ItemRewriter { 20 | 21 | public static String formatValue(ItemType itemType, String value) { 22 | return itemType == ItemType.BINARY_CLASS_NAME ? value.replace('/', '.') : value; 23 | } 24 | 25 | public DexCodec(TransformLogger logger, String logPrefix) { 26 | super(logger, logPrefix); 27 | } 28 | 29 | public final RewriterModule getModule() { 30 | return new DexCodecModule(this); 31 | } 32 | 33 | public final StringBuilder getMessageHeader(String definingClass, ItemType itemType, String value) { 34 | StringBuilder sb = (definingClass != null) ? getMessageHeaderForMember(definingClass) : getMessageHeader(); 35 | sb.append(itemType.label).append(" '").append(formatValue(itemType, value)).append("': "); 36 | return sb; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/StringCodec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec; 12 | 13 | public abstract class StringCodec { 14 | 15 | public static final String DEFAULT_CODE_MARKER = "_$$_"; 16 | 17 | public static boolean isValidCodeMarker(String marker) { 18 | return marker.length() >= 2 && marker.startsWith("_") && !marker.startsWith("__") && !marker.endsWith("__"); 19 | } 20 | 21 | protected final String codeMarker; 22 | 23 | public StringCodec(String codeMarker) { 24 | if (!isValidCodeMarker(codeMarker)) { 25 | throw new IllegalArgumentException("codeMarker"); 26 | } 27 | this.codeMarker = codeMarker; 28 | } 29 | 30 | public String getCodeMarker() { 31 | return codeMarker; 32 | } 33 | 34 | public boolean isCodedString(String string) { 35 | return string.contains(codeMarker); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/decoder/DexDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.decoder; 12 | 13 | import lanchon.dexpatcher.core.logger.Logger; 14 | import lanchon.dexpatcher.transform.TransformLogger; 15 | import lanchon.dexpatcher.transform.codec.DexCodec; 16 | import lanchon.dexpatcher.transform.codec.DexCodecModule.ItemType; 17 | 18 | public final class DexDecoder extends DexCodec { 19 | 20 | private final StringDecoder stringDecoder; 21 | private final Logger.Level infoLevel; 22 | private final Logger.Level errorLevel; 23 | 24 | public DexDecoder(StringDecoder stringDecoder, TransformLogger logger, String logPrefix, Logger.Level infoLevel, 25 | Logger.Level errorLevel) { 26 | super(logger, logPrefix); 27 | this.stringDecoder = stringDecoder; 28 | this.infoLevel = infoLevel; 29 | this.errorLevel = errorLevel; 30 | } 31 | 32 | @Override 33 | protected String getTransformedDefiningClass(String definingClass) { 34 | return stringDecoder.decodeString(definingClass); 35 | } 36 | 37 | @Override 38 | public String rewriteItem(final String definingClass, final ItemType itemType, final String value) { 39 | if (value == null) return null; 40 | String decodedValue = stringDecoder.decodeString(value, new StringDecoder.ErrorHandler() { 41 | @Override 42 | public void onError(String message, String string, int codeStart, int codeEnd, int errorStart, 43 | int errorEnd) { 44 | if (logger.isLogging(errorLevel)) { 45 | StringBuilder sb = getMessageHeader(definingClass, itemType, value); 46 | sb.append(message); 47 | sb.append(" in '").append(string, codeStart, errorStart) 48 | .append("[->]").append(string, errorStart, errorEnd).append("[<-]") 49 | .append(string, errorEnd, codeEnd).append("'"); 50 | logger.log(errorLevel, sb.toString()); 51 | } 52 | } 53 | }); 54 | if (logger.isLogging(infoLevel) && !decodedValue.equals(value)) { 55 | StringBuilder sb = getMessageHeader(definingClass, itemType, value); 56 | sb.append("decoded to '").append(formatValue(itemType, decodedValue)).append("'"); 57 | logger.log(infoLevel, sb.toString()); 58 | } 59 | return decodedValue; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/decoder/StringDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.decoder; 12 | 13 | import lanchon.dexpatcher.transform.codec.StringCodec; 14 | 15 | public final class StringDecoder extends StringCodec { 16 | 17 | public interface ErrorHandler { 18 | void onError(String message, String string, int codeStart, int codeEnd, int errorStart, int errorEnd); 19 | } 20 | 21 | public static final ErrorHandler NULL_ERROR_HANDLER = new ErrorHandler() { 22 | @Override 23 | public void onError(String message, String string, int codeStart, int codeEnd, int errorStart, int errorEnd) {} 24 | }; 25 | 26 | public StringDecoder(String codeMarker) { 27 | super(codeMarker); 28 | } 29 | 30 | public String decodeString(String string) { 31 | return decodeString(string, NULL_ERROR_HANDLER); 32 | } 33 | 34 | public String decodeString(String string, ErrorHandler errorHandler) { 35 | return string != null ? decodeStringTail(string, 0, errorHandler) : null; 36 | } 37 | 38 | private String decodeStringTail(String string, int start, ErrorHandler errorHandler) { 39 | 40 | int markerStart = string.indexOf(codeMarker, start); 41 | if (markerStart < 0) return string.substring(start); 42 | 43 | int markerEnd = markerStart + codeMarker.length(); 44 | int length = string.length(); 45 | 46 | recoverFromError: 47 | do { 48 | 49 | int codeStart = markerStart; 50 | while (--codeStart >= start) { 51 | if (string.startsWith("__", codeStart)) break; 52 | } 53 | int codeEnd = markerEnd; 54 | while (++codeEnd <= length) { 55 | if (string.startsWith("__", codeEnd - 2)) break; 56 | } 57 | 58 | if (codeStart < start) { 59 | if (codeEnd > length) codeEnd = length; 60 | errorHandler.onError("missing start of code", string, start, codeEnd, start, markerEnd); 61 | break recoverFromError; 62 | } 63 | if (codeEnd > length) { 64 | errorHandler.onError("missing end of code", string, codeStart, length, markerStart, length); 65 | break recoverFromError; 66 | } 67 | 68 | int escapeEnd = codeEnd - 2; 69 | if (markerEnd >= escapeEnd) { 70 | //return string.substring(start, codeStart) + decodeStringTail(string, codeEnd, errorHandler); 71 | errorHandler.onError("empty code", string, codeStart, codeEnd, markerStart, codeEnd); 72 | break recoverFromError; 73 | } 74 | 75 | StringBuilder sb = new StringBuilder(codeStart + (length - markerEnd - 2)); 76 | sb.append(string, start, codeStart); 77 | 78 | for (int i = markerEnd; i < escapeEnd; i++) { 79 | char c = string.charAt(i); 80 | switch (c) { 81 | case '_': 82 | errorHandler.onError("invalid character '_'", string, codeStart, codeEnd, i, i + 1); 83 | break recoverFromError; 84 | case '$': 85 | int escapeIndex = i; 86 | if (i + 1 < escapeEnd) { 87 | char e = string.charAt(++i); 88 | switch (e) { 89 | case 'S': 90 | sb.append('$'); 91 | continue; 92 | case 'U': 93 | sb.append('_'); 94 | continue; 95 | case 'a': 96 | case 'u': 97 | case 'p': 98 | int n = (e == 'a' ? 2 : e == 'u' ? 4 : 6); 99 | int value = 0; 100 | for (; n != 0; n--) { 101 | if (i + 1 >= escapeEnd) break; 102 | int digit = Character.digit(string.charAt(++i), 16); 103 | if (digit < 0) break; 104 | value = (value << 4 | digit); 105 | } 106 | if (n == 0) { 107 | if (Character.isValidCodePoint(value)) { 108 | sb.appendCodePoint(value); 109 | continue; 110 | } 111 | } 112 | } 113 | } 114 | i++; 115 | String message = "invalid escape sequence '" + string.substring(escapeIndex, i) + "'"; 116 | errorHandler.onError(message, string, codeStart, codeEnd, escapeIndex, i); 117 | break recoverFromError; 118 | default: 119 | sb.append(c); 120 | continue; 121 | } 122 | } 123 | 124 | sb.append(decodeStringTail(string, codeEnd, errorHandler)); 125 | return sb.toString(); 126 | 127 | } while (false); 128 | 129 | int i = markerStart + 1; 130 | return string.substring(start, i) + decodeStringTail(string, i, errorHandler); 131 | 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/BasicDexEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import lanchon.dexpatcher.core.logger.Logger; 14 | import lanchon.dexpatcher.transform.TransformLogger; 15 | import lanchon.dexpatcher.transform.codec.DexCodec; 16 | import lanchon.dexpatcher.transform.codec.DexCodecModule.ItemType; 17 | 18 | public final class BasicDexEncoder extends DexCodec { 19 | 20 | private final BasicStringEncoder basicStringEncoder; 21 | private final Logger.Level infoLevel; 22 | 23 | public BasicDexEncoder(BasicStringEncoder basicStringEncoder, TransformLogger logger, String logPrefix, 24 | Logger.Level infoLevel) { 25 | super(logger, logPrefix); 26 | this.basicStringEncoder = basicStringEncoder; 27 | this.infoLevel = infoLevel; 28 | } 29 | 30 | @Override 31 | protected String getTransformedDefiningClass(String definingClass) { 32 | return basicStringEncoder.encodeString(definingClass); 33 | } 34 | 35 | @Override 36 | public String rewriteItem(String definingClass, ItemType itemType, String value) { 37 | if (value == null) return null; 38 | String encodedValue = basicStringEncoder.encodeString(value); 39 | if (logger.isLogging(infoLevel) && !encodedValue.equals(value)) { 40 | StringBuilder sb = getMessageHeader(definingClass, itemType, value); 41 | sb.append("escaped to '").append(formatValue(itemType, encodedValue)).append("'"); 42 | logger.log(infoLevel, sb.toString()); 43 | } 44 | return encodedValue; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/BasicStringEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import lanchon.dexpatcher.transform.codec.StringCodec; 14 | 15 | public final class BasicStringEncoder extends StringCodec { 16 | 17 | protected final String encodedCodeMarker; 18 | 19 | public BasicStringEncoder(String codeMarker) { 20 | super(codeMarker); 21 | encodedCodeMarker = "_" + codeMarker + "$U__" + codeMarker.substring(1); 22 | } 23 | 24 | public BasicStringEncoder(String codeMarker, String encodedCodeMarker) { 25 | super(codeMarker); 26 | this.encodedCodeMarker = encodedCodeMarker; 27 | } 28 | 29 | public String encodeString(String string) { 30 | return string != null ? string.replace(codeMarker, encodedCodeMarker) : null; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/CopyStringBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | public final class CopyStringBuilder { 14 | 15 | private static final boolean CHECK_ARGS = false; 16 | 17 | private final int initialCapacity; 18 | private final String source; 19 | 20 | private StringBuilder sb; 21 | private int pos; 22 | 23 | public CopyStringBuilder(String source, int copyPos) { 24 | this(source.length() + 16, source, copyPos); 25 | } 26 | 27 | public CopyStringBuilder(int initialCapacity, String source, int copyPos) { 28 | if (CHECK_ARGS && (copyPos < 0 || copyPos > source.length())) throw new IllegalArgumentException("copyPos"); 29 | this.initialCapacity = initialCapacity; 30 | this.source = source; 31 | pos = copyPos; 32 | } 33 | 34 | public void copyCount(int count) { 35 | copy(pos + count); 36 | } 37 | 38 | public void copyToEnd() { 39 | copy(source.length()); 40 | } 41 | 42 | public void copy(int newPos) { 43 | if (CHECK_ARGS && (newPos < pos || newPos > source.length())) throw new IllegalArgumentException("newPos"); 44 | if (sb != null) sb.append(source, pos, newPos); 45 | pos = newPos; 46 | } 47 | 48 | public StringBuilder skip(int newPos) { 49 | if (CHECK_ARGS && (newPos < pos || newPos > source.length())) throw new IllegalArgumentException("newPos"); 50 | if (sb == null) sb = new StringBuilder(initialCapacity).append(source, 0, pos); 51 | pos = newPos; 52 | return sb; 53 | } 54 | 55 | public StringBuilder get() { 56 | if (sb == null) sb = new StringBuilder(initialCapacity).append(source, 0, pos); 57 | return sb; 58 | } 59 | 60 | public StringBuilder getOrNull() { 61 | return sb; 62 | } 63 | 64 | public String getSource() { 65 | return source; 66 | } 67 | 68 | public int getPos() { 69 | return pos; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/DefaultIgnoredHintTypes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import java.io.Closeable; 14 | import java.io.Externalizable; 15 | import java.io.Flushable; 16 | import java.io.Serializable; 17 | 18 | import lanchon.dexpatcher.core.util.TypeName; 19 | 20 | import com.google.common.collect.ImmutableSet; 21 | 22 | public final class DefaultIgnoredHintTypes { 23 | 24 | public static final ImmutableSet SET; 25 | 26 | static { 27 | 28 | Class[] types = new Class[] { 29 | 30 | // java.lang 31 | AutoCloseable.class, 32 | Cloneable.class, 33 | Comparable.class, 34 | 35 | // java.io 36 | Closeable.class, 37 | Externalizable.class, 38 | Flushable.class, 39 | Serializable.class 40 | 41 | }; 42 | 43 | ImmutableSet.Builder builder = ImmutableSet.builder(); 44 | for (Class type : types) builder.add(TypeName.toClassDescriptor(type.getName())); 45 | SET = builder.build(); 46 | 47 | } 48 | 49 | private DefaultIgnoredHintTypes() {} 50 | 51 | } 52 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/EncoderConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import java.util.Set; 14 | import java.util.regex.Pattern; 15 | 16 | public class EncoderConfiguration extends StringEscaperConfiguration { 17 | 18 | public Pattern obfuscatedBinaryTypeNamePattern; 19 | 20 | public Pattern obfuscatedPackageNamePattern; 21 | public Pattern obfuscatedClassNamePattern; 22 | public Pattern obfuscatedMemberNamePattern; 23 | 24 | //public boolean encodeAllPackages; 25 | public boolean encodeAllClasses; 26 | //public boolean encodeAllMembers; 27 | 28 | public boolean encodeObfuscatedPackages; 29 | public boolean encodeObfuscatedClasses; 30 | public boolean encodeObfuscatedMembers; 31 | 32 | public boolean encodeReservedCharacters; 33 | public boolean encodeReservedWords; 34 | 35 | public boolean encodeTypeHintsInClasses; 36 | public boolean encodeTypeHintsInMembers; 37 | public boolean encodeTypeInfoInMembers; 38 | 39 | public boolean includeIdentifierType = true; 40 | public boolean allowMultipleTypeHints = true; 41 | public boolean processNestedClasses = true; 42 | 43 | public Pattern ignoredHintTypePattern; 44 | public Set ignoredHintTypes = DefaultIgnoredHintTypes.SET; 45 | 46 | public void setEncodeCompilable() { 47 | encodeAllClasses = true; 48 | encodeReservedCharacters = true; 49 | encodeReservedWords = true; 50 | encodeTypeInfoInMembers = true; 51 | includeIdentifierType = true; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/ReservedWords.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import java.util.Arrays; 14 | import java.util.HashSet; 15 | import java.util.Set; 16 | 17 | public final class ReservedWords { 18 | 19 | // Based on the Java 13 JLS. 20 | 21 | private static final Set RESERVED_WORDS; 22 | 23 | static { 24 | String[] keywords = new String[] { 25 | "abstract", "continue", "for", "new", "switch", 26 | "assert", "default", "if", "package", "synchronized", 27 | "boolean", "do", "goto", "private", "this", 28 | "break", "double", "implements", "protected", "throw", 29 | "byte", "else", "import", "public", "throws", 30 | "case", "enum", "instanceof", "return", "transient", 31 | "catch", "extends", "int", "short", "try", 32 | "char", "final", "interface", "static", "void", 33 | "class", "finally", "long", "strictfp", "volatile", 34 | "const", "float", "native", "super", "while", 35 | "_" 36 | }; 37 | String[] literals = new String[] { 38 | "false", "true", "null" 39 | }; 40 | String[] reservedTypeNames = new String[] { 41 | "var" 42 | }; 43 | String[] restrictedKeywords = new String[] { 44 | "exports", "open", "provides", "to", "uses", 45 | "module", "opens", "requires", "transitive", "with" 46 | }; 47 | RESERVED_WORDS = new HashSet<>(); 48 | RESERVED_WORDS.addAll(Arrays.asList(keywords)); 49 | RESERVED_WORDS.addAll(Arrays.asList(literals)); 50 | RESERVED_WORDS.addAll(Arrays.asList(reservedTypeNames)); 51 | //RESERVED_WORDS.addAll(Arrays.asList(restrictedKeywords)); 52 | } 53 | 54 | public static boolean isReserved(String word) { 55 | return RESERVED_WORDS.contains(word); 56 | } 57 | 58 | private ReservedWords() {} 59 | 60 | } 61 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/StringEscaper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import java.util.Locale; 14 | 15 | public final class StringEscaper { 16 | 17 | private final boolean escapeNonAscii; 18 | private final boolean escapeNonAsciiLatin1; 19 | 20 | private final boolean disableAsciiLatin1Escapes; 21 | private final boolean disableCodePointEscapes; 22 | 23 | public StringEscaper(StringEscaperConfiguration configuration) { 24 | this.escapeNonAscii = configuration.escapeNonAscii; 25 | this.escapeNonAsciiLatin1 = configuration.escapeNonAsciiLatin1; 26 | this.disableAsciiLatin1Escapes = configuration.disableAsciiLatin1Escapes; 27 | this.disableCodePointEscapes = configuration.disableCodePointEscapes; 28 | } 29 | 30 | public boolean shouldEscape(int codePoint) { 31 | if (codePoint < 0) throw new IllegalArgumentException("codePoint"); 32 | return Character.isISOControl(codePoint) || 33 | (escapeNonAscii && codePoint > 0x7F) || 34 | (escapeNonAsciiLatin1 && codePoint > 0xFF) || 35 | !Character.isJavaIdentifierPart(codePoint) || 36 | Character.isIdentifierIgnorable(codePoint); 37 | } 38 | 39 | public boolean shouldEscape(String s) { 40 | int length = s.length(); 41 | int i = 0; 42 | while (i < length) { 43 | int codePoint = s.codePointAt(i); 44 | if (shouldEscape(codePoint)) return true; 45 | i += Character.charCount(codePoint); 46 | } 47 | return false; 48 | } 49 | 50 | public StringBuilder escape(StringBuilder sb, int codePoint) { 51 | if (codePoint <= 0xFF && !disableAsciiLatin1Escapes) { 52 | sb.append("$a").append(String.format(Locale.ROOT, "%02X", codePoint)); 53 | } else if (Character.isBmpCodePoint(codePoint)) { 54 | sb.append("$u").append(String.format(Locale.ROOT, "%04X", codePoint)); 55 | } else if (Character.isSupplementaryCodePoint(codePoint)) { 56 | if (!disableCodePointEscapes) { 57 | sb.append("$p").append(String.format(Locale.ROOT, "%06X", codePoint)); 58 | } else { 59 | sb.append("$u").append(String.format(Locale.ROOT, "%04X", (int) Character.highSurrogate(codePoint))); 60 | sb.append("$u").append(String.format(Locale.ROOT, "%04X", (int) Character.lowSurrogate(codePoint))); 61 | } 62 | } else { 63 | throw new IllegalArgumentException("codePoint"); 64 | } 65 | return sb; 66 | } 67 | 68 | public StringBuilder escape(StringBuilder sb, String s) { 69 | int length = s.length(); 70 | int i = 0; 71 | while (i < length) { 72 | int codePoint = s.codePointAt(i); 73 | if (codePoint == '$') { 74 | sb.append("$S"); 75 | } else if (codePoint == '_') { 76 | sb.append("$U"); 77 | } else if (shouldEscape(codePoint)) { 78 | escape(sb, codePoint); 79 | } else { 80 | sb.appendCodePoint(codePoint); 81 | } 82 | i += Character.charCount(codePoint); 83 | } 84 | return sb; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/StringEscaperConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | public class StringEscaperConfiguration { 14 | 15 | public boolean escapeNonAscii; 16 | public boolean escapeNonAsciiLatin1; 17 | 18 | public boolean disableAsciiLatin1Escapes; 19 | public boolean disableCodePointEscapes; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/TypeClassifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import java.util.regex.Pattern; 14 | 15 | public final class TypeClassifier { 16 | 17 | private final Pattern obfuscatedBinaryTypeNamePattern; 18 | private final Pattern obfuscatedClassNamePattern; 19 | 20 | public TypeClassifier(Pattern obfuscatedBinaryTypeNamePattern, Pattern obfuscatedClassNamePattern) { 21 | this.obfuscatedBinaryTypeNamePattern = obfuscatedBinaryTypeNamePattern; 22 | this.obfuscatedClassNamePattern = obfuscatedClassNamePattern; 23 | } 24 | 25 | public boolean isObfuscatedType(String type, boolean defaultValue) { 26 | //if (!DexUtils.isClassDescriptor(type)) throw new AssertionError("Invalid class descriptor"); 27 | if (obfuscatedBinaryTypeNamePattern != null) { 28 | if (obfuscatedBinaryTypeNamePattern.matcher(type).region(1, type.length() - 1).matches()) return true; 29 | defaultValue = false; 30 | } 31 | if (obfuscatedClassNamePattern != null) { 32 | int packageEnd = type.lastIndexOf('/'); 33 | int nameStart = (packageEnd < 0) ? 1 : packageEnd + 1; 34 | String name = type.substring(nameStart, type.length() - 1); 35 | return obfuscatedClassNamePattern.matcher(name).matches(); 36 | } 37 | return defaultValue; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/TypeHintMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.Set; 16 | import java.util.regex.Pattern; 17 | 18 | import org.jf.dexlib2.iface.ClassDef; 19 | import org.jf.dexlib2.iface.DexFile; 20 | 21 | public class TypeHintMapper extends TypeInfoMapper{ 22 | 23 | public static final String NON_OBFUSCATED_TYPE_HINT = new String(); 24 | 25 | public static Map buildBasicTypeHintMap(DexFile dex, TypeClassifier typeClassifier, 26 | boolean defaultValue) { 27 | Set classes = dex.getClasses(); 28 | Map typeHintMap = new HashMap<>(classes.size()); 29 | for (ClassDef classDef : classes) { 30 | String type = classDef.getType(); 31 | boolean obfuscated = typeClassifier.isObfuscatedType(type, defaultValue); 32 | typeHintMap.put(type, obfuscated ? "" : NON_OBFUSCATED_TYPE_HINT); 33 | } 34 | return typeHintMap; 35 | } 36 | 37 | public static StringBuilder encodeTypeHint(StringBuilder sb, String type, StringEscaper stringEscaper) { 38 | //if (!DexUtils.isClassDescriptor(type)) throw new AssertionError("Invalid class descriptor"); 39 | int packageEnd = type.lastIndexOf('/'); 40 | int nameStart = (packageEnd < 0) ? 1 : packageEnd + 1; 41 | stringEscaper.escape(sb.append('_'), type.substring(nameStart, type.length() - 1)); 42 | return sb; 43 | } 44 | 45 | protected final boolean allowMultipleTypeHints; 46 | protected final Pattern ignoredHintTypePattern; 47 | protected final Set ignoredHintTypes; 48 | protected final StringEscaper stringEscaper; 49 | 50 | public TypeHintMapper(TypeClassifier typeClassifier, boolean allowMultipleTypeHints, 51 | Pattern ignoredHintTypePattern, Set ignoredHintTypes, StringEscaper stringEscaper) { 52 | super(typeClassifier, true); 53 | this.allowMultipleTypeHints = allowMultipleTypeHints; 54 | this.ignoredHintTypePattern = ignoredHintTypePattern; 55 | this.ignoredHintTypes = ignoredHintTypes; 56 | this.stringEscaper = stringEscaper; 57 | } 58 | 59 | public Map buildTypeHintMap(DexFile dex) { 60 | Map typeInfoMap = buildTypeInfoMap(dex); 61 | Map typeHintMap = new HashMap<>(typeInfoMap.size()); 62 | for (Map.Entry entry : typeInfoMap.entrySet()) { 63 | String type = entry.getKey(); 64 | TypeInfoMapper.TypeInfo info = entry.getValue(); 65 | String hint; 66 | if (info == TypeInfoMapper.NON_OBFUSCATED_TYPE_INFO) { 67 | hint = NON_OBFUSCATED_TYPE_HINT; 68 | } else { 69 | StringBuilder sb = new StringBuilder(); 70 | if (info.clearType != null) { 71 | encodeTypeHint(sb, info.clearType, stringEscaper); 72 | } else { 73 | Set clearInterfaces = info.clearInterfaces; 74 | if (allowMultipleTypeHints || clearInterfaces.size() == 1) { 75 | for (String interfaceType : clearInterfaces) { 76 | encodeTypeHint(sb, interfaceType, stringEscaper); 77 | } 78 | } 79 | } 80 | hint = sb.toString(); 81 | } 82 | typeHintMap.put(type, hint); 83 | } 84 | return typeHintMap; 85 | } 86 | 87 | @Override 88 | public boolean isIgnoredType(String type) { 89 | return 90 | (ignoredHintTypes != null && 91 | ignoredHintTypes.contains(type)) || 92 | (ignoredHintTypePattern != null && 93 | ignoredHintTypePattern.matcher(type).region(1, type.length() - 1).matches()); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/codec/encoder/TypeInfoMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.codec.encoder; 12 | 13 | import java.util.Collections; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.Set; 17 | import java.util.TreeSet; 18 | 19 | import org.jf.dexlib2.iface.ClassDef; 20 | import org.jf.dexlib2.iface.DexFile; 21 | 22 | public class TypeInfoMapper { 23 | 24 | public static class TypeInfo { 25 | // Note: clearInterfaces can only contain elements if clearType is null. 26 | public String clearType; 27 | public Set clearInterfaces = Collections.emptySet(); 28 | } 29 | 30 | public static final TypeInfo NON_OBFUSCATED_TYPE_INFO = new TypeInfo(); 31 | 32 | private static final TypeInfo IGNORED_TYPE_INFO = new TypeInfo(); 33 | private static final String JAVA_LANG_OBJECT = "Ljava/lang/Object;"; 34 | 35 | protected final TypeClassifier typeClassifier; 36 | protected final boolean processInterfaces; 37 | 38 | public TypeInfoMapper(TypeClassifier typeClassifier, boolean processInterfaces) { 39 | this.typeClassifier = typeClassifier; 40 | this.processInterfaces = processInterfaces; 41 | } 42 | 43 | public Map buildTypeInfoMap(DexFile dex) { 44 | Set classes = dex.getClasses(); 45 | Map classMap = new HashMap<>(classes.size()); 46 | for (ClassDef classDef : classes) { 47 | classMap.put(classDef.getType(), classDef); 48 | } 49 | Map typeInfoMap = new HashMap<>(classes.size()); 50 | for (ClassDef classDef : classes) { 51 | buildTypeInfo(typeInfoMap, classMap, classDef, classDef.getType()); 52 | } 53 | return typeInfoMap; 54 | } 55 | 56 | private TypeInfo buildTypeInfo(Map typeInfoMap, Map classMap, 57 | String type) { 58 | ClassDef classDef = classMap.get(type); 59 | if (classDef == null) return null; 60 | return buildTypeInfo(typeInfoMap, classMap, classDef, type); 61 | } 62 | 63 | private TypeInfo buildTypeInfo(Map typeInfoMap, Map classMap, 64 | ClassDef classDef, String type) { 65 | TypeInfo info = typeInfoMap.get(type); 66 | if (info == NON_OBFUSCATED_TYPE_INFO) return null; 67 | if (info != null) return info; 68 | if (!typeClassifier.isObfuscatedType(type, true)) { 69 | typeInfoMap.put(type, NON_OBFUSCATED_TYPE_INFO); 70 | return null; 71 | } 72 | if (isIgnoredType(type)) { 73 | typeInfoMap.put(type, IGNORED_TYPE_INFO); 74 | return null; 75 | } 76 | info = new TypeInfo(); 77 | // Early put to avoid infinite recursion while processing malformed dex files. 78 | typeInfoMap.put(type, info); 79 | String superclassType = classDef.getSuperclass(); 80 | if (JAVA_LANG_OBJECT.equals(superclassType)) superclassType = null; 81 | Set superclassClearInterfaces = null; 82 | if (superclassType != null && !isIgnoredType(superclassType)) { 83 | TypeInfo superclassInfo = buildTypeInfo(typeInfoMap, classMap, superclassType); 84 | if (superclassInfo == null) { 85 | // Type is external to dex file or not obfuscated. 86 | info.clearType = superclassType; 87 | return info; 88 | } 89 | if (superclassInfo.clearType != null) { 90 | info.clearType = superclassInfo.clearType; 91 | return info; 92 | } 93 | superclassClearInterfaces = superclassInfo.clearInterfaces; 94 | } 95 | if (!processInterfaces) { 96 | return info; 97 | } 98 | Set clearInterfaces = new TreeSet<>(); 99 | if (superclassClearInterfaces != null) { 100 | clearInterfaces.addAll(superclassClearInterfaces); 101 | } 102 | for (String interfaceType : classDef.getInterfaces()) { 103 | if (!isIgnoredType(interfaceType)) { 104 | TypeInfo interfaceInfo = buildTypeInfo(typeInfoMap, classMap, interfaceType); 105 | if (interfaceInfo == null) { 106 | // Type is external to dex file or not obfuscated. 107 | clearInterfaces.add(interfaceType); 108 | } else { 109 | if (interfaceInfo.clearType != null) { 110 | clearInterfaces.add(interfaceInfo.clearType); 111 | } else { 112 | clearInterfaces.addAll(interfaceInfo.clearInterfaces); 113 | } 114 | } 115 | } 116 | } 117 | if (!clearInterfaces.isEmpty()) info.clearInterfaces = clearInterfaces; 118 | return info; 119 | } 120 | 121 | protected boolean isIgnoredType(String type) { 122 | return false; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/mapper/DexMapperModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.mapper; 12 | 13 | import lanchon.dexpatcher.core.util.DexUtils; 14 | import lanchon.dexpatcher.core.util.ElementalTypeRewriter; 15 | import lanchon.dexpatcher.transform.mapper.map.DexMap; 16 | import lanchon.dexpatcher.transform.util.wrapper.WrapperFieldReference; 17 | import lanchon.dexpatcher.transform.util.wrapper.WrapperMethodReference; 18 | import lanchon.dexpatcher.transform.util.wrapper.WrapperRewriterModule; 19 | 20 | import org.jf.dexlib2.iface.reference.FieldReference; 21 | import org.jf.dexlib2.iface.reference.MethodReference; 22 | import org.jf.dexlib2.rewriter.Rewriter; 23 | import org.jf.dexlib2.rewriter.RewriterModule; 24 | import org.jf.dexlib2.rewriter.Rewriters; 25 | 26 | public class DexMapperModule extends WrapperRewriterModule { 27 | 28 | protected final DexMap dexMap; 29 | 30 | public DexMapperModule(DexMap dexMap) { 31 | this(dexMap, new RewriterModule()); 32 | } 33 | 34 | public DexMapperModule(DexMap dexMap, RewriterModule wrappedModule) { 35 | super(wrappedModule); 36 | this.dexMap = dexMap; 37 | } 38 | 39 | @Override 40 | public Rewriter getTypeRewriter(Rewriters rewriters) { 41 | final Rewriter wrappedTypeRewriter = wrappedModule.getTypeRewriter(rewriters); 42 | return new ElementalTypeRewriter() { 43 | @Override 44 | public String rewriteElementalType(String type) { 45 | if (DexUtils.isClassDescriptor(type)) { 46 | String mapping = dexMap.getClassMapping(type); 47 | if (mapping != null) return mapping; 48 | } 49 | // WARNING: This routes the elemental type to the wrapped rewriter. 50 | return wrappedTypeRewriter.rewrite(type); 51 | } 52 | }; 53 | } 54 | 55 | @Override 56 | public Rewriter getFieldReferenceRewriter(Rewriters rewriters) { 57 | final Rewriter wrappedFieldReferenceRewriter = 58 | wrappedModule.getFieldReferenceRewriter(rewriters); 59 | return new Rewriter() { 60 | @Override 61 | public FieldReference rewrite(FieldReference field) { 62 | FieldReference rewrittenField = wrappedFieldReferenceRewriter.rewrite(field); 63 | final String mapping = dexMap.getFieldMapping(field); 64 | if (mapping != null) { 65 | return new WrapperFieldReference(rewrittenField) { 66 | @Override 67 | public String getName() { 68 | return mapping; 69 | } 70 | }; 71 | } 72 | return rewrittenField; 73 | } 74 | }; 75 | } 76 | 77 | @Override 78 | public Rewriter getMethodReferenceRewriter(Rewriters rewriters) { 79 | final Rewriter wrappedMethodReferenceRewriter = 80 | wrappedModule.getMethodReferenceRewriter(rewriters); 81 | return new Rewriter() { 82 | @Override 83 | public MethodReference rewrite(MethodReference method) { 84 | MethodReference rewrittenMethod = wrappedMethodReferenceRewriter.rewrite(method); 85 | final String mapping = dexMap.getMethodMapping(method); 86 | if (mapping != null) { 87 | return new WrapperMethodReference(rewrittenMethod) { 88 | @Override 89 | public String getName() { 90 | return mapping; 91 | } 92 | }; 93 | } 94 | return rewrittenMethod; 95 | } 96 | }; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/mapper/map/DexMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.mapper.map; 12 | 13 | import org.jf.dexlib2.iface.reference.FieldReference; 14 | import org.jf.dexlib2.iface.reference.MethodReference; 15 | 16 | public interface DexMap { 17 | 18 | String getClassMapping(String descriptor); 19 | String getFieldMapping(FieldReference field); 20 | String getMethodMapping(MethodReference method); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/mapper/map/DexMaps.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.mapper.map; 12 | 13 | import lanchon.dexpatcher.core.util.DexUtils; 14 | 15 | public class DexMaps { 16 | 17 | public static String mapType(String descriptor, DexMap dexMap) { 18 | int length = descriptor.length(); 19 | if (length == 0 || descriptor.charAt(0) != '[') return mapElementalType(descriptor, dexMap); 20 | int start = 1; 21 | while (start < length && descriptor.charAt(start) == '[') start++; 22 | String elementalType = descriptor.substring(start); 23 | String mappedElementalType = mapElementalType(elementalType, dexMap); 24 | if (mappedElementalType.equals(elementalType)) return descriptor; 25 | StringBuilder sb = new StringBuilder(start + mappedElementalType.length()); 26 | sb.append(descriptor, 0, start).append(mappedElementalType); 27 | return sb.toString(); 28 | } 29 | 30 | public static String mapElementalType(String descriptor, DexMap dexMap) { 31 | if (DexUtils.isClassDescriptor(descriptor)) { 32 | String mapping = dexMap.getClassMapping(descriptor); 33 | if (mapping != null) return mapping; 34 | } 35 | return descriptor; 36 | } 37 | 38 | private DexMaps() {} 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/mapper/map/LoggingDexMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.mapper.map; 12 | 13 | import java.util.Locale; 14 | 15 | import lanchon.dexpatcher.core.logger.Logger; 16 | import lanchon.dexpatcher.core.util.Label; 17 | import lanchon.dexpatcher.transform.MemberLogger; 18 | import lanchon.dexpatcher.transform.TransformLogger; 19 | 20 | import org.jf.dexlib2.iface.reference.FieldReference; 21 | import org.jf.dexlib2.iface.reference.MethodReference; 22 | 23 | public class LoggingDexMap extends MemberLogger implements DexMap { 24 | 25 | protected final DexMap wrappedDexMap; 26 | protected final String message; 27 | protected final Logger.Level infoLevel; 28 | 29 | public LoggingDexMap(DexMap wrappedDexMap, TransformLogger logger, String logPrefix, Logger.Level infoLevel) { 30 | this(wrappedDexMap, "mapped to '%s'", logger, logPrefix, infoLevel); 31 | } 32 | 33 | public LoggingDexMap(DexMap wrappedDexMap, boolean isInverseMap, TransformLogger logger, String logPrefix, 34 | Logger.Level infoLevel) { 35 | this(wrappedDexMap, isInverseMap ? "unmapped to '%s'" : "mapped to '%s'", logger, logPrefix, infoLevel); 36 | } 37 | 38 | public LoggingDexMap(DexMap wrappedDexMap, String message, TransformLogger logger, String logPrefix, 39 | Logger.Level infoLevel) { 40 | super(logger, logPrefix); 41 | this.wrappedDexMap = wrappedDexMap; 42 | this.message = message; 43 | this.infoLevel = infoLevel; 44 | } 45 | 46 | @Override 47 | protected String getTransformedDefiningClass(String definingClass) { 48 | return wrappedDexMap.getClassMapping(definingClass); 49 | } 50 | 51 | @Override 52 | public String getClassMapping(String descriptor) { 53 | String mapping = wrappedDexMap.getClassMapping(descriptor); 54 | if (mapping != null && logger.isLogging(infoLevel)) { 55 | StringBuilder sb = getMessageHeaderForClass(descriptor); 56 | sb.append(String.format(Locale.ROOT, message, Label.fromClassDescriptor(mapping))); 57 | logger.log(infoLevel, sb.toString()); 58 | } 59 | return mapping; 60 | } 61 | 62 | @Override 63 | public String getFieldMapping(FieldReference field) { 64 | String mapping = wrappedDexMap.getFieldMapping(field); 65 | if (mapping != null && logger.isLogging(infoLevel)) { 66 | StringBuilder sb = getMessageHeaderForField(field); 67 | sb.append(String.format(Locale.ROOT, message, Label.ofTargetMember(mapping))); 68 | logger.log(infoLevel, sb.toString()); 69 | } 70 | return mapping; 71 | } 72 | 73 | @Override 74 | public String getMethodMapping(MethodReference method) { 75 | String mapping = wrappedDexMap.getMethodMapping(method); 76 | if (mapping != null && logger.isLogging(infoLevel)) { 77 | StringBuilder sb = getMessageHeaderForMethod(method); 78 | sb.append(String.format(Locale.ROOT, message, Label.ofTargetMember(mapping))); 79 | logger.log(infoLevel, sb.toString()); 80 | } 81 | return mapping; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/mapper/map/builder/DexMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.mapper.map.builder; 12 | 13 | import java.util.Arrays; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | import lanchon.dexpatcher.core.util.Id; 18 | import lanchon.dexpatcher.transform.mapper.map.DexMap; 19 | 20 | import org.jf.dexlib2.iface.reference.FieldReference; 21 | import org.jf.dexlib2.iface.reference.MethodReference; 22 | 23 | public class DexMapping implements MapBuilder, DexMap { 24 | 25 | protected static class ClassMapping implements MemberMapBuilder { 26 | 27 | protected final String mapping; 28 | 29 | protected final Map fieldMappings = new HashMap<>(); 30 | protected final Map methodMappings = new HashMap<>(); 31 | 32 | public ClassMapping(String mapping) { 33 | this.mapping = mapping; 34 | } 35 | 36 | public String getMapping() { 37 | return mapping; 38 | } 39 | 40 | @Override 41 | public void addFieldMapping(String type, String name, String newName) { 42 | if (newName == null) throw new NullPointerException("newName"); 43 | String id = Id.ofField(type, name); 44 | String currentName = fieldMappings.put(id, newName); 45 | if (currentName != null) { 46 | fieldMappings.put(id, currentName); 47 | throw new BuilderException("duplicate field mapping"); 48 | } 49 | } 50 | 51 | public String getFieldMapping(String fieldId) { 52 | return fieldMappings.get(fieldId); 53 | } 54 | 55 | @Override 56 | public void addMethodMapping(String[] parameterTypes, String returnType, String name, String newName) { 57 | if (newName == null) throw new NullPointerException("newName"); 58 | String id = Id.ofMethod(Arrays.asList(parameterTypes), returnType, name); 59 | String currentName = methodMappings.put(id, newName); 60 | if (currentName != null) { 61 | methodMappings.put(id, currentName); 62 | throw new BuilderException("duplicate method mapping"); 63 | } 64 | } 65 | 66 | public String getMethodMapping(String methodId) { 67 | return methodMappings.get(methodId); 68 | } 69 | 70 | } 71 | 72 | protected final Map classMappings = new HashMap<>(); 73 | 74 | @Override 75 | public MemberMapBuilder addClassMapping(String name, String newName) { 76 | if (newName == null) throw new NullPointerException("newName"); 77 | String id = Id.ofClass(name); 78 | ClassMapping newMapping = new ClassMapping(newName); 79 | ClassMapping currentMapping = classMappings.put(id, newMapping); 80 | if (currentMapping != null) { 81 | classMappings.put(id, currentMapping); 82 | throw new BuilderException("duplicate type mapping"); 83 | } 84 | return newMapping; 85 | } 86 | 87 | @Override 88 | public String getClassMapping(String descriptor) { 89 | return getClassMappingById(Id.ofClass(descriptor)); 90 | } 91 | 92 | @Override 93 | public String getFieldMapping(FieldReference field) { 94 | return getFieldMappingById(Id.ofClass(field.getDefiningClass()), Id.ofField(field)); 95 | } 96 | 97 | @Override 98 | public String getMethodMapping(MethodReference method) { 99 | return getMethodMappingById(Id.ofClass(method.getDefiningClass()), Id.ofMethod(method)); 100 | 101 | } 102 | 103 | protected final String getClassMappingById(String classId) { 104 | ClassMapping classMapping = classMappings.get(classId); 105 | return classMapping != null ? classMapping.getMapping() : null; 106 | } 107 | 108 | protected final String getFieldMappingById(String classId, String fieldId) { 109 | ClassMapping classMapping = classMappings.get(classId); 110 | return classMapping != null ? classMapping.getFieldMapping(fieldId) : null; 111 | } 112 | 113 | protected final String getMethodMappingById(String classId, String methodId) { 114 | ClassMapping classMapping = classMappings.get(classId); 115 | return classMapping != null ? classMapping.getMethodMapping(methodId) : null; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/mapper/map/builder/InverseMapBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.mapper.map.builder; 12 | 13 | import lanchon.dexpatcher.transform.mapper.map.DexMap; 14 | import lanchon.dexpatcher.transform.mapper.map.DexMaps; 15 | 16 | public class InverseMapBuilder implements MapBuilder { 17 | 18 | private static final String EXCEPTION_HEADER = "on inverse map: "; 19 | 20 | protected final MapBuilder wrappedMapBuilder; 21 | protected final DexMap directDexMap; 22 | 23 | public InverseMapBuilder(MapBuilder wrappedMapBuilder, DexMap directDexMap) { 24 | this.wrappedMapBuilder = wrappedMapBuilder; 25 | this.directDexMap = directDexMap; 26 | } 27 | 28 | @Override 29 | public MemberMapBuilder addClassMapping(String name, String newName) { 30 | try { 31 | final MemberMapBuilder wrappedMemberMapBuilder = wrappedMapBuilder.addClassMapping(newName, name); 32 | return new MemberMapBuilder() { 33 | @Override 34 | public void addFieldMapping(String type, String name, String newName) { 35 | try { 36 | String mappedType = DexMaps.mapType(type, directDexMap); 37 | wrappedMemberMapBuilder.addFieldMapping(mappedType, newName, name); 38 | } catch (BuilderException e) { 39 | throw new BuilderException(EXCEPTION_HEADER + e.getMessage()); 40 | } 41 | } 42 | @Override 43 | public void addMethodMapping(String[] parameterTypes, String returnType, String name, String newName) { 44 | try { 45 | int length = parameterTypes.length; 46 | String[] mappedParameterTypes = new String[length]; 47 | for (int i = 0; i < length; i++) { 48 | mappedParameterTypes[i] = DexMaps.mapType(parameterTypes[i], directDexMap); 49 | } 50 | String mappedReturnType = DexMaps.mapType(returnType, directDexMap); 51 | wrappedMemberMapBuilder.addMethodMapping(mappedParameterTypes, mappedReturnType, newName, name); 52 | } catch (BuilderException e) { 53 | throw new BuilderException(EXCEPTION_HEADER + e.getMessage()); 54 | } 55 | } 56 | }; 57 | } catch (BuilderException e) { 58 | throw new BuilderException(EXCEPTION_HEADER + e.getMessage()); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/mapper/map/builder/MapBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.mapper.map.builder; 12 | 13 | public interface MapBuilder { 14 | 15 | MemberMapBuilder addClassMapping(String name, String newName); 16 | 17 | interface MemberMapBuilder { 18 | void addFieldMapping(String type, String name, String newName); 19 | void addMethodMapping(String[] parameterTypes, String returnType, String name, String newName); 20 | } 21 | 22 | class BuilderException extends RuntimeException { 23 | public BuilderException(String s) { 24 | super(s); 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/util/wrapper/WrapperAnnotation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.util.wrapper; 12 | 13 | import java.util.Set; 14 | 15 | import org.jf.dexlib2.base.BaseAnnotation; 16 | import org.jf.dexlib2.iface.Annotation; 17 | import org.jf.dexlib2.iface.AnnotationElement; 18 | 19 | public class WrapperAnnotation extends BaseAnnotation { 20 | 21 | protected final Annotation wrappedAnnotation; 22 | 23 | public WrapperAnnotation(Annotation wrappedAnnotation) { 24 | this.wrappedAnnotation = wrappedAnnotation; 25 | } 26 | 27 | @Override 28 | public int getVisibility() { 29 | return wrappedAnnotation.getVisibility(); 30 | } 31 | 32 | @Override 33 | public String getType() { 34 | return wrappedAnnotation.getType(); 35 | } 36 | 37 | @Override 38 | public Set getElements() { 39 | return wrappedAnnotation.getElements(); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/util/wrapper/WrapperAnnotationElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.util.wrapper; 12 | 13 | import org.jf.dexlib2.base.BaseAnnotationElement; 14 | import org.jf.dexlib2.iface.AnnotationElement; 15 | import org.jf.dexlib2.iface.value.EncodedValue; 16 | 17 | public class WrapperAnnotationElement extends BaseAnnotationElement { 18 | 19 | protected final AnnotationElement wrappedAnnotationElement; 20 | 21 | public WrapperAnnotationElement(AnnotationElement wrappedAnnotationElement) { 22 | this.wrappedAnnotationElement = wrappedAnnotationElement; 23 | } 24 | 25 | @Override 26 | public String getName() { 27 | return wrappedAnnotationElement.getName(); 28 | } 29 | 30 | @Override 31 | public EncodedValue getValue() { 32 | return wrappedAnnotationElement.getValue(); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/util/wrapper/WrapperClassDef.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.util.wrapper; 12 | 13 | import java.util.Iterator; 14 | import java.util.List; 15 | import java.util.Set; 16 | 17 | import com.google.common.collect.Iterators; 18 | import org.jf.dexlib2.base.reference.BaseTypeReference; 19 | import org.jf.dexlib2.iface.Annotation; 20 | import org.jf.dexlib2.iface.ClassDef; 21 | import org.jf.dexlib2.iface.Field; 22 | import org.jf.dexlib2.iface.Method; 23 | 24 | public class WrapperClassDef extends BaseTypeReference implements ClassDef { 25 | 26 | protected final ClassDef wrappedClassDef; 27 | 28 | public WrapperClassDef(ClassDef wrappedClassDef) { 29 | this.wrappedClassDef = wrappedClassDef; 30 | } 31 | 32 | @Override 33 | public String getType() { 34 | return wrappedClassDef.getType(); 35 | } 36 | 37 | @Override 38 | public int getAccessFlags() { 39 | return wrappedClassDef.getAccessFlags(); 40 | } 41 | 42 | @Override 43 | public String getSuperclass() { 44 | return wrappedClassDef.getSuperclass(); 45 | } 46 | 47 | @Override 48 | public List getInterfaces() { 49 | return wrappedClassDef.getInterfaces(); 50 | } 51 | 52 | @Override 53 | public String getSourceFile() { 54 | return wrappedClassDef.getSourceFile(); 55 | } 56 | 57 | @Override 58 | public Set getAnnotations() { 59 | return wrappedClassDef.getAnnotations(); 60 | } 61 | 62 | @Override 63 | public Iterable getStaticFields() { 64 | return wrappedClassDef.getStaticFields(); 65 | } 66 | 67 | @Override 68 | public Iterable getInstanceFields() { 69 | return wrappedClassDef.getInstanceFields(); 70 | } 71 | 72 | @Override 73 | public Iterable getDirectMethods() { 74 | return wrappedClassDef.getDirectMethods(); 75 | } 76 | 77 | @Override 78 | public Iterable getVirtualMethods() { 79 | return wrappedClassDef.getVirtualMethods(); 80 | } 81 | 82 | @Override 83 | public final Iterable getFields() { 84 | return new Iterable() { 85 | @Override 86 | public Iterator iterator() { 87 | return Iterators.concat(getStaticFields().iterator(), getInstanceFields().iterator()); 88 | } 89 | }; 90 | } 91 | 92 | @Override 93 | public final Iterable getMethods() { 94 | return new Iterable() { 95 | @Override 96 | public Iterator iterator() { 97 | return Iterators.concat(getDirectMethods().iterator(), getVirtualMethods().iterator()); 98 | } 99 | }; 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/util/wrapper/WrapperField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.util.wrapper; 12 | 13 | import java.util.Set; 14 | 15 | import org.jf.dexlib2.base.reference.BaseFieldReference; 16 | import org.jf.dexlib2.iface.Annotation; 17 | import org.jf.dexlib2.iface.Field; 18 | import org.jf.dexlib2.iface.value.EncodedValue; 19 | 20 | public class WrapperField extends BaseFieldReference implements Field { 21 | 22 | protected final Field wrappedField; 23 | 24 | public WrapperField(Field wrappedField) { 25 | this.wrappedField = wrappedField; 26 | } 27 | 28 | @Override 29 | public String getDefiningClass() { 30 | return wrappedField.getDefiningClass(); 31 | } 32 | 33 | @Override 34 | public String getName() { 35 | return wrappedField.getName(); 36 | } 37 | 38 | @Override 39 | public String getType() { 40 | return wrappedField.getType(); 41 | } 42 | 43 | @Override 44 | public int getAccessFlags() { 45 | return wrappedField.getAccessFlags(); 46 | } 47 | 48 | @Override 49 | public EncodedValue getInitialValue() { 50 | return wrappedField.getInitialValue(); 51 | } 52 | 53 | @Override 54 | public Set getAnnotations() { 55 | return wrappedField.getAnnotations(); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/util/wrapper/WrapperFieldReference.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.util.wrapper; 12 | 13 | import org.jf.dexlib2.base.reference.BaseFieldReference; 14 | import org.jf.dexlib2.iface.reference.FieldReference; 15 | 16 | public class WrapperFieldReference extends BaseFieldReference { 17 | 18 | protected final FieldReference wrappedFieldReference; 19 | 20 | public WrapperFieldReference(FieldReference wrappedFieldReference) { 21 | this.wrappedFieldReference = wrappedFieldReference; 22 | } 23 | 24 | @Override 25 | public String getDefiningClass() { 26 | return wrappedFieldReference.getDefiningClass(); 27 | } 28 | 29 | @Override 30 | public String getName() { 31 | return wrappedFieldReference.getName(); 32 | } 33 | 34 | @Override 35 | public String getType() { 36 | return wrappedFieldReference.getType(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/util/wrapper/WrapperMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.util.wrapper; 12 | 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | import org.jf.dexlib2.base.reference.BaseMethodReference; 17 | import org.jf.dexlib2.iface.Annotation; 18 | import org.jf.dexlib2.iface.Method; 19 | import org.jf.dexlib2.iface.MethodImplementation; 20 | import org.jf.dexlib2.iface.MethodParameter; 21 | 22 | public class WrapperMethod extends BaseMethodReference implements Method { 23 | 24 | protected final Method wrappedMethod; 25 | 26 | public WrapperMethod(Method wrappedMethod) { 27 | this.wrappedMethod = wrappedMethod; 28 | } 29 | 30 | @Override 31 | public String getDefiningClass() { 32 | return wrappedMethod.getDefiningClass(); 33 | } 34 | 35 | @Override 36 | public String getName() { 37 | return wrappedMethod.getName(); 38 | } 39 | 40 | @Override 41 | public List getParameters() { 42 | return wrappedMethod.getParameters(); 43 | } 44 | 45 | @Override 46 | public List getParameterTypes() { 47 | return wrappedMethod.getParameterTypes(); 48 | } 49 | 50 | @Override 51 | public String getReturnType() { 52 | return wrappedMethod.getReturnType(); 53 | } 54 | 55 | @Override 56 | public int getAccessFlags() { 57 | return wrappedMethod.getAccessFlags(); 58 | } 59 | 60 | @Override 61 | public Set getAnnotations() { 62 | return wrappedMethod.getAnnotations(); 63 | } 64 | 65 | @Override 66 | public MethodImplementation getImplementation() { 67 | return wrappedMethod.getImplementation(); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/util/wrapper/WrapperMethodReference.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.util.wrapper; 12 | 13 | import java.util.List; 14 | 15 | import org.jf.dexlib2.base.reference.BaseMethodReference; 16 | import org.jf.dexlib2.iface.reference.MethodReference; 17 | 18 | public class WrapperMethodReference extends BaseMethodReference { 19 | 20 | protected final MethodReference wrappedMethodReference; 21 | 22 | public WrapperMethodReference(MethodReference wrappedMethodReference) { 23 | this.wrappedMethodReference = wrappedMethodReference; 24 | } 25 | 26 | @Override 27 | public String getDefiningClass() { 28 | return wrappedMethodReference.getDefiningClass(); 29 | } 30 | 31 | @Override 32 | public String getName() { 33 | return wrappedMethodReference.getName(); 34 | } 35 | 36 | @Override 37 | public List getParameterTypes() { 38 | return wrappedMethodReference.getParameterTypes(); 39 | } 40 | 41 | @Override 42 | public String getReturnType() { 43 | return wrappedMethodReference.getReturnType(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /tool/src/main/java/lanchon/dexpatcher/transform/util/wrapper/WrapperRewriterModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DexPatcher - Copyright 2015-2020 Rodrigo Balerdi 3 | * (GNU General Public License version 3 or later) 4 | * 5 | * DexPatcher is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, 8 | * or (at your option) any later version. 9 | */ 10 | 11 | package lanchon.dexpatcher.transform.util.wrapper; 12 | 13 | import org.jf.dexlib2.iface.Annotation; 14 | import org.jf.dexlib2.iface.AnnotationElement; 15 | import org.jf.dexlib2.iface.ClassDef; 16 | import org.jf.dexlib2.iface.ExceptionHandler; 17 | import org.jf.dexlib2.iface.Field; 18 | import org.jf.dexlib2.iface.Method; 19 | import org.jf.dexlib2.iface.MethodImplementation; 20 | import org.jf.dexlib2.iface.MethodParameter; 21 | import org.jf.dexlib2.iface.TryBlock; 22 | import org.jf.dexlib2.iface.debug.DebugItem; 23 | import org.jf.dexlib2.iface.instruction.Instruction; 24 | import org.jf.dexlib2.iface.reference.FieldReference; 25 | import org.jf.dexlib2.iface.reference.MethodReference; 26 | import org.jf.dexlib2.iface.value.EncodedValue; 27 | import org.jf.dexlib2.rewriter.Rewriter; 28 | import org.jf.dexlib2.rewriter.RewriterModule; 29 | import org.jf.dexlib2.rewriter.Rewriters; 30 | 31 | public class WrapperRewriterModule extends RewriterModule { 32 | 33 | protected final M wrappedModule; 34 | 35 | public WrapperRewriterModule(M wrappedModule) { 36 | this.wrappedModule = wrappedModule; 37 | } 38 | 39 | @Override 40 | public Rewriter getClassDefRewriter(Rewriters rewriters) { 41 | return wrappedModule.getClassDefRewriter(rewriters); 42 | } 43 | 44 | @Override 45 | public Rewriter getFieldRewriter(Rewriters rewriters) { 46 | return wrappedModule.getFieldRewriter(rewriters); 47 | } 48 | 49 | @Override 50 | public Rewriter getMethodRewriter(Rewriters rewriters) { 51 | return wrappedModule.getMethodRewriter(rewriters); 52 | } 53 | 54 | @Override 55 | public Rewriter getMethodParameterRewriter(Rewriters rewriters) { 56 | return wrappedModule.getMethodParameterRewriter(rewriters); 57 | } 58 | 59 | @Override 60 | public Rewriter getMethodImplementationRewriter(Rewriters rewriters) { 61 | return wrappedModule.getMethodImplementationRewriter(rewriters); 62 | } 63 | 64 | @Override 65 | public Rewriter getInstructionRewriter(Rewriters rewriters) { 66 | return wrappedModule.getInstructionRewriter(rewriters); 67 | } 68 | 69 | @Override 70 | public Rewriter> getTryBlockRewriter(Rewriters rewriters) { 71 | return wrappedModule.getTryBlockRewriter(rewriters); 72 | } 73 | 74 | @Override 75 | public Rewriter getExceptionHandlerRewriter(Rewriters rewriters) { 76 | return wrappedModule.getExceptionHandlerRewriter(rewriters); 77 | } 78 | 79 | @Override 80 | public Rewriter getDebugItemRewriter(Rewriters rewriters) { 81 | return wrappedModule.getDebugItemRewriter(rewriters); 82 | } 83 | 84 | @Override 85 | public Rewriter getTypeRewriter(Rewriters rewriters) { 86 | return wrappedModule.getTypeRewriter(rewriters); 87 | } 88 | 89 | @Override 90 | public Rewriter getFieldReferenceRewriter(Rewriters rewriters) { 91 | return wrappedModule.getFieldReferenceRewriter(rewriters); 92 | } 93 | 94 | @Override 95 | public Rewriter getMethodReferenceRewriter(Rewriters rewriters) { 96 | return wrappedModule.getMethodReferenceRewriter(rewriters); 97 | } 98 | 99 | @Override 100 | public Rewriter getAnnotationRewriter(Rewriters rewriters) { 101 | return wrappedModule.getAnnotationRewriter(rewriters); 102 | } 103 | 104 | @Override 105 | public Rewriter getAnnotationElementRewriter(Rewriters rewriters) { 106 | return wrappedModule.getAnnotationElementRewriter(rewriters); 107 | } 108 | 109 | @Override 110 | public Rewriter getEncodedValueRewriter(Rewriters rewriters) { 111 | return wrappedModule.getEncodedValueRewriter(rewriters); 112 | } 113 | 114 | } 115 | --------------------------------------------------------------------------------