Compare commits

...

104 Commits

Author SHA1 Message Date
Skylot 156e4209f9 feat(build): use semantic-release for automatic release publishing 2018-08-26 22:18:06 +03:00
Skylot 7492889f4e core: prevent endless region processing (#340) 2018-08-23 23:16:36 +03:00
Skylot 0c041120f6 core: show all decompilation errors in code comments (#313) 2018-08-23 23:16:36 +03:00
Skylot ecbb53aaea core: fixed 'this' attribute propagation for move insn (#345) 2018-08-22 21:38:43 +03:00
skylot ffe739b7eb Merge pull request #343 from Donlon/master
Solve unreplaced fields names when deobfuscation is on (#241)
2018-08-22 11:59:04 +03:00
Donlon bd05be6fb6 Delete some changes 2018-08-22 12:14:29 +08:00
skylot ff7df3818f Merge pull request #344 from JaviLukiOfficial/master
New language: Spanish + Small typo corrected on a LOG.error String argument.
2018-08-21 16:49:26 +03:00
JaviLukiOfficial 39899e4edc -New language: Spanish
-Small typo corrected on a LOG.error String argument.
2018-08-21 15:33:22 +02:00
Donlon dc578f98e7 Fix deobfuscation issue 2018-08-21 17:10:20 +08:00
Skylot d7ce41e724 core: don't remove synthetic methods with some logic beside casts (#336) 2018-08-20 23:05:17 +03:00
Skylot eaaeb2c843 core: fix return block split after try/catch (#295) 2018-08-20 21:36:19 +03:00
Skylot 0ae7c1efbf core: rename wrapped synthetic method (#336) 2018-08-19 19:15:31 +03:00
Donlon cb13599739 Upgrade Chinese translation 2018-08-19 15:49:27 +03:00
Skylot a9251de1dd deobf: prevents overlaping of class names and packages (#335) 2018-08-19 13:06:47 +03:00
Skylot 56798e716a gui: min and max deobf lengths must be positive 2018-08-19 12:19:11 +03:00
Donlon 904f0a1197 A subtle bug repairing 2018-08-19 10:24:34 +03:00
Donlon 4d3f2740ce Language switch supported 2018-08-19 10:21:30 +03:00
Skylot f9e7a29c08 core: fix sythetic constructor replacement (#334) 2018-08-16 23:30:53 +03:00
Skylot 6cb14a1c50 core: use flag for mark 'this' register 2018-08-16 22:55:30 +03:00
Skylot ea9f933f9e core: fix register arg hashCode method (#321, #328) 2018-08-15 16:23:30 +03:00
Skylot eb2e5e3da5 cli: set lower java starting heap size 2018-08-15 16:02:29 +03:00
Skylot 9a4e8bdb48 set default deobfuscation min length to 3 (#332) 2018-08-15 15:29:43 +03:00
Sergey Toshin fad0091d87 Prevents generation of NSes second time in wrong place 2018-08-15 15:20:03 +03:00
Skylot b861151f63 core: rollback finally block extraction if some blocks not removed (#327) 2018-08-04 22:32:00 +03:00
Skylot feeafc407a core: exclude inner classes from dependencies (#318) 2018-08-04 15:35:32 +03:00
Skylot ea1c1eb803 core: fix insn move check for field assign (#326) 2018-08-04 14:32:27 +03:00
Skylot b83e20b571 core: improve immutable list implementation 2018-08-01 15:07:05 +03:00
skylot 160ad64e67 Merge pull request #325 from FlXME/patch-3
Making the Classloader threadsafe
2018-07-30 11:41:38 +03:00
skylot 1213ff26b4 Merge pull request #324 from FlXME/patch-1
Performance issue when building strings
2018-07-30 11:34:23 +03:00
skylot 3bf93f1f85 Merge pull request #323 from FlXME/patch-2
Directory Bug
2018-07-30 11:33:59 +03:00
Felix Bergmann a502581640 Making the Classloader threadsafe 2018-07-30 09:31:44 +02:00
Felix Bergmann 1ec041a48f Directory Bug
The correct pattern to make a directory is: `if (!dir.mkdirs() && !dir.isDirectory()) { error }` mkdirs checks for exists so the exists check is redundant.
2018-07-30 00:09:54 +02:00
Felix Bergmann fdaf8492ef Performance issue when building strings
Improve performance by using StringBuilder instead of StringBuffer.
2018-07-30 00:02:14 +02:00
Skylot 2433a7e89c core: fix exception handler jumps (#320) 2018-07-28 22:26:56 +03:00
Skylot 6e358d3eab core: use own immutable list 2018-07-28 22:26:56 +03:00
Skylot 7e462e800f update gradle wrapper and dependencies 2018-07-28 11:21:46 +03:00
Skylot 156e54c77f core: exclude inner classes from class dependencies (#318) 2018-07-22 15:01:38 +03:00
Skylot 9752ec2655 core: fix duplicate regions creation (#314) 2018-07-22 00:50:04 +03:00
asviridenko edc1e5fa84 gui: show the certificate if the certificate file name is not standard (#315)
* show the certificate if the certificate file name is not standard
eg https://play.google.com/store/apps/details?id=com.kms.free
2018-07-22 00:43:59 +03:00
Skylot a959af087b core: fix replace target in if instruction (#317) 2018-07-19 15:27:35 +03:00
Skylot c5994f954a core: fix NPE in signature parser (#313) 2018-07-16 18:16:13 +03:00
asviridenko 03a09debfa gui: show app certificate (#305)
* add node

* add node

* add certificate panel

* add certifcate manager

* работа над проектом

* ресурсы

* включение возможности показа

* небольшие исправления

* start tests

* more tests

* signature test

* fingerprint test

* public key test

* удалено лишний код

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

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