Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 156e4209f9 | |||
| 7492889f4e | |||
| 0c041120f6 | |||
| ecbb53aaea | |||
| ffe739b7eb | |||
| bd05be6fb6 | |||
| ff7df3818f | |||
| 39899e4edc | |||
| dc578f98e7 | |||
| d7ce41e724 | |||
| eaaeb2c843 | |||
| 0ae7c1efbf | |||
| cb13599739 | |||
| a9251de1dd | |||
| 56798e716a | |||
| 904f0a1197 | |||
| 4d3f2740ce | |||
| f9e7a29c08 | |||
| 6cb14a1c50 | |||
| ea9f933f9e | |||
| eb2e5e3da5 | |||
| 9a4e8bdb48 | |||
| fad0091d87 | |||
| b861151f63 | |||
| feeafc407a | |||
| ea1c1eb803 | |||
| b83e20b571 | |||
| 160ad64e67 | |||
| 1213ff26b4 | |||
| 3bf93f1f85 | |||
| a502581640 | |||
| 1ec041a48f | |||
| fdaf8492ef | |||
| 2433a7e89c | |||
| 6e358d3eab | |||
| 7e462e800f | |||
| 156e54c77f | |||
| 9752ec2655 | |||
| edc1e5fa84 | |||
| a959af087b | |||
| c5994f954a | |||
| 03a09debfa | |||
| 2cf6a9b691 | |||
| 5b712e8dbc | |||
| 5d7f2c706c | |||
| 22f51e1a28 | |||
| 61684ea73d | |||
| 45b37dcd10 | |||
| c0b2230b0b | |||
| 53fa8205f2 | |||
| ddbcf8bb19 | |||
| 6e50ddf5c8 | |||
| dda49f1501 | |||
| 4e2e5aa975 | |||
| 10fd3652d4 | |||
| 4d30510706 | |||
| ee74c4d870 | |||
| 45f5e0cb04 | |||
| 7d983f2847 | |||
| 3b2b5417aa | |||
| 0a0c4eac88 | |||
| d20cd43a99 | |||
| 7b4321ecee | |||
| 188bfd1a7e | |||
| 7b6825d85c | |||
| a27cb9c34e | |||
| 8445ebf107 | |||
| 6df315017c | |||
| 1931e78367 | |||
| 90692d89c5 | |||
| 4f02864e12 | |||
| 7562ec9e1a | |||
| 6d984c0407 | |||
| 3556e591b0 | |||
| 8fdb473d78 | |||
| 398cd15dcf | |||
| 5006b3e837 | |||
| 7216635d84 | |||
| 98ef7c39b7 | |||
| e039a5a9af | |||
| 412a185fa1 | |||
| 20bfe83849 | |||
| 39093130a3 | |||
| 9e9270a8b7 | |||
| 2c904c56f4 | |||
| a3b961e72f | |||
| 0e4c8df418 | |||
| 3b2d595a06 | |||
| b29223c5b6 | |||
| d805ec15b4 | |||
| d5cfdfb50d | |||
| 2e5d73a7e4 | |||
| 1c352cc81b | |||
| 9c6c18780f | |||
| cb23b65797 | |||
| 7a16814808 | |||
| 23553c9944 | |||
| 54fbe8a7c0 | |||
| ea01102f1d | |||
| 15e1e1dfab | |||
| 37ed9cd25b | |||
| 882af04027 | |||
| aa8a298ec7 | |||
| 05ab8fd868 |
+1
-2
@@ -18,7 +18,7 @@ build/
|
||||
classes/
|
||||
idea/
|
||||
.gradle/
|
||||
gradle.properties
|
||||
node_modules/
|
||||
|
||||
jadx-output/
|
||||
*-tmp/
|
||||
@@ -28,4 +28,3 @@ jadx-output/
|
||||
*.dump
|
||||
*.log
|
||||
*.cfg
|
||||
|
||||
|
||||
+2
-1
@@ -13,7 +13,7 @@ stages:
|
||||
build:
|
||||
stage: build
|
||||
script:
|
||||
- sed -i " 1 s/.*/&-b$(git rev-list --count HEAD)-$(git rev-parse --short HEAD)/" version
|
||||
- sed -i " 1 s/.*/&-glb$(git rev-list --count HEAD)-$(git rev-parse --short HEAD)/" version
|
||||
- cat version
|
||||
- ./gradlew -g /cache/.gradle clean build jacocoTestReport
|
||||
- ./gradlew -g /cache/.gradle clean sonarqube -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_TOKEN
|
||||
@@ -21,3 +21,4 @@ build:
|
||||
artifacts:
|
||||
paths:
|
||||
- build/jadx*.zip
|
||||
- build/jadx*.exe
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
branch: release
|
||||
verifyConditions:
|
||||
- '@semantic-release/github'
|
||||
prepare:
|
||||
- path: '@semantic-release/exec'
|
||||
cmd: "JADX_VERSION=${nextRelease.version} ./gradlew clean dist"
|
||||
publish:
|
||||
- path: '@semantic-release/exec'
|
||||
cmd: "JADX_VERSION=${nextRelease.version} BINTRAY_PACKAGE=releases bash scripts/bintray-upload.sh"
|
||||
- path: '@semantic-release/github'
|
||||
assets:
|
||||
- path: 'build/*.zip'
|
||||
label: 'zip bundle'
|
||||
- path: 'build/*.exe'
|
||||
label: 'jadx-gui windows'
|
||||
|
||||
+35
-24
@@ -1,39 +1,50 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
sudo: false
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
- TERM=dumb
|
||||
# don't build on tag push
|
||||
if: tag IS blank
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
before_install:
|
||||
- chmod +x gradlew
|
||||
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
|
||||
- chmod +x gradlew
|
||||
|
||||
env:
|
||||
global:
|
||||
- TERM=dumb
|
||||
- JADX_VERSION="$(git describe --abbrev=0 --tags)-b$TRAVIS_BUILD_NUMBER-$(git rev-parse --short HEAD)"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: JDK=oracle-8
|
||||
jdk: oraclejdk8
|
||||
- env: JDK=oracle-10
|
||||
install: . ./install-jdk.sh -F 10 -L BCL
|
||||
|
||||
script:
|
||||
- sed -i " 1 s/.*/&-b$TRAVIS_BUILD_NUMBER-$(git rev-parse --short HEAD)/" version
|
||||
- cat version
|
||||
- sed -i "s/BUILD_VERSION/$(head -c -1 version)/g" bintray.json
|
||||
- ./gradlew clean build
|
||||
|
||||
after_success:
|
||||
- ./gradlew clean build jacocoTestReport
|
||||
- bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports"
|
||||
- ./gradlew clean sonarqube -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_TOKEN
|
||||
- ./gradlew clean dist
|
||||
- java -version
|
||||
- ./gradlew clean build
|
||||
|
||||
deploy:
|
||||
provider: bintray
|
||||
edge:
|
||||
branch: v1.8.47
|
||||
file: bintray.json
|
||||
user: skylot
|
||||
key: $BINTRAY_KEY
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
on:
|
||||
branch: master
|
||||
tags: false
|
||||
condition: $JDK = oracle-8
|
||||
script: bash scripts/travis-master.sh
|
||||
|
||||
sudo: false
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
on:
|
||||
branch: release
|
||||
tags: false
|
||||
condition: $JDK = oracle-8
|
||||
script: bash scripts/travis-release.sh
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- skylot@gmail.com
|
||||
email:
|
||||
- skylot@gmail.com
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
[](https://travis-ci.org/skylot/jadx)
|
||||
[](https://codecov.io/gh/skylot/jadx)
|
||||
[](https://sonarcloud.io/dashboard?id=jadx)
|
||||
[](https://sonarcloud.io/dashboard?id=jadx)
|
||||
[](http://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://github.com/semantic-release/semantic-release)
|
||||
|
||||
**jadx** - Dex to Java decompiler
|
||||
|
||||
@@ -16,7 +17,6 @@ Command line and GUI tools for produce Java source code from Android Dex and Apk
|
||||
- latest [unstable build:  ](https://bintray.com/skylot/jadx/unstable/_latestVersion#files)
|
||||
- release from [github: ](https://github.com/skylot/jadx/releases/latest)
|
||||
- release from [bintray:  ](https://bintray.com/skylot/jadx/releases/_latestVersion#files)
|
||||
- release from [sourceforge](http://sourceforge.net/projects/jadx/files/)
|
||||
|
||||
After download unpack zip file go to `bin` directory and run:
|
||||
- `jadx` - command line version
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "unstable",
|
||||
"repo": "jadx",
|
||||
"subject": "skylot",
|
||||
"desc": "Unstable",
|
||||
"website_url": "https://github.com/skylot/jadx",
|
||||
"issue_tracker_url": "https://github.com/skylot/jadx/issues",
|
||||
"vcs_url": "https://github.com/skylot/jadx.git",
|
||||
"licenses": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"labels": [
|
||||
"unstable"
|
||||
],
|
||||
"public_download_numbers": true,
|
||||
"public_stats": true
|
||||
},
|
||||
"version": {
|
||||
"name": "BUILD_VERSION",
|
||||
"desc": "Dev build: BUILD_VERSION"
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"includePattern": "build/(jadx.*\.zip)",
|
||||
"uploadPattern": "$1",
|
||||
"matrixParams": {
|
||||
"override": 1
|
||||
},
|
||||
"list_in_downloads": true
|
||||
}
|
||||
],
|
||||
"publish": true
|
||||
}
|
||||
+19
-6
@@ -1,11 +1,12 @@
|
||||
plugins {
|
||||
id 'com.github.ksoichiro.console.reporter' version '0.5.0'
|
||||
id 'org.sonarqube' version '2.6.2'
|
||||
id 'com.github.ben-manes.versions' version '0.17.0'
|
||||
id 'com.github.ben-manes.versions' version '0.20.0'
|
||||
}
|
||||
|
||||
ext.jadxVersion = file('version').readLines().get(0)
|
||||
ext.jadxVersion = System.getenv('JADX_VERSION') ?: "dev"
|
||||
version = jadxVersion
|
||||
println("jadx version: ${jadxVersion}")
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'java'
|
||||
@@ -41,9 +42,9 @@ allprojects {
|
||||
testCompile 'ch.qos.logback:logback-classic:1.2.3'
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.hamcrest:hamcrest-library:1.3'
|
||||
testCompile 'org.mockito:mockito-core:2.15.0'
|
||||
testCompile 'org.mockito:mockito-core:2.20.1'
|
||||
testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
|
||||
testCompile 'cglib:cglib-nodep:3.2.6'
|
||||
testCompile 'cglib:cglib-nodep:3.2.7'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -53,7 +54,7 @@ allprojects {
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion = "0.8.0"
|
||||
toolVersion = "0.8.1"
|
||||
}
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
@@ -96,17 +97,29 @@ task pack(type: Zip, dependsOn: copyArtifacts) {
|
||||
from copyArtifacts.destinationDir
|
||||
}
|
||||
|
||||
task dist(dependsOn: pack) {
|
||||
task copyExe(type: Copy, dependsOn: 'jadx-gui:createExe') {
|
||||
group 'jadx'
|
||||
description = 'Copy exe to build dir'
|
||||
destinationDir buildDir
|
||||
from tasks.getByPath('jadx-gui:createExe').outputs
|
||||
include '*.exe'
|
||||
}
|
||||
|
||||
task dist(dependsOn: [pack, copyExe]) {
|
||||
group 'jadx'
|
||||
description = 'Build jadx distribution zip'
|
||||
}
|
||||
|
||||
task samples(dependsOn: 'jadx-samples:samples') {
|
||||
group 'jadx'
|
||||
}
|
||||
|
||||
task testAppCheck(dependsOn: 'jadx-test-app:testAppCheck') {
|
||||
group 'jadx'
|
||||
}
|
||||
|
||||
task cleanBuildDir(type: Delete) {
|
||||
group 'jadx'
|
||||
delete buildDir
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.gradle.daemon=false
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip
|
||||
|
||||
@@ -5,7 +5,7 @@ applicationName = 'jadx'
|
||||
|
||||
dependencies {
|
||||
compile(project(':jadx-core'))
|
||||
compile 'com.beust:jcommander:1.72'
|
||||
compile 'com.beust:jcommander:1.74'
|
||||
compile 'ch.qos.logback:logback-classic:1.2.3'
|
||||
}
|
||||
|
||||
@@ -19,5 +19,5 @@ applicationDistribution.with {
|
||||
}
|
||||
|
||||
startScripts {
|
||||
defaultJvmOpts = [ '-Xms2g', '-Xmx4g' ]
|
||||
defaultJvmOpts = ['-Xms128M', '-Xmx4g']
|
||||
}
|
||||
|
||||
@@ -10,31 +10,36 @@ public class JadxCLI {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
int result = 0;
|
||||
try {
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
if (jadxArgs.processArgs(args)) {
|
||||
processAndSave(jadxArgs);
|
||||
result = processAndSave(jadxArgs);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("jadx error: {}", e.getMessage(), e);
|
||||
System.exit(1);
|
||||
result = 1;
|
||||
} finally {
|
||||
System.exit(result);
|
||||
}
|
||||
}
|
||||
|
||||
static void processAndSave(JadxCLIArgs inputArgs) {
|
||||
static int processAndSave(JadxCLIArgs inputArgs) {
|
||||
JadxDecompiler jadx = new JadxDecompiler(inputArgs.toJadxArgs());
|
||||
try {
|
||||
jadx.load();
|
||||
} catch (JadxArgsValidateException e) {
|
||||
LOG.error("Incorrect arguments: {}", e.getMessage());
|
||||
System.exit(1);
|
||||
return 1;
|
||||
}
|
||||
jadx.save();
|
||||
if (jadx.getErrorsCount() != 0) {
|
||||
int errorsCount = jadx.getErrorsCount();
|
||||
if (errorsCount != 0) {
|
||||
jadx.printErrorsReport();
|
||||
LOG.error("finished with errors");
|
||||
} else {
|
||||
LOG.info("done");
|
||||
}
|
||||
return errorsCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,17 +63,17 @@ public class JadxCLIArgs {
|
||||
@Parameter(names = {"--deobf"}, description = "activate deobfuscation")
|
||||
protected boolean deobfuscationOn = false;
|
||||
|
||||
@Parameter(names = {"--deobf-min"}, description = "min length of name")
|
||||
protected int deobfuscationMinLength = 2;
|
||||
@Parameter(names = {"--deobf-min"}, description = "min length of name, renamed if shorter")
|
||||
protected int deobfuscationMinLength = 3;
|
||||
|
||||
@Parameter(names = {"--deobf-max"}, description = "max length of name")
|
||||
@Parameter(names = {"--deobf-max"}, description = "max length of name, renamed if longer")
|
||||
protected int deobfuscationMaxLength = 64;
|
||||
|
||||
@Parameter(names = {"--deobf-rewrite-cfg"}, description = "force to save deobfuscation map")
|
||||
protected boolean deobfuscationForceSave = false;
|
||||
|
||||
@Parameter(names = {"--deobf-use-sourcename"}, description = "use source file name as class name alias")
|
||||
protected boolean deobfuscationUseSourceNameAsAlias = false;
|
||||
protected boolean deobfuscationUseSourceNameAsAlias = true;
|
||||
|
||||
@Parameter(names = {"--cfg"}, description = "save methods control flow graph to dot file")
|
||||
protected boolean cfgOutput = false;
|
||||
@@ -162,7 +162,8 @@ public class JadxCLIArgs {
|
||||
maxNamesLen = len;
|
||||
}
|
||||
}
|
||||
Field[] fields = JadxCLIArgs.class.getDeclaredFields();
|
||||
JadxCLIArgs args = new JadxCLIArgs();
|
||||
Field[] fields = args.getClass().getDeclaredFields();
|
||||
for (Field f : fields) {
|
||||
String name = f.getName();
|
||||
ParameterDescription p = paramsMap.get(name);
|
||||
@@ -171,14 +172,27 @@ public class JadxCLIArgs {
|
||||
}
|
||||
StringBuilder opt = new StringBuilder();
|
||||
opt.append(" ").append(p.getNames());
|
||||
addSpaces(opt, maxNamesLen - opt.length() + 2);
|
||||
addSpaces(opt, maxNamesLen - opt.length() + 3);
|
||||
opt.append("- ").append(p.getDescription());
|
||||
addDefaultValue(args, f, opt);
|
||||
out.println(opt);
|
||||
}
|
||||
out.println("Example:");
|
||||
out.println(" jadx -d out classes.dex");
|
||||
}
|
||||
|
||||
private void addDefaultValue(JadxCLIArgs args, Field f, StringBuilder opt) {
|
||||
Class<?> fieldType = f.getType();
|
||||
if (fieldType == int.class) {
|
||||
try {
|
||||
int val = f.getInt(args);
|
||||
opt.append(" (default: ").append(val).append(")");
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSpaces(StringBuilder str, int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
str.append(' ');
|
||||
|
||||
@@ -5,11 +5,13 @@ dependencies {
|
||||
|
||||
compile files('lib/dx-1.14.jar')
|
||||
compile 'commons-io:commons-io:2.6'
|
||||
compile 'org.ow2.asm:asm:6.0'
|
||||
compile 'org.jetbrains:annotations:15.0'
|
||||
compile 'uk.com.robust-it:cloning:1.9.9'
|
||||
compile 'org.ow2.asm:asm:6.2'
|
||||
compile 'org.jetbrains:annotations:16.0.2'
|
||||
compile 'uk.com.robust-it:cloning:1.9.10'
|
||||
|
||||
testCompile 'org.smali:smali:2.2.2'
|
||||
testCompile 'org.smali:baksmali:2.2.2'
|
||||
testCompile 'org.smali:smali:2.2.4'
|
||||
testCompile 'org.smali:baksmali:2.2.4'
|
||||
|
||||
testCompile 'org.apache.commons:commons-lang3:3.7'
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ import jadx.core.xmlgen.ResourcesSaver;
|
||||
* jadx.load();
|
||||
* jadx.save();
|
||||
* </code></pre>
|
||||
* <p/>
|
||||
*
|
||||
* Instead of 'save()' you can iterate over decompiled classes:
|
||||
* <pre><code>
|
||||
* for(JavaClass cls : jadx.getClasses()) {
|
||||
@@ -197,8 +197,12 @@ public final class JadxDecompiler {
|
||||
continue;
|
||||
}
|
||||
executor.execute(() -> {
|
||||
cls.decompile();
|
||||
SaveCode.save(outDir, args, cls.getClassNode());
|
||||
try {
|
||||
cls.decompile();
|
||||
SaveCode.save(outDir, args, cls.getClassNode());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error saving class: {}", cls.getFullName(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -260,6 +264,13 @@ public final class JadxDecompiler {
|
||||
return root.getErrorsCounter().getErrorCount();
|
||||
}
|
||||
|
||||
public int getWarnsCount() {
|
||||
if (root == null) {
|
||||
return 0;
|
||||
}
|
||||
return root.getErrorsCounter().getWarnsCount();
|
||||
}
|
||||
|
||||
public void printErrorsReport() {
|
||||
if (root == null) {
|
||||
return;
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
package jadx.api;
|
||||
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -17,6 +9,14 @@ import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
public final class JavaClass implements JavaNode {
|
||||
|
||||
private final JadxDecompiler decompiler;
|
||||
@@ -64,6 +64,10 @@ public final class JavaClass implements JavaNode {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void unload() {
|
||||
cls.unload();
|
||||
}
|
||||
|
||||
ClassNode getClassNode() {
|
||||
return cls;
|
||||
}
|
||||
@@ -107,12 +111,7 @@ public final class JavaClass implements JavaNode {
|
||||
rootDecompiler.getMethodsMap().put(m, javaMethod);
|
||||
}
|
||||
}
|
||||
Collections.sort(mths, new Comparator<JavaMethod>() {
|
||||
@Override
|
||||
public int compare(JavaMethod o1, JavaMethod o2) {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
mths.sort(Comparator.comparing(JavaMethod::getName));
|
||||
this.methods = Collections.unmodifiableList(mths);
|
||||
}
|
||||
}
|
||||
@@ -126,7 +125,11 @@ public final class JavaClass implements JavaNode {
|
||||
|
||||
private Map<CodePosition, Object> getCodeAnnotations() {
|
||||
decompile();
|
||||
return cls.getCode().getAnnotations();
|
||||
CodeWriter code = cls.getCode();
|
||||
if (code == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return code.getAnnotations();
|
||||
}
|
||||
|
||||
public Map<CodePosition, JavaNode> getUsageMap() {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class JavaMethod implements JavaNode {
|
||||
private final MethodNode mth;
|
||||
private final JavaClass parent;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ResourceFile {
|
||||
|
||||
public static final class ZipRef {
|
||||
|
||||
@@ -99,7 +99,8 @@ public final class ResourcesLoader {
|
||||
jadxRef.getXmlParser().parse(inputStream));
|
||||
|
||||
case ARSC:
|
||||
return new ResTableParser().decodeFiles(inputStream);
|
||||
return new ResTableParser()
|
||||
.decodeFiles(inputStream);
|
||||
|
||||
case IMG:
|
||||
return ResContainer.singleImageFile(rf.getName(), inputStream);
|
||||
|
||||
@@ -94,10 +94,6 @@ public class Jadx {
|
||||
passes.add(new SimplifyVisitor());
|
||||
passes.add(new CheckRegions());
|
||||
|
||||
if (args.isCfgOutput()) {
|
||||
passes.add(DotGraphVisitor.dumpRegions());
|
||||
}
|
||||
|
||||
passes.add(new MethodInlineVisitor());
|
||||
passes.add(new ExtractFieldInit());
|
||||
passes.add(new ClassModifier());
|
||||
@@ -106,6 +102,10 @@ public class Jadx {
|
||||
passes.add(new LoopRegionVisitor());
|
||||
passes.add(new ProcessVariables());
|
||||
|
||||
if (args.isCfgOutput()) {
|
||||
passes.add(DotGraphVisitor.dumpRegions());
|
||||
}
|
||||
|
||||
passes.add(new DependencyCollector());
|
||||
|
||||
passes.add(new RenameVisitor());
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
package jadx.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.codegen.CodeGen;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.visitors.DepthTraversal;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static jadx.core.dex.nodes.ProcessState.GENERATED;
|
||||
import static jadx.core.dex.nodes.ProcessState.NOT_LOADED;
|
||||
import static jadx.core.dex.nodes.ProcessState.PROCESSED;
|
||||
import static jadx.core.dex.nodes.ProcessState.STARTED;
|
||||
import static jadx.core.dex.nodes.ProcessState.UNLOADED;
|
||||
|
||||
public final class ProcessClass {
|
||||
|
||||
@@ -25,7 +23,7 @@ public final class ProcessClass {
|
||||
if (codeGen == null && cls.getState() == PROCESSED) {
|
||||
return;
|
||||
}
|
||||
synchronized (cls.getClassInfo()) {
|
||||
synchronized (getSyncObj(cls)) {
|
||||
try {
|
||||
if (cls.getState() == NOT_LOADED) {
|
||||
cls.load();
|
||||
@@ -38,22 +36,18 @@ public final class ProcessClass {
|
||||
if (cls.getState() == PROCESSED && codeGen != null) {
|
||||
processDependencies(cls, passes);
|
||||
codeGen.visit(cls);
|
||||
cls.setState(GENERATED);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ErrorsCounter.classError(cls, e.getClass().getSimpleName(), e);
|
||||
} finally {
|
||||
if (cls.getState() == GENERATED) {
|
||||
cls.unload();
|
||||
cls.setState(UNLOADED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getSyncObj(ClassNode cls) {
|
||||
return cls.getClassInfo();
|
||||
}
|
||||
|
||||
private static void processDependencies(ClassNode cls, List<IDexTreeVisitor> passes) {
|
||||
for (ClassNode depCls : cls.getDependencies()) {
|
||||
process(depCls, passes, null);
|
||||
}
|
||||
cls.getDependencies().forEach(depCls -> process(depCls, passes, null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
package jadx.core.clsp;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
@@ -28,6 +20,14 @@ import java.util.zip.ZipOutputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.close;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package jadx.core.clsp;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -17,6 +13,10 @@ import java.util.WeakHashMap;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
/**
|
||||
* Classes hierarchy graph
|
||||
*/
|
||||
@@ -120,9 +120,11 @@ public class ClspGraph {
|
||||
}
|
||||
|
||||
private void addAncestorsNames(NClass cls, Set<String> result) {
|
||||
result.add(cls.getName());
|
||||
for (NClass p : cls.getParents()) {
|
||||
addAncestorsNames(p, result);
|
||||
boolean isNew = result.add(cls.getName());
|
||||
if (isNew) {
|
||||
for (NClass p : cls.getParents()) {
|
||||
addAncestorsNames(p, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package jadx.core.clsp;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -13,6 +8,11 @@ import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
|
||||
/**
|
||||
* Utility class for convert dex or jar to jadx classes set (.jcst)
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
@@ -14,11 +19,6 @@ import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class AnnotationGen {
|
||||
|
||||
private final ClassNode cls;
|
||||
|
||||
@@ -18,6 +18,8 @@ import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;
|
||||
import jadx.core.dex.attributes.nodes.JadxError;
|
||||
import jadx.core.dex.attributes.nodes.JadxWarn;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
@@ -253,6 +255,7 @@ public class ClassGen {
|
||||
if (code.getLine() != clsDeclLine) {
|
||||
code.newLine();
|
||||
}
|
||||
int savedIndent = code.getIndent();
|
||||
try {
|
||||
addMethod(code, mth);
|
||||
} catch (Exception e) {
|
||||
@@ -260,6 +263,7 @@ public class ClassGen {
|
||||
code.newLine().add(ErrorsCounter.methodError(mth, "Method generation error", e));
|
||||
code.newLine().add(Utils.getStackTrace(e));
|
||||
code.newLine().add("*/");
|
||||
code.setIndent(savedIndent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,12 +296,11 @@ public class ClassGen {
|
||||
}
|
||||
code.add(';');
|
||||
} else {
|
||||
insertDecompilationProblems(code, mth);
|
||||
boolean badCode = mth.contains(AFlag.INCONSISTENT_CODE);
|
||||
if (badCode) {
|
||||
code.startLine("/* JADX WARNING: inconsistent code. */");
|
||||
code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
|
||||
ErrorsCounter.methodError(mth, "Inconsistent code");
|
||||
if (showInconsistentCode) {
|
||||
code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
|
||||
mth.remove(AFlag.INCONSISTENT_CODE);
|
||||
badCode = false;
|
||||
}
|
||||
@@ -324,6 +327,26 @@ public class ClassGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void insertDecompilationProblems(CodeWriter code, MethodNode mth) {
|
||||
List<JadxError> errors = mth.getAll(AType.JADX_ERROR);
|
||||
List<JadxWarn> warns = mth.getAll(AType.JADX_WARN);
|
||||
if (!errors.isEmpty()) {
|
||||
errors.forEach(err -> {
|
||||
code.startLine("/* JADX ERROR: ").add(err.getError());
|
||||
Throwable cause = err.getCause();
|
||||
if (cause != null) {
|
||||
code.incIndent();
|
||||
Utils.appendStackTrace(code, cause);
|
||||
code.decIndent();
|
||||
}
|
||||
code.add("*/");
|
||||
});
|
||||
}
|
||||
if (!warns.isEmpty()) {
|
||||
warns.forEach(warn -> code.startLine("/* JADX WARNING: ").add(warn.getWarn()).add(" */"));
|
||||
}
|
||||
}
|
||||
|
||||
private void addFields(CodeWriter code) throws CodegenException {
|
||||
addEnumFields(code);
|
||||
for (FieldNode f : cls.getFields()) {
|
||||
@@ -435,7 +458,7 @@ public class ClassGen {
|
||||
}
|
||||
|
||||
public void useClass(CodeWriter code, ArgType type) {
|
||||
useClass(code, ClassInfo.extCls(cls.dex(), type));
|
||||
useClass(code, ClassInfo.extCls(cls.root(), type));
|
||||
ArgType[] generics = type.getGenericTypes();
|
||||
if (generics != null) {
|
||||
code.add('<');
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
|
||||
public class CodeGen extends AbstractVisitor {
|
||||
public class CodeGen {
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws CodegenException {
|
||||
ClassGen clsGen = new ClassGen(cls, cls.root().getArgs());
|
||||
CodeWriter clsCode = clsGen.makeClass();
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.api.CodePosition;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collections;
|
||||
@@ -17,6 +12,12 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.CodePosition;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
public class CodeWriter {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CodeWriter.class);
|
||||
|
||||
@@ -93,6 +94,15 @@ public class CodeWriter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter addMultiLine(String str) {
|
||||
buf.append(str);
|
||||
if (str.contains(NL)) {
|
||||
line += StringUtils.countMatches(str, NL);
|
||||
offset = 0;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter add(String str) {
|
||||
buf.append(str);
|
||||
offset += str.length();
|
||||
@@ -181,6 +191,11 @@ public class CodeWriter {
|
||||
return indent;
|
||||
}
|
||||
|
||||
public void setIndent(int indent) {
|
||||
this.indent = indent;
|
||||
updateIndent();
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
@@ -254,8 +269,9 @@ public class CodeWriter {
|
||||
}
|
||||
|
||||
private void removeFirstEmptyLine() {
|
||||
if (buf.indexOf(NL) == 0) {
|
||||
buf.delete(0, NL.length());
|
||||
int len = NL.length();
|
||||
if (buf.substring(0, len).equals(NL)) {
|
||||
buf.delete(0, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.instructions.ArithNode;
|
||||
import jadx.core.dex.instructions.IfOp;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
@@ -23,7 +20,6 @@ import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class ConditionGen extends InsnGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ConditionGen.class);
|
||||
|
||||
private static class CondStack {
|
||||
private final Queue<IfCondition> stack = new LinkedList<>();
|
||||
@@ -126,7 +122,7 @@ public class ConditionGen extends InsnGen {
|
||||
wrap(code, firstArg);
|
||||
return;
|
||||
}
|
||||
ErrorsCounter.methodError(mth, "Unsupported boolean condition " + op.getSymbol());
|
||||
ErrorsCounter.methodWarn(mth, "Unsupported boolean condition " + op.getSymbol());
|
||||
}
|
||||
|
||||
addArg(code, firstArg, isArgWrapNeeded(firstArg));
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
@@ -41,17 +52,6 @@ import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static jadx.core.utils.android.AndroidResourcesUtils.handleAppResField;
|
||||
|
||||
public class InsnGen {
|
||||
@@ -137,13 +137,7 @@ public class InsnGen {
|
||||
|
||||
private void instanceField(CodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {
|
||||
ClassNode pCls = mth.getParentClass();
|
||||
FieldNode fieldNode = pCls.searchField(field);
|
||||
while (fieldNode == null
|
||||
&& pCls.getParentClass() != pCls
|
||||
&& pCls.getParentClass() != null) {
|
||||
pCls = pCls.getParentClass();
|
||||
fieldNode = pCls.searchField(field);
|
||||
}
|
||||
FieldNode fieldNode = pCls.dex().root().deepResolveField(field);
|
||||
if (fieldNode != null) {
|
||||
FieldReplaceAttr replace = fieldNode.get(AType.FIELD_REPLACE);
|
||||
if (replace != null) {
|
||||
@@ -163,7 +157,11 @@ public class InsnGen {
|
||||
if (fieldNode != null) {
|
||||
code.attachAnnotation(fieldNode);
|
||||
}
|
||||
code.add(field.getAlias());
|
||||
if (fieldNode == null) {
|
||||
code.add(field.getAlias());
|
||||
} else {
|
||||
code.add(fieldNode.getAlias());
|
||||
}
|
||||
}
|
||||
|
||||
public static void makeStaticFieldAccess(CodeWriter code, FieldInfo field, ClassGen clsGen) {
|
||||
@@ -176,11 +174,15 @@ public class InsnGen {
|
||||
}
|
||||
code.add('.');
|
||||
}
|
||||
FieldNode fieldNode = clsGen.getClassNode().dex().resolveField(field);
|
||||
FieldNode fieldNode = clsGen.getClassNode().dex().root().deepResolveField(field);
|
||||
if (fieldNode != null) {
|
||||
code.attachAnnotation(fieldNode);
|
||||
}
|
||||
code.add(field.getAlias());
|
||||
if (fieldNode == null) {
|
||||
code.add(field.getAlias());
|
||||
} else {
|
||||
code.add(fieldNode.getAlias());
|
||||
}
|
||||
}
|
||||
|
||||
protected void staticField(CodeWriter code, FieldInfo field) {
|
||||
@@ -578,7 +580,7 @@ public class InsnGen {
|
||||
// anonymous class construction
|
||||
if (cls.contains(AFlag.DONT_GENERATE)) {
|
||||
code.add("/* anonymous class already generated */");
|
||||
ErrorsCounter.methodError(mth, "Anonymous class already generated: " + cls);
|
||||
ErrorsCounter.methodWarn(mth, "Anonymous class already generated: " + cls);
|
||||
return;
|
||||
}
|
||||
ArgType parent;
|
||||
@@ -612,7 +614,7 @@ public class InsnGen {
|
||||
MethodInfo callMth = insn.getCallMth();
|
||||
|
||||
// inline method
|
||||
MethodNode callMthNode = mth.dex().deepResolveMethod(callMth);
|
||||
MethodNode callMthNode = mth.root().deepResolveMethod(callMth);
|
||||
if (callMthNode != null) {
|
||||
if (inlineMethod(callMthNode, insn, code)) {
|
||||
return;
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
@@ -16,18 +22,9 @@ import jadx.core.dex.visitors.DepthTraversal;
|
||||
import jadx.core.dex.visitors.FallbackModeVisitor;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
public class MethodGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MethodGen.class);
|
||||
|
||||
@@ -109,7 +106,7 @@ public class MethodGen {
|
||||
} else if (args.size() > 2) {
|
||||
args = args.subList(2, args.size());
|
||||
} else {
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth,
|
||||
LOG.warn(ErrorsCounter.formatMsg(mth,
|
||||
"Incorrect number of args for enum constructor: " + args.size()
|
||||
+ " (expected >= 2)"
|
||||
));
|
||||
@@ -144,7 +141,7 @@ public class MethodGen {
|
||||
classGen.useType(argsCode, elType);
|
||||
argsCode.add("...");
|
||||
} else {
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Last argument in varargs method not array"));
|
||||
LOG.warn(ErrorsCounter.formatMsg(mth, "Last argument in varargs method not array"));
|
||||
classGen.useType(argsCode, arg.getType());
|
||||
}
|
||||
} else {
|
||||
@@ -164,17 +161,6 @@ public class MethodGen {
|
||||
if (mth.contains(AType.JADX_ERROR)
|
||||
|| mth.contains(AFlag.INCONSISTENT_CODE)
|
||||
|| mth.getRegion() == null) {
|
||||
JadxErrorAttr err = mth.get(AType.JADX_ERROR);
|
||||
if (err != null) {
|
||||
code.startLine("/* JADX: method processing error */");
|
||||
Throwable cause = err.getCause();
|
||||
if (cause != null) {
|
||||
code.newLine();
|
||||
code.add("/*");
|
||||
code.newLine().add("Error: ").add(Utils.getStackTrace(cause));
|
||||
code.add("*/");
|
||||
}
|
||||
}
|
||||
code.startLine("/*");
|
||||
addFallbackMethodCode(code);
|
||||
code.startLine("*/");
|
||||
@@ -190,19 +176,14 @@ public class MethodGen {
|
||||
|
||||
public void addFallbackMethodCode(CodeWriter code) {
|
||||
if (mth.getInstructions() == null) {
|
||||
JadxErrorAttr errorAttr = mth.get(AType.JADX_ERROR);
|
||||
if (errorAttr == null
|
||||
|| errorAttr.getCause() == null
|
||||
|| !errorAttr.getCause().getClass().equals(DecodeException.class)) {
|
||||
// load original instructions
|
||||
try {
|
||||
mth.load();
|
||||
DepthTraversal.visit(new FallbackModeVisitor(), mth);
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Error reload instructions in fallback mode:", e);
|
||||
code.startLine("// Can't load method instructions: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
// load original instructions
|
||||
try {
|
||||
mth.load();
|
||||
DepthTraversal.visit(new FallbackModeVisitor(), mth);
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Error reload instructions in fallback mode:", e);
|
||||
code.startLine("// Can't load method instructions: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
InsnNode[] insnArr = mth.getInstructions();
|
||||
@@ -252,5 +233,4 @@ public class MethodGen {
|
||||
public static String getLabelName(int offset) {
|
||||
return "L_" + InsnUtils.formatOffset(offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
@@ -17,16 +23,11 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class NameGen {
|
||||
|
||||
private static final Map<String, String> OBJ_ALIAS;
|
||||
|
||||
private final Set<String> varNames = new HashSet<>();
|
||||
private final Set<String> varNames = new LinkedHashSet<>();
|
||||
private final MethodNode mth;
|
||||
private final boolean fallback;
|
||||
|
||||
@@ -102,16 +103,11 @@ public class NameGen {
|
||||
if (fallback) {
|
||||
return getFallbackName(arg);
|
||||
}
|
||||
String name = arg.getName();
|
||||
String varName;
|
||||
if (name != null) {
|
||||
if ("this".equals(name)) {
|
||||
return name;
|
||||
}
|
||||
varName = name;
|
||||
} else {
|
||||
varName = guessName(arg);
|
||||
if (arg.isThis()) {
|
||||
return RegisterArg.THIS_ARG_NAME;
|
||||
}
|
||||
String name = arg.getName();
|
||||
String varName = name != null ? name : guessName(arg);
|
||||
if (NameMapper.isReserved(varName)) {
|
||||
return varName + "R";
|
||||
}
|
||||
@@ -141,11 +137,11 @@ public class NameGen {
|
||||
private String makeNameForType(ArgType type) {
|
||||
if (type.isPrimitive()) {
|
||||
return makeNameForPrimitive(type);
|
||||
} else if (type.isArray()) {
|
||||
return makeNameForType(type.getArrayRootElement()) + "Arr";
|
||||
} else {
|
||||
return makeNameForObject(type);
|
||||
}
|
||||
if (type.isArray()) {
|
||||
return makeNameForType(type.getArrayRootElement()) + "Arr";
|
||||
}
|
||||
return makeNameForObject(type);
|
||||
}
|
||||
|
||||
private static String makeNameForPrimitive(ArgType type) {
|
||||
@@ -158,7 +154,7 @@ public class NameGen {
|
||||
if (alias != null) {
|
||||
return alias;
|
||||
}
|
||||
ClassInfo extClsInfo = ClassInfo.extCls(mth.dex(), type);
|
||||
ClassInfo extClsInfo = ClassInfo.extCls(mth.root(), type);
|
||||
String shortName = extClsInfo.getShortName();
|
||||
String vName = fromName(shortName);
|
||||
if (vName != null) {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||
@@ -32,12 +38,6 @@ import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RegionGen extends InsnGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RegionGen.class);
|
||||
|
||||
@@ -134,17 +134,16 @@ public class RegionGen extends InsnGen {
|
||||
* Connect if-else-if block
|
||||
*/
|
||||
private boolean connectElseIf(CodeWriter code, IContainer els) throws CodegenException {
|
||||
if (!els.contains(AFlag.ELSE_IF_CHAIN)) {
|
||||
return false;
|
||||
}
|
||||
if (!(els instanceof Region)) {
|
||||
return false;
|
||||
}
|
||||
List<IContainer> subBlocks = ((Region) els).getSubBlocks();
|
||||
if (subBlocks.size() == 1
|
||||
&& subBlocks.get(0) instanceof IfRegion) {
|
||||
makeIf((IfRegion) subBlocks.get(0), code, false);
|
||||
return true;
|
||||
if (els.contains(AFlag.ELSE_IF_CHAIN) && els instanceof Region) {
|
||||
List<IContainer> subBlocks = ((Region) els).getSubBlocks();
|
||||
if (subBlocks.size() == 1) {
|
||||
IContainer elseBlock = subBlocks.get(0);
|
||||
if (elseBlock instanceof IfRegion) {
|
||||
declareVars(code, elseBlock);
|
||||
makeIf((IfRegion) elseBlock, code, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -154,7 +153,7 @@ public class RegionGen extends InsnGen {
|
||||
if (header != null) {
|
||||
List<InsnNode> headerInsns = header.getInstructions();
|
||||
if (headerInsns.size() > 1) {
|
||||
ErrorsCounter.methodError(mth, "Found not inlined instructions from loop header");
|
||||
ErrorsCounter.methodWarn(mth, "Found not inlined instructions from loop header");
|
||||
int last = headerInsns.size() - 1;
|
||||
for (int i = 0; i < last; i++) {
|
||||
InsnNode insn = headerInsns.get(i);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
import jadx.core.dex.nodes.IDexNode;
|
||||
@@ -8,9 +12,6 @@ import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TypeGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TypeGen.class);
|
||||
|
||||
@@ -57,7 +58,11 @@ public class TypeGen {
|
||||
case BOOLEAN:
|
||||
return lit == 0 ? "false" : "true";
|
||||
case CHAR:
|
||||
return stringUtils.unescapeChar((char) lit);
|
||||
char ch = (char) lit;
|
||||
if (!NameMapper.isPrintableChar(ch)) {
|
||||
return Integer.toString(ch);
|
||||
}
|
||||
return stringUtils.unescapeChar(ch);
|
||||
case BYTE:
|
||||
return formatByte((byte) lit);
|
||||
case SHORT:
|
||||
@@ -171,5 +176,4 @@ public class TypeGen {
|
||||
}
|
||||
return Float.toString(f) + "f";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -16,6 +12,10 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
|
||||
class DeobfPresets {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DeobfPresets.class);
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
@@ -12,20 +28,6 @@ import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Deobfuscator {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Deobfuscator.class);
|
||||
|
||||
@@ -48,6 +50,7 @@ public class Deobfuscator {
|
||||
|
||||
private final PackageNode rootPackage = new PackageNode("");
|
||||
private final Set<String> pkgSet = new TreeSet<>();
|
||||
private final Set<String> reservedClsNames = new HashSet<>();
|
||||
|
||||
private final int maxLength;
|
||||
private final int minLength;
|
||||
@@ -89,7 +92,12 @@ public class Deobfuscator {
|
||||
private void preProcess() {
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
for (ClassNode cls : dexNode.getClasses()) {
|
||||
doClass(cls);
|
||||
Collections.addAll(reservedClsNames, cls.getPackage().split("\\."));
|
||||
}
|
||||
}
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
for (ClassNode cls : dexNode.getClasses()) {
|
||||
preProcessClass(cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,7 +109,7 @@ public class Deobfuscator {
|
||||
}
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
for (ClassNode cls : dexNode.getClasses()) {
|
||||
processClass(dexNode, cls);
|
||||
processClass(cls);
|
||||
}
|
||||
}
|
||||
postProcess();
|
||||
@@ -128,7 +136,6 @@ public class Deobfuscator {
|
||||
mth.setAlias(aliasToUse);
|
||||
mth.setAliasFromPreset(aliasFromPreset);
|
||||
}
|
||||
|
||||
id++;
|
||||
}
|
||||
}
|
||||
@@ -143,114 +150,106 @@ public class Deobfuscator {
|
||||
ovrdMap.clear();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ClassNode resolveOverridingInternal(DexNode dex, ClassNode cls, String signature,
|
||||
Set<MethodInfo> overrideSet, ClassNode rootClass) {
|
||||
ClassNode result = null;
|
||||
private void resolveOverriding(MethodNode mth) {
|
||||
Set<ClassNode> clsParents = new LinkedHashSet<>();
|
||||
collectClassHierarchy(mth.getParentClass(), clsParents);
|
||||
|
||||
for (MethodNode m : cls.getMethods()) {
|
||||
if (m.getMethodInfo().getShortId().startsWith(signature)) {
|
||||
result = cls;
|
||||
if (!overrideSet.contains(m.getMethodInfo())) {
|
||||
overrideSet.add(m.getMethodInfo());
|
||||
}
|
||||
break;
|
||||
String mthSignature = mth.getMethodInfo().makeSignature(false);
|
||||
Set<MethodInfo> overrideSet = new LinkedHashSet<>();
|
||||
for (ClassNode classNode : clsParents) {
|
||||
MethodInfo methodInfo = getMthOverride(classNode.getMethods(), mthSignature);
|
||||
if (methodInfo != null) {
|
||||
overrideSet.add(methodInfo);
|
||||
}
|
||||
}
|
||||
|
||||
ArgType superClass = cls.getSuperClass();
|
||||
if (superClass != null) {
|
||||
ClassNode superNode = dex.resolveClass(superClass);
|
||||
if (superNode != null) {
|
||||
ClassNode clsWithMth = resolveOverridingInternal(dex, superNode, signature, overrideSet, rootClass);
|
||||
if (clsWithMth != null) {
|
||||
if ((result != null) && (result != cls)) {
|
||||
if (clsWithMth != result) {
|
||||
LOG.warn(String.format("Multiple overriding '%s' from '%s' and '%s' in '%s'",
|
||||
signature,
|
||||
result.getFullName(), clsWithMth.getFullName(),
|
||||
rootClass.getFullName()));
|
||||
}
|
||||
} else {
|
||||
result = clsWithMth;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (overrideSet.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ArgType iFaceType : cls.getInterfaces()) {
|
||||
ClassNode iFaceNode = dex.resolveClass(iFaceType);
|
||||
if (iFaceNode != null) {
|
||||
ClassNode clsWithMth = resolveOverridingInternal(dex, iFaceNode, signature, overrideSet, rootClass);
|
||||
if (clsWithMth != null) {
|
||||
if ((result != null) && (result != cls)) {
|
||||
if (clsWithMth != result) {
|
||||
LOG.warn(String.format("Multiple overriding '%s' from '%s' and '%s' in '%s'",
|
||||
signature,
|
||||
result.getFullName(), clsWithMth.getFullName(),
|
||||
rootClass.getFullName()));
|
||||
}
|
||||
} else {
|
||||
result = clsWithMth;
|
||||
}
|
||||
}
|
||||
}
|
||||
OverridedMethodsNode overrideNode = getOverrideMethodsNode(overrideSet);
|
||||
if (overrideNode == null) {
|
||||
overrideNode = new OverridedMethodsNode(overrideSet);
|
||||
ovrd.add(overrideNode);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void resolveOverriding(DexNode dex, ClassNode cls, MethodNode mth) {
|
||||
Set<MethodInfo> overrideSet = new HashSet<>();
|
||||
resolveOverridingInternal(dex, cls, mth.getMethodInfo().makeSignature(false), overrideSet, cls);
|
||||
if (overrideSet.size() > 1) {
|
||||
OverridedMethodsNode overrideNode = null;
|
||||
for (MethodInfo _mth : overrideSet) {
|
||||
if (ovrdMap.containsKey(_mth)) {
|
||||
overrideNode = ovrdMap.get(_mth);
|
||||
break;
|
||||
}
|
||||
for (MethodInfo overrideMth : overrideSet) {
|
||||
if (!ovrdMap.containsKey(overrideMth)) {
|
||||
ovrdMap.put(overrideMth, overrideNode);
|
||||
overrideNode.add(overrideMth);
|
||||
}
|
||||
if (overrideNode == null) {
|
||||
overrideNode = new OverridedMethodsNode(overrideSet);
|
||||
ovrd.add(overrideNode);
|
||||
}
|
||||
for (MethodInfo _mth : overrideSet) {
|
||||
if (!ovrdMap.containsKey(_mth)) {
|
||||
ovrdMap.put(_mth, overrideNode);
|
||||
if (!overrideNode.contains(_mth)) {
|
||||
overrideNode.add(_mth);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
overrideSet.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void processClass(DexNode dex, ClassNode cls) {
|
||||
private OverridedMethodsNode getOverrideMethodsNode(Set<MethodInfo> overrideSet) {
|
||||
for (MethodInfo overrideMth : overrideSet) {
|
||||
OverridedMethodsNode node = ovrdMap.get(overrideMth);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MethodInfo getMthOverride(List<MethodNode> methods, String mthSignature) {
|
||||
for (MethodNode m : methods) {
|
||||
MethodInfo mthInfo = m.getMethodInfo();
|
||||
if (mthInfo.getShortId().startsWith(mthSignature)) {
|
||||
return mthInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void collectClassHierarchy(ClassNode cls, Set<ClassNode> collected) {
|
||||
boolean added = collected.add(cls);
|
||||
if (added) {
|
||||
ArgType superClass = cls.getSuperClass();
|
||||
if (superClass != null) {
|
||||
ClassNode superNode = cls.dex().resolveClass(superClass);
|
||||
if (superNode != null) {
|
||||
collectClassHierarchy(superNode, collected);
|
||||
}
|
||||
}
|
||||
|
||||
for (ArgType argType : cls.getInterfaces()) {
|
||||
ClassNode interfaceNode = cls.dex().resolveClass(argType);
|
||||
if (interfaceNode != null) {
|
||||
collectClassHierarchy(interfaceNode, collected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processClass(ClassNode cls) {
|
||||
ClassInfo clsInfo = cls.getClassInfo();
|
||||
String fullName = getClassFullName(clsInfo);
|
||||
if (!fullName.equals(clsInfo.getFullName())) {
|
||||
clsInfo.rename(dex, fullName);
|
||||
clsInfo.rename(cls.dex().root(), fullName);
|
||||
}
|
||||
for (FieldNode field : cls.getFields()) {
|
||||
FieldInfo fieldInfo = field.getFieldInfo();
|
||||
String alias = getFieldAlias(field);
|
||||
if (alias != null) {
|
||||
fieldInfo.setAlias(alias);
|
||||
}
|
||||
renameField(field);
|
||||
}
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
MethodInfo methodInfo = mth.getMethodInfo();
|
||||
String alias = getMethodAlias(mth);
|
||||
if (alias != null) {
|
||||
methodInfo.setAlias(alias);
|
||||
}
|
||||
renameMethod(mth);
|
||||
}
|
||||
for (ClassNode innerCls : cls.getInnerClasses()) {
|
||||
processClass(innerCls);
|
||||
}
|
||||
}
|
||||
|
||||
if (mth.isVirtual()) {
|
||||
resolveOverriding(dex, cls, mth);
|
||||
}
|
||||
public void renameField(FieldNode field) {
|
||||
FieldInfo fieldInfo = field.getFieldInfo();
|
||||
String alias = getFieldAlias(field);
|
||||
if (alias != null) {
|
||||
fieldInfo.setAlias(alias);
|
||||
}
|
||||
}
|
||||
|
||||
public void renameMethod(MethodNode mth) {
|
||||
String alias = getMethodAlias(mth);
|
||||
if (alias != null) {
|
||||
mth.getMethodInfo().setAlias(alias);
|
||||
}
|
||||
if (mth.isVirtual()) {
|
||||
resolveOverriding(mth);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +310,7 @@ public class Deobfuscator {
|
||||
return prefix + clsInfo.getShortName();
|
||||
}
|
||||
|
||||
private void doClass(ClassNode cls) {
|
||||
private void preProcessClass(ClassNode cls) {
|
||||
ClassInfo classInfo = cls.getClassInfo();
|
||||
String pkgFullName = classInfo.getPackage();
|
||||
PackageNode pkg = getPackageNode(pkgFullName, true);
|
||||
@@ -320,13 +319,16 @@ public class Deobfuscator {
|
||||
String alias = deobfPresets.getForCls(classInfo);
|
||||
if (alias != null) {
|
||||
clsMap.put(classInfo, new DeobfClsInfo(this, cls, pkg, alias));
|
||||
return;
|
||||
} else {
|
||||
if (!clsMap.containsKey(classInfo)) {
|
||||
String clsShortName = classInfo.getShortName();
|
||||
if (shouldRename(clsShortName) || reservedClsNames.contains(clsShortName)) {
|
||||
makeClsAlias(cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clsMap.containsKey(classInfo)) {
|
||||
return;
|
||||
}
|
||||
if (shouldRename(classInfo.getShortName())) {
|
||||
makeClsAlias(cls);
|
||||
for (ClassNode innerCls : cls.getInnerClasses()) {
|
||||
preProcessClass(innerCls);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,21 +363,33 @@ public class Deobfuscator {
|
||||
if (sourceFileAttr == null) {
|
||||
return null;
|
||||
}
|
||||
if (cls.getClassInfo().isInner()) {
|
||||
return null;
|
||||
}
|
||||
String name = sourceFileAttr.getFileName();
|
||||
if (name.endsWith(".java")) {
|
||||
name = name.substring(0, name.length() - ".java".length());
|
||||
} else if (name.endsWith(".kt")) {
|
||||
name = name.substring(0, name.length() - ".kt".length());
|
||||
}
|
||||
if (NameMapper.isValidIdentifier(name)
|
||||
&& !NameMapper.isReserved(name)) {
|
||||
// TODO: check if no class with this name exists or already renamed
|
||||
cls.remove(AType.SOURCE_FILE);
|
||||
return name;
|
||||
if (!NameMapper.isValidIdentifier(name) || NameMapper.isReserved(name)) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
for (DeobfClsInfo deobfClsInfo : clsMap.values()) {
|
||||
if (deobfClsInfo.getAlias().equals(name)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
ClassNode otherCls = cls.dex().root().searchClassByName(cls.getPackage() + "." + name);
|
||||
if (otherCls != null) {
|
||||
return null;
|
||||
}
|
||||
cls.remove(AType.SOURCE_FILE);
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFieldAlias(FieldNode field) {
|
||||
private String getFieldAlias(FieldNode field) {
|
||||
FieldInfo fieldInfo = field.getFieldInfo();
|
||||
String alias = fldMap.get(fieldInfo);
|
||||
if (alias != null) {
|
||||
@@ -393,7 +407,7 @@ public class Deobfuscator {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMethodAlias(MethodNode mth) {
|
||||
private String getMethodAlias(MethodNode mth) {
|
||||
MethodInfo methodInfo = mth.getMethodInfo();
|
||||
String alias = mthMap.get(methodInfo);
|
||||
if (alias != null) {
|
||||
|
||||
@@ -5,6 +5,8 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static jadx.core.utils.StringUtils.notEmpty;
|
||||
|
||||
public class NameMapper {
|
||||
|
||||
private static final Pattern VALID_JAVA_IDENTIFIER = Pattern.compile(
|
||||
@@ -76,11 +78,15 @@ public class NameMapper {
|
||||
}
|
||||
|
||||
public static boolean isValidIdentifier(String str) {
|
||||
return VALID_JAVA_IDENTIFIER.matcher(str).matches() && isAllCharsPrintable(str);
|
||||
return notEmpty(str)
|
||||
&& VALID_JAVA_IDENTIFIER.matcher(str).matches()
|
||||
&& isAllCharsPrintable(str);
|
||||
}
|
||||
|
||||
public static boolean isValidFullIdentifier(String str) {
|
||||
return VALID_JAVA_FULL_IDENTIFIER.matcher(str).matches() && isAllCharsPrintable(str);
|
||||
return notEmpty(str)
|
||||
&& VALID_JAVA_FULL_IDENTIFIER.matcher(str).matches()
|
||||
&& isAllCharsPrintable(str);
|
||||
}
|
||||
|
||||
public static boolean isPrintableChar(int c) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/* package */ class OverridedMethodsNode {
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
|
||||
class OverridedMethodsNode {
|
||||
|
||||
private Set<MethodInfo> methods;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public enum AFlag {
|
||||
SKIP_ARG, // skip argument in invoke call
|
||||
ANONYMOUS_CONSTRUCTOR,
|
||||
ANONYMOUS_CLASS,
|
||||
THIS,
|
||||
|
||||
ELSE_IF_CHAIN,
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import jadx.core.dex.attributes.nodes.EnumMapAttr;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.nodes.JadxError;
|
||||
import jadx.core.dex.attributes.nodes.JadxWarn;
|
||||
import jadx.core.dex.attributes.nodes.JumpInfo;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
@@ -33,13 +34,15 @@ public class AType<T extends IAttribute> {
|
||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<>();
|
||||
public static final AType<AttrList<EdgeInsnAttr>> EDGE_INSN = new AType<>();
|
||||
|
||||
public static final AType<AttrList<JadxError>> JADX_ERROR = new AType<>();
|
||||
public static final AType<AttrList<JadxWarn>> JADX_WARN = new AType<>();
|
||||
|
||||
public static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<>();
|
||||
public static final AType<CatchAttr> CATCH_BLOCK = new AType<>();
|
||||
public static final AType<SplitterBlockAttr> SPLITTER_BLOCK = new AType<>();
|
||||
public static final AType<ForceReturnAttr> FORCE_RETURN = new AType<>();
|
||||
public static final AType<FieldInitAttr> FIELD_INIT = new AType<>();
|
||||
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<>();
|
||||
public static final AType<JadxErrorAttr> JADX_ERROR = new AType<>();
|
||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<>();
|
||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<>();
|
||||
public static final AType<EnumMapAttr> ENUM_MAP = new AType<>();
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class AttrList<T> implements IAttribute {
|
||||
|
||||
private final AType<AttrList<T>> type;
|
||||
private final List<T> list = new LinkedList<>();
|
||||
private final List<T> list = new ArrayList<>();
|
||||
|
||||
public AttrList(AType<AttrList<T>> type) {
|
||||
this.type = type;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
public abstract class AttrNode implements IAttributeNode {
|
||||
|
||||
private static final AttributeStorage EMPTY_ATTR_STORAGE = new EmptyAttrStorage();
|
||||
@@ -96,4 +96,8 @@ public abstract class AttrNode implements IAttributeNode {
|
||||
public String getAttributesString() {
|
||||
return storage.toString();
|
||||
}
|
||||
|
||||
public boolean isAttrStorageEmpty() {
|
||||
return storage.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
@@ -12,6 +8,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
/**
|
||||
* Storage for different attribute types:
|
||||
* 1. flags - boolean attribute (set or not)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
public final class EmptyAttrStorage extends AttributeStorage {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
public interface IAttributeNode {
|
||||
|
||||
void add(AFlag flag);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package jadx.core.dex.attributes.annotations;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
|
||||
public class Annotation {
|
||||
|
||||
public enum Visibility {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package jadx.core.dex.attributes.annotations;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class AnnotationsList implements IAttribute {
|
||||
|
||||
public static final AnnotationsList EMPTY = new AnnotationsList(Collections.<Annotation>emptyList());
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.core.dex.attributes.annotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MethodParameters implements IAttribute {
|
||||
|
||||
private final List<AnnotationsList> paramList;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* List of variables to be declared at region start.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
@@ -7,9 +10,6 @@ import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EnumClassAttr implements IAttribute {
|
||||
|
||||
public static class EnumField {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class EnumMapAttr implements IAttribute {
|
||||
|
||||
public static class KeyValueMap {
|
||||
@@ -21,9 +23,14 @@ public class EnumMapAttr implements IAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<FieldNode, KeyValueMap> fieldsMap = new HashMap<>();
|
||||
@Nullable
|
||||
private Map<FieldNode, KeyValueMap> fieldsMap;
|
||||
|
||||
@Nullable
|
||||
public KeyValueMap getMap(FieldNode field) {
|
||||
if (fieldsMap == null) {
|
||||
return null;
|
||||
}
|
||||
return fieldsMap.get(field);
|
||||
}
|
||||
|
||||
@@ -31,11 +38,18 @@ public class EnumMapAttr implements IAttribute {
|
||||
KeyValueMap map = getMap(field);
|
||||
if (map == null) {
|
||||
map = new KeyValueMap();
|
||||
if (fieldsMap == null) {
|
||||
fieldsMap = new HashMap<>();
|
||||
}
|
||||
fieldsMap.put(field, map);
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return fieldsMap == null || fieldsMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<EnumMapAttr> getType() {
|
||||
return AType.ENUM_MAP;
|
||||
@@ -45,5 +59,4 @@ public class EnumMapAttr implements IAttribute {
|
||||
public String toString() {
|
||||
return "Enum fields map: " + fieldsMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class IgnoreEdgeAttr implements IAttribute {
|
||||
|
||||
private final Set<BlockNode> blocks = new HashSet<>(3);
|
||||
|
||||
+17
-13
@@ -1,33 +1,38 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class JadxErrorAttr implements IAttribute {
|
||||
public class JadxError {
|
||||
|
||||
private final String error;
|
||||
private final Throwable cause;
|
||||
|
||||
public JadxErrorAttr(Throwable cause) {
|
||||
public JadxError(Throwable cause) {
|
||||
this(null, cause);
|
||||
}
|
||||
|
||||
public JadxError(String error, Throwable cause) {
|
||||
this.error = error;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public Throwable getCause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<JadxErrorAttr> getType() {
|
||||
return AType.JADX_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append("JadxError: ");
|
||||
if (cause == null) {
|
||||
str.append("null");
|
||||
} else {
|
||||
if (error != null) {
|
||||
str.append(error);
|
||||
str.append(' ');
|
||||
}
|
||||
if (cause != null) {
|
||||
str.append(cause.getClass());
|
||||
str.append(":");
|
||||
str.append(cause.getMessage());
|
||||
@@ -36,5 +41,4 @@ public class JadxErrorAttr implements IAttribute {
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class JadxWarn {
|
||||
|
||||
private final String warn;
|
||||
|
||||
public JadxWarn(String warn) {
|
||||
this.warn = Objects.requireNonNull(warn);
|
||||
}
|
||||
|
||||
public String getWarn() {
|
||||
return warn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JadxWarn: " + warn;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.Edge;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.Edge;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
|
||||
public class LoopInfo {
|
||||
|
||||
private final BlockNode start;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class PhiListAttr implements IAttribute {
|
||||
|
||||
private final List<PhiInsn> list = new LinkedList<>();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package jadx.core.dex.info;
|
||||
|
||||
import jadx.core.Consts;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
import jadx.core.Consts;
|
||||
|
||||
public class AccessInfo {
|
||||
|
||||
private final int accFlags;
|
||||
@@ -140,10 +140,8 @@ public class AccessInfo {
|
||||
if (isBridge()) {
|
||||
code.append("/* bridge */ ");
|
||||
}
|
||||
if (Consts.DEBUG) {
|
||||
if (isVarArgs()) {
|
||||
code.append("/* varargs */ ");
|
||||
}
|
||||
if (Consts.DEBUG && isVarArgs()) {
|
||||
code.append("/* varargs */ ");
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package jadx.core.dex.info;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public final class ClassInfo {
|
||||
|
||||
private final ArgType type;
|
||||
@@ -17,50 +18,50 @@ public final class ClassInfo {
|
||||
// class info after rename (deobfuscation)
|
||||
private ClassInfo alias;
|
||||
|
||||
private ClassInfo(DexNode dex, ArgType type) {
|
||||
this(dex, type, true);
|
||||
private ClassInfo(RootNode root, ArgType type) {
|
||||
this(root, type, true);
|
||||
}
|
||||
|
||||
private ClassInfo(DexNode dex, ArgType type, boolean inner) {
|
||||
private ClassInfo(RootNode root, ArgType type, boolean inner) {
|
||||
if (!type.isObject() || type.isGeneric()) {
|
||||
throw new JadxRuntimeException("Not class type: " + type);
|
||||
}
|
||||
this.type = type;
|
||||
this.alias = this;
|
||||
|
||||
splitNames(dex, inner);
|
||||
splitNames(root, inner);
|
||||
}
|
||||
|
||||
public static ClassInfo fromType(DexNode dex, ArgType type) {
|
||||
public static ClassInfo fromType(RootNode root, ArgType type) {
|
||||
if (type.isArray()) {
|
||||
type = ArgType.OBJECT;
|
||||
}
|
||||
ClassInfo cls = dex.root().getInfoStorage().getCls(type);
|
||||
ClassInfo cls = root.getInfoStorage().getCls(type);
|
||||
if (cls != null) {
|
||||
return cls;
|
||||
}
|
||||
cls = new ClassInfo(dex, type);
|
||||
return dex.root().getInfoStorage().putCls(cls);
|
||||
cls = new ClassInfo(root, type);
|
||||
return root.getInfoStorage().putCls(cls);
|
||||
}
|
||||
|
||||
public static ClassInfo fromDex(DexNode dex, int clsIndex) {
|
||||
if (clsIndex == DexNode.NO_INDEX) {
|
||||
return null;
|
||||
}
|
||||
return fromType(dex, dex.getType(clsIndex));
|
||||
return fromType(dex.root(), dex.getType(clsIndex));
|
||||
}
|
||||
|
||||
public static ClassInfo fromName(DexNode dex, String clsName) {
|
||||
return fromType(dex, ArgType.object(clsName));
|
||||
public static ClassInfo fromName(RootNode root, String clsName) {
|
||||
return fromType(root, ArgType.object(clsName));
|
||||
}
|
||||
|
||||
public static ClassInfo extCls(DexNode dex, ArgType type) {
|
||||
ClassInfo classInfo = fromName(dex, type.getObject());
|
||||
public static ClassInfo extCls(RootNode root, ArgType type) {
|
||||
ClassInfo classInfo = fromName(root, type.getObject());
|
||||
return classInfo.alias;
|
||||
}
|
||||
|
||||
public void rename(DexNode dex, String fullName) {
|
||||
ClassInfo newAlias = new ClassInfo(dex, ArgType.object(fullName), isInner());
|
||||
public void rename(RootNode root, String fullName) {
|
||||
ClassInfo newAlias = new ClassInfo(root, ArgType.object(fullName), isInner());
|
||||
if (!alias.getFullName().equals(newAlias.getFullName())) {
|
||||
this.alias = newAlias;
|
||||
}
|
||||
@@ -74,7 +75,7 @@ public final class ClassInfo {
|
||||
return alias;
|
||||
}
|
||||
|
||||
private void splitNames(DexNode dex, boolean canBeInner) {
|
||||
private void splitNames(RootNode root, boolean canBeInner) {
|
||||
String fullObjectName = type.getObject();
|
||||
String clsName;
|
||||
int dot = fullObjectName.lastIndexOf('.');
|
||||
@@ -93,7 +94,7 @@ public final class ClassInfo {
|
||||
parClsName = clsName.substring(0, sep);
|
||||
}
|
||||
|
||||
parentClass = fromName(dex, parClsName);
|
||||
parentClass = fromName(root, parClsName);
|
||||
clsName = clsName.substring(sep + 1);
|
||||
} else {
|
||||
parentClass = null;
|
||||
@@ -111,10 +112,10 @@ public final class ClassInfo {
|
||||
}
|
||||
|
||||
public String getFullPath() {
|
||||
ClassInfo alias = getAlias();
|
||||
return alias.getPackage().replace('.', File.separatorChar)
|
||||
ClassInfo usedAlias = getAlias();
|
||||
return usedAlias.getPackage().replace('.', File.separatorChar)
|
||||
+ File.separatorChar
|
||||
+ alias.getNameWithoutPackage().replace('.', '_');
|
||||
+ usedAlias.getNameWithoutPackage().replace('.', '_');
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
@@ -160,8 +161,8 @@ public final class ClassInfo {
|
||||
return parentClass != null;
|
||||
}
|
||||
|
||||
public void notInner(DexNode dex) {
|
||||
splitNames(dex, false);
|
||||
public void notInner(RootNode root) {
|
||||
splitNames(root, false);
|
||||
}
|
||||
|
||||
public ArgType getType() {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package jadx.core.dex.info;
|
||||
|
||||
import com.android.dex.FieldId;
|
||||
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
|
||||
import com.android.dex.FieldId;
|
||||
|
||||
public final class FieldInfo {
|
||||
|
||||
private final ClassInfo declClass;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package jadx.core.dex.info;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
|
||||
public class InfoStorage {
|
||||
|
||||
private final Map<ArgType, ClassInfo> classes = new HashMap<>();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package jadx.core.dex.info;
|
||||
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dex.MethodId;
|
||||
import com.android.dex.ProtoId;
|
||||
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public final class MethodInfo {
|
||||
|
||||
private final String name;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
@@ -7,8 +9,6 @@ import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
|
||||
public class ArithNode extends InsnNode {
|
||||
|
||||
private final ArithOp op;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.io.instructions.FillArrayDataPayloadDecodedInstruction;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
@@ -8,11 +13,6 @@ import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.io.instructions.FillArrayDataPayloadDecodedInstruction;
|
||||
|
||||
public final class FillArrayNode extends InsnNode {
|
||||
|
||||
private final Object data;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class FilledNewArrayNode extends InsnNode {
|
||||
|
||||
private final ArgType elemType;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
public class GotoNode extends InsnNode {
|
||||
public class GotoNode extends TargetInsnNode {
|
||||
|
||||
protected int target;
|
||||
protected final int target;
|
||||
|
||||
public GotoNode(int target) {
|
||||
this(InsnType.GOTO, target, 0);
|
||||
@@ -20,6 +20,15 @@ public class GotoNode extends InsnNode {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceTargetBlock(BlockNode origin, BlockNode replace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initBlocks(BlockNode curBlock) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-> " + InsnUtils.formatOffset(target);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
@@ -7,8 +11,6 @@ import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
|
||||
import static jadx.core.utils.BlockUtils.getBlockByOffset;
|
||||
import static jadx.core.utils.BlockUtils.selectOther;
|
||||
|
||||
@@ -47,7 +49,6 @@ public class IfNode extends GotoNode {
|
||||
BlockNode tmp = thenBlock;
|
||||
thenBlock = elseBlock;
|
||||
elseBlock = tmp;
|
||||
target = thenBlock.getStartOffset();
|
||||
}
|
||||
|
||||
public void changeCondition(IfOp op, InsnArg arg1, InsnArg arg2) {
|
||||
@@ -56,15 +57,31 @@ public class IfNode extends GotoNode {
|
||||
setArg(1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initBlocks(BlockNode curBlock) {
|
||||
thenBlock = getBlockByOffset(target, curBlock.getSuccessors());
|
||||
if (curBlock.getSuccessors().size() == 1) {
|
||||
List<BlockNode> successors = curBlock.getSuccessors();
|
||||
thenBlock = getBlockByOffset(target, successors);
|
||||
if (successors.size() == 1) {
|
||||
elseBlock = thenBlock;
|
||||
} else {
|
||||
elseBlock = selectOther(thenBlock, curBlock.getSuccessors());
|
||||
elseBlock = selectOther(thenBlock, successors);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceTargetBlock(BlockNode origin, BlockNode replace) {
|
||||
boolean replaced = false;
|
||||
if (thenBlock == origin) {
|
||||
thenBlock = replace;
|
||||
replaced = true;
|
||||
}
|
||||
if (elseBlock == origin) {
|
||||
elseBlock = replace;
|
||||
replaced = true;
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
public BlockNode getThenBlock() {
|
||||
return thenBlock;
|
||||
}
|
||||
@@ -73,6 +90,11 @@ public class IfNode extends GotoNode {
|
||||
return elseBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTarget() {
|
||||
return thenBlock == null ? target : thenBlock.getStartOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(InsnNode obj) {
|
||||
if (this == obj) {
|
||||
|
||||
@@ -2,7 +2,9 @@ package jadx.core.dex.instructions;
|
||||
|
||||
import java.io.EOFException;
|
||||
|
||||
import com.android.dex.ClassData;
|
||||
import com.android.dex.Code;
|
||||
import com.android.dex.FieldId;
|
||||
import com.android.dx.io.OpcodeInfo;
|
||||
import com.android.dx.io.Opcodes;
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
@@ -10,6 +12,9 @@ import com.android.dx.io.instructions.FillArrayDataPayloadDecodedInstruction;
|
||||
import com.android.dx.io.instructions.PackedSwitchPayloadDecodedInstruction;
|
||||
import com.android.dx.io.instructions.ShortArrayCodeInput;
|
||||
import com.android.dx.io.instructions.SparseSwitchPayloadDecodedInstruction;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
@@ -7,8 +9,6 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
|
||||
public class InvokeNode extends InsnNode {
|
||||
|
||||
private final InvokeType type;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NewArrayNode extends InsnNode {
|
||||
|
||||
private final ArgType arrType;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
@@ -10,18 +15,13 @@ import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class PhiInsn extends InsnNode {
|
||||
|
||||
private final Map<RegisterArg, BlockNode> blockBinds;
|
||||
|
||||
public PhiInsn(int regNum, int predecessors) {
|
||||
super(InsnType.PHI, predecessors);
|
||||
this.blockBinds = new IdentityHashMap<>(predecessors);
|
||||
this.blockBinds = new LinkedHashMap<>(predecessors);
|
||||
setResult(InsnArg.reg(regNum, ArgType.UNKNOWN));
|
||||
add(AFlag.DONT_INLINE);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import static jadx.core.utils.BlockUtils.getBlockByOffset;
|
||||
|
||||
public class SwitchNode extends InsnNode {
|
||||
public class SwitchNode extends TargetInsnNode {
|
||||
|
||||
private final Object[] keys;
|
||||
private final int[] targets;
|
||||
private final int def; // next instruction
|
||||
|
||||
private BlockNode[] targetBlocks;
|
||||
private BlockNode defTargetBlock;
|
||||
|
||||
public SwitchNode(InsnArg arg, Object[] keys, int[] targets, int def) {
|
||||
super(InsnType.SWITCH, 1);
|
||||
this.keys = keys;
|
||||
@@ -36,6 +43,42 @@ public class SwitchNode extends InsnNode {
|
||||
return def;
|
||||
}
|
||||
|
||||
public BlockNode[] getTargetBlocks() {
|
||||
return targetBlocks;
|
||||
}
|
||||
|
||||
public BlockNode getDefTargetBlock() {
|
||||
return defTargetBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initBlocks(BlockNode curBlock) {
|
||||
List<BlockNode> successors = curBlock.getSuccessors();
|
||||
int len = targets.length;
|
||||
targetBlocks = new BlockNode[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
targetBlocks[i] = getBlockByOffset(targets[i], successors);
|
||||
}
|
||||
defTargetBlock = getBlockByOffset(def, successors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceTargetBlock(BlockNode origin, BlockNode replace) {
|
||||
int count = 0;
|
||||
int len = targetBlocks.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (targetBlocks[i] == origin) {
|
||||
targetBlocks[i] = replace;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (defTargetBlock == origin) {
|
||||
defTargetBlock = replace;
|
||||
count++;
|
||||
}
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(InsnNode obj) {
|
||||
if (this == obj) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public abstract class TargetInsnNode extends InsnNode {
|
||||
|
||||
public TargetInsnNode(InsnType type, int argsCount) {
|
||||
super(type, argsCount);
|
||||
}
|
||||
|
||||
public abstract void initBlocks(BlockNode curBlock);
|
||||
|
||||
public abstract boolean replaceTargetBlock(BlockNode origin, BlockNode replace);
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.parser.SignatureParser;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.parser.SignatureParser;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public abstract class ArgType {
|
||||
|
||||
public static final ArgType INT = primitive(PrimitiveType.INT);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
|
||||
// TODO: don't extend RegisterArg (now used as a result of instruction)
|
||||
public final class FieldArg extends RegisterArg {
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
/**
|
||||
* Instruction argument,
|
||||
@@ -141,7 +140,6 @@ public abstract class InsnArg extends Typed {
|
||||
}
|
||||
|
||||
public boolean isThis() {
|
||||
// must be implemented in RegisterArg and MthParameterArg
|
||||
return false;
|
||||
return contains(AFlag.THIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class InsnWrapArg extends InsnArg {
|
||||
|
||||
private final InsnNode wrappedInsn;
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class RegisterArg extends InsnArg implements Named {
|
||||
|
||||
public static final String THIS_ARG_NAME = "this";
|
||||
|
||||
protected final int regNum;
|
||||
// not null after SSATransform pass
|
||||
private SSAVar sVar;
|
||||
@@ -43,6 +45,9 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (isThis()) {
|
||||
return THIS_ARG_NAME;
|
||||
}
|
||||
if (sVar == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -50,7 +55,7 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (sVar != null) {
|
||||
if (sVar != null && name != null) {
|
||||
sVar.setName(name);
|
||||
}
|
||||
}
|
||||
@@ -63,6 +68,21 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
return n.equals(((Named) arg).getName());
|
||||
}
|
||||
|
||||
public void mergeName(InsnArg arg) {
|
||||
if (arg instanceof Named) {
|
||||
Named otherArg = (Named) arg;
|
||||
String otherName = otherArg.getName();
|
||||
String name = getName();
|
||||
if (!Objects.equals(name, otherName)) {
|
||||
if (name == null) {
|
||||
setName(otherName);
|
||||
} else if (otherName == null) {
|
||||
otherArg.setName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(ArgType type) {
|
||||
if (sVar != null) {
|
||||
@@ -101,22 +121,6 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
return InsnUtils.getConstValueByInsn(dex, parInsn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isThis() {
|
||||
if ("this".equals(getName())) {
|
||||
return true;
|
||||
}
|
||||
// maybe it was moved from 'this' register
|
||||
InsnNode ai = getAssignInsn();
|
||||
if (ai != null && ai.getType() == InsnType.MOVE) {
|
||||
InsnArg arg = ai.getArg(0);
|
||||
if (arg != this) {
|
||||
return arg.isThis();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public InsnNode getAssignInsn() {
|
||||
if (sVar == null) {
|
||||
return null;
|
||||
@@ -142,7 +146,7 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return regNum * 31 + type.hashCode();
|
||||
return regNum;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,6 +176,9 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
}
|
||||
sb.append(" ");
|
||||
sb.append(type);
|
||||
if (!isAttrStorageEmpty()) {
|
||||
sb.append(' ').append(getAttributesString());
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
|
||||
public class SSAVar extends AttrNode {
|
||||
|
||||
private final int regNum;
|
||||
|
||||
@@ -4,8 +4,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class TypeImmutableArg extends RegisterArg {
|
||||
|
||||
private boolean isThis;
|
||||
|
||||
public TypeImmutableArg(int rn, ArgType type) {
|
||||
super(rn, type);
|
||||
}
|
||||
@@ -20,48 +18,9 @@ public class TypeImmutableArg extends RegisterArg {
|
||||
// not allowed
|
||||
}
|
||||
|
||||
public void markAsThis() {
|
||||
this.isThis = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isThis() {
|
||||
return isThis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (isThis) {
|
||||
return "this";
|
||||
}
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
void setSVar(@NotNull SSAVar sVar) {
|
||||
if (isThis) {
|
||||
sVar.setName("this");
|
||||
}
|
||||
sVar.setTypeImmutable(type);
|
||||
super.setSVar(sVar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof TypeImmutableArg)) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
return isThis == ((TypeImmutableArg) obj).isThis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + (isThis ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.instructions.mods;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
@@ -9,8 +11,6 @@ import jadx.core.dex.regions.conditions.IfCondition;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public final class TernaryInsn extends InsnNode {
|
||||
|
||||
private IfCondition condition;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
@@ -8,12 +12,9 @@ import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.EmptyBitSet;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import static jadx.core.utils.Utils.lockList;
|
||||
|
||||
public class BlockNode extends AttrNode implements IBlock {
|
||||
|
||||
@@ -32,7 +33,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
// immediate dominator
|
||||
private BlockNode idom;
|
||||
// blocks on which dominates this block
|
||||
private List<BlockNode> dominatesOn = Collections.emptyList();
|
||||
private List<BlockNode> dominatesOn = new ArrayList<>(3);
|
||||
|
||||
public BlockNode(int id, int offset) {
|
||||
this.id = id;
|
||||
@@ -68,13 +69,9 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
successors = lockList(successors);
|
||||
predecessors = lockList(predecessors);
|
||||
dominatesOn = lockList(dominatesOn);
|
||||
}
|
||||
|
||||
List<BlockNode> lockList(List<BlockNode> list) {
|
||||
if (list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
if (domFrontier == null) {
|
||||
throw new JadxRuntimeException("Dominance frontier not set for block: " + this);
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +82,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
if (sucList.isEmpty()) {
|
||||
return sucList;
|
||||
}
|
||||
List<BlockNode> toRemove = new LinkedList<>();
|
||||
List<BlockNode> toRemove = new ArrayList<>(sucList.size());
|
||||
for (BlockNode b : sucList) {
|
||||
if (BlockUtils.isBlockMustBeCleared(b)) {
|
||||
toRemove.add(b);
|
||||
@@ -160,9 +157,6 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
}
|
||||
|
||||
public void addDominatesOn(BlockNode block) {
|
||||
if (dominatesOn.isEmpty()) {
|
||||
dominatesOn = new LinkedList<>();
|
||||
}
|
||||
dominatesOn.add(block);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.android.dex.ClassData;
|
||||
import com.android.dex.ClassData.Field;
|
||||
import com.android.dex.ClassData.Method;
|
||||
import com.android.dex.ClassDef;
|
||||
import com.android.dex.Dex;
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
@@ -20,25 +38,7 @@ import jadx.core.dex.nodes.parser.StaticValuesParser;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dex.ClassData;
|
||||
import com.android.dex.ClassData.Field;
|
||||
import com.android.dex.ClassData.Method;
|
||||
import com.android.dex.ClassDef;
|
||||
import com.android.dex.Dex;
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
import static jadx.core.dex.nodes.ProcessState.UNLOADED;
|
||||
|
||||
public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
|
||||
@@ -191,23 +191,23 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (JadxRuntimeException e) {
|
||||
} catch (Exception e) {
|
||||
LOG.error("Class signature parse error: {}", this, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setFieldsTypesFromSignature() {
|
||||
for (FieldNode field : fields) {
|
||||
SignatureParser sp = SignatureParser.fromNode(field);
|
||||
if (sp != null) {
|
||||
try {
|
||||
try {
|
||||
SignatureParser sp = SignatureParser.fromNode(field);
|
||||
if (sp != null) {
|
||||
ArgType gType = sp.consumeType();
|
||||
if (gType != null) {
|
||||
field.setType(gType);
|
||||
}
|
||||
} catch (JadxRuntimeException e) {
|
||||
LOG.error("Field signature parse error: {}", field, e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Field signature parse error: {}.{}", this.getFullName(), field.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,7 +239,6 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
}
|
||||
}
|
||||
this.addAttr(new SourceFileAttr(fileName));
|
||||
LOG.debug("Class '{}' compiled from '{}'", this, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,8 +247,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
try {
|
||||
mth.load();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Method load error: {}", mth, e);
|
||||
mth.addAttr(new JadxErrorAttr(e));
|
||||
mth.addError("Method load error", e);
|
||||
}
|
||||
}
|
||||
for (ClassNode innerCls : getInnerClasses()) {
|
||||
@@ -265,6 +263,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
for (ClassNode innerCls : getInnerClasses()) {
|
||||
innerCls.unload();
|
||||
}
|
||||
setState(UNLOADED);
|
||||
}
|
||||
|
||||
private void buildCache() {
|
||||
@@ -322,7 +321,6 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public FieldNode searchFieldByName(String name) {
|
||||
for (FieldNode f : fields) {
|
||||
if (f.getName().equals(name)) {
|
||||
@@ -353,8 +351,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
if (parentClass == null) {
|
||||
if (clsInfo.isInner()) {
|
||||
ClassNode parent = dex().resolveClass(clsInfo.getParentClass());
|
||||
parent = parent == null ? this : parent;
|
||||
parentClass = parent;
|
||||
parentClass = parent == null ? this : parent;
|
||||
} else {
|
||||
parentClass = this;
|
||||
}
|
||||
@@ -364,7 +361,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
|
||||
public ClassNode getTopParentClass() {
|
||||
ClassNode parent = getParentClass();
|
||||
return parent == this ? this : parent.getParentClass();
|
||||
return parent == this ? this : parent.getTopParentClass();
|
||||
}
|
||||
|
||||
public List<ClassNode> getInnerClasses() {
|
||||
@@ -419,6 +416,11 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
return dex.root();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return "class";
|
||||
}
|
||||
|
||||
public String getRawName() {
|
||||
return clsInfo.getRawName();
|
||||
}
|
||||
@@ -484,7 +486,6 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
return clsInfo.equals(other.clsInfo);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -65,7 +65,7 @@ public class DexNode implements IDexNode {
|
||||
ClassNode parent = resolveClass(clsInfo.getParentClass());
|
||||
if (parent == null) {
|
||||
clsMap.remove(clsInfo);
|
||||
clsInfo.notInner(cls.dex());
|
||||
clsInfo.notInner(root);
|
||||
clsMap.put(clsInfo, cls);
|
||||
} else {
|
||||
parent.addInnerClass(cls);
|
||||
@@ -78,18 +78,24 @@ public class DexNode implements IDexNode {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassNode resolveClass(ClassInfo clsInfo) {
|
||||
ClassNode resolveClassLocal(ClassInfo clsInfo) {
|
||||
return clsMap.get(clsInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassNode resolveClass(ClassInfo clsInfo) {
|
||||
return root.resolveClass(clsInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassNode resolveClass(@NotNull ArgType type) {
|
||||
if (type.isGeneric()) {
|
||||
type = ArgType.object(type.getObject());
|
||||
}
|
||||
return resolveClass(ClassInfo.fromType(this, type));
|
||||
return resolveClass(ClassInfo.fromType(root, type));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public MethodNode resolveMethod(@NotNull MethodInfo mth) {
|
||||
ClassNode cls = resolveClass(mth.getDeclClass());
|
||||
@@ -99,20 +105,8 @@ public class DexNode implements IDexNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search method in class hierarchy.
|
||||
*/
|
||||
@Nullable
|
||||
public MethodNode deepResolveMethod(@NotNull MethodInfo mth) {
|
||||
ClassNode cls = resolveClass(mth.getDeclClass());
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
return deepResolveMethod(cls, mth.makeSignature(false));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MethodNode deepResolveMethod(@NotNull ClassNode cls, String signature) {
|
||||
MethodNode deepResolveMethod(@NotNull ClassNode cls, String signature) {
|
||||
for (MethodNode m : cls.getMethods()) {
|
||||
if (m.getMethodInfo().getShortId().startsWith(signature)) {
|
||||
return m;
|
||||
@@ -141,6 +135,7 @@ public class DexNode implements IDexNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public FieldNode resolveField(FieldInfo field) {
|
||||
ClassNode cls = resolveClass(field.getDeclClass());
|
||||
@@ -150,6 +145,35 @@ public class DexNode implements IDexNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
FieldNode deepResolveField(@NotNull ClassNode cls, FieldInfo fieldInfo) {
|
||||
FieldNode field = cls.searchFieldByName(fieldInfo.getName());
|
||||
if (field != null) {
|
||||
return field;
|
||||
}
|
||||
FieldNode found;
|
||||
ArgType superClass = cls.getSuperClass();
|
||||
if (superClass != null) {
|
||||
ClassNode superNode = resolveClass(superClass);
|
||||
if (superNode != null) {
|
||||
found = deepResolveField(superNode, fieldInfo);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ArgType iFaceType : cls.getInterfaces()) {
|
||||
ClassNode iFaceNode = resolveClass(iFaceType);
|
||||
if (iFaceNode != null) {
|
||||
found = deepResolveField(iFaceNode, fieldInfo);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public DexFile getDexFile() {
|
||||
return file;
|
||||
}
|
||||
@@ -197,6 +221,10 @@ public class DexNode implements IDexNode {
|
||||
return dexBuf.open(offset);
|
||||
}
|
||||
|
||||
public boolean checkOffset(int dataOffset) {
|
||||
return dataOffset >= 0 && dataOffset < dexBuf.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootNode root() {
|
||||
return root;
|
||||
@@ -207,12 +235,17 @@ public class DexNode implements IDexNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return "dex";
|
||||
}
|
||||
|
||||
public int getDexId() {
|
||||
return dexId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DEX";
|
||||
return "DEX: " + file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import com.android.dex.ClassData.Field;
|
||||
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.AccessInfo.AFType;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
|
||||
import com.android.dex.ClassData.Field;
|
||||
|
||||
public class FieldNode extends LineAttrNode {
|
||||
|
||||
private final ClassNode parent;
|
||||
|
||||
@@ -2,6 +2,8 @@ package jadx.core.dex.nodes;
|
||||
|
||||
public interface IDexNode {
|
||||
|
||||
String typeName();
|
||||
|
||||
DexNode dex();
|
||||
|
||||
RootNode root();
|
||||
|
||||
@@ -6,6 +6,8 @@ public interface IRegion extends IContainer {
|
||||
|
||||
IRegion getParent();
|
||||
|
||||
void setParent(IRegion parent);
|
||||
|
||||
List<IContainer> getSubBlocks();
|
||||
|
||||
boolean replaceSubBlock(IContainer oldBlock, IContainer newBlock);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
|
||||
public class InsnContainer extends AttrNode implements IBlock {
|
||||
|
||||
private final List<InsnNode> insns;
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
import com.rits.cloning.Cloner;
|
||||
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
@@ -12,14 +21,6 @@ import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
import com.rits.cloning.Cloner;
|
||||
|
||||
public class InsnNode extends LineAttrNode {
|
||||
|
||||
private static final Cloner INSN_CLONER = new Cloner();
|
||||
@@ -251,12 +252,14 @@ public class InsnNode extends LineAttrNode {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (insnType != other.insnType
|
||||
|| arguments.size() != other.arguments.size()) {
|
||||
if (insnType != other.insnType) {
|
||||
return false;
|
||||
}
|
||||
int size = arguments.size();
|
||||
if (size != other.arguments.size()) {
|
||||
return false;
|
||||
}
|
||||
// check wrapped instructions
|
||||
int size = arguments.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
InsnArg arg = arguments.get(i);
|
||||
InsnArg otherArg = other.arguments.get(i);
|
||||
@@ -273,6 +276,16 @@ public class InsnNode extends LineAttrNode {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 'Hard' equals, compare all arguments
|
||||
*/
|
||||
public boolean isDeepEquals(InsnNode other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
return isSame(other)
|
||||
&& Objects.equals(arguments, other.arguments);
|
||||
}
|
||||
|
||||
protected <T extends InsnNode> T copyCommonParams(T copy) {
|
||||
copy.setResult(result);
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.android.dex.ClassData.Method;
|
||||
import com.android.dex.Code;
|
||||
import com.android.dex.Code.CatchHandler;
|
||||
import com.android.dex.Code.Try;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.JumpInfo;
|
||||
@@ -23,26 +39,12 @@ import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dex.ClassData.Method;
|
||||
import com.android.dex.Code;
|
||||
import com.android.dex.Code.CatchHandler;
|
||||
import com.android.dex.Code.Try;
|
||||
import static jadx.core.utils.Utils.lockList;
|
||||
|
||||
public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MethodNode.class);
|
||||
@@ -153,8 +155,12 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
}
|
||||
instructions = null;
|
||||
blocks = null;
|
||||
enterBlock = null;
|
||||
exitBlocks = null;
|
||||
exceptionHandlers.clear();
|
||||
exceptionHandlers = Collections.emptyList();
|
||||
sVars.clear();
|
||||
region = null;
|
||||
loops = Collections.emptyList();
|
||||
}
|
||||
|
||||
private boolean parseSignature() {
|
||||
@@ -188,11 +194,11 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
}
|
||||
}
|
||||
initArguments(argsTypes);
|
||||
return true;
|
||||
} catch (JadxRuntimeException e) {
|
||||
LOG.error("Method signature parse error: {}", this, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void initArguments(List<ArgType> args) {
|
||||
@@ -209,7 +215,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
thisArg = null;
|
||||
} else {
|
||||
TypeImmutableArg arg = InsnArg.typeImmutableReg(pos - 1, parentClass.getClassInfo().getType());
|
||||
arg.markAsThis();
|
||||
arg.add(AFlag.THIS);
|
||||
thisArg = arg;
|
||||
}
|
||||
if (args.isEmpty()) {
|
||||
@@ -238,6 +244,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
return argsList.remove(0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RegisterArg getThisArg() {
|
||||
return thisArg;
|
||||
}
|
||||
@@ -404,15 +411,10 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
}
|
||||
|
||||
public void finishBasicBlocks() {
|
||||
((ArrayList<BlockNode>) blocks).trimToSize();
|
||||
((ArrayList<BlockNode>) exitBlocks).trimToSize();
|
||||
|
||||
blocks = Collections.unmodifiableList(blocks);
|
||||
exitBlocks = Collections.unmodifiableList(exitBlocks);
|
||||
|
||||
for (BlockNode block : blocks) {
|
||||
block.lock();
|
||||
}
|
||||
blocks = lockList(blocks);
|
||||
exitBlocks = lockList(exitBlocks);
|
||||
loops = lockList(loops);
|
||||
blocks.forEach(BlockNode::lock);
|
||||
}
|
||||
|
||||
public List<BlockNode> getBasicBlocks() {
|
||||
@@ -604,6 +606,19 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
return dex().root();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return "method";
|
||||
}
|
||||
|
||||
public void addWarn(String errStr) {
|
||||
ErrorsCounter.methodWarn(this, errStr);
|
||||
}
|
||||
|
||||
public void addError(String errStr, Exception e) {
|
||||
ErrorsCounter.methodError(this, errStr, e);
|
||||
}
|
||||
|
||||
public MethodInfo getMethodInfo() {
|
||||
return mthInfo;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
public class ResRefField extends FieldNode {
|
||||
|
||||
public ResRefField(DexNode dex, String str) {
|
||||
|
||||
@@ -5,6 +5,9 @@ import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dex.Dex;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -17,6 +20,7 @@ import jadx.core.clsp.ClspGraph;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.ConstStorage;
|
||||
import jadx.core.dex.info.InfoStorage;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.android.AndroidResourcesUtils;
|
||||
@@ -82,12 +86,9 @@ public class RootNode {
|
||||
}
|
||||
ResTableParser parser = new ResTableParser();
|
||||
try {
|
||||
ResourcesLoader.decodeStream(arsc, new ResourcesLoader.ResourceDecoder() {
|
||||
@Override
|
||||
public ResContainer decode(long size, InputStream is) throws IOException {
|
||||
parser.decode(is);
|
||||
return null;
|
||||
}
|
||||
ResourcesLoader.decodeStream(arsc, (size, is) -> {
|
||||
parser.decode(is);
|
||||
return null;
|
||||
});
|
||||
} catch (JadxException e) {
|
||||
LOG.error("Failed to parse '.arsc' file", e);
|
||||
@@ -144,10 +145,10 @@ public class RootNode {
|
||||
return classes;
|
||||
}
|
||||
|
||||
public ClassNode searchClassByName(String fullName) {
|
||||
@Nullable
|
||||
public ClassNode resolveClass(ClassInfo clsInfo) {
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
ClassInfo clsInfo = ClassInfo.fromName(dexNode, fullName);
|
||||
ClassNode cls = dexNode.resolveClass(clsInfo);
|
||||
ClassNode cls = dexNode.resolveClassLocal(clsInfo);
|
||||
if (cls != null) {
|
||||
return cls;
|
||||
}
|
||||
@@ -155,6 +156,12 @@ public class RootNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassNode searchClassByName(String fullName) {
|
||||
ClassInfo clsInfo = ClassInfo.fromName(this, fullName);
|
||||
return resolveClass(clsInfo);
|
||||
}
|
||||
|
||||
public List<ClassNode> searchClassByShortName(String shortName) {
|
||||
List<ClassNode> list = new ArrayList<>();
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
@@ -167,6 +174,24 @@ public class RootNode {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MethodNode deepResolveMethod(@NotNull MethodInfo mth) {
|
||||
ClassNode cls = resolveClass(mth.getDeclClass());
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
return cls.dex().deepResolveMethod(cls, mth.makeSignature(false));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FieldNode deepResolveField(@NotNull FieldInfo field) {
|
||||
ClassNode cls = resolveClass(field.getDeclClass());
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
return cls.dex().deepResolveField(cls, field);
|
||||
}
|
||||
|
||||
public List<DexNode> getDexNodes() {
|
||||
return dexNodes;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package jadx.core.dex.nodes.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.annotations.Annotation.Visibility;
|
||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
||||
@@ -11,13 +18,6 @@ import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
|
||||
public class AnnotationsParser {
|
||||
|
||||
private static final Visibility[] VISIBILITIES = {
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.List;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
@@ -78,6 +79,8 @@ public class DebugInfoParser {
|
||||
addrChange(-1, 1, line);
|
||||
setLine(addr, line);
|
||||
|
||||
boolean varsInfoFound = false;
|
||||
|
||||
int c = section.readByte() & 0xFF;
|
||||
while (c != DBG_END_SEQUENCE) {
|
||||
switch (c) {
|
||||
@@ -98,6 +101,7 @@ public class DebugInfoParser {
|
||||
int type = section.readUleb128() - 1;
|
||||
LocalVar var = new LocalVar(dex, regNum, nameId, type, DexNode.NO_INDEX);
|
||||
startVar(var, addr, line);
|
||||
varsInfoFound = true;
|
||||
break;
|
||||
}
|
||||
case DBG_START_LOCAL_EXTENDED: {
|
||||
@@ -107,6 +111,7 @@ public class DebugInfoParser {
|
||||
int sign = section.readUleb128() - 1;
|
||||
LocalVar var = new LocalVar(dex, regNum, nameId, type, sign);
|
||||
startVar(var, addr, line);
|
||||
varsInfoFound = true;
|
||||
break;
|
||||
}
|
||||
case DBG_RESTART_LOCAL: {
|
||||
@@ -118,6 +123,7 @@ public class DebugInfoParser {
|
||||
}
|
||||
var.start(addr, line);
|
||||
}
|
||||
varsInfoFound = true;
|
||||
break;
|
||||
}
|
||||
case DBG_END_LOCAL: {
|
||||
@@ -127,6 +133,7 @@ public class DebugInfoParser {
|
||||
var.end(addr, line);
|
||||
setVar(var);
|
||||
}
|
||||
varsInfoFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -160,10 +167,12 @@ public class DebugInfoParser {
|
||||
c = section.readByte() & 0xFF;
|
||||
}
|
||||
|
||||
for (LocalVar var : locals) {
|
||||
if (var != null && !var.isEnd()) {
|
||||
var.end(mth.getCodeSize() - 1, line);
|
||||
setVar(var);
|
||||
if (varsInfoFound) {
|
||||
for (LocalVar var : locals) {
|
||||
if (var != null && !var.isEnd()) {
|
||||
var.end(mth.getCodeSize() - 1, line);
|
||||
setVar(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
setSourceLines(addr, insnByOffset.length, line);
|
||||
@@ -272,7 +281,14 @@ public class DebugInfoParser {
|
||||
}
|
||||
|
||||
if (mergeRequired) {
|
||||
reg.mergeDebugInfo(var.getType(), var.getName());
|
||||
applyDebugInfo(reg, var);
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyDebugInfo(RegisterArg reg, LocalVar var) {
|
||||
String varName = var.getName();
|
||||
if (NameMapper.isValidIdentifier(varName)) {
|
||||
reg.mergeDebugInfo(var.getType(), varName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package jadx.core.dex.nodes.parser;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class LocalVar {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LocalVar.class);
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package jadx.core.dex.nodes.parser;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
@@ -15,6 +9,12 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class SignatureParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SignatureParser.class);
|
||||
@@ -180,6 +180,9 @@ public class SignatureParser {
|
||||
next();
|
||||
// type parsing not completed, proceed to inner class
|
||||
ArgType inner = consumeObjectType(true);
|
||||
if (inner == null) {
|
||||
throw new JadxRuntimeException("No inner type found: " + debugString());
|
||||
}
|
||||
return ArgType.genericInner(genericType, inner.getObject(), inner.getGenericTypes());
|
||||
} else {
|
||||
consume(';');
|
||||
@@ -289,6 +292,9 @@ public class SignatureParser {
|
||||
}
|
||||
|
||||
private String debugString() {
|
||||
if (pos >= sign.length()) {
|
||||
return sign;
|
||||
}
|
||||
return sign + " at position " + pos + " ('" + sign.charAt(pos) + "')";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package jadx.core.dex.nodes.parser;
|
||||
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
import com.android.dex.Leb128;
|
||||
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
public class StaticValuesParser extends EncValueParser {
|
||||
|
||||
public StaticValuesParser(DexNode dex, Section in) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.core.dex.regions;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class AbstractRegion extends AttrNode implements IRegion {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractRegion.class);
|
||||
|
||||
@@ -21,6 +21,7 @@ public abstract class AbstractRegion extends AttrNode implements IRegion {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(IRegion parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
@@ -30,4 +31,10 @@ public abstract class AbstractRegion extends AttrNode implements IRegion {
|
||||
LOG.warn("Replace sub block not supported for class \"{}\"", this.getClass());
|
||||
return false;
|
||||
}
|
||||
|
||||
public void updateParent(IContainer container, IRegion newParent) {
|
||||
if (container instanceof IRegion) {
|
||||
((IRegion) container).setParent(newParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package jadx.core.dex.regions;
|
||||
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public final class Region extends AbstractRegion {
|
||||
|
||||
private final List<IContainer> blocks;
|
||||
@@ -21,9 +22,7 @@ public final class Region extends AbstractRegion {
|
||||
}
|
||||
|
||||
public void add(IContainer region) {
|
||||
if (region instanceof AbstractRegion) {
|
||||
((AbstractRegion) region).setParent(this);
|
||||
}
|
||||
updateParent(region, this);
|
||||
blocks.add(region);
|
||||
}
|
||||
|
||||
@@ -32,6 +31,7 @@ public final class Region extends AbstractRegion {
|
||||
int i = blocks.indexOf(oldBlock);
|
||||
if (i != -1) {
|
||||
blocks.set(i, newBlock);
|
||||
updateParent(newBlock, this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -40,15 +40,19 @@ public final class Region extends AbstractRegion {
|
||||
@Override
|
||||
public String baseString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(blocks.size());
|
||||
for (IContainer cont : blocks) {
|
||||
sb.append(cont.baseString());
|
||||
int size = blocks.size();
|
||||
sb.append('(');
|
||||
sb.append(size);
|
||||
if (size > 0) {
|
||||
sb.append(':');
|
||||
Utils.listToString(sb, blocks, "|", IContainer::baseString);
|
||||
}
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "R:" + baseString();
|
||||
return "R" + baseString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package jadx.core.dex.regions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IBranchRegion;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class SwitchRegion extends AbstractRegion implements IBranchRegion {
|
||||
|
||||
private final BlockNode header;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.core.dex.regions;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public final class SynchronizedRegion extends AbstractRegion {
|
||||
|
||||
private final InsnNode enterInsn;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user