Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a736aadf5 | |||
| 2cea653249 | |||
| 1085238031 | |||
| 1356d91423 | |||
| fd2dc14ede | |||
| f91c5d3647 | |||
| 1f3aebf584 | |||
| b39d79a0f9 | |||
| c410914208 | |||
| a046f1caec | |||
| c25f918cc5 | |||
| 6fb1c8d3b9 | |||
| 6047a27c89 | |||
| 8446d016e4 | |||
| ab040d36d5 | |||
| a2781b5bd3 | |||
| a7903f31ac | |||
| c134837ca6 | |||
| f0a57e6714 | |||
| d9b0365c9f | |||
| 948f9456f5 | |||
| 32f94b463f | |||
| 035506496e | |||
| 477222a395 | |||
| 4bdfdfcb36 | |||
| 3167cd0817 | |||
| b52f35259b | |||
| 20bf85b14d | |||
| f02b99a1d0 | |||
| 9132ef57f1 | |||
| d42bf2d43c | |||
| 89042fbf4a | |||
| fc4dcd2db5 | |||
| 4e07d80ebc | |||
| c4a462d601 | |||
| 7fe46fb6f3 | |||
| 2cb94856fd | |||
| f53fc03c6c | |||
| 9278c51035 | |||
| ca9dc5f944 | |||
| f30cfb6166 | |||
| 9614929f77 | |||
| 8e418d4414 | |||
| 5e81bd833b | |||
| cc2ae80e7b | |||
| b921f6097d | |||
| 9679ef893b | |||
| e4fc3cebfd | |||
| 75135819cf | |||
| 072b6cce36 | |||
| 5d60f2cdf2 | |||
| c476593925 | |||
| 089467a419 | |||
| ee68e04f84 | |||
| 9cd46e74be | |||
| 5781220415 | |||
| 965fd66e0f | |||
| 7d3caa2875 | |||
| 418546a659 | |||
| d586c84b56 | |||
| 7b9e5fe99f | |||
| 648f0edc79 | |||
| 4d9d0884c3 | |||
| 19c0bbb94c | |||
| c6995c2283 | |||
| 49a263454c | |||
| 454519220f | |||
| 118fa98ca9 | |||
| 001fa639be | |||
| 009749cf8b | |||
| da94e7b1be | |||
| ea346145f6 | |||
| a01c379c95 | |||
| c9b781d5e1 | |||
| 0b49abf3f5 | |||
| e5fe4b0a99 | |||
| 7474d305fb | |||
| 4716929158 | |||
| 528ca09e8e | |||
| 233054219f | |||
| 0e2c4d4af1 | |||
| f101e9a775 | |||
| bf3863d1bf | |||
| 94e9291c40 | |||
| 459d133b5d | |||
| 773fad66bb | |||
| e250c73109 | |||
| 6870c05ffa | |||
| 199581bf74 | |||
| a9ae971602 | |||
| fe03c85b97 | |||
| c338652045 | |||
| 1f5cdeb01b | |||
| e53a72c5f5 | |||
| fc2690888e | |||
| b4472fd7d4 | |||
| 796d02506a | |||
| 467f729f06 | |||
| 050ec8b988 |
@@ -0,0 +1,14 @@
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "50...100"
|
||||
|
||||
status:
|
||||
project:
|
||||
default: on
|
||||
patch:
|
||||
default: on
|
||||
changes:
|
||||
default: off
|
||||
|
||||
comment: false
|
||||
@@ -10,13 +10,17 @@ out/
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
**/.DS_Store
|
||||
|
||||
bin/
|
||||
target/
|
||||
build/
|
||||
classes/
|
||||
idea/
|
||||
.gradle/
|
||||
gradle.properties
|
||||
|
||||
jadx-output/
|
||||
*-tmp/
|
||||
|
||||
*.dex
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
image: java:8
|
||||
|
||||
variables:
|
||||
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
|
||||
TERM: "dumb"
|
||||
|
||||
before_script:
|
||||
- chmod +x gradlew
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script:
|
||||
- sed -i " 1 s/.*/&-b$(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
|
||||
- ./gradlew -g /cache/.gradle clean dist
|
||||
artifacts:
|
||||
paths:
|
||||
- build/jadx*.zip
|
||||
+23
-12
@@ -1,28 +1,39 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- oraclejdk7
|
||||
- openjdk6
|
||||
|
||||
env:
|
||||
- TERM=dumb
|
||||
|
||||
before_install:
|
||||
- chmod +x gradlew
|
||||
|
||||
script:
|
||||
- TERM=dumb ./gradlew clean build dist
|
||||
- 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:
|
||||
- TERM=dumb ./gradlew jacocoTestReport coveralls
|
||||
- ./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
|
||||
|
||||
deploy:
|
||||
provider: bintray
|
||||
edge:
|
||||
branch: v1.8.47
|
||||
file: bintray.json
|
||||
user: skylot
|
||||
key: $BINTRAY_KEY
|
||||
skip_cleanup: true
|
||||
on:
|
||||
branch: master
|
||||
tags: false
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- skylot@gmail.com
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Contribution
|
||||
|
||||
To support this project you can:
|
||||
- Post thoughts about new features/optimizations that important to you
|
||||
- Submit bug using one of following patterns:
|
||||
* Java code examples which decompiles incorrectly
|
||||
* Error log and link to _public available_ apk file or app page on Google play
|
||||
@@ -1,24 +1,33 @@
|
||||
## JADX
|
||||
|
||||
[](https://travis-ci.org/skylot/jadx)
|
||||
[](https://drone.io/github.com/skylot/jadx/latest)
|
||||
[](https://coveralls.io/r/skylot/jadx)
|
||||
[](https://scan.coverity.com/projects/2166)
|
||||
[](https://codecov.io/gh/skylot/jadx)
|
||||
[](https://sonarcloud.io/dashboard?id=jadx)
|
||||
[](http://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
|
||||
**jadx** - Dex to Java decompiler
|
||||
|
||||
Command line and GUI tools for produce Java source code from Android Dex and Apk files
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
### Downloads
|
||||
- [unstable](https://drone.io/github.com/skylot/jadx/files)
|
||||
- from [github](https://github.com/skylot/jadx/releases)
|
||||
- from [sourceforge](http://sourceforge.net/projects/jadx/files/)
|
||||
- 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
|
||||
- `jadx-gui` - graphical version
|
||||
|
||||
On Windows run `.bat` files with double-click\
|
||||
**Note:** ensure you have installed Java 8 64-bit version
|
||||
|
||||
### Building from source
|
||||
Java 8 JDK or higher must be installed:
|
||||
|
||||
git clone https://github.com/skylot/jadx.git
|
||||
cd jadx
|
||||
./gradlew dist
|
||||
@@ -34,7 +43,7 @@ Run **jadx** on itself:
|
||||
|
||||
cd build/jadx/
|
||||
bin/jadx -d out lib/jadx-core-*.jar
|
||||
#or
|
||||
# or
|
||||
bin/jadx-gui lib/jadx-core-*.jar
|
||||
|
||||
|
||||
@@ -42,27 +51,31 @@ Run **jadx** on itself:
|
||||
```
|
||||
jadx[-gui] [options] <input file> (.dex, .apk, .jar or .class)
|
||||
options:
|
||||
-d, --output-dir - output directory
|
||||
-j, --threads-count - processing threads count
|
||||
-r, --no-res - do not decode resources
|
||||
-s, --no-src - do not decompile source code
|
||||
-e, --export-gradle - save as android gradle project
|
||||
--show-bad-code - show inconsistent code (incorrectly decompiled)
|
||||
--no-replace-consts - don't replace constant value with matching constant field
|
||||
--escape-unicode - escape non latin characters in strings (with \u)
|
||||
--deobf - activate deobfuscation
|
||||
--deobf-min - min length of name
|
||||
--deobf-max - max length of name
|
||||
--deobf-rewrite-cfg - force to save deobfuscation map
|
||||
--deobf-use-sourcename - use source file name as class name alias
|
||||
--cfg - save methods control flow graph to dot file
|
||||
--raw-cfg - save methods control flow graph (use raw instructions)
|
||||
-f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
|
||||
-v, --verbose - verbose output
|
||||
-h, --help - print this help
|
||||
-d, --output-dir - output directory
|
||||
-ds, --output-dir-src - output directory for sources
|
||||
-dr, --output-dir-res - output directory for resources
|
||||
-j, --threads-count - processing threads count
|
||||
-r, --no-res - do not decode resources
|
||||
-s, --no-src - do not decompile source code
|
||||
-e, --export-gradle - save as android gradle project
|
||||
--show-bad-code - show inconsistent code (incorrectly decompiled)
|
||||
--no-imports - disable use of imports, always write entire package name
|
||||
--no-replace-consts - don't replace constant value with matching constant field
|
||||
--escape-unicode - escape non latin characters in strings (with \u)
|
||||
--deobf - activate deobfuscation
|
||||
--deobf-min - min length of name
|
||||
--deobf-max - max length of name
|
||||
--deobf-rewrite-cfg - force to save deobfuscation map
|
||||
--deobf-use-sourcename - use source file name as class name alias
|
||||
--cfg - save methods control flow graph to dot file
|
||||
--raw-cfg - save methods control flow graph (use raw instructions)
|
||||
-f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
|
||||
-v, --verbose - verbose output
|
||||
-h, --help - print this help
|
||||
Example:
|
||||
jadx -d out classes.dex
|
||||
```
|
||||
These options also worked on jadx-gui running from command line and override options from preferences dialog
|
||||
|
||||
### Troubleshooting
|
||||
##### Out of memory error:
|
||||
@@ -73,21 +86,7 @@ Example:
|
||||
* edit 'jadx' script (jadx.bat on Windows) and setup bigger heap size:
|
||||
`DEFAULT_JVM_OPTS="-Xmx2500M"`
|
||||
|
||||
|
||||
### Contribution
|
||||
|
||||
To support this project you can:
|
||||
- Post thoughts about new features/optimizations that important to you
|
||||
- Submit bug using one of following patterns:
|
||||
* Java code examples which decompiles incorrectly
|
||||
* Error log and link to _public available_ apk file or app page on Google play
|
||||
|
||||
And any other comments will be very helpfull,
|
||||
because at current stage of development it is very time consuming
|
||||
to **find** new bugs, design and implement new features.
|
||||
Also I need to **prioritize** these task for complete most important at first.
|
||||
|
||||
---------------------------------------
|
||||
*Licensed under the Apache 2.0 License*
|
||||
|
||||
*Copyright 2015 by Skylot*
|
||||
*Copyright 2018 by Skylot*
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
+41
-27
@@ -1,36 +1,33 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.github.kt3k.coveralls" version "2.3.1"
|
||||
id "info.solidsoft.pitest" version "1.1.4"
|
||||
// id "com.github.ben-manes.versions" version "0.8"
|
||||
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'
|
||||
}
|
||||
|
||||
ext.jadxVersion = file('version').readLines().get(0)
|
||||
version = jadxVersion
|
||||
|
||||
subprojects {
|
||||
allprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'groovy'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'com.github.kt3k.coveralls'
|
||||
apply plugin: 'com.github.ksoichiro.console.reporter'
|
||||
|
||||
version = jadxVersion
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_6
|
||||
targetCompatibility = JavaVersion.VERSION_1_6
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
if (!"$it".contains(':jadx-samples:')) {
|
||||
options.compilerArgs << '-Xlint' << '-Xlint:unchecked' << '-Xlint:deprecation'
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
jar {
|
||||
version = jadxVersion
|
||||
manifest {
|
||||
@@ -39,30 +36,53 @@ subprojects {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.slf4j:slf4j-api:1.7.10'
|
||||
compile 'org.slf4j:slf4j-api:1.7.25'
|
||||
|
||||
testCompile 'ch.qos.logback:logback-classic:1.1.2'
|
||||
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:1.10.19'
|
||||
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
|
||||
testCompile 'cglib:cglib-nodep:3.1'
|
||||
testCompile 'org.mockito:mockito-core:2.15.0'
|
||||
testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
|
||||
testCompile 'cglib:cglib-nodep:3.2.6'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion = "0.8.0"
|
||||
}
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.enabled = true // coveralls plugin depends on xml format report
|
||||
xml.enabled = true
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sonarqube {
|
||||
properties {
|
||||
property 'sonar.exclusions', '**/jadx/samples/**/*,**/test-app/**/*'
|
||||
property 'sonar.coverage.exclusions', '**/jadx/gui/**/*'
|
||||
}
|
||||
}
|
||||
|
||||
dependencyUpdates.resolutionStrategy = {
|
||||
componentSelection { rules ->
|
||||
rules.all { ComponentSelection selection ->
|
||||
boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm', 'atlassian'].any { qualifier ->
|
||||
selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
|
||||
}
|
||||
if (rejected) {
|
||||
selection.reject('Release candidate')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task copyArtifacts(type: Sync, dependsOn: ['jadx-cli:installDist', 'jadx-gui:installDist']) {
|
||||
destinationDir file("$buildDir/jadx")
|
||||
['jadx-cli', 'jadx-gui'].each {
|
||||
@@ -86,9 +106,6 @@ task samples(dependsOn: 'jadx-samples:samples') {
|
||||
task testAppCheck(dependsOn: 'jadx-test-app:testAppCheck') {
|
||||
}
|
||||
|
||||
task pitest(overwrite: true, dependsOn: 'jadx-core:pitest') {
|
||||
}
|
||||
|
||||
task cleanBuildDir(type: Delete) {
|
||||
delete buildDir
|
||||
}
|
||||
@@ -97,6 +114,3 @@ build.dependsOn(dist, samples)
|
||||
|
||||
clean.dependsOn(cleanBuildDir)
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '2.7'
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -6,47 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
@@ -90,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@@ -114,6 +113,7 @@ fi
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
@@ -154,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
Vendored
+4
-10
@@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
@@ -5,8 +5,8 @@ applicationName = 'jadx'
|
||||
|
||||
dependencies {
|
||||
compile(project(':jadx-core'))
|
||||
compile 'com.beust:jcommander:1.47'
|
||||
compile 'ch.qos.logback:logback-classic:1.1.2'
|
||||
compile 'com.beust:jcommander:1.72'
|
||||
compile 'ch.qos.logback:logback-classic:1.2.3'
|
||||
}
|
||||
|
||||
applicationDistribution.with {
|
||||
@@ -18,3 +18,6 @@ applicationDistribution.with {
|
||||
}
|
||||
}
|
||||
|
||||
startScripts {
|
||||
defaultJvmOpts = [ '-Xms2g', '-Xmx4g' ]
|
||||
}
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
package jadx.cli;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.exceptions.JadxArgsValidateException;
|
||||
|
||||
public class JadxCLI {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
|
||||
|
||||
public static void main(String[] args) throws JadxException {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
if (processArgs(jadxArgs, args)) {
|
||||
if (jadxArgs.processArgs(args)) {
|
||||
processAndSave(jadxArgs);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
} catch (Exception e) {
|
||||
LOG.error("jadx error: {}", e.getMessage(), e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException {
|
||||
JadxDecompiler jadx = new JadxDecompiler(jadxArgs);
|
||||
jadx.setOutputDir(jadxArgs.getOutDir());
|
||||
jadx.loadFiles(jadxArgs.getInput());
|
||||
static void processAndSave(JadxCLIArgs inputArgs) {
|
||||
JadxDecompiler jadx = new JadxDecompiler(inputArgs.toJadxArgs());
|
||||
try {
|
||||
jadx.load();
|
||||
} catch (JadxArgsValidateException e) {
|
||||
LOG.error("Incorrect arguments: {}", e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
jadx.save();
|
||||
if (jadx.getErrorsCount() != 0) {
|
||||
jadx.printErrorsReport();
|
||||
@@ -35,34 +37,4 @@ public class JadxCLI {
|
||||
LOG.info("done");
|
||||
}
|
||||
}
|
||||
|
||||
static boolean processArgs(JadxCLIArgs jadxArgs, String[] args) throws JadxException {
|
||||
if (!jadxArgs.processArgs(args)) {
|
||||
return false;
|
||||
}
|
||||
if (jadxArgs.getInput().isEmpty()) {
|
||||
LOG.error("Please specify input file");
|
||||
jadxArgs.printUsage();
|
||||
return false;
|
||||
}
|
||||
File outputDir = jadxArgs.getOutDir();
|
||||
if (outputDir == null) {
|
||||
String outDirName;
|
||||
File file = jadxArgs.getInput().get(0);
|
||||
String name = file.getName();
|
||||
int pos = name.lastIndexOf('.');
|
||||
if (pos != -1) {
|
||||
outDirName = name.substring(0, pos);
|
||||
} else {
|
||||
outDirName = name + "-jadx-out";
|
||||
}
|
||||
LOG.info("output directory: {}", outDirName);
|
||||
outputDir = new File(outDirName);
|
||||
jadxArgs.setOutputDir(outputDir);
|
||||
}
|
||||
if (outputDir.exists() && !outputDir.isDirectory()) {
|
||||
throw new JadxException("Output directory exists as file " + outputDir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
package jadx.cli;
|
||||
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.Appender;
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.beust.jcommander.IStringConverter;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.Appender;
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.ParameterDescription;
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class JadxCLIArgs implements IJadxArgs {
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
@Parameter(description = "<input file> (.dex, .apk, .jar or .class)")
|
||||
protected List<String> files;
|
||||
public class JadxCLIArgs {
|
||||
|
||||
@Parameter(description = "<input file> (.apk, .dex, .jar or .class)")
|
||||
protected List<String> files = new ArrayList<>(1);
|
||||
|
||||
@Parameter(names = {"-d", "--output-dir"}, description = "output directory")
|
||||
protected String outDirName;
|
||||
protected String outDir;
|
||||
|
||||
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
||||
protected int threadsCount = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
|
||||
@Parameter(names = {"-ds", "--output-dir-src"}, description = "output directory for sources")
|
||||
protected String outDirSrc;
|
||||
|
||||
@Parameter(names = {"-dr", "--output-dir-res"}, description = "output directory for resources")
|
||||
protected String outDirRes;
|
||||
|
||||
@Parameter(names = {"-r", "--no-res"}, description = "do not decode resources")
|
||||
protected boolean skipResources = false;
|
||||
@@ -43,11 +45,16 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
@Parameter(names = {"-e", "--export-gradle"}, description = "save as android gradle project")
|
||||
protected boolean exportAsGradleProject = false;
|
||||
|
||||
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
||||
protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
|
||||
|
||||
@Parameter(names = {"--show-bad-code"}, description = "show inconsistent code (incorrectly decompiled)")
|
||||
protected boolean showInconsistentCode = false;
|
||||
|
||||
@Parameter(names = "--no-replace-consts", converter = InvertedBooleanConverter.class,
|
||||
description = "don't replace constant value with matching constant field")
|
||||
@Parameter(names = {"--no-imports"}, description = "disable use of imports, always write entire package name")
|
||||
protected boolean useImports = true;
|
||||
|
||||
@Parameter(names = "--no-replace-consts", description = "don't replace constant value with matching constant field")
|
||||
protected boolean replaceConsts = true;
|
||||
|
||||
@Parameter(names = {"--escape-unicode"}, description = "escape non latin characters in strings (with \\u)")
|
||||
@@ -80,19 +87,19 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
@Parameter(names = {"-v", "--verbose"}, description = "verbose output")
|
||||
protected boolean verbose = false;
|
||||
|
||||
@Parameter(names = {"--version"}, description = "print jadx version")
|
||||
protected boolean printVersion = false;
|
||||
|
||||
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
|
||||
protected boolean printHelp = false;
|
||||
|
||||
private final List<File> input = new ArrayList<File>(1);
|
||||
private File outputDir;
|
||||
|
||||
public boolean processArgs(String[] args) {
|
||||
return parse(args) && process();
|
||||
}
|
||||
|
||||
private boolean parse(String[] args) {
|
||||
try {
|
||||
new JCommander(this, args);
|
||||
makeJCommander().parse(args);
|
||||
return true;
|
||||
} catch (ParameterException e) {
|
||||
System.err.println("Arguments parse error: " + e.getMessage());
|
||||
@@ -101,32 +108,24 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
}
|
||||
}
|
||||
|
||||
private JCommander makeJCommander() {
|
||||
return JCommander.newBuilder().addObject(this).build();
|
||||
}
|
||||
|
||||
private boolean process() {
|
||||
if (isPrintHelp()) {
|
||||
if (printHelp) {
|
||||
printUsage();
|
||||
return false;
|
||||
}
|
||||
if (printVersion) {
|
||||
System.out.println(JadxDecompiler.getVersion());
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (threadsCount <= 0) {
|
||||
throw new JadxException("Threads count must be positive");
|
||||
throw new JadxException("Threads count must be positive, got: " + threadsCount);
|
||||
}
|
||||
if (files != null) {
|
||||
for (String fileName : files) {
|
||||
File file = new File(fileName);
|
||||
if (file.exists()) {
|
||||
input.add(file);
|
||||
} else {
|
||||
throw new JadxException("File not found: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (input.size() > 1) {
|
||||
throw new JadxException("Only one input file is supported");
|
||||
}
|
||||
if (outDirName != null) {
|
||||
outputDir = new File(outDirName);
|
||||
}
|
||||
if (isVerbose()) {
|
||||
if (verbose) {
|
||||
ch.qos.logback.classic.Logger rootLogger =
|
||||
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||
// remove INFO ThresholdFilter
|
||||
@@ -144,7 +143,7 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
}
|
||||
|
||||
public void printUsage() {
|
||||
JCommander jc = new JCommander(this);
|
||||
JCommander jc = makeJCommander();
|
||||
// print usage in not sorted fields order (by default its sorted by description)
|
||||
PrintStream out = System.out;
|
||||
out.println();
|
||||
@@ -154,7 +153,7 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
out.println("options:");
|
||||
|
||||
List<ParameterDescription> params = jc.getParameters();
|
||||
Map<String, ParameterDescription> paramsMap = new LinkedHashMap<String, ParameterDescription>(params.size());
|
||||
Map<String, ParameterDescription> paramsMap = new LinkedHashMap<>(params.size());
|
||||
int maxNamesLen = 0;
|
||||
for (ParameterDescription p : params) {
|
||||
paramsMap.put(p.getParameterized().getName(), p);
|
||||
@@ -171,13 +170,13 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
continue;
|
||||
}
|
||||
StringBuilder opt = new StringBuilder();
|
||||
opt.append(' ').append(p.getNames());
|
||||
opt.append(" ").append(p.getNames());
|
||||
addSpaces(opt, maxNamesLen - opt.length() + 2);
|
||||
opt.append("- ").append(p.getDescription());
|
||||
out.println(opt);
|
||||
}
|
||||
out.println("Example:");
|
||||
out.println(" jadx -d out classes.dex");
|
||||
out.println(" jadx -d out classes.dex");
|
||||
}
|
||||
|
||||
private static void addSpaces(StringBuilder str, int count) {
|
||||
@@ -186,106 +185,111 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvertedBooleanConverter implements IStringConverter<Boolean> {
|
||||
@Override
|
||||
public Boolean convert(String value) {
|
||||
return "false".equals(value);
|
||||
}
|
||||
public JadxArgs toJadxArgs() {
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.setInputFiles(files.stream().map(FileUtils::toFile).collect(Collectors.toList()));
|
||||
args.setOutDir(FileUtils.toFile(outDir));
|
||||
args.setOutDirSrc(FileUtils.toFile(outDirSrc));
|
||||
args.setOutDirRes(FileUtils.toFile(outDirRes));
|
||||
args.setThreadsCount(threadsCount);
|
||||
args.setSkipSources(skipSources);
|
||||
args.setSkipResources(skipResources);
|
||||
args.setFallbackMode(fallbackMode);
|
||||
args.setShowInconsistentCode(showInconsistentCode);
|
||||
args.setCfgOutput(cfgOutput);
|
||||
args.setRawCFGOutput(rawCfgOutput);
|
||||
args.setReplaceConsts(replaceConsts);
|
||||
args.setDeobfuscationOn(deobfuscationOn);
|
||||
args.setDeobfuscationForceSave(deobfuscationForceSave);
|
||||
args.setDeobfuscationMinLength(deobfuscationMinLength);
|
||||
args.setDeobfuscationMaxLength(deobfuscationMaxLength);
|
||||
args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias);
|
||||
args.setEscapeUnicode(escapeUnicode);
|
||||
args.setExportAsGradleProject(exportAsGradleProject);
|
||||
args.setUseImports(useImports);
|
||||
return args;
|
||||
}
|
||||
|
||||
public List<File> getInput() {
|
||||
return input;
|
||||
public List<String> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutDir() {
|
||||
return outputDir;
|
||||
public String getOutDir() {
|
||||
return outDir;
|
||||
}
|
||||
|
||||
public void setOutputDir(File outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
public String getOutDirSrc() {
|
||||
return outDirSrc;
|
||||
}
|
||||
|
||||
public boolean isPrintHelp() {
|
||||
return printHelp;
|
||||
public String getOutDirRes() {
|
||||
return outDirRes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipResources() {
|
||||
return skipResources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipSources() {
|
||||
return skipSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadsCount() {
|
||||
return threadsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCFGOutput() {
|
||||
return cfgOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRawCFGOutput() {
|
||||
return rawCfgOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFallbackMode() {
|
||||
return fallbackMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowInconsistentCode() {
|
||||
return showInconsistentCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerbose() {
|
||||
return verbose;
|
||||
public boolean isUseImports() {
|
||||
return useImports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeobfuscationOn() {
|
||||
return deobfuscationOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeobfuscationMinLength() {
|
||||
return deobfuscationMinLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeobfuscationMaxLength() {
|
||||
return deobfuscationMaxLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeobfuscationForceSave() {
|
||||
return deobfuscationForceSave;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useSourceNameAsClassAlias() {
|
||||
public boolean isDeobfuscationUseSourceNameAsAlias() {
|
||||
return deobfuscationUseSourceNameAsAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean escapeUnicode() {
|
||||
return escapeUnicode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEscapeUnicode() {
|
||||
return escapeUnicode;
|
||||
}
|
||||
|
||||
public boolean isCfgOutput() {
|
||||
return cfgOutput;
|
||||
}
|
||||
|
||||
public boolean isRawCfgOutput() {
|
||||
return rawCfgOutput;
|
||||
}
|
||||
|
||||
public boolean isReplaceConsts() {
|
||||
return replaceConsts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExportAsGradleProject() {
|
||||
return exportAsGradleProject;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss} %-5level - %msg%n</pattern>
|
||||
<pattern>%-5level - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
@@ -1,22 +1,40 @@
|
||||
package jadx.cli;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class JadxCLIArgsTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxCLIArgsTest.class);
|
||||
|
||||
@Test
|
||||
public void testInvertedBooleanOption() throws Exception {
|
||||
public void testInvertedBooleanOption() {
|
||||
assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false));
|
||||
assertThat(parse("").isReplaceConsts(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEscapeUnicodeOption() {
|
||||
assertThat(parse("--escape-unicode").isEscapeUnicode(), is(true));
|
||||
assertThat(parse("").isEscapeUnicode(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSrcOption() {
|
||||
assertThat(parse("--no-src").isSkipSources(), is(true));
|
||||
assertThat(parse("-s").isSkipSources(), is(true));
|
||||
assertThat(parse("").isSkipSources(), is(false));
|
||||
}
|
||||
|
||||
private JadxCLIArgs parse(String... args) {
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
boolean res = jadxArgs.processArgs(args);
|
||||
assertThat(res, is(true));
|
||||
LOG.info("Jadx args: {}", jadxArgs.toJadxArgs());
|
||||
return jadxArgs;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-20
@@ -1,28 +1,15 @@
|
||||
ext.jadxClasspath = 'clsp-data/android-5.1.jar'
|
||||
|
||||
apply plugin: "info.solidsoft.pitest"
|
||||
|
||||
dependencies {
|
||||
runtime files(jadxClasspath)
|
||||
|
||||
compile files('lib/dx-1.10.jar')
|
||||
compile 'commons-io:commons-io:2.4'
|
||||
compile 'org.ow2.asm:asm:5.0.3'
|
||||
compile 'com.intellij:annotations:12.0'
|
||||
compile 'uk.com.robust-it:cloning:1.9.2'
|
||||
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'
|
||||
|
||||
testCompile 'org.smali:smali:2.0.3'
|
||||
testCompile 'org.smali:smali:2.2.2'
|
||||
testCompile 'org.smali:baksmali:2.2.2'
|
||||
}
|
||||
|
||||
task packTests(type: Jar) {
|
||||
classifier = 'tests'
|
||||
from sourceSets.test.output
|
||||
}
|
||||
|
||||
pitest {
|
||||
excludedMethods = ['toString']
|
||||
threads = 4
|
||||
enableDefaultIncrementalAnalysis = true
|
||||
outputFormats = ['XML', 'HTML']
|
||||
jvmArgs = ['-Xmx12G']
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,45 +0,0 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface IJadxArgs {
|
||||
File getOutDir();
|
||||
|
||||
int getThreadsCount();
|
||||
|
||||
boolean isCFGOutput();
|
||||
|
||||
boolean isRawCFGOutput();
|
||||
|
||||
boolean isFallbackMode();
|
||||
|
||||
boolean isShowInconsistentCode();
|
||||
|
||||
boolean isVerbose();
|
||||
|
||||
boolean isSkipResources();
|
||||
|
||||
boolean isSkipSources();
|
||||
|
||||
boolean isDeobfuscationOn();
|
||||
|
||||
int getDeobfuscationMinLength();
|
||||
|
||||
int getDeobfuscationMaxLength();
|
||||
|
||||
boolean isDeobfuscationForceSave();
|
||||
|
||||
boolean useSourceNameAsClassAlias();
|
||||
|
||||
boolean escapeUnicode();
|
||||
|
||||
/**
|
||||
* Replace constant values with static final fields with same value
|
||||
*/
|
||||
boolean isReplaceConsts();
|
||||
|
||||
/**
|
||||
* Save as gradle project
|
||||
*/
|
||||
boolean isExportAsGradleProject();
|
||||
}
|
||||
@@ -1,19 +1,33 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JadxArgs implements IJadxArgs {
|
||||
public class JadxArgs {
|
||||
|
||||
private File outDir = new File("jadx-output");
|
||||
private int threadsCount = Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||
public static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
|
||||
|
||||
public static final String DEFAULT_OUT_DIR = "jadx-output";
|
||||
public static final String DEFAULT_SRC_DIR = "sources";
|
||||
public static final String DEFAULT_RES_DIR = "resources";
|
||||
|
||||
private List<File> inputFiles = new ArrayList<>(1);
|
||||
|
||||
private File outDir;
|
||||
private File outDirSrc;
|
||||
private File outDirRes;
|
||||
|
||||
private int threadsCount = DEFAULT_THREADS_COUNT;
|
||||
|
||||
private boolean cfgOutput = false;
|
||||
private boolean rawCFGOutput = false;
|
||||
|
||||
private boolean isVerbose = false;
|
||||
private boolean fallbackMode = false;
|
||||
private boolean showInconsistentCode = false;
|
||||
|
||||
private boolean useImports = true;
|
||||
|
||||
private boolean isSkipResources = false;
|
||||
private boolean isSkipSources = false;
|
||||
|
||||
@@ -28,7 +42,24 @@ public class JadxArgs implements IJadxArgs {
|
||||
private boolean replaceConsts = true;
|
||||
private boolean exportAsGradleProject = false;
|
||||
|
||||
@Override
|
||||
public JadxArgs() {
|
||||
// use default options
|
||||
}
|
||||
|
||||
public void setRootDir(File rootDir) {
|
||||
setOutDir(rootDir);
|
||||
setOutDirSrc(new File(rootDir, DEFAULT_SRC_DIR));
|
||||
setOutDirRes(new File(rootDir, DEFAULT_RES_DIR));
|
||||
}
|
||||
|
||||
public List<File> getInputFiles() {
|
||||
return inputFiles;
|
||||
}
|
||||
|
||||
public void setInputFiles(List<File> inputFiles) {
|
||||
this.inputFiles = inputFiles;
|
||||
}
|
||||
|
||||
public File getOutDir() {
|
||||
return outDir;
|
||||
}
|
||||
@@ -37,7 +68,22 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.outDir = outDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutDirSrc() {
|
||||
return outDirSrc;
|
||||
}
|
||||
|
||||
public void setOutDirSrc(File outDirSrc) {
|
||||
this.outDirSrc = outDirSrc;
|
||||
}
|
||||
|
||||
public File getOutDirRes() {
|
||||
return outDirRes;
|
||||
}
|
||||
|
||||
public void setOutDirRes(File outDirRes) {
|
||||
this.outDirRes = outDirRes;
|
||||
}
|
||||
|
||||
public int getThreadsCount() {
|
||||
return threadsCount;
|
||||
}
|
||||
@@ -46,8 +92,7 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.threadsCount = threadsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCFGOutput() {
|
||||
public boolean isCfgOutput() {
|
||||
return cfgOutput;
|
||||
}
|
||||
|
||||
@@ -55,7 +100,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.cfgOutput = cfgOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRawCFGOutput() {
|
||||
return rawCFGOutput;
|
||||
}
|
||||
@@ -64,7 +108,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.rawCFGOutput = rawCFGOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFallbackMode() {
|
||||
return fallbackMode;
|
||||
}
|
||||
@@ -73,7 +116,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.fallbackMode = fallbackMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowInconsistentCode() {
|
||||
return showInconsistentCode;
|
||||
}
|
||||
@@ -82,16 +124,14 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.showInconsistentCode = showInconsistentCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerbose() {
|
||||
return isVerbose;
|
||||
public boolean isUseImports() {
|
||||
return useImports;
|
||||
}
|
||||
|
||||
public void setVerbose(boolean verbose) {
|
||||
isVerbose = verbose;
|
||||
public void setUseImports(boolean useImports) {
|
||||
this.useImports = useImports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipResources() {
|
||||
return isSkipResources;
|
||||
}
|
||||
@@ -100,7 +140,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
isSkipResources = skipResources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipSources() {
|
||||
return isSkipSources;
|
||||
}
|
||||
@@ -109,7 +148,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
isSkipSources = skipSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeobfuscationOn() {
|
||||
return isDeobfuscationOn;
|
||||
}
|
||||
@@ -118,7 +156,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
isDeobfuscationOn = deobfuscationOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeobfuscationForceSave() {
|
||||
return isDeobfuscationForceSave;
|
||||
}
|
||||
@@ -127,8 +164,7 @@ public class JadxArgs implements IJadxArgs {
|
||||
isDeobfuscationForceSave = deobfuscationForceSave;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useSourceNameAsClassAlias() {
|
||||
public boolean isUseSourceNameAsClassAlias() {
|
||||
return useSourceNameAsClassAlias;
|
||||
}
|
||||
|
||||
@@ -136,7 +172,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.useSourceNameAsClassAlias = useSourceNameAsClassAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeobfuscationMinLength() {
|
||||
return deobfuscationMinLength;
|
||||
}
|
||||
@@ -145,7 +180,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.deobfuscationMinLength = deobfuscationMinLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeobfuscationMaxLength() {
|
||||
return deobfuscationMaxLength;
|
||||
}
|
||||
@@ -154,8 +188,7 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.deobfuscationMaxLength = deobfuscationMaxLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean escapeUnicode() {
|
||||
public boolean isEscapeUnicode() {
|
||||
return escapeUnicode;
|
||||
}
|
||||
|
||||
@@ -163,7 +196,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.escapeUnicode = escapeUnicode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplaceConsts() {
|
||||
return replaceConsts;
|
||||
}
|
||||
@@ -172,7 +204,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.replaceConsts = replaceConsts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExportAsGradleProject() {
|
||||
return exportAsGradleProject;
|
||||
}
|
||||
@@ -180,4 +211,31 @@ public class JadxArgs implements IJadxArgs {
|
||||
public void setExportAsGradleProject(boolean exportAsGradleProject) {
|
||||
this.exportAsGradleProject = exportAsGradleProject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("JadxArgs{");
|
||||
sb.append("inputFiles=").append(inputFiles);
|
||||
sb.append(", outDir=").append(outDir);
|
||||
sb.append(", outDirSrc=").append(outDirSrc);
|
||||
sb.append(", outDirRes=").append(outDirRes);
|
||||
sb.append(", threadsCount=").append(threadsCount);
|
||||
sb.append(", cfgOutput=").append(cfgOutput);
|
||||
sb.append(", rawCFGOutput=").append(rawCFGOutput);
|
||||
sb.append(", fallbackMode=").append(fallbackMode);
|
||||
sb.append(", showInconsistentCode=").append(showInconsistentCode);
|
||||
sb.append(", useImports=").append(useImports);
|
||||
sb.append(", isSkipResources=").append(isSkipResources);
|
||||
sb.append(", isSkipSources=").append(isSkipSources);
|
||||
sb.append(", isDeobfuscationOn=").append(isDeobfuscationOn);
|
||||
sb.append(", isDeobfuscationForceSave=").append(isDeobfuscationForceSave);
|
||||
sb.append(", useSourceNameAsClassAlias=").append(useSourceNameAsClassAlias);
|
||||
sb.append(", deobfuscationMinLength=").append(deobfuscationMinLength);
|
||||
sb.append(", deobfuscationMaxLength=").append(deobfuscationMaxLength);
|
||||
sb.append(", escapeUnicode=").append(escapeUnicode);
|
||||
sb.append(", replaceConsts=").append(replaceConsts);
|
||||
sb.append(", exportAsGradleProject=").append(exportAsGradleProject);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxArgsValidateException;
|
||||
|
||||
public class JadxArgsValidator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidator.class);
|
||||
|
||||
public static void validate(JadxArgs args) {
|
||||
checkInputFiles(args);
|
||||
validateOutDirs(args);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Effective jadx args: {}", args);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkInputFiles(JadxArgs args) {
|
||||
List<File> inputFiles = args.getInputFiles();
|
||||
if (inputFiles.isEmpty()) {
|
||||
throw new JadxArgsValidateException("Please specify input file");
|
||||
}
|
||||
if (inputFiles.size() > 1) {
|
||||
for (File inputFile : inputFiles) {
|
||||
String fileName = inputFile.getName();
|
||||
if (fileName.startsWith("--")) {
|
||||
throw new JadxArgsValidateException("Unknown argument: " + fileName);
|
||||
}
|
||||
}
|
||||
throw new JadxArgsValidateException("Only one input file supported");
|
||||
}
|
||||
for (File file : inputFiles) {
|
||||
checkFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateOutDirs(JadxArgs args) {
|
||||
File outDir = args.getOutDir();
|
||||
File srcDir = args.getOutDirSrc();
|
||||
File resDir = args.getOutDirRes();
|
||||
if (outDir == null) {
|
||||
if (srcDir != null) {
|
||||
outDir = srcDir;
|
||||
} else if (resDir != null) {
|
||||
outDir = resDir;
|
||||
} else {
|
||||
outDir = makeDirFromInput(args);
|
||||
}
|
||||
}
|
||||
args.setOutDir(outDir);
|
||||
setFromOut(args);
|
||||
|
||||
checkDir(args.getOutDir());
|
||||
checkDir(args.getOutDirSrc());
|
||||
checkDir(args.getOutDirRes());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File makeDirFromInput(JadxArgs args) {
|
||||
File outDir;
|
||||
String outDirName;
|
||||
File file = args.getInputFiles().get(0);
|
||||
String name = file.getName();
|
||||
int pos = name.lastIndexOf('.');
|
||||
if (pos != -1) {
|
||||
outDirName = name.substring(0, pos);
|
||||
} else {
|
||||
outDirName = name + "-" + JadxArgs.DEFAULT_OUT_DIR;
|
||||
}
|
||||
LOG.info("output directory: {}", outDirName);
|
||||
outDir = new File(outDirName);
|
||||
return outDir;
|
||||
}
|
||||
|
||||
private static void setFromOut(JadxArgs args) {
|
||||
if (args.getOutDirSrc() == null) {
|
||||
args.setOutDirSrc(new File(args.getOutDir(), JadxArgs.DEFAULT_SRC_DIR));
|
||||
}
|
||||
if (args.getOutDirRes() == null) {
|
||||
args.setOutDirRes(new File(args.getOutDir(), JadxArgs.DEFAULT_RES_DIR));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkFile(File file) {
|
||||
if (!file.exists()) {
|
||||
throw new JadxArgsValidateException("File not found " + file.getAbsolutePath());
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
throw new JadxArgsValidateException("Expected file but found directory instead: " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkDir(File dir) {
|
||||
if (dir != null && dir.exists() && !dir.isDirectory()) {
|
||||
throw new JadxArgsValidateException("Output directory exists as file " + dir);
|
||||
}
|
||||
}
|
||||
|
||||
private JadxArgsValidator() {
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,20 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.ProcessClass;
|
||||
import jadx.core.codegen.CodeGen;
|
||||
@@ -11,38 +26,24 @@ import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.dex.visitors.SaveCode;
|
||||
import jadx.core.export.ExportGradleProject;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.xmlgen.BinaryXMLParser;
|
||||
import jadx.core.xmlgen.ResourcesSaver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Jadx API usage example:
|
||||
* <pre><code>
|
||||
* JadxDecompiler jadx = new JadxDecompiler();
|
||||
* jadx.loadFile(new File("classes.dex"));
|
||||
* jadx.setOutputDir(new File("out"));
|
||||
* jadx.save();
|
||||
* JadxArgs args = new JadxArgs();
|
||||
* args.getInputFiles().add(new File("test.apk"));
|
||||
* args.setOutDir(new File("jadx-test-output"));
|
||||
*
|
||||
* JadxDecompiler jadx = new JadxDecompiler(args);
|
||||
* jadx.load();
|
||||
* jadx.save();
|
||||
* </code></pre>
|
||||
* <p/>
|
||||
* Instead of 'save()' you can get list of decompiled classes:
|
||||
* Instead of 'save()' you can iterate over decompiled classes:
|
||||
* <pre><code>
|
||||
* for(JavaClass cls : jadx.getClasses()) {
|
||||
* System.out.println(cls.getCode());
|
||||
@@ -52,10 +53,9 @@ import org.slf4j.LoggerFactory;
|
||||
public final class JadxDecompiler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
|
||||
|
||||
private final IJadxArgs args;
|
||||
private final List<InputFile> inputFiles = new ArrayList<InputFile>();
|
||||
private JadxArgs args;
|
||||
|
||||
private File outDir;
|
||||
private final List<InputFile> inputFiles = new ArrayList<>();
|
||||
|
||||
private RootNode root;
|
||||
private List<IDexTreeVisitor> passes;
|
||||
@@ -66,32 +66,39 @@ public final class JadxDecompiler {
|
||||
|
||||
private BinaryXMLParser xmlParser;
|
||||
|
||||
private Map<ClassNode, JavaClass> classesMap = new HashMap<ClassNode, JavaClass>();
|
||||
private Map<MethodNode, JavaMethod> methodsMap = new HashMap<MethodNode, JavaMethod>();
|
||||
private Map<FieldNode, JavaField> fieldsMap = new HashMap<FieldNode, JavaField>();
|
||||
private Map<ClassNode, JavaClass> classesMap = new ConcurrentHashMap<>();
|
||||
private Map<MethodNode, JavaMethod> methodsMap = new ConcurrentHashMap<>();
|
||||
private Map<FieldNode, JavaField> fieldsMap = new ConcurrentHashMap<>();
|
||||
|
||||
public JadxDecompiler() {
|
||||
this(new JadxArgs());
|
||||
}
|
||||
|
||||
public JadxDecompiler(IJadxArgs jadxArgs) {
|
||||
this.args = jadxArgs;
|
||||
this.outDir = jadxArgs.getOutDir();
|
||||
reset();
|
||||
init();
|
||||
public JadxDecompiler(JadxArgs args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public void setOutputDir(File outDir) {
|
||||
this.outDir = outDir;
|
||||
public void load() {
|
||||
reset();
|
||||
JadxArgsValidator.validate(args);
|
||||
init();
|
||||
LOG.info("loading ...");
|
||||
|
||||
loadFiles(args.getInputFiles());
|
||||
|
||||
root = new RootNode(args);
|
||||
root.load(inputFiles);
|
||||
|
||||
root.initClassPath();
|
||||
root.loadResources(getResources());
|
||||
root.initAppResClass();
|
||||
|
||||
initVisitors();
|
||||
}
|
||||
|
||||
void init() {
|
||||
if (outDir == null) {
|
||||
outDir = new JadxArgs().getOutDir();
|
||||
}
|
||||
this.passes = Jadx.getPassesList(args, outDir);
|
||||
this.codeGen = new CodeGen(args);
|
||||
this.passes = Jadx.getPassesList(args);
|
||||
this.codeGen = new CodeGen();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
@@ -107,23 +114,18 @@ public final class JadxDecompiler {
|
||||
return Jadx.getVersion();
|
||||
}
|
||||
|
||||
public void loadFile(File file) throws JadxException {
|
||||
loadFiles(Collections.singletonList(file));
|
||||
}
|
||||
|
||||
public void loadFiles(List<File> files) throws JadxException {
|
||||
private void loadFiles(List<File> files) {
|
||||
if (files.isEmpty()) {
|
||||
throw new JadxException("Empty file list");
|
||||
throw new JadxRuntimeException("Empty file list");
|
||||
}
|
||||
inputFiles.clear();
|
||||
for (File file : files) {
|
||||
try {
|
||||
InputFile.addFilesFrom(file, inputFiles);
|
||||
} catch (IOException e) {
|
||||
throw new JadxException("Error load file: " + file, e);
|
||||
InputFile.addFilesFrom(file, inputFiles, args.isSkipSources());
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Error load file: " + file, e);
|
||||
}
|
||||
}
|
||||
parse();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
@@ -139,12 +141,13 @@ public final class JadxDecompiler {
|
||||
}
|
||||
|
||||
private void save(boolean saveSources, boolean saveResources) {
|
||||
ExecutorService ex = getSaveExecutor(saveSources, saveResources);
|
||||
ex.shutdown();
|
||||
try {
|
||||
ExecutorService ex = getSaveExecutor(saveSources, saveResources);
|
||||
ex.shutdown();
|
||||
ex.awaitTermination(1, TimeUnit.DAYS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new JadxRuntimeException("Save interrupted", e);
|
||||
LOG.error("Save interrupted", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,13 +168,13 @@ public final class JadxDecompiler {
|
||||
File sourcesOutDir;
|
||||
File resOutDir;
|
||||
if (args.isExportAsGradleProject()) {
|
||||
ExportGradleProject export = new ExportGradleProject(root, outDir);
|
||||
ExportGradleProject export = new ExportGradleProject(root, args.getOutDir());
|
||||
export.init();
|
||||
sourcesOutDir = export.getSrcOutDir();
|
||||
resOutDir = export.getResOutDir();
|
||||
} else {
|
||||
sourcesOutDir = outDir;
|
||||
resOutDir = outDir;
|
||||
sourcesOutDir = args.getOutDirSrc();
|
||||
resOutDir = args.getOutDirRes();
|
||||
}
|
||||
if (saveSources) {
|
||||
appendSourcesSave(executor, sourcesOutDir);
|
||||
@@ -188,17 +191,14 @@ public final class JadxDecompiler {
|
||||
}
|
||||
}
|
||||
|
||||
private void appendSourcesSave(ExecutorService executor, final File outDir) {
|
||||
for (final JavaClass cls : getClasses()) {
|
||||
private void appendSourcesSave(ExecutorService executor, File outDir) {
|
||||
for (JavaClass cls : getClasses()) {
|
||||
if (cls.getClassNode().contains(AFlag.DONT_GENERATE)) {
|
||||
continue;
|
||||
}
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cls.decompile();
|
||||
SaveCode.save(outDir, args, cls.getClassNode());
|
||||
}
|
||||
executor.execute(() -> {
|
||||
cls.decompile();
|
||||
SaveCode.save(outDir, args, cls.getClassNode());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ public final class JadxDecompiler {
|
||||
}
|
||||
if (classes == null) {
|
||||
List<ClassNode> classNodeList = root.getClasses(false);
|
||||
List<JavaClass> clsList = new ArrayList<JavaClass>(classNodeList.size());
|
||||
List<JavaClass> clsList = new ArrayList<>(classNodeList.size());
|
||||
classesMap.clear();
|
||||
for (ClassNode classNode : classNodeList) {
|
||||
JavaClass javaClass = new JavaClass(classNode, this);
|
||||
@@ -236,28 +236,19 @@ public final class JadxDecompiler {
|
||||
if (classList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<String, List<JavaClass>> map = new HashMap<String, List<JavaClass>>();
|
||||
Map<String, List<JavaClass>> map = new HashMap<>();
|
||||
for (JavaClass javaClass : classList) {
|
||||
String pkg = javaClass.getPackage();
|
||||
List<JavaClass> clsList = map.get(pkg);
|
||||
if (clsList == null) {
|
||||
clsList = new ArrayList<JavaClass>();
|
||||
map.put(pkg, clsList);
|
||||
}
|
||||
List<JavaClass> clsList = map.computeIfAbsent(pkg, k -> new ArrayList<>());
|
||||
clsList.add(javaClass);
|
||||
}
|
||||
List<JavaPackage> packages = new ArrayList<JavaPackage>(map.size());
|
||||
List<JavaPackage> packages = new ArrayList<>(map.size());
|
||||
for (Map.Entry<String, List<JavaClass>> entry : map.entrySet()) {
|
||||
packages.add(new JavaPackage(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
Collections.sort(packages);
|
||||
for (JavaPackage pkg : packages) {
|
||||
Collections.sort(pkg.getClasses(), new Comparator<JavaClass>() {
|
||||
@Override
|
||||
public int compare(JavaClass o1, JavaClass o2) {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
pkg.getClasses().sort(Comparator.comparing(JavaClass::getName));
|
||||
}
|
||||
return Collections.unmodifiableList(packages);
|
||||
}
|
||||
@@ -277,21 +268,6 @@ public final class JadxDecompiler {
|
||||
root.getErrorsCounter().printReport();
|
||||
}
|
||||
|
||||
void parse() throws DecodeException {
|
||||
reset();
|
||||
init();
|
||||
|
||||
root = new RootNode(args);
|
||||
LOG.info("loading ...");
|
||||
root.load(inputFiles);
|
||||
|
||||
root.initClassPath();
|
||||
root.loadResources(getResources());
|
||||
root.initAppResClass();
|
||||
|
||||
initVisitors();
|
||||
}
|
||||
|
||||
private void initVisitors() {
|
||||
for (IDexTreeVisitor pass : passes) {
|
||||
try {
|
||||
@@ -329,7 +305,7 @@ public final class JadxDecompiler {
|
||||
return fieldsMap;
|
||||
}
|
||||
|
||||
public IJadxArgs getArgs() {
|
||||
public JadxArgs getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -337,5 +313,4 @@ public final class JadxDecompiler {
|
||||
public String toString() {
|
||||
return "jadx decompiler " + getVersion();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public final class JavaClass implements JavaNode {
|
||||
JadxDecompiler rootDecompiler = getRootDecompiler();
|
||||
int inClsCount = cls.getInnerClasses().size();
|
||||
if (inClsCount != 0) {
|
||||
List<JavaClass> list = new ArrayList<JavaClass>(inClsCount);
|
||||
List<JavaClass> list = new ArrayList<>(inClsCount);
|
||||
for (ClassNode inner : cls.getInnerClasses()) {
|
||||
if (!inner.contains(AFlag.DONT_GENERATE)) {
|
||||
JavaClass javaClass = new JavaClass(inner, this);
|
||||
@@ -86,7 +86,7 @@ public final class JavaClass implements JavaNode {
|
||||
|
||||
int fieldsCount = cls.getFields().size();
|
||||
if (fieldsCount != 0) {
|
||||
List<JavaField> flds = new ArrayList<JavaField>(fieldsCount);
|
||||
List<JavaField> flds = new ArrayList<>(fieldsCount);
|
||||
for (FieldNode f : cls.getFields()) {
|
||||
if (!f.contains(AFlag.DONT_GENERATE)) {
|
||||
JavaField javaField = new JavaField(f, this);
|
||||
@@ -99,7 +99,7 @@ public final class JavaClass implements JavaNode {
|
||||
|
||||
int methodsCount = cls.getMethods().size();
|
||||
if (methodsCount != 0) {
|
||||
List<JavaMethod> mths = new ArrayList<JavaMethod>(methodsCount);
|
||||
List<JavaMethod> mths = new ArrayList<>(methodsCount);
|
||||
for (MethodNode m : cls.getMethods()) {
|
||||
if (!m.contains(AFlag.DONT_GENERATE)) {
|
||||
JavaMethod javaMethod = new JavaMethod(this, m);
|
||||
@@ -134,7 +134,7 @@ public final class JavaClass implements JavaNode {
|
||||
if (map.isEmpty() || decompiler == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<CodePosition, JavaNode> resultMap = new HashMap<CodePosition, JavaNode>(map.size());
|
||||
Map<CodePosition, JavaNode> resultMap = new HashMap<>(map.size());
|
||||
for (Map.Entry<CodePosition, Object> entry : map.entrySet()) {
|
||||
CodePosition codePosition = entry.getKey();
|
||||
Object obj = entry.getValue();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.api;
|
||||
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
|
||||
import java.io.File;
|
||||
@@ -34,7 +35,7 @@ public class ResourceFile {
|
||||
private final ResourceType type;
|
||||
private ZipRef zipRef;
|
||||
|
||||
ResourceFile(JadxDecompiler decompiler, String name, ResourceType type) {
|
||||
protected ResourceFile(JadxDecompiler decompiler, String name, ResourceType type) {
|
||||
this.decompiler = decompiler;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
@@ -56,7 +57,7 @@ public class ResourceFile {
|
||||
this.zipRef = zipRef;
|
||||
}
|
||||
|
||||
ZipRef getZipRef() {
|
||||
public ZipRef getZipRef() {
|
||||
return zipRef;
|
||||
}
|
||||
|
||||
@@ -64,4 +65,11 @@ public class ResourceFile {
|
||||
public String toString() {
|
||||
return "ResourceFile{name='" + name + '\'' + ", type=" + type + "}";
|
||||
}
|
||||
|
||||
public static ResourceFile createResourceFileInstance(JadxDecompiler decompiler, String name, ResourceType type) {
|
||||
if(!ZipSecurity.isValidZipEntryName(name)) {
|
||||
return null;
|
||||
}
|
||||
return new ResourceFile(decompiler, name, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package jadx.api;
|
||||
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
|
||||
public class ResourceFileContent extends ResourceFile {
|
||||
|
||||
private final CodeWriter content;
|
||||
|
||||
public ResourceFileContent(String name, ResourceType type, CodeWriter content) {
|
||||
private ResourceFileContent(String name, ResourceType type, CodeWriter content) {
|
||||
super(null, name, type);
|
||||
this.content = content;
|
||||
}
|
||||
@@ -16,4 +17,11 @@ public class ResourceFileContent extends ResourceFile {
|
||||
public ResContainer loadContent() {
|
||||
return ResContainer.singleFile(getName(), content);
|
||||
}
|
||||
|
||||
public static ResourceFileContent createResourceFileContentInstance(String name, ResourceType type, CodeWriter content) {
|
||||
if(!ZipSecurity.isValidZipEntryName(name)) {
|
||||
return null;
|
||||
}
|
||||
return new ResourceFileContent(name, type, content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
package jadx.api;
|
||||
|
||||
import jadx.api.ResourceFile.ZipRef;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
@@ -22,8 +15,16 @@ import java.util.zip.ZipFile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ResourceFile.ZipRef;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.READ_BUFFER_SIZE;
|
||||
import static jadx.core.utils.files.FileUtils.close;
|
||||
import static jadx.core.utils.files.FileUtils.copyStream;
|
||||
|
||||
// TODO: move to core package
|
||||
@@ -39,7 +40,7 @@ public final class ResourcesLoader {
|
||||
}
|
||||
|
||||
List<ResourceFile> load(List<InputFile> inputFiles) {
|
||||
List<ResourceFile> list = new ArrayList<ResourceFile>(inputFiles.size());
|
||||
List<ResourceFile> list = new ArrayList<>(inputFiles.size());
|
||||
for (InputFile file : inputFiles) {
|
||||
loadFile(list, file.getFile());
|
||||
}
|
||||
@@ -51,44 +52,35 @@ public final class ResourcesLoader {
|
||||
}
|
||||
|
||||
public static ResContainer decodeStream(ResourceFile rf, ResourceDecoder decoder) throws JadxException {
|
||||
ZipRef zipRef = rf.getZipRef();
|
||||
if (zipRef == null) {
|
||||
return null;
|
||||
}
|
||||
ZipFile zipFile = null;
|
||||
InputStream inputStream = null;
|
||||
ResContainer result = null;
|
||||
try {
|
||||
zipFile = new ZipFile(zipRef.getZipFile());
|
||||
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
|
||||
if (entry == null) {
|
||||
throw new IOException("Zip entry not found: " + zipRef);
|
||||
}
|
||||
inputStream = new BufferedInputStream(zipFile.getInputStream(entry));
|
||||
result = decoder.decode(entry.getSize(), inputStream);
|
||||
} catch (Exception e) {
|
||||
throw new JadxException("Error decode: " + zipRef.getEntryName(), e);
|
||||
} finally {
|
||||
try {
|
||||
if (zipFile != null) {
|
||||
zipFile.close();
|
||||
ZipRef zipRef = rf.getZipRef();
|
||||
if (zipRef == null) {
|
||||
File file = new File(rf.getName());
|
||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
|
||||
return decoder.decode(file.length(), inputStream);
|
||||
}
|
||||
} else {
|
||||
try (ZipFile zipFile = new ZipFile(zipRef.getZipFile())) {
|
||||
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
|
||||
if (entry == null) {
|
||||
throw new IOException("Zip entry not found: " + zipRef);
|
||||
}
|
||||
if (!ZipSecurity.isValidZipEntry(entry)) {
|
||||
return null;
|
||||
}
|
||||
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(entry))) {
|
||||
return decoder.decode(entry.getSize(), inputStream);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error close zip file: {}", zipRef, e);
|
||||
}
|
||||
close(inputStream);
|
||||
} catch (Exception e) {
|
||||
throw new JadxException("Error decode: " + rf.getName(), e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ResContainer loadContent(final JadxDecompiler jadxRef, final ResourceFile rf) {
|
||||
static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) {
|
||||
try {
|
||||
return decodeStream(rf, new ResourceDecoder() {
|
||||
@Override
|
||||
public ResContainer decode(long size, InputStream is) throws IOException {
|
||||
return loadContent(jadxRef, rf, is, size);
|
||||
}
|
||||
});
|
||||
return decodeStream(rf, (size, is) -> loadContent(jadxRef, rf, is, size));
|
||||
} catch (JadxException e) {
|
||||
LOG.error("Decode error", e);
|
||||
CodeWriter cw = new CodeWriter();
|
||||
@@ -99,7 +91,7 @@ public final class ResourcesLoader {
|
||||
}
|
||||
|
||||
private static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf,
|
||||
InputStream inputStream, long size) throws IOException {
|
||||
InputStream inputStream, long size) throws IOException {
|
||||
switch (rf.getType()) {
|
||||
case MANIFEST:
|
||||
case XML:
|
||||
@@ -111,36 +103,40 @@ public final class ResourcesLoader {
|
||||
|
||||
case IMG:
|
||||
return ResContainer.singleImageFile(rf.getName(), inputStream);
|
||||
|
||||
default:
|
||||
if (size > LOAD_SIZE_LIMIT) {
|
||||
return ResContainer.singleFile(rf.getName(),
|
||||
new CodeWriter().add("File too big, size: " + String.format("%.2f KB", size / 1024.)));
|
||||
}
|
||||
return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream));
|
||||
}
|
||||
if (size > LOAD_SIZE_LIMIT) {
|
||||
return ResContainer.singleFile(rf.getName(),
|
||||
new CodeWriter().add("File too big, size: " + String.format("%.2f KB", size / 1024.)));
|
||||
}
|
||||
return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream));
|
||||
}
|
||||
|
||||
private void loadFile(List<ResourceFile> list, File file) {
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
ZipFile zip = null;
|
||||
try {
|
||||
zip = new ZipFile(file);
|
||||
try (ZipFile zip = new ZipFile(file)) {
|
||||
Enumeration<? extends ZipEntry> entries = zip.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = entries.nextElement();
|
||||
addEntry(list, file, entry);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Not a zip file: {}", file.getAbsolutePath());
|
||||
} finally {
|
||||
if (zip != null) {
|
||||
try {
|
||||
zip.close();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Zip file close error: {}", file.getAbsolutePath(), e);
|
||||
if (ZipSecurity.isValidZipEntry(entry)) {
|
||||
addEntry(list, file, entry);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.debug("Not a zip file: {}", file.getAbsolutePath());
|
||||
addResourceFile(list, file);
|
||||
}
|
||||
}
|
||||
|
||||
private void addResourceFile(List<ResourceFile> list, File file) {
|
||||
String name = file.getAbsolutePath();
|
||||
ResourceType type = ResourceType.getFileType(name);
|
||||
ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type);
|
||||
if (rf != null) {
|
||||
list.add(rf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +146,11 @@ public final class ResourcesLoader {
|
||||
}
|
||||
String name = entry.getName();
|
||||
ResourceType type = ResourceType.getFileType(name);
|
||||
ResourceFile rf = new ResourceFile(jadxRef, name, type);
|
||||
rf.setZipRef(new ZipRef(zipFile, name));
|
||||
list.add(rf);
|
||||
ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type);
|
||||
if (rf != null) {
|
||||
rf.setZipRef(new ZipRef(zipFile, name));
|
||||
list.add(rf);
|
||||
}
|
||||
}
|
||||
|
||||
public static CodeWriter loadToCodeWriter(InputStream is) throws IOException {
|
||||
|
||||
@@ -21,4 +21,7 @@ public class Consts {
|
||||
public static final String ANONYMOUS_CLASS_PREFIX = "AnonymousClass";
|
||||
|
||||
public static final String MTH_TOSTRING_SIGNATURE = "toString()Ljava/lang/String;";
|
||||
|
||||
private Consts() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
package jadx.core;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.visitors.ClassModifier;
|
||||
import jadx.core.dex.visitors.CodeShrinker;
|
||||
import jadx.core.dex.visitors.ConstInlineVisitor;
|
||||
@@ -33,27 +42,20 @@ import jadx.core.dex.visitors.ssa.SSATransform;
|
||||
import jadx.core.dex.visitors.typeinference.FinishTypeInference;
|
||||
import jadx.core.dex.visitors.typeinference.TypeInference;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Jadx {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Jadx.class);
|
||||
|
||||
private Jadx() {
|
||||
}
|
||||
|
||||
static {
|
||||
if (Consts.DEBUG) {
|
||||
LOG.info("debug enabled");
|
||||
}
|
||||
}
|
||||
|
||||
public static List<IDexTreeVisitor> getPassesList(IJadxArgs args, File outDir) {
|
||||
List<IDexTreeVisitor> passes = new ArrayList<IDexTreeVisitor>();
|
||||
public static List<IDexTreeVisitor> getPassesList(JadxArgs args) {
|
||||
List<IDexTreeVisitor> passes = new ArrayList<>();
|
||||
if (args.isFallbackMode()) {
|
||||
passes.add(new FallbackModeVisitor());
|
||||
} else {
|
||||
@@ -68,7 +70,7 @@ public class Jadx {
|
||||
passes.add(new TypeInference());
|
||||
|
||||
if (args.isRawCFGOutput()) {
|
||||
passes.add(DotGraphVisitor.dumpRaw(outDir));
|
||||
passes.add(DotGraphVisitor.dumpRaw());
|
||||
}
|
||||
|
||||
passes.add(new ConstInlineVisitor());
|
||||
@@ -80,8 +82,8 @@ public class Jadx {
|
||||
passes.add(new CodeShrinker());
|
||||
passes.add(new ReSugarCode());
|
||||
|
||||
if (args.isCFGOutput()) {
|
||||
passes.add(DotGraphVisitor.dump(outDir));
|
||||
if (args.isCfgOutput()) {
|
||||
passes.add(DotGraphVisitor.dump());
|
||||
}
|
||||
|
||||
passes.add(new RegionMakerVisitor());
|
||||
@@ -92,8 +94,8 @@ public class Jadx {
|
||||
passes.add(new SimplifyVisitor());
|
||||
passes.add(new CheckRegions());
|
||||
|
||||
if (args.isCFGOutput()) {
|
||||
passes.add(DotGraphVisitor.dumpRegions(outDir));
|
||||
if (args.isCfgOutput()) {
|
||||
passes.add(DotGraphVisitor.dumpRegions());
|
||||
}
|
||||
|
||||
passes.add(new MethodInlineVisitor());
|
||||
|
||||
@@ -9,8 +9,6 @@ import jadx.core.utils.ErrorsCounter;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static jadx.core.dex.nodes.ProcessState.GENERATED;
|
||||
import static jadx.core.dex.nodes.ProcessState.NOT_LOADED;
|
||||
@@ -19,7 +17,6 @@ import static jadx.core.dex.nodes.ProcessState.STARTED;
|
||||
import static jadx.core.dex.nodes.ProcessState.UNLOADED;
|
||||
|
||||
public final class ProcessClass {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ProcessClass.class);
|
||||
|
||||
private ProcessClass() {
|
||||
}
|
||||
@@ -28,7 +25,7 @@ public final class ProcessClass {
|
||||
if (codeGen == null && cls.getState() == PROCESSED) {
|
||||
return;
|
||||
}
|
||||
synchronized (cls) {
|
||||
synchronized (cls.getClassInfo()) {
|
||||
try {
|
||||
if (cls.getState() == NOT_LOADED) {
|
||||
cls.load();
|
||||
@@ -54,7 +51,7 @@ public final class ProcessClass {
|
||||
}
|
||||
}
|
||||
|
||||
static void processDependencies(ClassNode cls, List<IDexTreeVisitor> passes) {
|
||||
private static void processDependencies(ClassNode cls, List<IDexTreeVisitor> passes) {
|
||||
for (ClassNode depCls : cls.getDependencies()) {
|
||||
process(depCls, passes, null);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ 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;
|
||||
@@ -48,7 +49,7 @@ public class ClsSet {
|
||||
|
||||
public void load(RootNode root) {
|
||||
List<ClassNode> list = root.getClasses(true);
|
||||
Map<String, NClass> names = new HashMap<String, NClass>(list.size());
|
||||
Map<String, NClass> names = new HashMap<>(list.size());
|
||||
int k = 0;
|
||||
for (ClassNode cls : list) {
|
||||
String clsRawName = cls.getRawName();
|
||||
@@ -78,7 +79,7 @@ public class ClsSet {
|
||||
}
|
||||
|
||||
public static NClass[] makeParentsArray(ClassNode cls, Map<String, NClass> names) {
|
||||
List<NClass> parents = new ArrayList<NClass>(1 + cls.getInterfaces().size());
|
||||
List<NClass> parents = new ArrayList<>(1 + cls.getInterfaces().size());
|
||||
ArgType superClass = cls.getSuperClass();
|
||||
if (superClass != null) {
|
||||
NClass c = getCls(superClass.getObject(), names);
|
||||
@@ -105,9 +106,7 @@ public class ClsSet {
|
||||
|
||||
void save(File output) throws IOException {
|
||||
FileUtils.makeDirsForFile(output);
|
||||
|
||||
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
|
||||
try {
|
||||
try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output))) {
|
||||
String outputName = output.getName();
|
||||
if (outputName.endsWith(CLST_EXTENSION)) {
|
||||
save(outputStream);
|
||||
@@ -122,14 +121,11 @@ public class ClsSet {
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + outputName);
|
||||
}
|
||||
} finally {
|
||||
close(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(OutputStream output) throws IOException {
|
||||
DataOutputStream out = new DataOutputStream(output);
|
||||
try {
|
||||
try (DataOutputStream out = new DataOutputStream(output)) {
|
||||
out.writeBytes(JADX_CLS_SET_HEADER);
|
||||
out.writeByte(VERSION);
|
||||
|
||||
@@ -145,53 +141,41 @@ public class ClsSet {
|
||||
out.writeInt(parent.getId());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
close(out);
|
||||
}
|
||||
}
|
||||
|
||||
public void load() throws IOException, DecodeException {
|
||||
InputStream input = getClass().getResourceAsStream(CLST_FILENAME);
|
||||
if (input == null) {
|
||||
throw new JadxRuntimeException("Can't load classpath file: " + CLST_FILENAME);
|
||||
}
|
||||
try {
|
||||
try (InputStream input = getClass().getResourceAsStream(CLST_FILENAME)) {
|
||||
if (input == null) {
|
||||
throw new JadxRuntimeException("Can't load classpath file: " + CLST_FILENAME);
|
||||
}
|
||||
load(input);
|
||||
} finally {
|
||||
close(input);
|
||||
}
|
||||
}
|
||||
|
||||
public void load(File input) throws IOException, DecodeException {
|
||||
String name = input.getName();
|
||||
InputStream inputStream = new FileInputStream(input);
|
||||
try {
|
||||
try (InputStream inputStream = new FileInputStream(input)) {
|
||||
if (name.endsWith(CLST_EXTENSION)) {
|
||||
load(inputStream);
|
||||
} else if (name.endsWith(".jar")) {
|
||||
ZipInputStream in = new ZipInputStream(inputStream);
|
||||
try {
|
||||
try (ZipInputStream in = new ZipInputStream(inputStream)) {
|
||||
ZipEntry entry = in.getNextEntry();
|
||||
while (entry != null) {
|
||||
if (entry.getName().endsWith(CLST_EXTENSION)) {
|
||||
if (entry.getName().endsWith(CLST_EXTENSION) && ZipSecurity.isValidZipEntry(entry)) {
|
||||
load(in);
|
||||
}
|
||||
entry = in.getNextEntry();
|
||||
}
|
||||
} finally {
|
||||
close(in);
|
||||
}
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + name);
|
||||
}
|
||||
} finally {
|
||||
close(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
public void load(InputStream input) throws IOException, DecodeException {
|
||||
DataInputStream in = new DataInputStream(input);
|
||||
try {
|
||||
try (DataInputStream in = new DataInputStream(input)) {
|
||||
byte[] header = new byte[JADX_CLS_SET_HEADER.length()];
|
||||
int readHeaderLength = in.read(header);
|
||||
int version = in.readByte();
|
||||
@@ -214,8 +198,6 @@ public class ClsSet {
|
||||
}
|
||||
classes[i].setParents(parents);
|
||||
}
|
||||
} finally {
|
||||
close(in);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ import org.slf4j.LoggerFactory;
|
||||
public class ClspGraph {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClspGraph.class);
|
||||
|
||||
private final Map<String, Set<String>> ancestorCache = new WeakHashMap<String, Set<String>>();
|
||||
private final Map<String, Set<String>> ancestorCache = Collections.synchronizedMap(new WeakHashMap<String, Set<String>>());
|
||||
private Map<String, NClass> nameMap;
|
||||
|
||||
private final Set<String> missingClasses = new HashSet<String>();
|
||||
private final Set<String> missingClasses = new HashSet<>();
|
||||
|
||||
public void load() throws IOException, DecodeException {
|
||||
ClsSet set = new ClsSet();
|
||||
@@ -36,7 +36,7 @@ public class ClspGraph {
|
||||
|
||||
public void addClasspath(ClsSet set) {
|
||||
if (nameMap == null) {
|
||||
nameMap = new HashMap<String, NClass>(set.getClassesCount());
|
||||
nameMap = new HashMap<>(set.getClassesCount());
|
||||
set.addToMap(nameMap);
|
||||
} else {
|
||||
throw new JadxRuntimeException("Classpath already loaded");
|
||||
@@ -110,7 +110,7 @@ public class ClspGraph {
|
||||
missingClasses.add(clsName);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
result = new HashSet<String>();
|
||||
result = new HashSet<>();
|
||||
addAncestorsNames(cls, result);
|
||||
if (result.isEmpty()) {
|
||||
result = Collections.emptySet();
|
||||
@@ -133,7 +133,7 @@ public class ClspGraph {
|
||||
}
|
||||
LOG.warn("Found {} references to unknown classes", count);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
List<String> clsNames = new ArrayList<String>(missingClasses);
|
||||
List<String> clsNames = new ArrayList<>(missingClasses);
|
||||
Collections.sort(clsNames);
|
||||
for (String cls : clsNames) {
|
||||
LOG.debug(" {}", cls);
|
||||
|
||||
@@ -30,7 +30,7 @@ public class ConvertToClsSet {
|
||||
}
|
||||
File output = new File(args[0]);
|
||||
|
||||
List<InputFile> inputFiles = new ArrayList<InputFile>(args.length - 1);
|
||||
List<InputFile> inputFiles = new ArrayList<>(args.length - 1);
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
File f = new File(args[i]);
|
||||
if (f.isDirectory()) {
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
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.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
@@ -23,52 +36,31 @@ import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
public class ClassGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClassGen.class);
|
||||
|
||||
public static final Comparator<MethodNode> METHOD_LINE_COMPARATOR = new Comparator<MethodNode>() {
|
||||
@Override
|
||||
public int compare(MethodNode a, MethodNode b) {
|
||||
return Utils.compare(a.getSourceLine(), b.getSourceLine());
|
||||
}
|
||||
};
|
||||
|
||||
private final ClassNode cls;
|
||||
private final ClassGen parentGen;
|
||||
private final AnnotationGen annotationGen;
|
||||
private final boolean fallback;
|
||||
private final boolean useImports;
|
||||
private final boolean showInconsistentCode;
|
||||
|
||||
private final Set<ClassInfo> imports = new HashSet<ClassInfo>();
|
||||
private final Set<ClassInfo> imports = new HashSet<>();
|
||||
private int clsDeclLine;
|
||||
|
||||
public ClassGen(ClassNode cls, IJadxArgs jadxArgs) {
|
||||
this(cls, null, jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode());
|
||||
public ClassGen(ClassNode cls, JadxArgs jadxArgs) {
|
||||
this(cls, null, jadxArgs.isUseImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode());
|
||||
}
|
||||
|
||||
public ClassGen(ClassNode cls, ClassGen parentClsGen) {
|
||||
this(cls, parentClsGen, parentClsGen.fallback, parentClsGen.showInconsistentCode);
|
||||
this(cls, parentClsGen, parentClsGen.useImports, parentClsGen.fallback, parentClsGen.showInconsistentCode);
|
||||
}
|
||||
|
||||
public ClassGen(ClassNode cls, ClassGen parentClsGen, boolean fallback, boolean showBadCode) {
|
||||
public ClassGen(ClassNode cls, ClassGen parentClsGen, boolean useImports, boolean fallback, boolean showBadCode) {
|
||||
this.cls = cls;
|
||||
this.parentGen = parentClsGen;
|
||||
this.fallback = fallback;
|
||||
this.useImports = useImports;
|
||||
this.showInconsistentCode = showBadCode;
|
||||
|
||||
this.annotationGen = new AnnotationGen(cls, this);
|
||||
@@ -89,7 +81,7 @@ public class ClassGen {
|
||||
}
|
||||
int importsCount = imports.size();
|
||||
if (importsCount != 0) {
|
||||
List<String> sortImports = new ArrayList<String>(importsCount);
|
||||
List<String> sortImports = new ArrayList<>(importsCount);
|
||||
for (ClassInfo ic : imports) {
|
||||
sortImports.add(ic.getAlias().getFullName());
|
||||
}
|
||||
@@ -273,8 +265,8 @@ public class ClassGen {
|
||||
}
|
||||
|
||||
private static List<MethodNode> sortMethodsByLine(List<MethodNode> methods) {
|
||||
List<MethodNode> out = new ArrayList<MethodNode>(methods);
|
||||
Collections.sort(out, METHOD_LINE_COMPARATOR);
|
||||
List<MethodNode> out = new ArrayList<>(methods);
|
||||
out.sort(Comparator.comparingInt(LineAttrNode::getSourceLine));
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -339,6 +331,10 @@ public class ClassGen {
|
||||
continue;
|
||||
}
|
||||
annotationGen.addForField(code, f);
|
||||
|
||||
if (f.getFieldInfo().isRenamed()) {
|
||||
code.startLine("/* renamed from: ").add(f.getName()).add(" */");
|
||||
}
|
||||
code.startLine(f.getAccessFlags().makeString());
|
||||
useType(code, f.getType());
|
||||
code.add(' ');
|
||||
@@ -476,7 +472,7 @@ public class ClassGen {
|
||||
|
||||
private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) {
|
||||
String fullName = extClsInfo.getFullName();
|
||||
if (fallback) {
|
||||
if (fallback || !useImports) {
|
||||
return fullName;
|
||||
}
|
||||
String shortName = extClsInfo.getShortName();
|
||||
@@ -586,9 +582,8 @@ public class ClassGen {
|
||||
|
||||
private void insertRenameInfo(CodeWriter code, ClassNode cls) {
|
||||
ClassInfo classInfo = cls.getClassInfo();
|
||||
if (classInfo.isRenamed()
|
||||
&& !cls.getShortName().equals(cls.getAlias().getShortName())) {
|
||||
code.startLine("/* renamed from: ").add(classInfo.getFullName()).add(" */");
|
||||
if (classInfo.isRenamed()) {
|
||||
code.startLine("/* renamed from: ").add(classInfo.getType().getObject()).add(" */");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
|
||||
public class CodeGen extends AbstractVisitor {
|
||||
|
||||
private final IJadxArgs args;
|
||||
|
||||
public CodeGen(IJadxArgs args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws CodegenException {
|
||||
ClassGen clsGen = new ClassGen(cls, args);
|
||||
ClassGen clsGen = new ClassGen(cls, cls.root().getArgs());
|
||||
CodeWriter clsCode = clsGen.makeClass();
|
||||
clsCode.finish();
|
||||
cls.setCode(clsCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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;
|
||||
@@ -16,23 +17,21 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.close;
|
||||
|
||||
public class CodeWriter {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CodeWriter.class);
|
||||
|
||||
public static final String NL = System.getProperty("line.separator");
|
||||
public static final String INDENT = " ";
|
||||
public static final String INDENT_STR = " ";
|
||||
|
||||
private static final boolean ADD_LINE_NUMBERS = false;
|
||||
|
||||
private static final String[] INDENT_CACHE = {
|
||||
"",
|
||||
INDENT,
|
||||
INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT + INDENT + INDENT,
|
||||
INDENT_STR,
|
||||
INDENT_STR + INDENT_STR,
|
||||
INDENT_STR + INDENT_STR + INDENT_STR,
|
||||
INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR,
|
||||
INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR,
|
||||
};
|
||||
|
||||
private StringBuilder buf = new StringBuilder();
|
||||
@@ -127,7 +126,7 @@ public class CodeWriter {
|
||||
}
|
||||
|
||||
public CodeWriter addIndent() {
|
||||
add(INDENT);
|
||||
add(INDENT_STR);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -148,9 +147,9 @@ public class CodeWriter {
|
||||
if (curIndent < INDENT_CACHE.length) {
|
||||
this.indentStr = INDENT_CACHE[curIndent];
|
||||
} else {
|
||||
StringBuilder s = new StringBuilder(curIndent * INDENT.length());
|
||||
StringBuilder s = new StringBuilder(curIndent * INDENT_STR.length());
|
||||
for (int i = 0; i < curIndent; i++) {
|
||||
s.append(INDENT);
|
||||
s.append(INDENT_STR);
|
||||
}
|
||||
this.indentStr = s.toString();
|
||||
}
|
||||
@@ -209,7 +208,7 @@ public class CodeWriter {
|
||||
|
||||
private Object attachAnnotation(Object obj, CodePosition pos) {
|
||||
if (annotations.isEmpty()) {
|
||||
annotations = new HashMap<CodePosition, Object>();
|
||||
annotations = new HashMap<>();
|
||||
}
|
||||
return annotations.put(pos, obj);
|
||||
}
|
||||
@@ -227,7 +226,7 @@ public class CodeWriter {
|
||||
|
||||
private void attachSourceLine(int decompiledLine, int sourceLine) {
|
||||
if (lineMap.isEmpty()) {
|
||||
lineMap = new TreeMap<Integer, Integer>();
|
||||
lineMap = new TreeMap<>();
|
||||
}
|
||||
lineMap.put(decompiledLine, sourceLine);
|
||||
}
|
||||
@@ -274,10 +273,16 @@ public class CodeWriter {
|
||||
}
|
||||
|
||||
public void save(File dir, String subDir, String fileName) {
|
||||
if(!ZipSecurity.isValidZipEntryName(subDir) || !ZipSecurity.isValidZipEntryName(fileName)) {
|
||||
return;
|
||||
}
|
||||
save(dir, new File(subDir, fileName).getPath());
|
||||
}
|
||||
|
||||
public void save(File dir, String fileName) {
|
||||
if(!ZipSecurity.isValidZipEntryName(fileName)) {
|
||||
return;
|
||||
}
|
||||
save(new File(dir, fileName));
|
||||
}
|
||||
|
||||
@@ -286,15 +291,10 @@ public class CodeWriter {
|
||||
finish();
|
||||
}
|
||||
File outFile = FileUtils.prepareFile(file);
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
out = new PrintWriter(outFile, "UTF-8");
|
||||
try (PrintWriter out = new PrintWriter(outFile, "UTF-8")) {
|
||||
out.println(code);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Save file error", e);
|
||||
} finally {
|
||||
close(out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
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;
|
||||
@@ -15,18 +22,11 @@ import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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<IfCondition>();
|
||||
private final Queue<IfCondition> stack = new LinkedList<>();
|
||||
|
||||
public Queue<IfCondition> getStack() {
|
||||
return stack;
|
||||
@@ -126,7 +126,7 @@ public class ConditionGen extends InsnGen {
|
||||
wrap(code, firstArg);
|
||||
return;
|
||||
}
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Unsupported boolean condition " + op.getSymbol()));
|
||||
ErrorsCounter.methodError(mth, "Unsupported boolean condition " + op.getSymbol());
|
||||
}
|
||||
|
||||
addArg(code, firstArg, isArgWrapNeeded(firstArg));
|
||||
@@ -179,6 +179,9 @@ public class ConditionGen extends InsnGen {
|
||||
case DIV:
|
||||
case REM:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
switch (insnType) {
|
||||
@@ -189,10 +192,10 @@ public class ConditionGen extends InsnGen {
|
||||
case CONST:
|
||||
case ARRAY_LENGTH:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,18 +270,13 @@ public class InsnGen {
|
||||
makeArith((ArithNode) insn, code, state);
|
||||
break;
|
||||
|
||||
case NEG: {
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
code.add('-');
|
||||
addArg(code, insn.getArg(0));
|
||||
if (wrap) {
|
||||
code.add(')');
|
||||
}
|
||||
case NEG:
|
||||
oneArgInsn(code, insn, state, '-');
|
||||
break;
|
||||
|
||||
case NOT:
|
||||
oneArgInsn(code, insn, state, '~');
|
||||
break;
|
||||
}
|
||||
|
||||
case RETURN:
|
||||
if (insn.getArgsCount() != 0) {
|
||||
@@ -525,6 +520,18 @@ public class InsnGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void oneArgInsn(CodeWriter code, InsnNode insn, Set<Flags> state, char op) throws CodegenException {
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
code.add(op);
|
||||
addArg(code, insn.getArg(0));
|
||||
if (wrap) {
|
||||
code.add(')');
|
||||
}
|
||||
}
|
||||
|
||||
private void fallbackOnlyInsn(InsnNode insn) throws CodegenException {
|
||||
if (!fallback) {
|
||||
throw new CodegenException(insn.getType() + " can be used only in fallback mode");
|
||||
@@ -752,7 +759,7 @@ public class InsnGen {
|
||||
}
|
||||
// replace args
|
||||
InsnNode inlCopy = inl.copy();
|
||||
List<RegisterArg> inlArgs = new ArrayList<RegisterArg>();
|
||||
List<RegisterArg> inlArgs = new ArrayList<>();
|
||||
inlCopy.getRegisterArgs(inlArgs);
|
||||
for (RegisterArg r : inlArgs) {
|
||||
int regNum = r.getRegNum();
|
||||
|
||||
@@ -80,6 +80,10 @@ public class MethodGen {
|
||||
if (clsAccFlags.isAnnotation()) {
|
||||
ai = ai.remove(AccessFlags.ACC_PUBLIC);
|
||||
}
|
||||
|
||||
if (mth.getMethodInfo().isRenamed()) {
|
||||
code.startLine("/* renamed from: ").add(mth.getName()).add(" */");
|
||||
}
|
||||
code.startLineWithNum(mth.getSourceLine());
|
||||
code.add(ai.makeString());
|
||||
|
||||
@@ -129,7 +133,7 @@ public class MethodGen {
|
||||
annotationGen.addForParameter(argsCode, paramsAnnotation, i);
|
||||
}
|
||||
SSAVar argSVar = arg.getSVar();
|
||||
if (argSVar!= null && argSVar.contains(AFlag.FINAL)) {
|
||||
if (argSVar != null && argSVar.contains(AFlag.FINAL)) {
|
||||
argsCode.add("final ");
|
||||
}
|
||||
if (!it.hasNext() && mth.getAccessFlags().isVarArgs()) {
|
||||
@@ -241,7 +245,7 @@ public class MethodGen {
|
||||
* Return fallback variant of method codegen
|
||||
*/
|
||||
public static MethodGen getFallbackMethodGen(MethodNode mth) {
|
||||
ClassGen clsGen = new ClassGen(mth.getParentClass(), null, true, true);
|
||||
ClassGen clsGen = new ClassGen(mth.getParentClass(), null, true, true, true);
|
||||
return new MethodGen(clsGen, mth);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@ public class NameGen {
|
||||
|
||||
private static final Map<String, String> OBJ_ALIAS;
|
||||
|
||||
private final Set<String> varNames = new HashSet<String>();
|
||||
private final Set<String> varNames = new HashSet<>();
|
||||
private final MethodNode mth;
|
||||
private final boolean fallback;
|
||||
|
||||
static {
|
||||
OBJ_ALIAS = new HashMap<String, String>();
|
||||
OBJ_ALIAS = new HashMap<>();
|
||||
OBJ_ALIAS.put(Consts.CLASS_STRING, "str");
|
||||
OBJ_ALIAS.put(Consts.CLASS_CLASS, "cls");
|
||||
OBJ_ALIAS.put(Consts.CLASS_THROWABLE, "th");
|
||||
@@ -164,6 +164,9 @@ public class NameGen {
|
||||
if (vName != null) {
|
||||
return vName;
|
||||
}
|
||||
if (shortName != null) {
|
||||
return StringUtils.escape(shortName.toLowerCase());
|
||||
}
|
||||
}
|
||||
return StringUtils.escape(type.toString());
|
||||
}
|
||||
|
||||
@@ -207,11 +207,13 @@ public class RegionGen extends InsnGen {
|
||||
if (region.isConditionAtEnd()) {
|
||||
code.startLine("do {");
|
||||
makeRegionIndent(code, region.getBody());
|
||||
code.startLine("} while (");
|
||||
code.startLineWithNum(region.getConditionSourceLine());
|
||||
code.add("} while (");
|
||||
conditionGen.add(code, condition);
|
||||
code.add(");");
|
||||
} else {
|
||||
code.startLine("while (");
|
||||
code.startLineWithNum(region.getConditionSourceLine());
|
||||
code.add("while (");
|
||||
conditionGen.add(code, condition);
|
||||
code.add(") {");
|
||||
makeRegionIndent(code, region.getBody());
|
||||
|
||||
@@ -24,9 +24,9 @@ class DeobfPresets {
|
||||
private final Deobfuscator deobfuscator;
|
||||
private final File deobfMapFile;
|
||||
|
||||
private final Map<String, String> clsPresetMap = new HashMap<String, String>();
|
||||
private final Map<String, String> fldPresetMap = new HashMap<String, String>();
|
||||
private final Map<String, String> mthPresetMap = new HashMap<String, String>();
|
||||
private final Map<String, String> clsPresetMap = new HashMap<>();
|
||||
private final Map<String, String> fldPresetMap = new HashMap<>();
|
||||
private final Map<String, String> mthPresetMap = new HashMap<>();
|
||||
|
||||
public DeobfPresets(Deobfuscator deobfuscator, File deobfMapFile) {
|
||||
this.deobfuscator = deobfuscator;
|
||||
@@ -98,7 +98,7 @@ class DeobfPresets {
|
||||
* Saves DefaultDeobfuscator presets
|
||||
*/
|
||||
private void dumpMapping() throws IOException {
|
||||
List<String> list = new ArrayList<String>();
|
||||
List<String> list = new ArrayList<>();
|
||||
// packages
|
||||
for (PackageNode p : deobfuscator.getRootPackage().getInnerPackages()) {
|
||||
for (PackageNode pp : p.getInnerPackages()) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
@@ -16,7 +16,6 @@ import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -35,20 +34,20 @@ public class Deobfuscator {
|
||||
public static final String CLASS_NAME_SEPARATOR = ".";
|
||||
public static final String INNER_CLASS_SEPARATOR = "$";
|
||||
|
||||
private final IJadxArgs args;
|
||||
private final JadxArgs args;
|
||||
@NotNull
|
||||
private final List<DexNode> dexNodes;
|
||||
private final DeobfPresets deobfPresets;
|
||||
|
||||
private final Map<ClassInfo, DeobfClsInfo> clsMap = new HashMap<ClassInfo, DeobfClsInfo>();
|
||||
private final Map<FieldInfo, String> fldMap = new HashMap<FieldInfo, String>();
|
||||
private final Map<MethodInfo, String> mthMap = new HashMap<MethodInfo, String>();
|
||||
private final Map<ClassInfo, DeobfClsInfo> clsMap = new HashMap<>();
|
||||
private final Map<FieldInfo, String> fldMap = new HashMap<>();
|
||||
private final Map<MethodInfo, String> mthMap = new HashMap<>();
|
||||
|
||||
private final Map<MethodInfo, OverridedMethodsNode> ovrdMap = new HashMap<MethodInfo, OverridedMethodsNode>();
|
||||
private final List<OverridedMethodsNode> ovrd = new ArrayList<OverridedMethodsNode>();
|
||||
private final Map<MethodInfo, OverridedMethodsNode> ovrdMap = new HashMap<>();
|
||||
private final List<OverridedMethodsNode> ovrd = new ArrayList<>();
|
||||
|
||||
private final PackageNode rootPackage = new PackageNode("");
|
||||
private final Set<String> pkgSet = new TreeSet<String>();
|
||||
private final Set<String> pkgSet = new TreeSet<>();
|
||||
|
||||
private final int maxLength;
|
||||
private final int minLength;
|
||||
@@ -59,13 +58,13 @@ public class Deobfuscator {
|
||||
private int fldIndex = 0;
|
||||
private int mthIndex = 0;
|
||||
|
||||
public Deobfuscator(IJadxArgs args, @NotNull List<DexNode> dexNodes, File deobfMapFile) {
|
||||
public Deobfuscator(JadxArgs args, @NotNull List<DexNode> dexNodes, File deobfMapFile) {
|
||||
this.args = args;
|
||||
this.dexNodes = dexNodes;
|
||||
|
||||
this.minLength = args.getDeobfuscationMinLength();
|
||||
this.maxLength = args.getDeobfuscationMaxLength();
|
||||
this.useSourceNameAsAlias = args.useSourceNameAsClassAlias();
|
||||
this.useSourceNameAsAlias = args.isUseSourceNameAsClassAlias();
|
||||
|
||||
this.deobfPresets = new DeobfPresets(this, deobfMapFile);
|
||||
}
|
||||
@@ -111,22 +110,23 @@ public class Deobfuscator {
|
||||
private void postProcess() {
|
||||
int id = 1;
|
||||
for (OverridedMethodsNode o : ovrd) {
|
||||
|
||||
Iterator<MethodInfo> it = o.getMethods().iterator();
|
||||
if (it.hasNext()) {
|
||||
MethodInfo mth = it.next();
|
||||
|
||||
if (mth.isRenamed() && !mth.isAliasFromPreset()) {
|
||||
mth.setAlias(String.format("mo%d%s", id, makeName(mth.getName())));
|
||||
boolean aliasFromPreset = false;
|
||||
String aliasToUse = null;
|
||||
for (MethodInfo mth : o.getMethods()) {
|
||||
if (mth.isAliasFromPreset()) {
|
||||
aliasToUse = mth.getAlias();
|
||||
aliasFromPreset = true;
|
||||
}
|
||||
String firstMethodAlias = mth.getAlias();
|
||||
|
||||
while (it.hasNext()) {
|
||||
mth = it.next();
|
||||
if (!mth.getAlias().equals(firstMethodAlias)) {
|
||||
mth.setAlias(firstMethodAlias);
|
||||
}
|
||||
for (MethodInfo mth : o.getMethods()) {
|
||||
if (aliasToUse == null) {
|
||||
if (mth.isRenamed() && !mth.isAliasFromPreset()) {
|
||||
mth.setAlias(String.format("mo%d%s", id, makeName(mth.getName())));
|
||||
}
|
||||
aliasToUse = mth.getAlias();
|
||||
}
|
||||
mth.setAlias(aliasToUse);
|
||||
mth.setAliasFromPreset(aliasFromPreset);
|
||||
}
|
||||
|
||||
id++;
|
||||
@@ -201,9 +201,8 @@ public class Deobfuscator {
|
||||
}
|
||||
|
||||
private void resolveOverriding(DexNode dex, ClassNode cls, MethodNode mth) {
|
||||
Set<MethodInfo> overrideSet = new HashSet<MethodInfo>();
|
||||
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) {
|
||||
@@ -212,12 +211,10 @@ public class Deobfuscator {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (overrideNode == null) {
|
||||
overrideNode = new OverridedMethodsNode(overrideSet);
|
||||
ovrd.add(overrideNode);
|
||||
}
|
||||
|
||||
for (MethodInfo _mth : overrideSet) {
|
||||
if (!ovrdMap.containsKey(_mth)) {
|
||||
ovrdMap.put(_mth, overrideNode);
|
||||
@@ -228,7 +225,6 @@ public class Deobfuscator {
|
||||
}
|
||||
} else {
|
||||
overrideSet.clear();
|
||||
overrideSet = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,9 +438,9 @@ public class Deobfuscator {
|
||||
parentPkg = parentPkg.getParentPackage();
|
||||
}
|
||||
|
||||
final String pkgName = pkg.getName();
|
||||
String pkgName = pkg.getName();
|
||||
if (!pkg.hasAlias() && shouldRename(pkgName)) {
|
||||
final String pkgAlias = String.format("p%03d%s", pkgIndex++, makeName(pkgName));
|
||||
String pkgAlias = String.format("p%03d%s", pkgIndex++, makeName(pkgName));
|
||||
pkg.setAlias(pkgAlias);
|
||||
}
|
||||
}
|
||||
@@ -501,7 +497,7 @@ public class Deobfuscator {
|
||||
}
|
||||
|
||||
private String getPackageName(String packageName) {
|
||||
final PackageNode pkg = getPackageNode(packageName, false);
|
||||
PackageNode pkg = getPackageNode(packageName, false);
|
||||
if (pkg != null) {
|
||||
return pkg.getFullAlias();
|
||||
}
|
||||
@@ -509,7 +505,7 @@ public class Deobfuscator {
|
||||
}
|
||||
|
||||
private String getClassName(ClassInfo clsInfo) {
|
||||
final DeobfClsInfo deobfClsInfo = clsMap.get(clsInfo);
|
||||
DeobfClsInfo deobfClsInfo = clsMap.get(clsInfo);
|
||||
if (deobfClsInfo != null) {
|
||||
return deobfClsInfo.makeNameWithoutPkg();
|
||||
}
|
||||
@@ -521,7 +517,7 @@ public class Deobfuscator {
|
||||
}
|
||||
|
||||
private String getClassFullName(ClassInfo clsInfo) {
|
||||
final DeobfClsInfo deobfClsInfo = clsMap.get(clsInfo);
|
||||
DeobfClsInfo deobfClsInfo = clsMap.get(clsInfo);
|
||||
if (deobfClsInfo != null) {
|
||||
return deobfClsInfo.getFullName();
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ public class NameMapper {
|
||||
private static final Pattern VALID_JAVA_FULL_IDENTIFIER = Pattern.compile(
|
||||
"(" + VALID_JAVA_IDENTIFIER + "\\.)*" + VALID_JAVA_IDENTIFIER);
|
||||
|
||||
private static final Set<String> RESERVED_NAMES = new HashSet<String>(
|
||||
Arrays.asList(new String[]{
|
||||
private static final Set<String> RESERVED_NAMES = new HashSet<>(
|
||||
Arrays.asList(
|
||||
"abstract",
|
||||
"assert",
|
||||
"boolean",
|
||||
@@ -67,8 +67,8 @@ public class NameMapper {
|
||||
"try",
|
||||
"void",
|
||||
"volatile",
|
||||
"while",
|
||||
})
|
||||
"while"
|
||||
)
|
||||
);
|
||||
|
||||
public static boolean isReserved(String str) {
|
||||
@@ -96,4 +96,7 @@ public class NameMapper {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private NameMapper() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
public class PackageNode {
|
||||
|
||||
@@ -29,11 +30,11 @@ public class PackageNode {
|
||||
|
||||
public String getFullName() {
|
||||
if (cachedPackageFullName == null) {
|
||||
Stack<PackageNode> pp = getParentPackages();
|
||||
Deque<PackageNode> pp = getParentPackages();
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(pp.pop().getName());
|
||||
while (pp.size() > 0) {
|
||||
while (!pp.isEmpty()) {
|
||||
result.append(SEPARATOR_CHAR);
|
||||
result.append(pp.pop().getName());
|
||||
}
|
||||
@@ -59,12 +60,12 @@ public class PackageNode {
|
||||
|
||||
public String getFullAlias() {
|
||||
if (cachedPackageFullAlias == null) {
|
||||
Stack<PackageNode> pp = getParentPackages();
|
||||
Deque<PackageNode> pp = getParentPackages();
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
if (pp.size() > 0) {
|
||||
if (!pp.isEmpty()) {
|
||||
result.append(pp.pop().getAlias());
|
||||
while (pp.size() > 0) {
|
||||
while (!pp.isEmpty()) {
|
||||
result.append(SEPARATOR_CHAR);
|
||||
result.append(pp.pop().getAlias());
|
||||
}
|
||||
@@ -86,7 +87,7 @@ public class PackageNode {
|
||||
|
||||
public void addInnerPackage(PackageNode pkg) {
|
||||
if (innerPackages.isEmpty()) {
|
||||
innerPackages = new ArrayList<PackageNode>();
|
||||
innerPackages = new ArrayList<>();
|
||||
}
|
||||
innerPackages.add(pkg);
|
||||
pkg.parentPackage = this;
|
||||
@@ -114,16 +115,15 @@ public class PackageNode {
|
||||
*
|
||||
* @return stack with parent packages
|
||||
*/
|
||||
private Stack<PackageNode> getParentPackages() {
|
||||
Stack<PackageNode> pp = new Stack<PackageNode>();
|
||||
private Deque<PackageNode> getParentPackages() {
|
||||
Deque<PackageNode> pp = new ArrayDeque<>();
|
||||
|
||||
PackageNode currentP = this;
|
||||
PackageNode parentP = currentP.getParentPackage();
|
||||
|
||||
while (currentP != parentP) {
|
||||
pp.push(currentP);
|
||||
currentP = parentP;
|
||||
parentP = currentP.getParentPackage();
|
||||
PackageNode currentPkg = this;
|
||||
PackageNode parentPkg = currentPkg.getParentPackage();
|
||||
while (currentPkg != parentPkg) {
|
||||
pp.push(currentPkg);
|
||||
currentPkg = parentPkg;
|
||||
parentPkg = currentPkg.getParentPackage();
|
||||
}
|
||||
return pp;
|
||||
}
|
||||
|
||||
@@ -29,25 +29,25 @@ import jadx.core.dex.trycatch.SplitterBlockAttr;
|
||||
*/
|
||||
public class AType<T extends IAttribute> {
|
||||
|
||||
public static final AType<AttrList<JumpInfo>> JUMP = new AType<AttrList<JumpInfo>>();
|
||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<AttrList<LoopInfo>>();
|
||||
public static final AType<AttrList<EdgeInsnAttr>> EDGE_INSN = new AType<AttrList<EdgeInsnAttr>>();
|
||||
public static final AType<AttrList<JumpInfo>> JUMP = new AType<>();
|
||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<>();
|
||||
public static final AType<AttrList<EdgeInsnAttr>> EDGE_INSN = new AType<>();
|
||||
|
||||
public static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<ExcHandlerAttr>();
|
||||
public static final AType<CatchAttr> CATCH_BLOCK = new AType<CatchAttr>();
|
||||
public static final AType<SplitterBlockAttr> SPLITTER_BLOCK = new AType<SplitterBlockAttr>();
|
||||
public static final AType<ForceReturnAttr> FORCE_RETURN = new AType<ForceReturnAttr>();
|
||||
public static final AType<FieldInitAttr> FIELD_INIT = new AType<FieldInitAttr>();
|
||||
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<FieldReplaceAttr>();
|
||||
public static final AType<JadxErrorAttr> JADX_ERROR = new AType<JadxErrorAttr>();
|
||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<MethodInlineAttr>();
|
||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<EnumClassAttr>();
|
||||
public static final AType<EnumMapAttr> ENUM_MAP = new AType<EnumMapAttr>();
|
||||
public static final AType<AnnotationsList> ANNOTATION_LIST = new AType<AnnotationsList>();
|
||||
public static final AType<MethodParameters> ANNOTATION_MTH_PARAMETERS = new AType<MethodParameters>();
|
||||
public static final AType<PhiListAttr> PHI_LIST = new AType<PhiListAttr>();
|
||||
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<SourceFileAttr>();
|
||||
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<DeclareVariablesAttr>();
|
||||
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<LoopLabelAttr>();
|
||||
public static final AType<IgnoreEdgeAttr> IGNORE_EDGE = new AType<IgnoreEdgeAttr>();
|
||||
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<>();
|
||||
public static final AType<AnnotationsList> ANNOTATION_LIST = new AType<>();
|
||||
public static final AType<MethodParameters> ANNOTATION_MTH_PARAMETERS = new AType<>();
|
||||
public static final AType<PhiListAttr> PHI_LIST = new AType<>();
|
||||
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<>();
|
||||
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<>();
|
||||
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<>();
|
||||
public static final AType<IgnoreEdgeAttr> IGNORE_EDGE = new AType<>();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.List;
|
||||
public class AttrList<T> implements IAttribute {
|
||||
|
||||
private final AType<AttrList<T>> type;
|
||||
private final List<T> list = new LinkedList<T>();
|
||||
private final List<T> list = new LinkedList<>();
|
||||
|
||||
public AttrList(AType<AttrList<T>> type) {
|
||||
this.type = type;
|
||||
|
||||
@@ -24,7 +24,7 @@ public class AttributeStorage {
|
||||
|
||||
public AttributeStorage() {
|
||||
flags = EnumSet.noneOf(AFlag.class);
|
||||
attributes = new IdentityHashMap<AType<?>, IAttribute>();
|
||||
attributes = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
public void add(AFlag flag) {
|
||||
@@ -38,7 +38,7 @@ public class AttributeStorage {
|
||||
public <T> void add(AType<AttrList<T>> type, T obj) {
|
||||
AttrList<T> list = get(type);
|
||||
if (list == null) {
|
||||
list = new AttrList<T>(type);
|
||||
list = new AttrList<>(type);
|
||||
add(list);
|
||||
}
|
||||
list.getList().add(obj);
|
||||
@@ -84,7 +84,7 @@ public class AttributeStorage {
|
||||
}
|
||||
|
||||
public void remove(IAttribute attr) {
|
||||
AType<?> type = attr.getType();
|
||||
AType<? extends IAttribute> type = attr.getType();
|
||||
IAttribute a = attributes.get(type);
|
||||
if (a == attr) {
|
||||
attributes.remove(type);
|
||||
@@ -101,7 +101,7 @@ public class AttributeStorage {
|
||||
if (size == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> list = new ArrayList<String>(size);
|
||||
List<String> list = new ArrayList<>(size);
|
||||
for (AFlag a : flags) {
|
||||
list.add(a.toString());
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
public interface IAttribute {
|
||||
|
||||
AType<?> getType();
|
||||
|
||||
AType<? extends IAttribute> getType();
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class AnnotationsList implements IAttribute {
|
||||
private final Map<String, Annotation> map;
|
||||
|
||||
public AnnotationsList(List<Annotation> anList) {
|
||||
map = new HashMap<String, Annotation>(anList.size());
|
||||
map = new HashMap<>(anList.size());
|
||||
for (Annotation a : anList) {
|
||||
map.put(a.getAnnotationClass(), a);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public class MethodParameters implements IAttribute {
|
||||
private final List<AnnotationsList> paramList;
|
||||
|
||||
public MethodParameters(int paramCount) {
|
||||
paramList = new ArrayList<AnnotationsList>(paramCount);
|
||||
paramList = new ArrayList<>(paramCount);
|
||||
}
|
||||
|
||||
public List<AnnotationsList> getParamList() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
*/
|
||||
public class DeclareVariablesAttr implements IAttribute {
|
||||
|
||||
private final List<RegisterArg> vars = new LinkedList<RegisterArg>();
|
||||
private final List<RegisterArg> vars = new LinkedList<>();
|
||||
|
||||
public Iterable<RegisterArg> getVars() {
|
||||
return vars;
|
||||
|
||||
@@ -54,7 +54,7 @@ public class EnumClassAttr implements IAttribute {
|
||||
private MethodNode staticMethod;
|
||||
|
||||
public EnumClassAttr(int fieldsCount) {
|
||||
this.fields = new ArrayList<EnumField>(fieldsCount);
|
||||
this.fields = new ArrayList<>(fieldsCount);
|
||||
}
|
||||
|
||||
public List<EnumField> getFields() {
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.Map;
|
||||
public class EnumMapAttr implements IAttribute {
|
||||
|
||||
public static class KeyValueMap {
|
||||
private final Map<Object, Object> map = new HashMap<Object, Object>();
|
||||
private final Map<Object, Object> map = new HashMap<>();
|
||||
|
||||
public Object get(Object key) {
|
||||
return map.get(key);
|
||||
@@ -21,7 +21,7 @@ public class EnumMapAttr implements IAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<FieldNode, KeyValueMap> fieldsMap = new HashMap<FieldNode, KeyValueMap>();
|
||||
private final Map<FieldNode, KeyValueMap> fieldsMap = new HashMap<>();
|
||||
|
||||
public KeyValueMap getMap(FieldNode field) {
|
||||
return fieldsMap.get(field);
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.Set;
|
||||
|
||||
public class IgnoreEdgeAttr implements IAttribute {
|
||||
|
||||
private final Set<BlockNode> blocks = new HashSet<BlockNode>(3);
|
||||
private final Set<BlockNode> blocks = new HashSet<>(3);
|
||||
|
||||
public Set<BlockNode> getBlocks() {
|
||||
return blocks;
|
||||
|
||||
@@ -43,7 +43,7 @@ public class LoopInfo {
|
||||
* Exit nodes belongs to loop (contains in {@code loopBlocks})
|
||||
*/
|
||||
public Set<BlockNode> getExitNodes() {
|
||||
Set<BlockNode> nodes = new HashSet<BlockNode>();
|
||||
Set<BlockNode> nodes = new HashSet<>();
|
||||
Set<BlockNode> blocks = getLoopBlocks();
|
||||
for (BlockNode block : blocks) {
|
||||
// exit: successor node not from this loop, (don't change to getCleanSuccessors)
|
||||
@@ -60,7 +60,7 @@ public class LoopInfo {
|
||||
* Return loop exit edges.
|
||||
*/
|
||||
public List<Edge> getExitEdges() {
|
||||
List<Edge> edges = new LinkedList<Edge>();
|
||||
List<Edge> edges = new LinkedList<>();
|
||||
Set<BlockNode> blocks = getLoopBlocks();
|
||||
for (BlockNode block : blocks) {
|
||||
for (BlockNode s : block.getSuccessors()) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.List;
|
||||
|
||||
public class PhiListAttr implements IAttribute {
|
||||
|
||||
private final List<PhiInsn> list = new LinkedList<PhiInsn>();
|
||||
private final List<PhiInsn> list = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public AType<PhiListAttr> getType() {
|
||||
|
||||
@@ -35,12 +35,12 @@ public final class ClassInfo {
|
||||
if (type.isArray()) {
|
||||
type = ArgType.OBJECT;
|
||||
}
|
||||
ClassInfo cls = dex.getInfoStorage().getCls(type);
|
||||
ClassInfo cls = dex.root().getInfoStorage().getCls(type);
|
||||
if (cls != null) {
|
||||
return cls;
|
||||
}
|
||||
cls = new ClassInfo(dex, type);
|
||||
return dex.getInfoStorage().putCls(cls);
|
||||
return dex.root().getInfoStorage().putCls(cls);
|
||||
}
|
||||
|
||||
public static ClassInfo fromDex(DexNode dex, int clsIndex) {
|
||||
@@ -89,6 +89,10 @@ public final class ClassInfo {
|
||||
int sep = clsName.lastIndexOf('$');
|
||||
if (canBeInner && sep > 0 && sep != clsName.length() - 1) {
|
||||
String parClsName = pkg + "." + clsName.substring(0, sep);
|
||||
if (pkg.isEmpty()) {
|
||||
parClsName = clsName.substring(0, sep);
|
||||
}
|
||||
|
||||
parentClass = fromName(dex, parClsName);
|
||||
clsName = clsName.substring(sep + 1);
|
||||
} else {
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
package jadx.core.dex.info;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.ResRefField;
|
||||
import jadx.core.dex.nodes.parser.FieldInitAttr;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -18,11 +8,21 @@ import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.ResRefField;
|
||||
import jadx.core.dex.nodes.parser.FieldInitAttr;
|
||||
|
||||
public class ConstStorage {
|
||||
|
||||
private static final class Values {
|
||||
private final Map<Object, FieldNode> values = new HashMap<Object, FieldNode>();
|
||||
private final Set<Object> duplicates = new HashSet<Object>();
|
||||
private static final class ValueStorage {
|
||||
private final Map<Object, FieldNode> values = new HashMap<>();
|
||||
private final Set<Object> duplicates = new HashSet<>();
|
||||
|
||||
public Map<Object, FieldNode> getValues() {
|
||||
return values;
|
||||
@@ -55,12 +55,12 @@ public class ConstStorage {
|
||||
}
|
||||
|
||||
private final boolean replaceEnabled;
|
||||
private final Values globalValues = new Values();
|
||||
private final Map<ClassNode, Values> classes = new HashMap<ClassNode, Values>();
|
||||
private final ValueStorage globalValues = new ValueStorage();
|
||||
private final Map<ClassNode, ValueStorage> classes = new HashMap<>();
|
||||
|
||||
private Map<Integer, String> resourcesNames = new HashMap<Integer, String>();
|
||||
private Map<Integer, String> resourcesNames = new HashMap<>();
|
||||
|
||||
public ConstStorage(IJadxArgs args) {
|
||||
public ConstStorage(JadxArgs args) {
|
||||
this.replaceEnabled = args.isReplaceConsts();
|
||||
}
|
||||
|
||||
@@ -90,10 +90,10 @@ public class ConstStorage {
|
||||
}
|
||||
}
|
||||
|
||||
private Values getClsValues(ClassNode cls) {
|
||||
Values classValues = classes.get(cls);
|
||||
private ValueStorage getClsValues(ClassNode cls) {
|
||||
ValueStorage classValues = classes.get(cls);
|
||||
if (classValues == null) {
|
||||
classValues = new Values();
|
||||
classValues = new ValueStorage();
|
||||
classes.put(cls, classValues);
|
||||
}
|
||||
return classValues;
|
||||
@@ -117,7 +117,7 @@ public class ConstStorage {
|
||||
}
|
||||
ClassNode current = cls;
|
||||
while (current != null) {
|
||||
Values classValues = classes.get(current);
|
||||
ValueStorage classValues = classes.get(current);
|
||||
if (classValues != null) {
|
||||
FieldNode field = classValues.get(value);
|
||||
if (field != null) {
|
||||
@@ -161,12 +161,14 @@ public class ConstStorage {
|
||||
return getConstField(cls, literal, Math.abs(literal) > 1000);
|
||||
case FLOAT:
|
||||
float f = Float.intBitsToFloat((int) literal);
|
||||
return getConstField(cls, f, f != 0.0);
|
||||
return getConstField(cls, f, Float.compare(f, 0) == 0);
|
||||
case DOUBLE:
|
||||
double d = Double.longBitsToDouble(literal);
|
||||
return getConstField(cls, d, d != 0);
|
||||
return getConstField(cls, d, Double.compare(d, 0) == 0);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setResourcesNames(Map<Integer, String> resourcesNames) {
|
||||
|
||||
@@ -22,7 +22,7 @@ public final class FieldInfo {
|
||||
|
||||
public static FieldInfo from(DexNode dex, ClassInfo declClass, String name, ArgType type) {
|
||||
FieldInfo field = new FieldInfo(declClass, name, type);
|
||||
return dex.getInfoStorage().getField(field);
|
||||
return dex.root().getInfoStorage().getField(field);
|
||||
}
|
||||
|
||||
public static FieldInfo fromDex(DexNode dex, int index) {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
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;
|
||||
|
||||
public class InfoStorage {
|
||||
|
||||
private final Map<ArgType, ClassInfo> classes = new HashMap<ArgType, ClassInfo>();
|
||||
private final Map<Integer, MethodInfo> methods = new HashMap<Integer, MethodInfo>();
|
||||
private final Map<FieldInfo, FieldInfo> fields = new HashMap<FieldInfo, FieldInfo>();
|
||||
private final Map<ArgType, ClassInfo> classes = new HashMap<>();
|
||||
private final Map<Integer, MethodInfo> methods = new HashMap<>();
|
||||
private final Map<FieldInfo, FieldInfo> fields = new HashMap<>();
|
||||
|
||||
public ClassInfo getCls(ArgType type) {
|
||||
return classes.get(type);
|
||||
@@ -22,13 +23,17 @@ public class InfoStorage {
|
||||
}
|
||||
}
|
||||
|
||||
public MethodInfo getMethod(int mtdId) {
|
||||
return methods.get(mtdId);
|
||||
private int generateMethodLookupId(DexNode dex, int mthId) {
|
||||
return dex.getDexId() << 16 | mthId;
|
||||
}
|
||||
|
||||
public MethodInfo putMethod(int mthId, MethodInfo mth) {
|
||||
public MethodInfo getMethod(DexNode dex, int mtdId) {
|
||||
return methods.get(generateMethodLookupId(dex, mtdId));
|
||||
}
|
||||
|
||||
public MethodInfo putMethod(DexNode dex, int mthId, MethodInfo mth) {
|
||||
synchronized (methods) {
|
||||
MethodInfo prev = methods.put(mthId, mth);
|
||||
MethodInfo prev = methods.put(generateMethodLookupId(dex, mthId), mth);
|
||||
return prev == null ? mth : prev;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ public final class MethodInfo {
|
||||
}
|
||||
|
||||
public static MethodInfo fromDex(DexNode dex, int mthIndex) {
|
||||
MethodInfo mth = dex.getInfoStorage().getMethod(mthIndex);
|
||||
MethodInfo mth = dex.root().getInfoStorage().getMethod(dex, mthIndex);
|
||||
if (mth != null) {
|
||||
return mth;
|
||||
}
|
||||
mth = new MethodInfo(dex, mthIndex);
|
||||
return dex.getInfoStorage().putMethod(mthIndex, mth);
|
||||
return dex.root().getInfoStorage().putMethod(dex, mthIndex, mth);
|
||||
}
|
||||
|
||||
public String makeSignature(boolean includeRetType) {
|
||||
|
||||
@@ -66,7 +66,7 @@ public final class FillArrayNode extends InsnNode {
|
||||
}
|
||||
|
||||
public List<LiteralArg> getLiteralArgs() {
|
||||
List<LiteralArg> list = new ArrayList<LiteralArg>(size);
|
||||
List<LiteralArg> list = new ArrayList<>(size);
|
||||
Object array = data;
|
||||
if (array instanceof int[]) {
|
||||
for (int b : (int[]) array) {
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import java.io.EOFException;
|
||||
|
||||
import com.android.dex.Code;
|
||||
import com.android.dx.io.OpcodeInfo;
|
||||
import com.android.dx.io.Opcodes;
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
@@ -12,16 +26,8 @@ import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import com.android.dex.Code;
|
||||
import com.android.dx.io.OpcodeInfo;
|
||||
import com.android.dx.io.Opcodes;
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
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;
|
||||
|
||||
public class InsnDecoder {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InsnDecoder.class);
|
||||
|
||||
private final MethodNode method;
|
||||
private final DexNode dex;
|
||||
@@ -37,26 +43,36 @@ public class InsnDecoder {
|
||||
int size = encodedInstructions.length;
|
||||
DecodedInstruction[] decoded = new DecodedInstruction[size];
|
||||
ShortArrayCodeInput in = new ShortArrayCodeInput(encodedInstructions);
|
||||
|
||||
try {
|
||||
while (in.hasMore()) {
|
||||
decoded[in.cursor()] = DecodedInstruction.decode(in);
|
||||
decoded[in.cursor()] = decodeRawInsn(in);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new DecodeException(method, "", e);
|
||||
throw new DecodeException(method, e.getMessage(), e);
|
||||
}
|
||||
insnArr = decoded;
|
||||
}
|
||||
|
||||
private DecodedInstruction decodeRawInsn(ShortArrayCodeInput in) throws EOFException {
|
||||
int opcodeUnit = in.read();
|
||||
int opcode = Opcodes.extractOpcodeFromUnit(opcodeUnit);
|
||||
OpcodeInfo.Info opcodeInfo;
|
||||
try {
|
||||
opcodeInfo = OpcodeInfo.get(opcode);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("Ignore decode error: '{}', replace with NOP instruction", e.getMessage());
|
||||
opcodeInfo = OpcodeInfo.NOP;
|
||||
}
|
||||
return opcodeInfo.getFormat().decode(opcodeUnit, in);
|
||||
}
|
||||
|
||||
public InsnNode[] process() throws DecodeException {
|
||||
InsnNode[] instructions = new InsnNode[insnArr.length];
|
||||
for (int i = 0; i < insnArr.length; i++) {
|
||||
DecodedInstruction rawInsn = insnArr[i];
|
||||
if (rawInsn != null) {
|
||||
InsnNode insn = decode(rawInsn, i);
|
||||
if (insn != null) {
|
||||
insn.setOffset(i);
|
||||
}
|
||||
insn.setOffset(i);
|
||||
instructions[i] = insn;
|
||||
} else {
|
||||
instructions[i] = null;
|
||||
@@ -66,13 +82,14 @@ public class InsnDecoder {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private InsnNode decode(DecodedInstruction insn, int offset) throws DecodeException {
|
||||
switch (insn.getOpcode()) {
|
||||
case Opcodes.NOP:
|
||||
case Opcodes.PACKED_SWITCH_PAYLOAD:
|
||||
case Opcodes.SPARSE_SWITCH_PAYLOAD:
|
||||
case Opcodes.FILL_ARRAY_DATA_PAYLOAD:
|
||||
return null;
|
||||
return new InsnNode(InsnType.NOP, 0);
|
||||
|
||||
// move-result will be process in invoke and filled-new-array instructions
|
||||
case Opcodes.MOVE_RESULT:
|
||||
@@ -307,6 +324,11 @@ public class InsnDecoder {
|
||||
case Opcodes.NEG_DOUBLE:
|
||||
return neg(insn, ArgType.DOUBLE);
|
||||
|
||||
case Opcodes.NOT_INT:
|
||||
return not(insn, ArgType.INT);
|
||||
case Opcodes.NOT_LONG:
|
||||
return not(insn, ArgType.LONG);
|
||||
|
||||
case Opcodes.INT_TO_BYTE:
|
||||
return cast(insn, ArgType.INT, ArgType.BYTE);
|
||||
case Opcodes.INT_TO_CHAR:
|
||||
@@ -563,9 +585,10 @@ public class InsnDecoder {
|
||||
return insn(InsnType.MONITOR_EXIT,
|
||||
null,
|
||||
InsnArg.reg(insn, 0, ArgType.UNKNOWN_OBJECT));
|
||||
}
|
||||
|
||||
throw new DecodeException("Unknown instruction: " + OpcodeInfo.getName(insn.getOpcode()));
|
||||
default:
|
||||
throw new DecodeException("Unknown instruction: '" + OpcodeInfo.getName(insn.getOpcode()) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private InsnNode decodeSwitch(DecodedInstruction insn, int offset, boolean packed) {
|
||||
@@ -681,6 +704,13 @@ public class InsnDecoder {
|
||||
return inode;
|
||||
}
|
||||
|
||||
private InsnNode not(DecodedInstruction insn, ArgType type) {
|
||||
InsnNode inode = new InsnNode(InsnType.NOT, 1);
|
||||
inode.setResult(InsnArg.reg(insn, 0, type));
|
||||
inode.addArg(InsnArg.reg(insn, 1, type));
|
||||
return inode;
|
||||
}
|
||||
|
||||
private InsnNode insn(InsnType type, RegisterArg res) {
|
||||
InsnNode node = new InsnNode(type, 0);
|
||||
node.setResult(res);
|
||||
|
||||
@@ -8,6 +8,7 @@ public enum InsnType {
|
||||
|
||||
ARITH,
|
||||
NEG,
|
||||
NOT,
|
||||
|
||||
MOVE,
|
||||
CAST,
|
||||
|
||||
@@ -21,7 +21,7 @@ public final class PhiInsn extends InsnNode {
|
||||
|
||||
public PhiInsn(int regNum, int predecessors) {
|
||||
super(InsnType.PHI, predecessors);
|
||||
this.blockBinds = new IdentityHashMap<RegisterArg, BlockNode>(predecessors);
|
||||
this.blockBinds = new IdentityHashMap<>(predecessors);
|
||||
setResult(InsnArg.reg(regNum, ArgType.UNKNOWN));
|
||||
add(AFlag.DONT_INLINE);
|
||||
}
|
||||
|
||||
@@ -140,16 +140,16 @@ public abstract class ArgType {
|
||||
}
|
||||
|
||||
private static class ObjectType extends KnownType {
|
||||
private final String object;
|
||||
private final String objName;
|
||||
|
||||
public ObjectType(String obj) {
|
||||
this.object = Utils.cleanObjectName(obj);
|
||||
this.hash = object.hashCode();
|
||||
this.objName = Utils.cleanObjectName(obj);
|
||||
this.hash = objName.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getObject() {
|
||||
return object;
|
||||
return objName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,12 +164,12 @@ public abstract class ArgType {
|
||||
|
||||
@Override
|
||||
boolean internalEquals(Object obj) {
|
||||
return object.equals(((ObjectType) obj).object);
|
||||
return objName.equals(((ObjectType) obj).objName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return object;
|
||||
return objName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ public abstract class ArgType {
|
||||
}
|
||||
|
||||
private static final class ArrayArg extends KnownType {
|
||||
public static final PrimitiveType[] ARRAY_POSSIBLES = new PrimitiveType[]{PrimitiveType.ARRAY};
|
||||
private static final PrimitiveType[] ARRAY_POSSIBLES = new PrimitiveType[]{PrimitiveType.ARRAY};
|
||||
private final ArgType arrayElement;
|
||||
|
||||
public ArrayArg(ArgType arrayElement) {
|
||||
@@ -495,7 +495,7 @@ public abstract class ArgType {
|
||||
return null;
|
||||
} else {
|
||||
// both types unknown
|
||||
List<PrimitiveType> types = new ArrayList<PrimitiveType>();
|
||||
List<PrimitiveType> types = new ArrayList<>();
|
||||
for (PrimitiveType type : a.getPossibleTypes()) {
|
||||
if (b.contains(type)) {
|
||||
types.add(type);
|
||||
@@ -622,8 +622,10 @@ public abstract class ArgType {
|
||||
return DOUBLE;
|
||||
case 'V':
|
||||
return VOID;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getRegCount() {
|
||||
|
||||
@@ -57,10 +57,7 @@ public final class FieldArg extends RegisterArg {
|
||||
if (!field.equals(fieldArg.field)) {
|
||||
return false;
|
||||
}
|
||||
if (instArg != null ? !instArg.equals(fieldArg.instArg) : fieldArg.instArg != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return instArg != null ? instArg.equals(fieldArg.instArg) : fieldArg.instArg == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,7 +101,7 @@ public abstract class InsnArg extends Typed {
|
||||
}
|
||||
|
||||
public static void updateParentInsn(InsnNode fromInsn, InsnNode toInsn) {
|
||||
List<RegisterArg> args = new ArrayList<RegisterArg>();
|
||||
List<RegisterArg> args = new ArrayList<>();
|
||||
fromInsn.getRegisterArgs(args);
|
||||
for (RegisterArg reg : args) {
|
||||
reg.setParentInsn(toInsn);
|
||||
|
||||
@@ -6,12 +6,11 @@ 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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RegisterArg extends InsnArg implements Named {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RegisterArg.class);
|
||||
|
||||
protected final int regNum;
|
||||
// not null after SSATransform pass
|
||||
@@ -151,23 +150,13 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof RegisterArg)) {
|
||||
return false;
|
||||
}
|
||||
RegisterArg other = (RegisterArg) obj;
|
||||
if (regNum != other.regNum) {
|
||||
return false;
|
||||
}
|
||||
if (!type.equals(other.type)) {
|
||||
return false;
|
||||
}
|
||||
if (sVar != null && !sVar.equals(other.getSVar())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return regNum == other.regNum
|
||||
&& type.equals(other.type)
|
||||
&& Objects.equals(sVar, other.getSVar());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,7 @@ public class SSAVar extends AttrNode {
|
||||
|
||||
@NotNull
|
||||
private RegisterArg assign;
|
||||
private final List<RegisterArg> useList = new ArrayList<RegisterArg>(2);
|
||||
private final List<RegisterArg> useList = new ArrayList<>(2);
|
||||
@Nullable
|
||||
private PhiInsn usedInPhi;
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
|
||||
private int id;
|
||||
private final int startOffset;
|
||||
private final List<InsnNode> instructions = new ArrayList<InsnNode>(2);
|
||||
private final List<InsnNode> instructions = new ArrayList<>(2);
|
||||
|
||||
private List<BlockNode> predecessors = new ArrayList<BlockNode>(1);
|
||||
private List<BlockNode> successors = new ArrayList<BlockNode>(1);
|
||||
private List<BlockNode> predecessors = new ArrayList<>(1);
|
||||
private List<BlockNode> successors = new ArrayList<>(1);
|
||||
private List<BlockNode> cleanSuccessors;
|
||||
|
||||
// all dominators
|
||||
@@ -85,7 +85,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
if (sucList.isEmpty()) {
|
||||
return sucList;
|
||||
}
|
||||
List<BlockNode> toRemove = new LinkedList<BlockNode>();
|
||||
List<BlockNode> toRemove = new LinkedList<>();
|
||||
for (BlockNode b : sucList) {
|
||||
if (BlockUtils.isBlockMustBeCleared(b)) {
|
||||
toRemove.add(b);
|
||||
@@ -104,7 +104,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
if (toRemove.isEmpty()) {
|
||||
return sucList;
|
||||
}
|
||||
List<BlockNode> result = new ArrayList<BlockNode>(sucList);
|
||||
List<BlockNode> result = new ArrayList<>(sucList);
|
||||
result.removeAll(toRemove);
|
||||
return result;
|
||||
}
|
||||
@@ -161,7 +161,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
|
||||
public void addDominatesOn(BlockNode block) {
|
||||
if (dominatesOn.isEmpty()) {
|
||||
dominatesOn = new LinkedList<BlockNode>();
|
||||
dominatesOn = new LinkedList<>();
|
||||
}
|
||||
dominatesOn.add(block);
|
||||
}
|
||||
|
||||
@@ -60,12 +60,12 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
private ClassNode parentClass;
|
||||
|
||||
private ProcessState state = ProcessState.NOT_LOADED;
|
||||
private final Set<ClassNode> dependencies = new HashSet<ClassNode>();
|
||||
private final Set<ClassNode> dependencies = new HashSet<>();
|
||||
|
||||
// cache maps
|
||||
private Map<MethodInfo, MethodNode> mthInfoMap = Collections.emptyMap();
|
||||
|
||||
public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
|
||||
public ClassNode(DexNode dex, ClassDef cls) {
|
||||
this.dex = dex;
|
||||
this.clsInfo = ClassInfo.fromDex(dex, cls.getTypeIndex());
|
||||
try {
|
||||
@@ -74,7 +74,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
} else {
|
||||
this.superClass = dex.getType(cls.getSupertypeIndex());
|
||||
}
|
||||
this.interfaces = new ArrayList<ArgType>(cls.getInterfaces().length);
|
||||
this.interfaces = new ArrayList<>(cls.getInterfaces().length);
|
||||
for (short interfaceIdx : cls.getInterfaces()) {
|
||||
this.interfaces.add(dex.getType(interfaceIdx));
|
||||
}
|
||||
@@ -83,8 +83,8 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
int mthsCount = clsData.getDirectMethods().length + clsData.getVirtualMethods().length;
|
||||
int fieldsCount = clsData.getStaticFields().length + clsData.getInstanceFields().length;
|
||||
|
||||
methods = new ArrayList<MethodNode>(mthsCount);
|
||||
fields = new ArrayList<FieldNode>(fieldsCount);
|
||||
methods = new ArrayList<>(mthsCount);
|
||||
fields = new ArrayList<>(fieldsCount);
|
||||
|
||||
for (Method mth : clsData.getDirectMethods()) {
|
||||
methods.add(new MethodNode(this, mth, false));
|
||||
@@ -128,7 +128,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
|
||||
buildCache();
|
||||
} catch (Exception e) {
|
||||
throw new DecodeException("Error decode class: " + clsInfo, e);
|
||||
throw new JadxRuntimeException("Error decode class: " + clsInfo, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
}
|
||||
|
||||
private void buildCache() {
|
||||
mthInfoMap = new HashMap<MethodInfo, MethodNode>(methods.size());
|
||||
mthInfoMap = new HashMap<>(methods.size());
|
||||
for (MethodNode mth : methods) {
|
||||
mthInfoMap.put(mth.getMethodInfo(), mth);
|
||||
}
|
||||
@@ -373,7 +373,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
|
||||
public void addInnerClass(ClassNode cls) {
|
||||
if (innerClasses.isEmpty()) {
|
||||
innerClasses = new ArrayList<ClassNode>(3);
|
||||
innerClasses = new ArrayList<>(3);
|
||||
}
|
||||
innerClasses.add(cls);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.InfoStorage;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.files.DexFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.android.dex.ClassData;
|
||||
import com.android.dex.ClassData.Method;
|
||||
import com.android.dex.ClassDef;
|
||||
@@ -27,6 +16,14 @@ import com.android.dex.FieldId;
|
||||
import com.android.dex.MethodId;
|
||||
import com.android.dex.ProtoId;
|
||||
import com.android.dex.TypeList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.utils.files.DexFile;
|
||||
|
||||
public class DexNode implements IDexNode {
|
||||
|
||||
@@ -35,19 +32,19 @@ public class DexNode implements IDexNode {
|
||||
private final RootNode root;
|
||||
private final Dex dexBuf;
|
||||
private final DexFile file;
|
||||
private final int dexId;
|
||||
|
||||
private final List<ClassNode> classes = new ArrayList<ClassNode>();
|
||||
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<ClassInfo, ClassNode>();
|
||||
private final List<ClassNode> classes = new ArrayList<>();
|
||||
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<>();
|
||||
|
||||
private final InfoStorage infoStorage = new InfoStorage();
|
||||
|
||||
public DexNode(RootNode root, DexFile input) {
|
||||
public DexNode(RootNode root, DexFile input, int dexId) {
|
||||
this.root = root;
|
||||
this.file = input;
|
||||
this.dexBuf = input.getDexBuf();
|
||||
this.dexId = dexId;
|
||||
}
|
||||
|
||||
public void loadClasses() throws DecodeException {
|
||||
public void loadClasses() {
|
||||
for (ClassDef cls : dexBuf.classDefs()) {
|
||||
ClassNode clsNode = new ClassNode(this, cls);
|
||||
classes.add(clsNode);
|
||||
@@ -57,7 +54,7 @@ public class DexNode implements IDexNode {
|
||||
|
||||
void initInnerClasses() {
|
||||
// move inner classes
|
||||
List<ClassNode> inner = new ArrayList<ClassNode>();
|
||||
List<ClassNode> inner = new ArrayList<>();
|
||||
for (ClassNode cls : classes) {
|
||||
if (cls.getClassInfo().isInner()) {
|
||||
inner.add(cls);
|
||||
@@ -153,10 +150,6 @@ public class DexNode implements IDexNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
public InfoStorage getInfoStorage() {
|
||||
return infoStorage;
|
||||
}
|
||||
|
||||
public DexFile getDexFile() {
|
||||
return file;
|
||||
}
|
||||
@@ -189,7 +182,7 @@ public class DexNode implements IDexNode {
|
||||
|
||||
public List<ArgType> readParamList(int parametersOffset) {
|
||||
TypeList paramList = dexBuf.readTypeList(parametersOffset);
|
||||
List<ArgType> args = new ArrayList<ArgType>(paramList.getTypes().length);
|
||||
List<ArgType> args = new ArrayList<>(paramList.getTypes().length);
|
||||
for (short t : paramList.getTypes()) {
|
||||
args.add(getType(t));
|
||||
}
|
||||
@@ -214,6 +207,10 @@ public class DexNode implements IDexNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getDexId() {
|
||||
return dexId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DEX";
|
||||
|
||||
@@ -6,8 +6,6 @@ public interface ILoadable {
|
||||
|
||||
/**
|
||||
* On demand loading
|
||||
*
|
||||
* @throws DecodeException
|
||||
*/
|
||||
void load() throws DecodeException;
|
||||
|
||||
@@ -15,5 +13,4 @@ public interface ILoadable {
|
||||
* Free resources
|
||||
*/
|
||||
void unload();
|
||||
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class InsnNode extends LineAttrNode {
|
||||
if (argsCount == 0) {
|
||||
this.arguments = Collections.emptyList();
|
||||
} else {
|
||||
this.arguments = new ArrayList<InsnArg>(argsCount);
|
||||
this.arguments = new ArrayList<>(argsCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,12 +113,12 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
load();
|
||||
noCode = false;
|
||||
}
|
||||
throw new DecodeException(this, "Load method exception", e);
|
||||
throw new DecodeException(this, "Load method exception: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkInstructions() {
|
||||
List<RegisterArg> list = new ArrayList<RegisterArg>();
|
||||
List<RegisterArg> list = new ArrayList<>();
|
||||
for (InsnNode insnNode : instructions) {
|
||||
if (insnNode == null) {
|
||||
continue;
|
||||
@@ -216,7 +216,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
argsList = Collections.emptyList();
|
||||
return;
|
||||
}
|
||||
argsList = new ArrayList<RegisterArg>(args.size());
|
||||
argsList = new ArrayList<>(args.size());
|
||||
for (ArgType arg : args) {
|
||||
argsList.add(InsnArg.typeImmutableReg(pos, arg));
|
||||
pos += arg.getRegCount();
|
||||
@@ -225,7 +225,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
|
||||
public List<RegisterArg> getArguments(boolean includeThis) {
|
||||
if (includeThis && thisArg != null) {
|
||||
List<RegisterArg> list = new ArrayList<RegisterArg>(argsList.size() + 1);
|
||||
List<RegisterArg> list = new ArrayList<>(argsList.size() + 1);
|
||||
list.add(thisArg);
|
||||
list.addAll(argsList);
|
||||
return list;
|
||||
@@ -259,8 +259,8 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
}
|
||||
|
||||
int hc = 0;
|
||||
Set<Integer> addrs = new HashSet<Integer>();
|
||||
List<TryCatchBlock> catches = new ArrayList<TryCatchBlock>(catchBlocks.length);
|
||||
Set<Integer> addrs = new HashSet<>();
|
||||
List<TryCatchBlock> catches = new ArrayList<>(catchBlocks.length);
|
||||
|
||||
for (CatchHandler handler : catchBlocks) {
|
||||
TryCatchBlock tcBlock = new TryCatchBlock();
|
||||
@@ -399,8 +399,8 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
}
|
||||
|
||||
public void initBasicBlocks() {
|
||||
blocks = new ArrayList<BlockNode>();
|
||||
exitBlocks = new ArrayList<BlockNode>(1);
|
||||
blocks = new ArrayList<>();
|
||||
exitBlocks = new ArrayList<>(1);
|
||||
}
|
||||
|
||||
public void finishBasicBlocks() {
|
||||
@@ -437,7 +437,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
|
||||
public void registerLoop(LoopInfo loop) {
|
||||
if (loops.isEmpty()) {
|
||||
loops = new ArrayList<LoopInfo>(5);
|
||||
loops = new ArrayList<>(5);
|
||||
}
|
||||
loop.setId(loops.size());
|
||||
loops.add(loop);
|
||||
@@ -460,7 +460,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
if (loops.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<LoopInfo> list = new ArrayList<LoopInfo>(loops.size());
|
||||
List<LoopInfo> list = new ArrayList<>(loops.size());
|
||||
for (LoopInfo loop : loops) {
|
||||
if (loop.getLoopBlocks().contains(block)) {
|
||||
list.add(loop);
|
||||
@@ -479,7 +479,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
|
||||
public ExceptionHandler addExceptionHandler(ExceptionHandler handler) {
|
||||
if (exceptionHandlers.isEmpty()) {
|
||||
exceptionHandlers = new ArrayList<ExceptionHandler>(2);
|
||||
exceptionHandlers = new ArrayList<>(2);
|
||||
} else {
|
||||
for (ExceptionHandler h : exceptionHandlers) {
|
||||
if (h == handler || h.getHandleOffset() == handler.getHandleOffset()) {
|
||||
@@ -528,7 +528,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
boolean result = false;
|
||||
if (accFlags.isConstructor() && mthInfo.isConstructor()) {
|
||||
int defaultArgCount = 0;
|
||||
/** workaround for non-static inner class constructor, that has synthetic argument */
|
||||
// workaround for non-static inner class constructor, that has synthetic argument
|
||||
if (parentClass.getClassInfo().isInner()
|
||||
&& !parentClass.getAccessFlags().isStatic()) {
|
||||
ClassNode outerCls = parentClass.getParentClass();
|
||||
@@ -557,7 +557,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
public SSAVar makeNewSVar(int regNum, int version, @NotNull RegisterArg assignArg) {
|
||||
SSAVar var = new SSAVar(regNum, version, assignArg);
|
||||
if (sVars.isEmpty()) {
|
||||
sVars = new ArrayList<SSAVar>();
|
||||
sVars = new ArrayList<>();
|
||||
}
|
||||
sVars.add(var);
|
||||
return var;
|
||||
|
||||
@@ -1,23 +1,5 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.core.clsp.ClspGraph;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.ConstStorage;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.android.AndroidResourcesUtils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.DexFile;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
import jadx.core.xmlgen.ResourceStorage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
@@ -27,13 +9,33 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.ResourcesLoader;
|
||||
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.utils.ErrorsCounter;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.android.AndroidResourcesUtils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.DexFile;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
import jadx.core.xmlgen.ResourceStorage;
|
||||
|
||||
public class RootNode {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RootNode.class);
|
||||
|
||||
private final ErrorsCounter errorsCounter = new ErrorsCounter();
|
||||
private final IJadxArgs args;
|
||||
private final JadxArgs args;
|
||||
private final StringUtils stringUtils;
|
||||
private final ConstStorage constValues;
|
||||
private final InfoStorage infoStorage = new InfoStorage();
|
||||
|
||||
private List<DexNode> dexNodes;
|
||||
@Nullable
|
||||
@@ -41,22 +43,22 @@ public class RootNode {
|
||||
private ClassNode appResClass;
|
||||
private ClspGraph clsp;
|
||||
|
||||
public RootNode(IJadxArgs args) {
|
||||
public RootNode(JadxArgs args) {
|
||||
this.args = args;
|
||||
this.stringUtils = new StringUtils(args);
|
||||
this.constValues = new ConstStorage(args);
|
||||
}
|
||||
|
||||
public void load(List<InputFile> inputFiles) throws DecodeException {
|
||||
dexNodes = new ArrayList<DexNode>();
|
||||
public void load(List<InputFile> inputFiles) {
|
||||
dexNodes = new ArrayList<>();
|
||||
for (InputFile input : inputFiles) {
|
||||
for (DexFile dexFile : input.getDexFiles()) {
|
||||
try {
|
||||
LOG.debug("Load: {}", dexFile);
|
||||
DexNode dexNode = new DexNode(this, dexFile);
|
||||
DexNode dexNode = new DexNode(this, dexFile, dexNodes.size());
|
||||
dexNodes.add(dexNode);
|
||||
} catch (Exception e) {
|
||||
throw new DecodeException("Error decode file: " + dexFile, e);
|
||||
throw new JadxRuntimeException("Error decode file: " + dexFile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,7 +80,7 @@ public class RootNode {
|
||||
LOG.debug("'.arsc' file not found");
|
||||
return;
|
||||
}
|
||||
final ResTableParser parser = new ResTableParser();
|
||||
ResTableParser parser = new ResTableParser();
|
||||
try {
|
||||
ResourcesLoader.decodeStream(arsc, new ResourcesLoader.ResourceDecoder() {
|
||||
@Override
|
||||
@@ -101,22 +103,22 @@ public class RootNode {
|
||||
appResClass = AndroidResourcesUtils.searchAppResClass(this);
|
||||
}
|
||||
|
||||
public void initClassPath() throws DecodeException {
|
||||
public void initClassPath() {
|
||||
try {
|
||||
if (this.clsp == null) {
|
||||
ClspGraph clsp = new ClspGraph();
|
||||
clsp.load();
|
||||
ClspGraph newClsp = new ClspGraph();
|
||||
newClsp.load();
|
||||
|
||||
List<ClassNode> classes = new ArrayList<ClassNode>();
|
||||
List<ClassNode> classes = new ArrayList<>();
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
classes.addAll(dexNode.getClasses());
|
||||
}
|
||||
clsp.addApp(classes);
|
||||
newClsp.addApp(classes);
|
||||
|
||||
this.clsp = clsp;
|
||||
this.clsp = newClsp;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new DecodeException("Error loading classpath", e);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Error loading classpath", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +129,7 @@ public class RootNode {
|
||||
}
|
||||
|
||||
public List<ClassNode> getClasses(boolean includeInner) {
|
||||
List<ClassNode> classes = new ArrayList<ClassNode>();
|
||||
List<ClassNode> classes = new ArrayList<>();
|
||||
for (DexNode dex : dexNodes) {
|
||||
if (includeInner) {
|
||||
classes.addAll(dex.getClasses());
|
||||
@@ -154,7 +156,7 @@ public class RootNode {
|
||||
}
|
||||
|
||||
public List<ClassNode> searchClassByShortName(String shortName) {
|
||||
List<ClassNode> list = new ArrayList<ClassNode>();
|
||||
List<ClassNode> list = new ArrayList<>();
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
for (ClassNode cls : dexNode.getClasses()) {
|
||||
if (cls.getClassInfo().getShortName().equals(shortName)) {
|
||||
@@ -186,10 +188,6 @@ public class RootNode {
|
||||
return appResClass;
|
||||
}
|
||||
|
||||
public IJadxArgs getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public StringUtils getStringUtils() {
|
||||
return stringUtils;
|
||||
}
|
||||
@@ -197,4 +195,12 @@ public class RootNode {
|
||||
public ConstStorage getConstValues() {
|
||||
return constValues;
|
||||
}
|
||||
|
||||
public InfoStorage getInfoStorage() {
|
||||
return infoStorage;
|
||||
}
|
||||
|
||||
public JadxArgs getArgs() {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class AnnotationsParser {
|
||||
if (size == 0) {
|
||||
return AnnotationsList.EMPTY;
|
||||
}
|
||||
List<Annotation> list = new ArrayList<Annotation>(size);
|
||||
List<Annotation> list = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Section anSection = dex.openSection(section.readInt());
|
||||
Annotation a = readAnnotation(dex, anSection, true);
|
||||
@@ -97,7 +97,7 @@ public class AnnotationsParser {
|
||||
}
|
||||
int typeIndex = s.readUleb128();
|
||||
int size = s.readUleb128();
|
||||
Map<String, Object> values = new LinkedHashMap<String, Object>(size);
|
||||
Map<String, Object> values = new LinkedHashMap<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
String name = dex.getString(s.readUleb128());
|
||||
values.put(name, parser.parseValue());
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package jadx.core.dex.nodes.parser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
@@ -9,16 +13,8 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
|
||||
public class DebugInfoParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DebugInfoParser.class);
|
||||
private static final int DBG_END_SEQUENCE = 0x00;
|
||||
private static final int DBG_ADVANCE_PC = 0x01;
|
||||
private static final int DBG_ADVANCE_LINE = 0x02;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package jadx.core.dex.nodes.parser;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
import com.android.dex.Leb128;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
public class EncValueParser {
|
||||
|
||||
private static final int ENCODED_BYTE = 0x00;
|
||||
@@ -83,7 +83,7 @@ public class EncValueParser {
|
||||
|
||||
case ENCODED_ARRAY:
|
||||
int count = Leb128.readUnsignedLeb128(in);
|
||||
List<Object> values = new ArrayList<Object>(count);
|
||||
List<Object> values = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
values.add(parseValue());
|
||||
}
|
||||
@@ -91,8 +91,10 @@ public class EncValueParser {
|
||||
|
||||
case ENCODED_ANNOTATION:
|
||||
return AnnotationsParser.readAnnotation(dex, in, false);
|
||||
|
||||
default:
|
||||
throw new DecodeException("Unknown encoded value type: 0x" + Integer.toHexString(type));
|
||||
}
|
||||
throw new DecodeException("Unknown encoded value type: 0x" + Integer.toHexString(type));
|
||||
}
|
||||
|
||||
private int parseUnsignedInt(int byteCount) {
|
||||
|
||||
@@ -189,7 +189,7 @@ public class SignatureParser {
|
||||
}
|
||||
|
||||
private ArgType[] consumeGenericArgs() {
|
||||
List<ArgType> list = new LinkedList<ArgType>();
|
||||
List<ArgType> list = new LinkedList<>();
|
||||
ArgType type;
|
||||
do {
|
||||
if (lookAhead('*')) {
|
||||
@@ -220,7 +220,7 @@ public class SignatureParser {
|
||||
if (!lookAhead('<')) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<ArgType, List<ArgType>> map = new LinkedHashMap<ArgType, List<ArgType>>(2);
|
||||
Map<ArgType, List<ArgType>> map = new LinkedHashMap<>(2);
|
||||
consume('<');
|
||||
while (true) {
|
||||
if (lookAhead('>') || next() == STOP_CHAR) {
|
||||
@@ -251,7 +251,7 @@ public class SignatureParser {
|
||||
ArgType argType = consumeType();
|
||||
if (!argType.equals(ArgType.OBJECT)) {
|
||||
if (types.isEmpty()) {
|
||||
types = new LinkedList<ArgType>();
|
||||
types = new LinkedList<>();
|
||||
}
|
||||
types.add(argType);
|
||||
}
|
||||
@@ -269,7 +269,7 @@ public class SignatureParser {
|
||||
consume(')');
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ArgType> args = new LinkedList<ArgType>();
|
||||
List<ArgType> args = new LinkedList<>();
|
||||
do {
|
||||
args.add(consumeType());
|
||||
} while (!lookAhead(')'));
|
||||
|
||||
@@ -12,7 +12,7 @@ public final class Region extends AbstractRegion {
|
||||
|
||||
public Region(IRegion parent) {
|
||||
super(parent);
|
||||
this.blocks = new ArrayList<IContainer>(1);
|
||||
this.blocks = new ArrayList<>(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,8 +20,8 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
|
||||
public SwitchRegion(IRegion parent, BlockNode header) {
|
||||
super(parent);
|
||||
this.header = header;
|
||||
this.keys = new ArrayList<List<Object>>();
|
||||
this.cases = new ArrayList<IContainer>();
|
||||
this.keys = new ArrayList<>();
|
||||
this.cases = new ArrayList<>();
|
||||
}
|
||||
|
||||
public BlockNode getHeader() {
|
||||
@@ -51,7 +51,7 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
|
||||
|
||||
@Override
|
||||
public List<IContainer> getSubBlocks() {
|
||||
List<IContainer> all = new ArrayList<IContainer>(cases.size() + 2);
|
||||
List<IContainer> all = new ArrayList<>(cases.size() + 2);
|
||||
all.add(header);
|
||||
all.addAll(cases);
|
||||
if (defCase != null) {
|
||||
@@ -62,7 +62,7 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
|
||||
|
||||
@Override
|
||||
public List<IContainer> getBranches() {
|
||||
List<IContainer> branches = new ArrayList<IContainer>(cases.size() + 1);
|
||||
List<IContainer> branches = new ArrayList<>(cases.size() + 1);
|
||||
branches.addAll(cases);
|
||||
branches.add(defCase);
|
||||
return Collections.unmodifiableList(branches);
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
public final class SynchronizedRegion extends AbstractRegion {
|
||||
|
||||
private final InsnNode enterInsn;
|
||||
private final List<InsnNode> exitInsns = new LinkedList<InsnNode>();
|
||||
private final List<InsnNode> exitInsns = new LinkedList<>();
|
||||
private final Region region;
|
||||
|
||||
public SynchronizedRegion(IRegion parent, InsnNode insn) {
|
||||
|
||||
@@ -28,7 +28,7 @@ public final class TryCatchRegion extends AbstractRegion implements IBranchRegio
|
||||
public void setTryCatchBlock(TryCatchBlock tryCatchBlock) {
|
||||
this.tryCatchBlock = tryCatchBlock;
|
||||
int count = tryCatchBlock.getHandlersCount();
|
||||
this.catchRegions = new LinkedHashMap<ExceptionHandler, IContainer>(count);
|
||||
this.catchRegions = new LinkedHashMap<>(count);
|
||||
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
|
||||
IContainer handlerRegion = handler.getHandlerRegion();
|
||||
if (handlerRegion != null) {
|
||||
@@ -63,7 +63,7 @@ public final class TryCatchRegion extends AbstractRegion implements IBranchRegio
|
||||
|
||||
@Override
|
||||
public List<IContainer> getSubBlocks() {
|
||||
List<IContainer> all = new ArrayList<IContainer>(2 + catchRegions.size());
|
||||
List<IContainer> all = new ArrayList<>(2 + catchRegions.size());
|
||||
all.add(tryRegion);
|
||||
all.addAll(catchRegions.values());
|
||||
if (finallyRegion != null) {
|
||||
|
||||
@@ -49,7 +49,7 @@ public final class IfCondition {
|
||||
if (c.mode == Mode.COMPARE) {
|
||||
this.args = Collections.emptyList();
|
||||
} else {
|
||||
this.args = new ArrayList<IfCondition>(c.args);
|
||||
this.args = new ArrayList<>(c.args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ public final class IfCondition {
|
||||
case AND:
|
||||
case OR:
|
||||
List<IfCondition> args = cond.getArgs();
|
||||
List<IfCondition> newArgs = new ArrayList<IfCondition>(args.size());
|
||||
List<IfCondition> newArgs = new ArrayList<>(args.size());
|
||||
for (IfCondition arg : args) {
|
||||
newArgs.add(invert(arg));
|
||||
}
|
||||
@@ -154,7 +154,7 @@ public final class IfCondition {
|
||||
IfCondition simpl = simplify(arg);
|
||||
if (simpl != arg) {
|
||||
if (args == null) {
|
||||
args = new ArrayList<IfCondition>(cond.getArgs());
|
||||
args = new ArrayList<>(cond.getArgs());
|
||||
}
|
||||
args.set(i, simpl);
|
||||
}
|
||||
@@ -206,7 +206,7 @@ public final class IfCondition {
|
||||
}
|
||||
|
||||
public List<RegisterArg> getRegisterArgs() {
|
||||
List<RegisterArg> list = new LinkedList<RegisterArg>();
|
||||
List<RegisterArg> list = new LinkedList<>();
|
||||
if (mode == Mode.COMPARE) {
|
||||
compare.getInsn().getRegisterArgs(list);
|
||||
} else {
|
||||
|
||||
@@ -82,7 +82,7 @@ public final class IfRegion extends AbstractRegion implements IBranchRegion {
|
||||
|
||||
@Override
|
||||
public List<IContainer> getSubBlocks() {
|
||||
List<IContainer> all = new ArrayList<IContainer>(3);
|
||||
List<IContainer> all = new ArrayList<>(3);
|
||||
all.add(header);
|
||||
if (thenRegion != null) {
|
||||
all.add(thenRegion);
|
||||
@@ -95,7 +95,7 @@ public final class IfRegion extends AbstractRegion implements IBranchRegion {
|
||||
|
||||
@Override
|
||||
public List<IContainer> getBranches() {
|
||||
List<IContainer> branches = new ArrayList<IContainer>(2);
|
||||
List<IContainer> branches = new ArrayList<>(2);
|
||||
branches.add(thenRegion);
|
||||
branches.add(elseRegion);
|
||||
return Collections.unmodifiableList(branches);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package jadx.core.dex.regions.loops;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
@@ -9,15 +13,15 @@ import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.regions.AbstractRegion;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class LoopRegion extends AbstractRegion {
|
||||
|
||||
private final LoopInfo info;
|
||||
// loop header contains one 'if' insn, equals null for infinite loop
|
||||
/**
|
||||
* loop header contains one 'if' insn, equals null for infinite loop
|
||||
*/
|
||||
@Nullable
|
||||
private IfCondition condition;
|
||||
private final BlockNode conditionBlock;
|
||||
// instruction which must be executed before condition in every loop
|
||||
@@ -27,7 +31,7 @@ public final class LoopRegion extends AbstractRegion {
|
||||
|
||||
private LoopType type;
|
||||
|
||||
public LoopRegion(IRegion parent, LoopInfo info, BlockNode header, boolean reversed) {
|
||||
public LoopRegion(IRegion parent, LoopInfo info, @Nullable BlockNode header, boolean reversed) {
|
||||
super(parent);
|
||||
this.info = info;
|
||||
this.conditionBlock = header;
|
||||
@@ -126,6 +130,16 @@ public final class LoopRegion extends AbstractRegion {
|
||||
}
|
||||
}
|
||||
|
||||
public int getConditionSourceLine() {
|
||||
if (conditionBlock != null) {
|
||||
List<InsnNode> condInsns = conditionBlock.getInstructions();
|
||||
if (!condInsns.isEmpty()) {
|
||||
return condInsns.get(0).getSourceLine();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public LoopType getType() {
|
||||
return type;
|
||||
}
|
||||
@@ -136,7 +150,7 @@ public final class LoopRegion extends AbstractRegion {
|
||||
|
||||
@Override
|
||||
public List<IContainer> getSubBlocks() {
|
||||
List<IContainer> all = new ArrayList<IContainer>(3);
|
||||
List<IContainer> all = new ArrayList<>(3);
|
||||
if (preCondition != null) {
|
||||
all.add(preCondition);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ExceptionHandler {
|
||||
private final int handleOffset;
|
||||
|
||||
private BlockNode handlerBlock;
|
||||
private final List<BlockNode> blocks = new ArrayList<BlockNode>();
|
||||
private final List<BlockNode> blocks = new ArrayList<>();
|
||||
private IContainer handlerRegion;
|
||||
private InsnArg arg;
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ public class TryCatchBlock {
|
||||
private final CatchAttr attr;
|
||||
|
||||
public TryCatchBlock() {
|
||||
handlers = new LinkedList<ExceptionHandler>();
|
||||
insns = new ArrayList<InsnNode>();
|
||||
handlers = new LinkedList<>();
|
||||
insns = new ArrayList<>();
|
||||
attr = new CatchAttr(this);
|
||||
}
|
||||
|
||||
@@ -68,10 +68,9 @@ public class TryCatchBlock {
|
||||
BlockUtils.skipPredSyntheticPaths(block);
|
||||
block.add(AFlag.SKIP);
|
||||
ExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER);
|
||||
if (excHandlerAttr != null) {
|
||||
if (excHandlerAttr.getHandler().equals(handler)) {
|
||||
block.remove(AType.EXC_HANDLER);
|
||||
}
|
||||
if (excHandlerAttr != null
|
||||
&& excHandlerAttr.getHandler().equals(handler)) {
|
||||
block.remove(AType.EXC_HANDLER);
|
||||
}
|
||||
SplitterBlockAttr splitter = handler.getHandlerBlock().get(AType.SPLITTER_BLOCK);
|
||||
if (splitter != null) {
|
||||
|
||||
@@ -9,15 +9,17 @@ public class AbstractVisitor implements IDexTreeVisitor {
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) throws JadxException {
|
||||
// no op implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws JadxException {
|
||||
// no op implementation
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
// no op implementation
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
}
|
||||
|
||||
public static List<RegisterArg> getArgs(InsnNode insn) {
|
||||
List<RegisterArg> args = new LinkedList<RegisterArg>();
|
||||
List<RegisterArg> args = new LinkedList<>();
|
||||
addArgs(insn, args);
|
||||
return args;
|
||||
}
|
||||
@@ -191,11 +191,11 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
}
|
||||
InsnList insnList = new InsnList(block.getInstructions());
|
||||
int insnCount = insnList.size();
|
||||
List<ArgsInfo> argsList = new ArrayList<ArgsInfo>(insnCount);
|
||||
List<ArgsInfo> argsList = new ArrayList<>(insnCount);
|
||||
for (int i = 0; i < insnCount; i++) {
|
||||
argsList.add(new ArgsInfo(insnList.get(i), argsList, i));
|
||||
}
|
||||
List<WrapInfo> wrapList = new ArrayList<WrapInfo>();
|
||||
List<WrapInfo> wrapList = new ArrayList<>();
|
||||
for (ArgsInfo argsInfo : argsList) {
|
||||
List<RegisterArg> args = argsInfo.getArgs();
|
||||
if (args.isEmpty()) {
|
||||
|
||||
@@ -31,7 +31,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
List<InsnNode> toRemove = new ArrayList<InsnNode>();
|
||||
List<InsnNode> toRemove = new ArrayList<>();
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
toRemove.clear();
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
@@ -99,7 +99,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
||||
|
||||
private static boolean replaceConst(MethodNode mth, InsnNode constInsn, long literal) {
|
||||
SSAVar sVar = constInsn.getResult().getSVar();
|
||||
List<RegisterArg> use = new ArrayList<RegisterArg>(sVar.getUseList());
|
||||
List<RegisterArg> use = new ArrayList<>(sVar.getUseList());
|
||||
int replaceCount = 0;
|
||||
for (RegisterArg arg : use) {
|
||||
InsnNode useInsn = arg.getParentInsn();
|
||||
|
||||
@@ -7,40 +7,53 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.parser.DebugInfoParser;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DebugInfoVisitor extends AbstractVisitor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DebugInfoVisitor.class);
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
int debugOffset = mth.getDebugInfoOffset();
|
||||
if (debugOffset > 0) {
|
||||
InsnNode[] insnArr = mth.getInstructions();
|
||||
DebugInfoParser debugInfoParser = new DebugInfoParser(mth, debugOffset, insnArr);
|
||||
debugInfoParser.process();
|
||||
|
||||
// set method source line from first instruction
|
||||
if (insnArr.length != 0) {
|
||||
for (InsnNode insn : insnArr) {
|
||||
if (insn != null) {
|
||||
int line = insn.getSourceLine();
|
||||
if (line != 0) {
|
||||
mth.setSourceLine(line - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
int debugOffset = mth.getDebugInfoOffset();
|
||||
if (debugOffset > 0) {
|
||||
processDebugInfo(mth, debugOffset);
|
||||
}
|
||||
if (!mth.getReturnType().equals(ArgType.VOID)) {
|
||||
// fix debug info for splitter 'return' instructions
|
||||
for (BlockNode exit : mth.getExitBlocks()) {
|
||||
InsnNode ret = BlockUtils.getLastInsn(exit);
|
||||
if (ret == null) {
|
||||
continue;
|
||||
}
|
||||
InsnNode oldRet = insnArr[ret.getOffset()];
|
||||
if (oldRet == ret) {
|
||||
continue;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error in debug info parser: {}", ErrorsCounter.formatErrorMsg(mth, e.getMessage()), e);
|
||||
} finally {
|
||||
mth.unloadInsnArr();
|
||||
}
|
||||
}
|
||||
|
||||
private void processDebugInfo(MethodNode mth, int debugOffset) throws DecodeException {
|
||||
InsnNode[] insnArr = mth.getInstructions();
|
||||
DebugInfoParser debugInfoParser = new DebugInfoParser(mth, debugOffset, insnArr);
|
||||
debugInfoParser.process();
|
||||
|
||||
if (insnArr.length != 0) {
|
||||
setMethodSourceLine(mth, insnArr);
|
||||
}
|
||||
if (!mth.getReturnType().equals(ArgType.VOID)) {
|
||||
setLineForReturn(mth, insnArr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix debug info for splitter 'return' instructions
|
||||
*/
|
||||
private void setLineForReturn(MethodNode mth, InsnNode[] insnArr) {
|
||||
for (BlockNode exit : mth.getExitBlocks()) {
|
||||
InsnNode ret = BlockUtils.getLastInsn(exit);
|
||||
if (ret != null) {
|
||||
InsnNode oldRet = insnArr[ret.getOffset()];
|
||||
if (oldRet != ret) {
|
||||
RegisterArg oldArg = (RegisterArg) oldRet.getArg(0);
|
||||
RegisterArg newArg = (RegisterArg) ret.getArg(0);
|
||||
newArg.mergeDebugInfo(oldArg.getType(), oldArg.getName());
|
||||
@@ -48,6 +61,20 @@ public class DebugInfoVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
}
|
||||
mth.unloadInsnArr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set method source line from first instruction
|
||||
*/
|
||||
private void setMethodSourceLine(MethodNode mth, InsnNode[] insnArr) {
|
||||
for (InsnNode insn : insnArr) {
|
||||
if (insn != null) {
|
||||
int line = insn.getSourceLine();
|
||||
if (line != 0) {
|
||||
mth.setSourceLine(line - 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class DepthTraversal {
|
||||
visit(visitor, mth);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
} catch (Exception e) {
|
||||
ErrorsCounter.classError(cls,
|
||||
e.getClass().getSimpleName() + " in pass: " + visitor.getClass().getSimpleName(), e);
|
||||
}
|
||||
@@ -29,9 +29,12 @@ public class DepthTraversal {
|
||||
}
|
||||
try {
|
||||
visitor.visit(mth);
|
||||
} catch (Throwable e) {
|
||||
} catch (Exception e) {
|
||||
ErrorsCounter.methodError(mth,
|
||||
e.getClass().getSimpleName() + " in pass: " + visitor.getClass().getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private DepthTraversal() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,42 +23,33 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DotGraphVisitor extends AbstractVisitor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DotGraphVisitor.class);
|
||||
|
||||
private static final String NL = "\\l";
|
||||
private static final boolean PRINT_DOMINATORS = false;
|
||||
|
||||
private final File dir;
|
||||
private final boolean useRegions;
|
||||
private final boolean rawInsn;
|
||||
|
||||
public static DotGraphVisitor dump(File outDir) {
|
||||
return new DotGraphVisitor(outDir, false, false);
|
||||
public static DotGraphVisitor dump() {
|
||||
return new DotGraphVisitor(false, false);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRaw(File outDir) {
|
||||
return new DotGraphVisitor(outDir, false, true);
|
||||
public static DotGraphVisitor dumpRaw() {
|
||||
return new DotGraphVisitor(false, true);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRegions(File outDir) {
|
||||
return new DotGraphVisitor(outDir, true, false);
|
||||
public static DotGraphVisitor dumpRegions() {
|
||||
return new DotGraphVisitor(true, false);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRawRegions(File outDir) {
|
||||
return new DotGraphVisitor(outDir, true, true);
|
||||
public static DotGraphVisitor dumpRawRegions() {
|
||||
return new DotGraphVisitor(true, true);
|
||||
}
|
||||
|
||||
private DotGraphVisitor(File outDir, boolean useRegions, boolean rawInsn) {
|
||||
this.dir = outDir;
|
||||
private DotGraphVisitor(boolean useRegions, boolean rawInsn) {
|
||||
this.useRegions = useRegions;
|
||||
this.rawInsn = rawInsn;
|
||||
LOG.debug("DOT {}{}graph dump dir: {}",
|
||||
useRegions ? "regions " : "", rawInsn ? "raw " : "", outDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,12 +57,25 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
new DumpDotGraph().process(mth);
|
||||
File outRootDir = mth.root().getArgs().getOutDir();
|
||||
new DumpDotGraph(outRootDir).process(mth);
|
||||
}
|
||||
|
||||
public void save(File dir, MethodNode mth) {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
new DumpDotGraph(dir).process(mth);
|
||||
}
|
||||
|
||||
private class DumpDotGraph {
|
||||
private final CodeWriter dot = new CodeWriter();
|
||||
private final CodeWriter conn = new CodeWriter();
|
||||
private final File dir;
|
||||
|
||||
public DumpDotGraph(File dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public void process(MethodNode mth) {
|
||||
dot.startLine("digraph \"CFG for");
|
||||
@@ -96,7 +100,7 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
+ "(" + Utils.listToString(mth.getArguments(true)) + ") "));
|
||||
|
||||
String attrs = attributesString(mth);
|
||||
if (attrs.length() != 0) {
|
||||
if (!attrs.isEmpty()) {
|
||||
dot.add(" | ").add(attrs);
|
||||
}
|
||||
dot.add("}\"];");
|
||||
@@ -122,7 +126,7 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
processRegion(mth, h.getHandlerRegion());
|
||||
}
|
||||
}
|
||||
Set<IBlock> regionsBlocks = new HashSet<IBlock>(mth.getBasicBlocks().size());
|
||||
Set<IBlock> regionsBlocks = new HashSet<>(mth.getBasicBlocks().size());
|
||||
RegionUtils.getAllRegionBlocks(mth.getRegion(), regionsBlocks);
|
||||
for (ExceptionHandler handler : mth.getExceptionHandlers()) {
|
||||
IContainer handlerRegion = handler.getHandlerRegion();
|
||||
@@ -143,7 +147,7 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
dot.startLine("subgraph " + makeName(region) + " {");
|
||||
dot.startLine("label = \"").add(r.toString());
|
||||
String attrs = attributesString(r);
|
||||
if (attrs.length() != 0) {
|
||||
if (!attrs.isEmpty()) {
|
||||
dot.add(" | ").add(attrs);
|
||||
}
|
||||
dot.add("\";");
|
||||
@@ -171,11 +175,11 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
dot.add("label=\"{");
|
||||
dot.add(String.valueOf(block.getId())).add("\\:\\ ");
|
||||
dot.add(InsnUtils.formatOffset(block.getStartOffset()));
|
||||
if (attrs.length() != 0) {
|
||||
if (!attrs.isEmpty()) {
|
||||
dot.add('|').add(attrs);
|
||||
}
|
||||
String insns = insertInsns(mth, block);
|
||||
if (insns.length() != 0) {
|
||||
if (!insns.isEmpty()) {
|
||||
dot.add('|').add(insns);
|
||||
}
|
||||
dot.add("}\"];");
|
||||
@@ -208,11 +212,11 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
dot.add("color=red,");
|
||||
}
|
||||
dot.add("label=\"{");
|
||||
if (attrs.length() != 0) {
|
||||
if (!attrs.isEmpty()) {
|
||||
dot.add(attrs);
|
||||
}
|
||||
String insns = insertInsns(mth, block);
|
||||
if (insns.length() != 0) {
|
||||
if (!insns.isEmpty()) {
|
||||
dot.add('|').add(insns);
|
||||
}
|
||||
dot.add("}\"];");
|
||||
|
||||
@@ -64,7 +64,7 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
String valuesMethod = "values()" + TypeGen.signature(ArgType.array(clsType));
|
||||
|
||||
// collect enum fields, remove synthetic
|
||||
List<FieldNode> enumFields = new ArrayList<FieldNode>();
|
||||
List<FieldNode> enumFields = new ArrayList<>();
|
||||
for (FieldNode f : cls.getFields()) {
|
||||
if (f.getAccessFlags().isEnum()) {
|
||||
enumFields.add(f);
|
||||
@@ -101,7 +101,7 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
|
||||
// move enum specific instruction from static method to separate list
|
||||
BlockNode staticBlock = staticMethod.getBasicBlocks().get(0);
|
||||
List<InsnNode> enumPutInsns = new ArrayList<InsnNode>();
|
||||
List<InsnNode> enumPutInsns = new ArrayList<>();
|
||||
List<InsnNode> list = staticBlock.getInstructions();
|
||||
int size = list.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
||||
@@ -102,7 +102,7 @@ public class ExtractFieldInit extends AbstractVisitor {
|
||||
|
||||
private static class InitInfo {
|
||||
private final MethodNode constrMth;
|
||||
private final List<InsnNode> putInsns = new ArrayList<InsnNode>();
|
||||
private final List<InsnNode> putInsns = new ArrayList<>();
|
||||
|
||||
private InitInfo(MethodNode constrMth) {
|
||||
this.constrMth = constrMth;
|
||||
@@ -122,7 +122,7 @@ public class ExtractFieldInit extends AbstractVisitor {
|
||||
if (constrList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<InitInfo> infoList = new ArrayList<InitInfo>(constrList.size());
|
||||
List<InitInfo> infoList = new ArrayList<>(constrList.size());
|
||||
for (MethodNode constrMth : constrList) {
|
||||
if (constrMth.isNoCode() || constrMth.getBasicBlocks().isEmpty()) {
|
||||
return;
|
||||
@@ -151,7 +151,7 @@ public class ExtractFieldInit extends AbstractVisitor {
|
||||
if (common == null) {
|
||||
return;
|
||||
}
|
||||
Set<FieldInfo> fields = new HashSet<FieldInfo>();
|
||||
Set<FieldInfo> fields = new HashSet<>();
|
||||
for (InsnNode insn : common.getPutInsns()) {
|
||||
FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) insn).getIndex();
|
||||
FieldNode field = cls.dex().resolveField(fieldInfo);
|
||||
@@ -200,7 +200,7 @@ public class ExtractFieldInit extends AbstractVisitor {
|
||||
} else {
|
||||
return arg.isLiteral() || arg.isThis();
|
||||
}
|
||||
Set<RegisterArg> regs = new HashSet<RegisterArg>();
|
||||
Set<RegisterArg> regs = new HashSet<>();
|
||||
insn.getRegisterArgs(regs);
|
||||
if (!regs.isEmpty()) {
|
||||
for (RegisterArg reg : regs) {
|
||||
@@ -213,7 +213,7 @@ public class ExtractFieldInit extends AbstractVisitor {
|
||||
}
|
||||
|
||||
private static List<MethodNode> getConstructorsList(ClassNode cls) {
|
||||
List<MethodNode> list = new ArrayList<MethodNode>();
|
||||
List<MethodNode> list = new ArrayList<>();
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
AccessInfo accFlags = mth.getAccessFlags();
|
||||
if (!accFlags.isStatic() && accFlags.isConstructor()) {
|
||||
@@ -230,7 +230,7 @@ public class ExtractFieldInit extends AbstractVisitor {
|
||||
if (mth.isNoCode()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<InsnNode> assignInsns = new ArrayList<InsnNode>();
|
||||
List<InsnNode> assignInsns = new ArrayList<>();
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
if (insn.getType() == putInsn) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user