Repository: contra/JMD
Branch: master
Commit: 0f3051ca70fd
Files: 46
Total size: 162.5 KB
Directory structure:
gitextract_nu5m95t5/
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jmd-cli/
│ ├── gradle/
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── src/
│ └── main/
│ └── java/
│ └── net/
│ └── contra/
│ └── jmd/
│ ├── Deobfuscator.java
│ └── Version.java
├── jmd-core/
│ ├── gradle/
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── net/
│ │ └── contra/
│ │ └── jmd/
│ │ ├── transformers/
│ │ │ ├── Transformer.java
│ │ │ ├── allatori/
│ │ │ │ └── AllatoriTransformer.java
│ │ │ ├── dasho/
│ │ │ │ └── DashOTransformer.java
│ │ │ ├── generic/
│ │ │ │ ├── ForeignCallRemover.java
│ │ │ │ ├── GenericStringDeobfuscator.java
│ │ │ │ ├── Renamer.java
│ │ │ │ ├── StackFixer.java
│ │ │ │ ├── StringFixer.java
│ │ │ │ ├── StringScanner.java
│ │ │ │ └── TransformerTemplate.java
│ │ │ ├── jshrink/
│ │ │ │ ├── JShrinkTransformer.java
│ │ │ │ └── StoreHandler.java
│ │ │ ├── smokescreen/
│ │ │ │ └── SmokeScreenTransformer.java
│ │ │ └── zkm/
│ │ │ └── ZKMTransformer.java
│ │ └── util/
│ │ ├── GenericClassLoader.java
│ │ ├── GenericMethods.java
│ │ ├── HandleSearcher.java
│ │ ├── LogHandler.java
│ │ └── NonClassEntries.java
│ └── test/
│ └── java/
│ └── net/
│ └── contra/
│ └── jmd/
│ └── transformers/
│ └── dasho/
│ └── DashOTransformerTest.java
├── jmd-gui/
│ ├── gradle/
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── src/
│ └── main/
│ ├── java/
│ │ └── net/
│ │ └── contra/
│ │ └── jmd/
│ │ └── ConfigureApp.java
│ └── resources/
│ └── view/
│ └── configure.fxml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.gradle
.idea
*.iml
build
================================================
FILE: .travis.yml
================================================
language: java
jdk:
- oraclejdk8
after_success:
- ./gradlew jacocoTestReport coveralls
================================================
FILE: LICENSE
================================================
Copyright (c) 2011 Contra <contra@australia.edu>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
**JMD is a general purpose Java bytecode deobfuscation tool**
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url]
## Usage
java -jar JMD.jar <jarfile location> <transformer name> <debug true/false> <transformer specific args>
JMDGUI.exe provides an optional frontend to the command line interface.
## Road map
* Actualization transformers
* Migrate from BCEL to ASM
* Add tests
## Examples
Remove ZKM obfuscation: java -jar JMD.jar "C:/Files/Magic.jar" zkm true
Remove Allatori obfuscation: java -jar JMD.jar "C:/Files/Magic.jar" allatori true
Remove Allatori-Strong obfuscation: java -jar JMD.jar "C:/Files/Magic.jar" allatori-strong true
Remove JShrink obfuscation: java -jar JMD.jar "C:/Files/Magic.jar" jshrink true
Remove DashO obfuscation: java -jar JMD.jar "C:/Files/Magic.jar" dasho true
Remove SmokeScreen obfuscation: java -jar JMD.jar "C:/Files/Magic.jar" smokescreen true
Remove Generic String obfuscation: java -jar JMD.jar "C:/Files/Magic.jar" genericstringdeobfuscator true
Find all instances of example.com: java -jar JMD.jar "C:/Files/Magic.jar" stringscanner "example.com" true
Replace all instances of example.com: java -jar JMD.jar "C:/Files/Magic.jar" stringreplacer "example.com" true "rscbunlocked.net"
Renamer (currently breaks code): java -jar JMD.jar "C:/Files/Magic.jar" renamer true
Correct stack issues: java -jar JMD.jar "C:/Files/Magic.jar" stackfixer true
## Download
Stable releases: https://github.com/Contra/JMD/downloads
Latest release (most likely stable): Get the files from the repo
## Requirements
JMD - Java 6 (hasn't been tested on anything else but should work)
JMDGUI - .NET 4 or higher
## License
Copyright (c) 2011 Contra <contra@australia.edu>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[travis-image]: https://travis-ci.org/contra/JMD.svg?branch=master
[travis-url]: https://travis-ci.org/contra/JMD
[coveralls-image]: https://coveralls.io/repos/github/contra/JMD/badge.svg?branch=master
[coveralls-url]: https://coveralls.io/github/contra/JMD?branch=master
================================================
FILE: build.gradle
================================================
buildscript {
ext {
projectVersion = '1.6'
gradleWrapperVersion = '3.1'
daggerVersion = '2.6'
bcelVersion = '6.0'
commonsIoVersion = '2.5'
slf4jVersion = '1.7.21'
logbackVersion = '1.1.7'
testngVersion = '6.9.13.6'
}
repositories {
mavenCentral()
}
}
plugins {
id "com.github.kt3k.coveralls" version '2.6.3'
}
description = 'JMD is a general purpose Java bytecode deobfuscation tool'
subprojects {
apply plugin: "java"
apply plugin: "findbugs"
apply plugin: "pmd"
apply plugin: "jacoco"
apply plugin: "com.github.kt3k.coveralls"
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
version = projectVersion
repositories {
mavenCentral()
}
task wrapper(type: Wrapper) {
gradleVersion = gradleWrapperVersion
}
findbugs {
ignoreFailures = true
effort = 'max'
}
pmd {
ignoreFailures = true
ruleSets = [
'java-basic',
'java-braces',
'java-clone',
'java-codesize',
'java-comments',
'java-controversial',
'java-design',
'java-empty',
'java-finalizers',
'java-imports',
'java-j2ee',
'java-javabeans',
'java-junit',
'java-logging-jakarta-commons',
'java-logging-java',
'java-migrating',
'java-naming',
'java-optimizations',
'java-strictexception',
'java-strings',
'java-sunsecure',
'java-typeresolution',
'java-unnecessary',
'java-unusedcode'
]
}
tasks.withType(Pmd) {
reports {
xml.enabled = false
html.enabled = true
}
}
tasks.withType(FindBugs) {
reports {
xml.enabled = false
html.enabled = true
}
}
tasks.coveralls {
dependsOn 'check'
}
jacocoTestReport {
reports {
xml.enabled = true
html.enabled = false
}
}
}
project(':jmd-cli') {
dependencies {
compile project(':jmd-core')
}
jar {
manifest {
attributes 'Implementation-Title': 'net.contra.jmd',
'Implementation-Version': version,
'Main-Class': 'net.contra.jmd.Deobfuscator'
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
}
project(':jmd-core') {
dependencies {
compile group: 'com.google.dagger', name: 'dagger', version: daggerVersion
compile group: 'org.apache.bcel', name: 'bcel', version: bcelVersion
compile group: 'commons-io', name: 'commons-io', version: commonsIoVersion
compile group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion
compile group: 'ch.qos.logback', name: 'logback-classic', version: logbackVersion
compile group: 'org.testng', name: 'testng', version: testngVersion
}
}
project(':jmd-gui') {
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Nov 08 23:54:47 MSK 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: jmd-cli/gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Nov 08 23:54:48 MSK 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip
================================================
FILE: jmd-cli/gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: jmd-cli/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: jmd-cli/src/main/java/net/contra/jmd/Deobfuscator.java
================================================
package net.contra.jmd;
import net.contra.jmd.transformers.allatori.AllatoriTransformer;
import net.contra.jmd.transformers.dasho.DashOTransformer;
import net.contra.jmd.transformers.generic.*;
import net.contra.jmd.transformers.jshrink.JShrinkTransformer;
import net.contra.jmd.transformers.smokescreen.SmokeScreenTransformer;
import net.contra.jmd.transformers.zkm.ZKMTransformer;
import net.contra.jmd.util.LogHandler;
import java.util.Scanner;
/**
* Created by IntelliJ IDEA.
* User: Eric
* Date: Nov 24, 2010
* Time: 9:21:10 PM
*/
public class Deobfuscator {
private static String version = Version.getVersion();
private static String credits = "skoalman, super_, ollie, popcorn89, the prophecy, and saevion";
private static LogHandler logger = new LogHandler("Deobfuscator");
//TODO: Load single class files... herpa derp
public static void main(String[] argv) throws Exception {
logger.message("Java Multi-Purpose Deobfuscator");
logger.message("Version " + version);
logger.message("Created by Contra. Please read LICENSE.txt");
logger.message("Tons of code from " + credits);
if (!(argv.length >= 3)) {
logger.error("java -jar JMD.jar <file location> <type> <debug true/false> <optional args>");
logger.error("Example ZKM: java -jar JMD.jar \"C:/Files/Magic.jar\" zkm true");
logger.error("Example StringScan: java -jar JMD.jar \"C:/Files/Magic.jar\" stringscanner \"rscheata.net\" false");
logger.error("Example StringReplace: java -jar JMD.jar \"C:/Files/Magic.jar\" stringreplacer \"rscheata.net\" true \"rscbunlocked.net\"");
return;
}
if (argv[2].equals("true")) {
}
if (argv[1].toLowerCase().equals("zkm")) {
ZKMTransformer zt = new ZKMTransformer(argv[0]);
zt.transform();
} else if (argv[1].toLowerCase().equals("allatori")) {
AllatoriTransformer at = new AllatoriTransformer(argv[0], false);
at.transform();
} else if (argv[1].toLowerCase().equals("allatori-strong")) {
AllatoriTransformer at = new AllatoriTransformer(argv[0], true);
at.transform();
} else if (argv[1].toLowerCase().equals("jshrink")) {
JShrinkTransformer jt = new JShrinkTransformer(argv[0]);
jt.transform();
} else if (argv[1].toLowerCase().equals("dasho")) {
DashOTransformer dt = new DashOTransformer(argv[0]);
dt.transform();
} else if (argv[1].toLowerCase().equals("smokescreen")) {
SmokeScreenTransformer st = new SmokeScreenTransformer(argv[0]);
st.transform();
} else if (argv[1].toLowerCase().equals("renamer")) {
Renamer re = new Renamer(argv[0]);
re.transform();
} else if (argv[1].toLowerCase().equals("genericstringdeobfuscator")) {
GenericStringDeobfuscator gsd = new GenericStringDeobfuscator(argv[0]);
gsd.transform();
} else if (argv[1].toLowerCase().equals("stringfixer")) {
StringFixer sf = new StringFixer(argv[0]);
sf.transform();
} else if (argv[1].toLowerCase().equals("stackfixer")) {
StackFixer sf = new StackFixer(argv[0]);
sf.transform();
} else if (argv[1].toLowerCase().equals("foreigncallremover")) {
ForeignCallRemover fc = new ForeignCallRemover(argv[0]);
fc.transform();
} else if (argv[1].toLowerCase().equals("stringscanner")) {
StringScanner us = new StringScanner(argv[0], argv[3], false, "");
us.scan();
} else if (argv[1].toLowerCase().equals("stringreplacer")) {
StringScanner us = new StringScanner(argv[0], argv[3], true, argv[4]);
us.scan();
} else {
logger.error("Types are: ZKM, Allatori, JShrink, DashO, SmokeScreen, StringFixer, StringScanner, " +
"\n ForeignCallRemover, GenericStringDeobfuscator, StackFixer, StringFixer, Renamer, and StringReplacer (not case sensitive)");
}
Scanner in = new Scanner(System.in);
logger.message("Press <Enter> key to exit...");
in.nextLine();
in.close();
}
}
================================================
FILE: jmd-cli/src/main/java/net/contra/jmd/Version.java
================================================
package net.contra.jmd;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public final class Version {
private static final String UNKNOWN = "UNKNOWN";
private Version() {
}
public static String getVersion() {
final URL url = Version.class.getResource(JarFile.MANIFEST_NAME);
if (url == null) {
return UNKNOWN;
}
try {
final InputStream inputStream = url.openStream();
final Manifest manifest = new Manifest(inputStream);
return manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
} catch (IOException e) {
// empty
}
return UNKNOWN;
}
}
================================================
FILE: jmd-core/gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Nov 08 23:54:48 MSK 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip
================================================
FILE: jmd-core/gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: jmd-core/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/Transformer.java
================================================
package net.contra.jmd.transformers;
import org.apache.bcel.generic.TargetLostException;
public interface Transformer {
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/allatori/AllatoriTransformer.java
================================================
package net.contra.jmd.transformers.allatori;
import net.contra.jmd.transformers.Transformer;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class AllatoriTransformer implements Transformer {
LogHandler logger = new LogHandler("AllatoriTransformer");
Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
ClassGen ALLATORI_CLASS;
String JAR_NAME;
boolean isStrong = false;
public AllatoriTransformer(String jarfile, boolean strong) throws Exception {
logger.log("Allatori Deobfuscator");
isStrong = strong;
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf
.getInputStream(entry), entry.getName()).parse());
if (isStringClass(cg) || isStringClassB(cg)) {
ALLATORI_CLASS = cg;
logger.debug("Allatori Class: " + ALLATORI_CLASS.getClassName());
}
cgs.put(cg.getClassName(), cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
jf.close();
}
private boolean isStringClass(ClassGen cg) {
if (cg.getMethods().length == 2 && cg.getMethods()[0].isStatic()
&& cg.getMethods()[1].isStatic()) {
if (cg.getMethods()[0].getReturnType().toString().equals(
"java.lang.String")
&& cg.getMethods()[1].getReturnType().toString().equals(
"java.lang.String")) {
return true;
}
}
return false;
}
private boolean isStringClassB(ClassGen cg) {
if (cg.getMethods().length == 1 && cg.getMethods()[0].isStatic()) {
if (cg.getMethods()[0].getReturnType().toString().equals("java.lang.String")) {
return true;
}
}
return false;
}
public static String decode(String string) {
int i = 85;
char[] cs = new char[string.length()];
int pos = cs.length - 1;
int index = pos;
int xor = i;
while (pos >= 0) {
char c = (char) (string.charAt(index) ^ xor);
int c1_index = index;
xor = (char) ((char) (c1_index ^ xor) & '?');
cs[c1_index] = c;
if (--index < 0) {
break;
}
char c2 = (char) (string.charAt(index) ^ xor);
int c2_index = index;
xor = (char) ((char) (c2_index ^ xor) & '?');
cs[c2_index] = c2;
pos = --index;
}
return new String(cs);
}
public static String decodeContext(String encrypted, String callingClass, String callingMethod) {
String keyString = callingClass + callingMethod;
int lastKeyIndex = keyString.length() - 1;
int xor = 85;
int keyIndex = lastKeyIndex;
int length = encrypted.length();
char[] cs = new char[length];
for (int i = length - 1; i >= 0; i--) {
if (keyIndex < 0) {
keyIndex = lastKeyIndex;
}
char keyChar = keyString.charAt(keyIndex--);
cs[i] = (char) (keyChar ^ (encrypted.charAt(i) ^ xor));
xor = (char) (63 & (xor ^ (i ^ keyChar)));
}
return new String(cs);
}
public void transform() throws TargetLostException {
logger.log("Starting Encrypted String Removal...");
replaceStrings();
logger.log("Deobfuscation Finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
public void replaceStrings() throws TargetLostException {
for (ClassGen cg : cgs.values()) {
int replaced = 0;
for (Method method : cg.getMethods()) {
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
continue;
}
InstructionHandle[] handles = list.getInstructionHandles();
for (int i = 1; i < handles.length; i++) {
if (handles[i].getInstruction() instanceof INVOKESTATIC && handles[i - 1].getInstruction() instanceof LDC) {
INVOKESTATIC methodCall = (INVOKESTATIC) handles[i].getInstruction();
if (methodCall.getClassName(cg.getConstantPool()).contains(ALLATORI_CLASS.getClassName())) {
LDC encryptedLDC = (LDC) handles[i - 1].getInstruction();
String encryptedString = encryptedLDC.getValue(cg.getConstantPool()).toString();
String decryptedString;
if(isStrong){
decryptedString = decodeContext(encryptedString, cg.getClassName(), method.getName());
} else {
decryptedString = decode(encryptedString);
}
logger.debug(encryptedString + " -> " + decryptedString + " in " + cg.getClassName() + "." + method.getName());
int stringRef = cg.getConstantPool().addString(decryptedString);
LDC lc = new LDC(stringRef);
NOP nop = new NOP();
handles[i].setInstruction(lc);
handles[i - 1].setInstruction(nop);
replaced++;
}
}
}
mg.setInstructionList(list);
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
}
if (replaced > 0) {
logger.debug("decrypted " + replaced + " strings in class " + cg.getClassName());
}
}
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/dasho/DashOTransformer.java
================================================
package net.contra.jmd.transformers.dasho;
import net.contra.jmd.transformers.Transformer;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
public class DashOTransformer implements Transformer {
private static LogHandler logger = new LogHandler("DashOTransformer");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
String JAR_NAME;
String decryptor = "NOTFOUND";
String decryptorclass = "NOTFOUND";
public DashOTransformer(String jarfile) throws Exception {
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
cgs.put(cg.getClassName(), cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
jf.close();
}
public static String decrypt(String input) {
if (isEmpty(input)) {
return input;
}
char[] inputChars = input.toCharArray();
int length = inputChars.length;
char[] inputCharsCopy = new char[length];
int j = 0;
int i = 0;
while (j < length) {
inputCharsCopy[j] = ((char) (inputChars[j] - '\1' ^ i));
i = (char) (i + 1);
j++;
}
return new String(inputCharsCopy);
}
private static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
public void setDecryptor() {
for (ClassGen cg : cgs.values()) {
for (Method m : cg.getMethods()) {
try {
if (m.isPublic() && m.isStatic() &&
m.getArgumentTypes()[0].toString().equals("java.lang.String")
&& m.getReturnType().toString().equals("java.lang.String")) {
String dc = cg.getClassName() + "." + m.getName();
decryptor = m.getName();
decryptorclass = cg.getClassName();
logger.debug("Found String Decryptor! " + dc);
return;
}
} catch (Exception e) {
continue;
}
}
}
logger.error("String decrypt not found!");
}
public void removeStringEncryption() {
for (ClassGen cg : cgs.values()) {
for (Method m : cg.getMethods()) {
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionList il = mg.getInstructionList();
InstructionHandle[] handles = il.getInstructionHandles();
for (int i = 1; i < handles.length; i++) {
if ((handles[i].getInstruction() instanceof LDC)
&& (handles[i + 1].getInstruction() instanceof INVOKESTATIC)) {
INVOKESTATIC invoke = (INVOKESTATIC) handles[i + 1].getInstruction();
if (decryptor.equals("NOTFOUND")) {
logger.error("String Decryption Method not Set!");
return;
}
String call = invoke.getClassName(cg.getConstantPool());
String mcall = invoke.getMethodName(cg.getConstantPool());
if (call.equals(decryptorclass) && mcall.equals(decryptor)) {
LDC orig = ((LDC) handles[i].getInstruction());
String enc = (String) orig.getValue(cg.getConstantPool());
int index = cg.getConstantPool().addString(decrypt(enc));
LDC lc = new LDC(index);
handles[i].setInstruction(lc);
handles[i + 1].setInstruction(new NOP());
logger.debug(enc + " -> " + decrypt(enc));
}
}
}
mg.setInstructionList(il);
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(m, mg.getMethod());
}
}
}
public void dumpJar(String path) {
FileOutputStream os;
try {
os = new FileOutputStream(new File(path));
} catch (FileNotFoundException fnfe) {
throw new RuntimeException("could not create file \"" + path + "\": " + fnfe);
}
JarOutputStream jos;
try {
jos = new JarOutputStream(os);
for (JarEntry jbe : NonClassEntries.entries) {
JarEntry destEntry = new JarEntry(jbe.getName());
byte[] bite = IOUtils.toByteArray(NonClassEntries.ins.get(jbe));
jos.putNextEntry(destEntry);
jos.write(bite);
jos.closeEntry();
}
for (ClassGen classIt : cgs.values()) {
jos.putNextEntry(new JarEntry(classIt.getClassName().replace('.', '/') + ".class"));
jos.write(classIt.getJavaClass().getBytes());
jos.closeEntry();
jos.flush();
}
jos.closeEntry();
jos.close();
} catch (IOException ioe) {
}
}
public void transform() {
logger.log("DashO Deobfuscator");
logger.log("Finding String Decryption Method...");
setDecryptor();
logger.log("Starting String Encryption Removal...");
removeStringEncryption();
//logger.log("Starting Unconditional Branch Remover...");
//removeControlFlow();
//unconditionalBranchTransformer();
//logger.log("Starting Exit Flow Corrector...");
//exitFlowTransformer();
logger.log("Deobfuscation finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/ForeignCallRemover.java
================================================
package net.contra.jmd.transformers.generic;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
public class ForeignCallRemover {
private static LogHandler logger = new LogHandler("ForeignCallRemover");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
String JAR_NAME;
String AuthClass;
public ForeignCallRemover(String jarfile) throws Exception {
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
if (isAuthClass(cg)) {
logger.debug("Found auth class! " + cg.getClassName());
AuthClass = cg.getClassName();
}
cgs.put(cg.getClassName(), cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
jf.close();
}
public boolean isAuthClass(ClassGen cg) {
if (cg.getMethods().length == 2 && cg.getMethods()[0].getArgumentTypes().length == 1) {
if (cg.getMethods()[0].getArgumentTypes()[0].getSignature().contains("String")) {
if (cg.getMethods()[0].getReturnType().toString().contains("boolean")) {
return true;
}
}
}
return false;
}
public void dumpJar(String path) {
FileOutputStream os;
try {
os = new FileOutputStream(new File(path));
} catch (FileNotFoundException fnfe) {
throw new RuntimeException("could not create file \"" + path + "\": " + fnfe);
}
JarOutputStream jos;
try {
jos = new JarOutputStream(os);
for (ClassGen classIt : cgs.values()) {
jos.putNextEntry(new JarEntry(classIt.getClassName().replace('.', '/') + ".class"));
jos.write(classIt.getJavaClass().getBytes());
jos.closeEntry();
jos.flush();
}
for (JarEntry jbe : NonClassEntries.entries) {
if (!jbe.isDirectory()) {
JarEntry destEntry = new JarEntry(jbe.getName());
byte[] bite = IOUtils.toByteArray(NonClassEntries.ins.get(jbe));
jos.putNextEntry(destEntry);
jos.write(bite);
jos.closeEntry();
}
}
jos.closeEntry();
jos.close();
} catch (IOException ioe) {
}
}
public void RemoveCalls() {
for (ClassGen cg : cgs.values()) {
int replaced = 0;
for (Method method : cg.getMethods()) {
//logger.debug("in method " + method.getName());
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
continue;
}
InstructionHandle[] handles = list.getInstructionHandles();
for (int i = 0; i < handles.length; i++) {
if (GenericMethods.isCall(handles[i].getInstruction())) {
String callClass = GenericMethods.getCallClassName(handles[i].getInstruction(), cg.getConstantPool());
String callMethod = GenericMethods.getCallMethodName(handles[i].getInstruction(), cg.getConstantPool());
if (!callClass.startsWith("java") &&
(!callClass.startsWith("org") || callClass.contains("PingBack"))
&& !cgs.containsKey(callClass)) {
if (GenericMethods.getCallArgTypes(handles[i].getInstruction(), cg.getConstantPool()).length == 0) {
handles[i].setInstruction(new NOP());
if (handles[i + 1].getInstruction() instanceof ASTORE) {
handles[i + 1].setInstruction(new NOP());
}
logger.debug(callClass + "." + callMethod + " invoke had no arguments, so we NOP");
replaced++;
} else {
//TODO: WRITE SOMETHING TO DETECT MULTIPLE ARGUMENTS
handles[i - 1].setInstruction(new NOP());
handles[i].setInstruction(new NOP());
if (handles[i + 1].getInstruction() instanceof ASTORE) {
handles[i + 1].setInstruction(new NOP());
}
logger.debug(callClass + "." + callMethod + " invoke had arguments, so we NOP it and the line before");
replaced++;
}
mg.setInstructionList(list);
mg.removeNOPs();
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
}
}
if (handles[i].getInstruction() instanceof NEW) {
String callClass = ((NEW) handles[i].getInstruction()).getLoadClassType(cg.getConstantPool()).getClassName();
//logger.debug(callClass);
if (!callClass.startsWith("java") &&
(!callClass.startsWith("org") || callClass.contains("PingBack"))
&& !cgs.containsKey(callClass)) {
handles[i].setInstruction(new NOP());
logger.debug("NOPed out NEW " + callClass);
replaced++;
}
mg.setInstructionList(list);
mg.removeNOPs();
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
}
}
}
if (replaced > 0) {
logger.debug("Removed " + replaced + " foreign calls in " + cg.getClassName());
}
}
}
public void replaceCheckMethod() {
ClassGen cg = cgs.get(AuthClass);
Method method = cg.getMethods()[0];
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = new InstructionList();
list.append(new ICONST(1));
list.append(new IRETURN());
mg.removeExceptionHandlers();
mg.removeLineNumbers();
mg.removeLocalVariables();
mg.removeExceptions();
mg.setInstructionList(list);
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
}
public void fixPOPs() {
for (ClassGen cg : cgs.values()) {
int replaced = 0;
for (Method method : cg.getMethods()) {
//logger.debug("in method " + method.getName());
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
continue;
}
InstructionHandle[] handles = list.getInstructionHandles();
if (handles[0].getInstruction() instanceof DUP || handles[0].getInstruction() instanceof ASTORE
|| handles[0].getInstruction() instanceof POP) {
handles[0].setInstruction(new NOP());
replaced++;
}
mg.setInstructionList(list);
mg.removeNOPs();
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
}
if (replaced > 0) {
logger.debug("Removed " + replaced + " invalid POPs in " + cg.getClassName());
}
}
}
public void transform() {
//logger.log("Removing Exception Handlers...");
//removeExceptions();
logger.log("Removing Foreign Calls...");
RemoveCalls();
logger.log("Fixing DUPs...");
fixPOPs();
if (AuthClass != null) {
logger.log("Replacing Authentication System...");
replaceCheckMethod();
} else {
logger.error("Auth class wasn't found so we couldn't remove it!");
}
logger.log("Deobfuscation finished! Dumping jar...");
dumpJar(JAR_NAME.replace(".jar", "") + "-deob.jar");
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/GenericStringDeobfuscator.java
================================================
/*
TODO: Write a dynamic string decryptor like the one in SAE; Pattern is LDC INVOKESTATIC,
grab the IL from the invokestatic method and put it in a new methodgen and run the LDC through that and replace it with the result
Please see http://www.java-tips.org/java-se-tips/java.lang.reflect/invoke-method-using-reflection.html
Also ask somebody how you can trace the stack for stringfixer, foreigncallremover
AND to get the key, check the method arguments. If it is just string pass the string, otherwise grab the integer above it.
If there is more leave it be or attempt to grab the values
*/
package net.contra.jmd.transformers.generic;
import net.contra.jmd.util.GenericClassLoader;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class GenericStringDeobfuscator {
private static LogHandler logger = new LogHandler("GenericStringDeobfuscator");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
String JAR_NAME;
public GenericStringDeobfuscator(String jarfile) throws Exception {
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
cgs.put(cg.getClassName(), cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
jf.close();
}
public void replaceStrings() {
for (ClassGen cg : cgs.values()) {
int replaced = 0;
for (Method method : cg.getMethods()) {
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
continue;
}
InstructionHandle[] handles = list.getInstructionHandles();
for (int i = 1; i < handles.length; i++) {
//java.lang.reflect.Method
if (GenericMethods.isCall(handles[i].getInstruction()) && handles[i - 1].getInstruction() instanceof LDC) {
String methodCallClass = GenericMethods.getCallClassName(handles[i].getInstruction(), cg.getConstantPool());
String methodCallMethod = GenericMethods.getCallMethodName(handles[i].getInstruction(), cg.getConstantPool());
String methodCallSig = GenericMethods.getCallSignature(handles[i].getInstruction(), cg.getConstantPool());
String methodRetType = GenericMethods.getCallReturnType(handles[i].getInstruction(), cg.getConstantPool());
if (GenericMethods.getCallArgTypes(handles[i].getInstruction(), cg.getConstantPool()).length == 1
&& methodCallClass != null && methodCallMethod != null && methodRetType.contains("String")) {
//Begin classloader bullshit
GenericClassLoader loader = new GenericClassLoader(GenericStringDeobfuscator.class.getClassLoader());
Class cl;
ClassGen ourCz = cgs.get(methodCallClass);
if (ourCz != null && ourCz.containsMethod(methodCallMethod, methodCallSig) != null) {
byte[] bit = ourCz.getJavaClass().getBytes();
logger.debug(methodCallClass + " " + methodCallSig);
logger.debug(ourCz.getClassName());
cl = loader.loadClass(ourCz.getClassName(), bit);
} else {
continue;
}
if (cl == null) {
continue;
}
java.lang.reflect.Method mthd;
try {
mthd = cl.getMethod(methodCallMethod, String.class);
mthd.setAccessible(true);
} catch (NoSuchMethodException e) {
continue;
}
LDC encryptedLDC = (LDC) handles[i - 1].getInstruction();
String encryptedString = encryptedLDC.getValue(cg.getConstantPool()).toString();
String decryptedString;
try {
decryptedString = mthd.invoke(null, encryptedString).toString();
} catch (Exception e) {
continue;
}
logger.debug(encryptedString + " -> " + decryptedString + " in " + cg.getClassName() + "." + method.getName());
int stringRef = cg.getConstantPool().addString(decryptedString);
LDC lc = new LDC(stringRef);
NOP nop = new NOP();
handles[i].setInstruction(lc);
handles[i - 1].setInstruction(nop);
replaced++;
} else {
continue;
}
}
}
mg.setInstructionList(list);
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
}
if (replaced > 0) {
logger.debug("decrypted " + replaced + " strings in class " + cg.getClassName());
}
}
}
public void transform() {
logger.log("Generic String Deobfuscator");
logger.log("Still basic (and very buggy)");
replaceStrings();
logger.log("Deobfuscation finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/Renamer.java
================================================
package net.contra.jmd.transformers.generic;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class Renamer {
private static LogHandler logger = new LogHandler("Renamer");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
private Map<String, ClassGen> tempcgs = new HashMap<String, ClassGen>();
private Map<String, String> methodNames = new HashMap<String, String>(); //OldName, NewName
String JAR_NAME;
public Renamer(String jarfile) throws Exception {
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
//TODO: Make it not rename the main class
//TODO: Keep it from renaming like, methods that shouldn't be renamed and shit??
//TODO: Rename FIELDS MOTHERFUCKER
//Manifest jm = jf.getManifest();
//if(jm.getAttributes("Main-class") != null &&
//logger.debug("Found main class for jar: " + );
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
cgs.put(cg.getClassName(), cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
jf.close();
}
public void renameClasses() {
int classCount = 1;
for (ClassGen cg : cgs.values()) {
String className = cg.getClassName();
String shortClassName = className.substring(className.lastIndexOf(".") + 1, className.length());
String newClassName = className.replace(shortClassName, "Class" + classCount);
cg.setClassName(newClassName);
tempcgs.put(newClassName, cg);
classCount++;
}
if (classCount > 1) {
logger.debug("Renamed " + classCount + " classes.");
cgs = tempcgs;
}
}
public void replaceMethodRefs() {
for (ClassGen cg : cgs.values()) {
for (Method m : cg.getMethods()) {
int replaced = 0;
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
continue;
}
InstructionHandle[] handles = list.getInstructionHandles();
for (InstructionHandle handle : handles) {
if (GenericMethods.isCall(handle.getInstruction())) {
String oldClassName = GenericMethods.getCallClassName(handle.getInstruction(), cg.getConstantPool());
String oldMethodName = GenericMethods.getCallMethodName(handle.getInstruction(), cg.getConstantPool());
String oldSignature = GenericMethods.getCallSignature(handle.getInstruction(), cg.getConstantPool());
String mod = oldClassName + "-" + oldMethodName + "-" + oldSignature;
if (methodNames.containsKey(mod)) {
//logger.debug("Accessing " + methodNames.get(mod));
String[] args = methodNames.get(mod).split("-");
String newClassName = args[0];
String newMethodName = args[1];
String newSignature = args[2];
int newindex = cg.getConstantPool().addMethodref(newClassName, newMethodName, newSignature);
Instruction newInvoke = GenericMethods.getNewInvoke(handle.getInstruction(), newindex);
handle.setInstruction(newInvoke);
replaced++;
}
}
}
mg.setInstructionList(list);
mg.setMaxLocals();
mg.setMaxStack();
if (replaced > 0) {
logger.debug("replaced " + replaced + " methodrefs in " + m.getName());
cg.replaceMethod(m, mg.getMethod());
}
}
}
}
public void renameMethods() {
for (ClassGen cg : cgs.values()) {
if (cg.isAbstract() || cg.isInterface()) {
continue;
}
int count = 1;
for (Method m : cg.getMethods()) {
if (m.getName().equalsIgnoreCase("<clinit>")
|| m.getName().equalsIgnoreCase("<init>")
|| m.getName().equalsIgnoreCase("main")) {
continue;
}
ConstantPoolGen cpg = cg.getConstantPool();
String name = "";
if (m.isStatic()) {
name += "static";
}
String type = m.getReturnType().toString();
name += type.substring(type.lastIndexOf(".") + 1, type.length());
name = name.replace("void", "");
if (name.contains("[]")) {
name = name.replace("[]", "Array");
}
name += "Method" + count;
//TODO: Get it to fully change the name (updated methodref name index) and not corrupt the constant pool lol
//TODO: Rename classes first, then methods, then fields.
MethodGen mg = new MethodGen(m, cg.getClassName(), cpg);
mg.setName(name);
cg.replaceMethod(m, mg.getMethod());
cg.setConstantPool(cpg);
//y.pb.methodsig - Class.Method04.MethodSig
methodNames.put(cg.getClassName() + "-" + m.getName() + "-" + m.getSignature(), cg.getClassName() + "-" + name + "-" + m.getSignature());
count++;
logger.debug(cg.getClassName() + "." + m.getName() + " -> " + cg.getClassName() + "." + name);
}
}
}
public void transform() {
logger.log("Generic Renamer");
renameClasses();
renameMethods();
replaceMethodRefs();
logger.log("Deobfuscation finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/StackFixer.java
================================================
package net.contra.jmd.transformers.generic;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.MethodGen;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Created by IntelliJ IDEA.
* User: Eric
* Date: Nov 30, 2010
* Time: 4:52:48 AM
*/
public class StackFixer {
private static LogHandler logger = new LogHandler("StackFixer");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
String JAR_NAME;
public StackFixer(String jarfile) throws Exception {
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
cgs.put(cg.getClassName(), cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
jf.close();
}
public void fixStack() {
for (ClassGen cg : cgs.values()) {
for (Method method : cg.getMethods()) {
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
mg.removeNOPs();
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
logger.debug(String.format("Reset MaxStack and MaxLocals in %s.%s", cg.getClassName(), mg.getName()));
}
}
}
public void transform() {
logger.log("Starting StackFixer");
fixStack();
logger.log("Deobfuscation finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/StringFixer.java
================================================
package net.contra.jmd.transformers.generic;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
public class StringFixer {
private static LogHandler logger = new LogHandler("StringFixer");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
String JAR_NAME;
boolean replacing = false;
public StringFixer(String jarfile) throws Exception {
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
cgs.put(cg.getClassName(), cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
jf.close();
}
public void removeBASA() {
for (ClassGen cg : cgs.values()) {
for (Method method : cg.getMethods()) {
String type = "";
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
continue;
}
InstructionHandle[] handles = list.getInstructionHandles();
int startLoc = -1;
int endLoc = -1;
int arrayLength = -1;
for (int i = 0; i < handles.length; i++) {
if ((handles[i].getInstruction() instanceof NEW)
&& (handles[i + 1].getInstruction() instanceof DUP)
&& GenericMethods.isNumber(handles[i + 2].getInstruction())
&& (handles[i + 3].getInstruction() instanceof NEWARRAY)) {
String newType = ((NEW) handles[i].getInstruction()).getLoadClassType(cg.getConstantPool()).toString();
type = ((NEWARRAY) handles[i + 3].getInstruction()).getType().toString();
logger.debug("Found new array conversion pattern: " + type + "->" + newType + " in " + cg.getClassName() + "." + method.getName());
//if(newType.equals("java.lang.String") && (type.equals("byte[]") || type.equals("char[]"))) {
if (newType.equals("java.lang.String") && type.contains("[]")) {
startLoc = i;
arrayLength = GenericMethods.getValueOfNumber(handles[i + 2].getInstruction(), cg.getConstantPool());
//logger.debug("Start Location for BASA replacement: " + startLoc);
//logger.debug("Length of Array: " + arrayLength);
}
}
if (startLoc >= 0 && arrayLength >= 1) {
if (handles.length > (i + 1)) {
if ((handles[i].getInstruction() instanceof BASTORE)
&& GenericMethods.isNumber(handles[i - 1].getInstruction())
&& GenericMethods.isNumber(handles[i - 2].getInstruction())
&& (handles[i - 3].getInstruction() instanceof DUP)) {
/*
TODO: Also you need to make the endLoc INVOKESTATIC STRING<init> or something because it is leaving a trailing command and fucking up.
*/
if (GenericMethods.isCall(handles[i + 1].getInstruction())) {
if (GenericMethods.getCallArgTypes(handles[i + 1].getInstruction(), cg.getConstantPool()) != null) {
Type[] tp = GenericMethods.getCallArgTypes(handles[i + 1].getInstruction(), cg.getConstantPool());
String lasttp = tp[tp.length - 1].toString();
if (!lasttp.contains(type)) {
int tendLoc = GenericMethods.getValueOfNumber(handles[i - 2].getInstruction(), cg.getConstantPool());
if (tendLoc == (arrayLength - 1)) {
endLoc = i;
//logger.debug("End Location for BASA replacement: " + endLoc);
}
} else {
logger.debug("Can't replace String(" + type + "), following method call has argument of same type");
startLoc = -1;
endLoc = -1;
arrayLength = -1;
continue;
}
}
}
}
}
}
if ((startLoc >= 0) && (endLoc >= 0) && (arrayLength > 1)
&& (endLoc < handles.length)) {
byte[] stringBytes = new byte[arrayLength];
int loc = 0;
for (int x = startLoc; x <= endLoc; x++) {
if (GenericMethods.isNumber(handles[x].getInstruction())
&& GenericMethods.isNumber(handles[x + 1].getInstruction())
&& (handles[x + 2].getInstruction() instanceof BASTORE)
&& (GenericMethods.getValueOfNumber(handles[x].getInstruction(), cg.getConstantPool()) == loc)) {
stringBytes[loc] = (byte) GenericMethods.getValueOfNumber(handles[x + 1].getInstruction(), cg.getConstantPool());
loc++;
if (loc == stringBytes.length) {
if (replacing) {
int stringRef = cg.getConstantPool().addString(new String(stringBytes));
LDC lc = new LDC(stringRef);
handles[startLoc].setInstruction(lc);
for (int z = startLoc + 1; z <= endLoc; z++) {
NOP nop = new NOP();
handles[z].setInstruction(nop);
}
logger.debug("Replaced conversion with string " + new String(stringBytes) + " in " + cg.getClassName() + "." + method.getName());
} else {
logger.debug("Found conversion, string:" + new String(stringBytes));
}
startLoc = -1;
endLoc = -1;
arrayLength = -1;
type = "";
mg.setInstructionList(list);
mg.removeNOPs();
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
continue;
}
}
}
}
}
}
}
}
public void dumpJar(String path) {
FileOutputStream os;
try {
os = new FileOutputStream(new File(path));
} catch (FileNotFoundException fnfe) {
throw new RuntimeException("could not create file \"" + path + "\": " + fnfe);
}
JarOutputStream jos;
try {
jos = new JarOutputStream(os);
for (ClassGen classIt : cgs.values()) {
jos.putNextEntry(new JarEntry(classIt.getClassName().replace('.', '/') + ".class"));
jos.write(classIt.getJavaClass().getBytes());
jos.closeEntry();
jos.flush();
}
for (JarEntry jbe : NonClassEntries.entries) {
JarEntry destEntry = new JarEntry(jbe.getName());
byte[] bite = IOUtils.toByteArray(NonClassEntries.ins.get(jbe));
jos.putNextEntry(destEntry);
jos.write(bite);
jos.closeEntry();
}
jos.closeEntry();
jos.close();
} catch (IOException ioe) {
}
}
public void transform() {
logger.log("Generic String Fixer");
logger.log("This hasn't been finished yet.");
logger.log("Scanning/Replacing Strings hidden as byte arrays...");
removeBASA();
logger.log("Deobfuscation finished! Dumping jar...");
dumpJar(JAR_NAME.replace(".jar", "") + "-deob.jar");
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/StringScanner.java
================================================
package net.contra.jmd.transformers.generic;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class StringScanner {
private static LogHandler logger = new LogHandler("StringScanner");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
boolean replaceMode = false;
String substitute = "";
String inputScan = "";
public StringScanner(String jarfile, String scanstring, boolean replace, String replacestring) throws Exception {
inputScan = scanstring;
replaceMode = replace;
substitute = replacestring;
File jar = new File(jarfile);
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
cgs.put(cg.getClassName(), cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
jf.close();
}
public void searchConstantPool() {
for (ClassGen cg : cgs.values()) {
for (Method m : cg.getMethods()) {
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
InstructionHandle[] handles;
if (list != null && list.size() > 0) {
handles = list.getInstructionHandles();
} else {
break;
}
for (InstructionHandle handle : handles) {
if (handle.getInstruction() instanceof LDC) {
LDC newldc = (LDC) handle.getInstruction();
String val = newldc.getValue(cg.getConstantPool()).toString();
if (val.contains(inputScan)) {
if (!replaceMode) {
logger.log(val + " in " + cg.getClassName() + "." + m.getName());
} else {
String newz = val.replace(inputScan, substitute);
int stringRef = cg.getConstantPool().addString(newz);
handle.setInstruction(new LDC(stringRef));
logger.log(val + "->" + newz + " in " + cg.getClassName() + "." + m.getName());
}
}
}
}
if (replaceMode) {
list.setPositions();
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(m, mg.getMethod());
}
}
}
}
public void scan() {
logger.log("Generic URL Scanner/Replacer");
if (!replaceMode) {
logger.log("Scanning for Strings containing " + inputScan);
} else {
logger.log("Replacing " + inputScan + " with " + substitute);
}
searchConstantPool();
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/TransformerTemplate.java
================================================
package net.contra.jmd.transformers.generic;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import org.apache.bcel.generic.ClassGen;
import java.util.HashMap;
import java.util.Map;
public class TransformerTemplate {
private static LogHandler logger = new LogHandler("StringFixer");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
String JAR_NAME;
public void transform() {
logger.log("Generic Transformer");
logger.log("Deobfuscation finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/jshrink/JShrinkTransformer.java
================================================
package net.contra.jmd.transformers.jshrink;
import net.contra.jmd.transformers.Transformer;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JShrinkTransformer implements Transformer {
private static LogHandler logger = new LogHandler("JShrinkTransformer");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
String JAR_NAME;
ClassGen LoaderClass = null;
public JShrinkTransformer(String jarfile) throws Exception {
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
String className = entry.getName().replace(".class", "").replace("\\", "/").replace("/", ".");
if (isLoader(cg) && LoaderClass == null) {
logger.debug("Found JShrink Loader! " + cg.getClassName());
LoaderClass = cg;
} else {
cgs.put(className, cg);
}
} else {
if (!entry.isDirectory()) {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
}
logger.debug("Classes loaded from JAR");
if (LoaderClass == null) {
logger.error("Loader class not found! Class not using JShrink");
}
jf.close();
}
public boolean isLoader(ClassGen cg) {
return cg.getMethods().length == 3 && cg.getMethods()[1].isStatic()
&& cg.getMethods()[1].isFinal()
&& cg.getMethods()[1].isPublic()
&& cg.getMethods()[1].isSynchronized()
&& cg.getMethods()[1].getReturnType().toString().equals("java.lang.String");
}
public void replaceStrings() throws TargetLostException {
for (ClassGen cg : cgs.values()) {
int replaced = 0;
for (Method method : cg.getMethods()) {
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
continue;
}
InstructionHandle[] handles = list.getInstructionHandles();
for (int i = 1; i < handles.length; i++) {
if (handles[i].getInstruction() instanceof INVOKESTATIC && GenericMethods.isNumber(handles[i - 1].getInstruction())) {
INVOKESTATIC methodCall = (INVOKESTATIC) handles[i].getInstruction();
if (methodCall.getClassName(cg.getConstantPool()).contains(LoaderClass.getClassName())) {
int push = GenericMethods.getValueOfNumber(handles[i - 1].getInstruction(), cg.getConstantPool());
String decryptedString = StoreHandler.I(push);
if (decryptedString != null) {
int stringRef = cg.getConstantPool().addString(decryptedString);
LDC lc = new LDC(stringRef);
NOP nop = new NOP();
handles[i].setInstruction(lc);
handles[i - 1].setInstruction(nop);
replaced++;
}
}
}
}
mg.setInstructionList(list);
mg.removeNOPs();
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
}
if (replaced > 0) {
logger.debug("replaced " + replaced + " calls in class " + cg.getClassName());
}
}
}
public void transform() {
try {
logger.log("Starting String Replacer...");
replaceStrings();
} catch (TargetLostException e) {
e.printStackTrace();
}
logger.log("Deobfuscation Finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/jshrink/StoreHandler.java
================================================
package net.contra.jmd.transformers.jshrink;
import net.contra.jmd.util.NonClassEntries;
import java.io.InputStream;
/**
* Created by IntelliJ IDEA.
* User: Eric
* Date: Nov 29, 2010
* Time: 10:25:20 PM
*/
public class StoreHandler {
static byte[] WSVZ;
static String[] append = new String[256];
static int[] close = new int[256];
public static synchronized String I(int paramInt) {
int i = paramInt & 0xFF;
if (close[i] != paramInt) {
close[i] = paramInt;
if (paramInt < 0) {
paramInt &= 65535;
}
String str = new String(WSVZ, paramInt, WSVZ[(paramInt - 1)] & 0xFF).intern();
append[i] = str;
}
return append[i];
}
static {
try {
InputStream localInputStream = NonClassEntries.ins.get(NonClassEntries.getByName("I/I.gif"));
if (localInputStream != null) {
int i = localInputStream.read() << 16 | localInputStream.read() << 8 | localInputStream.read();
WSVZ = new byte[i];
int j = 0;
int k = (byte) i;
byte[] arrayOfByte = WSVZ;
while (i != 0) {
int m = localInputStream.read(arrayOfByte, j, i);
if (m == -1) {
break;
}
i -= m;
m += j;
while (j < m) {
int int2 = j;
arrayOfByte[int2] = (byte) (arrayOfByte[int2] ^ k);
j++;
}
}
localInputStream.reset();
localInputStream.close();
}
} catch (Exception localException) {
}
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/smokescreen/SmokeScreenTransformer.java
================================================
package net.contra.jmd.transformers.smokescreen;
import net.contra.jmd.transformers.Transformer;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import org.apache.bcel.util.InstructionFinder;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class SmokeScreenTransformer implements Transformer {
private static LogHandler logger = new LogHandler("SmokeScreenTransformer");
private Map<String, ClassGen> cgs;
private Map<String, String> ssStrings = new HashMap<String, String>();
String JAR_NAME;
public String getActualString(String className, int i1, int i2) {
return ssStrings.get(className).substring(i1, i2);
}
public void replaceStrings() {
for (ClassGen cg : cgs.values()) {
int replaced = 0;
for (Method m : cg.getMethods()) {
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
return;
}
InstructionHandle[] handles = list.getInstructionHandles();
for (int x = 0; x < handles.length; x++) {
if (x + 3 < handles.length) {
if (handles[x].getInstruction() instanceof GETSTATIC
&& GenericMethods.isNumber(handles[x + 1].getInstruction())
&& GenericMethods.isNumber(handles[x + 2].getInstruction())
&& GenericMethods.isCall(handles[x + 3].getInstruction())) {
if (GenericMethods.getCallMethodName(handles[x + 3].getInstruction(), cg.getConstantPool()).contains("substring")) {
int con1 = GenericMethods.getValueOfNumber(handles[x + 1].getInstruction(), cg.getConstantPool());
int con2 = GenericMethods.getValueOfNumber(handles[x + 2].getInstruction(), cg.getConstantPool());
NOP nop = new NOP();
int stringRef = cg.getConstantPool().addString(getActualString(cg.getClassName(), con1, con2));
LDC data = new LDC(stringRef);
handles[x].setInstruction(data);
handles[x + 1].setInstruction(nop);
handles[x + 2].setInstruction(nop);
handles[x + 3].setInstruction(nop);
replaced++;
mg.setInstructionList(list);
mg.removeNOPs();
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(m, mg.getMethod());
continue;
}
}
}
}
}
if (replaced > 0) {
logger.debug("Replaced " + replaced + " calls in class " + cg.getClassName());
}
}
}
public void grabStrings() {
for (ClassGen cg : cgs.values()) {
for (Method m : cg.getMethods()) {
int key = 0;
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
return;
}
InstructionHandle[] handles = list.getInstructionHandles();
for (int x = 0; x < handles.length; x++) {
if (x + 3 < handles.length) {
if (handles[x].getInstruction() instanceof LDC
&& handles[x + 1].getInstruction() instanceof ASTORE
&& GenericMethods.isNumber(handles[x + 2].getInstruction())
&& handles[x + 3].getInstruction() instanceof ISTORE) {
key = GenericMethods.getValueOfNumber(handles[x + 2].getInstruction(), cg.getConstantPool());
LDC tx = (LDC) handles[x].getInstruction();
String encryptedContent = tx.getValue(cg.getConstantPool()).toString();
String decryptedContent = decrypt(encryptedContent, key);
logger.debug("Found key for " + cg.getClassName() + ": " + key);
logger.debug("Strings for class: " + decryptedContent);
ssStrings.put(cg.getClassName(), decryptedContent);
continue;
}
}
}
}
}
}
public SmokeScreenTransformer(String jarfile) throws Exception {
cgs = new HashMap<String, ClassGen>();
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
String className = entry.getName().replace(".class", "").replace("\\", "/").replace("/", ".");
cgs.put(className, cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
logger.debug("Classes loaded from JAR");
jf.close();
}
public static String decrypt(String encrypted, int myKey) {
int key = myKey;
char[] encChars = encrypted.toCharArray();
char[] tmpChars = new char[encChars.length];
for (int j = 0; j < encChars.length; j++) {
tmpChars = encChars;
tmpChars[j] = (char) (tmpChars[j] ^ key);
key = (char) (key + encChars[j] & 0x3E);
}
return new String(tmpChars);
}
public void transform() {
logger.log("SmokeScreen Deobfuscator");
logger.log("Decrypting Strings...");
grabStrings();
logger.log("Replacing string calls with LDC");
replaceStrings();
logger.log("Starting Exit Flow Corrector...");
exitFlowTransformer();
logger.log("Removing Unconditional Branches...");
unconditionalBranchTransformer();
logger.log("Deobfuscation finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
public void unconditionalBranchTransformer() {
for (ClassGen cg : cgs.values()) {
for (Method method : cg.getMethods()) {
final MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
if (method.isAbstract() || method.isNative()) {
return;
}
final InstructionList list = mg.getInstructionList();
InstructionFinder finder = new InstructionFinder(list);
final ConstantPoolGen cpg = cg.getConstantPool();
int branchesSimplified = 0;
Iterator<InstructionHandle[]> matches = finder.search("IfInstruction");
while (matches.hasNext()) {
InstructionHandle ifHandle = matches.next()[0];
InstructionHandle target = ((BranchHandle) ifHandle).getTarget();
if (target.getInstruction() instanceof GOTO) {
branchesSimplified++;
((BranchHandle) ifHandle).setTarget(((BranchHandle) target).getTarget());
}
}
matches = finder.search("GOTO GOTO");
while (matches.hasNext()) {
InstructionHandle[] match = matches.next();
try {
list.delete(match[0]);
} catch (TargetLostException tlex) {
for (InstructionHandle target : tlex.getTargets()) {
for (InstructionTargeter targeter : target.getTargeters()) {
targeter.updateTarget(target, match[1]);
}
}
}
}
mg.setInstructionList(list);
mg.setMaxLocals();
mg.setMaxStack();
if (branchesSimplified > 0) {
logger.debug("simplified " + branchesSimplified + " unconditional branches");
cg.replaceMethod(method, mg.getMethod());
}
}
}
}
public void exitFlowTransformer() {
for (ClassGen cg : cgs.values()) {
int correct = 0;
for (Method method : cg.getMethods()) {
final MethodGen mgen = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
if (!method.isAbstract() && !method.isNative()) {
InstructionList list = mgen.getInstructionList();
InstructionFinder finder = new InstructionFinder(list);
CodeExceptionGen[] exceptionGens = mgen.getExceptionHandlers();
for (Iterator<InstructionHandle[]> matches = finder.search(
"ASTORE ALOAD (NEW DUP (PushInstruction InvokeInstruction)+ (ALOAD IfInstruction LDC GOTO LDC " +
"INVOKEVIRTUAL)? (PushInstruction InvokeInstruction)* InvokeInstruction+)?");
matches.hasNext();) {
/* thanks to popcorn89 */
InstructionHandle[] match = matches.next();
if (!(match[match.length - 1].getInstruction() instanceof ATHROW)) {
continue;
}
InstructionHandle astoreInstr = match[0];
InstructionHandle athrowInstr = match[match.length - 1];
InstructionHandle toRedirect = athrowInstr.getNext();
for (CodeExceptionGen exgen : exceptionGens) {
if (exgen.getHandlerPC().equals(astoreInstr)) {
mgen.removeExceptionHandler(exgen);
}
}
try {
list.delete(astoreInstr, athrowInstr);
} catch (TargetLostException tlex) {
if (athrowInstr == list.getEnd()) {
toRedirect = astoreInstr.getPrev();
}
for (InstructionHandle target : tlex.getTargets()) {
for (InstructionTargeter targeter : target.getTargeters()) {
targeter.updateTarget(target, toRedirect);
}
}
}
}
list.setPositions(true);
InstructionHandle lastInstr = list.getEnd();
InstructionHandle secondToLastInstr = lastInstr.getPrev();
if (secondToLastInstr != null && (lastInstr.getInstruction() instanceof RETURN)
&& (secondToLastInstr.getInstruction() instanceof RETURN)) {
try {
list.delete(secondToLastInstr);
} catch (TargetLostException tlex) {
for (InstructionHandle target : tlex.getTargets()) {
for (InstructionTargeter targeter : target.getTargeters()) {
targeter.updateTarget(target, lastInstr);
}
}
}
}
if (mgen.getMethod() != method) {
correct++;
//logger.debug("corrected exit flow in " + cg.getClassName() + "." + mgen.getName() + mgen.getSignature());
mgen.setInstructionList(list);
mgen.setMaxLocals();
mgen.setMaxStack();
cg.replaceMethod(method, mgen.getMethod());
}
}
}
logger.debug("Corrected exit flow " + correct + " times in " + cg.getClassName());
}
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/zkm/ZKMTransformer.java
================================================
package net.contra.jmd.transformers.zkm;
import net.contra.jmd.transformers.Transformer;
import net.contra.jmd.util.GenericMethods;
import net.contra.jmd.util.LogHandler;
import net.contra.jmd.util.NonClassEntries;
import org.apache.bcel.classfile.*;
import org.apache.bcel.generic.*;
import org.apache.bcel.util.*;
import java.io.File;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ZKMTransformer implements Transformer {
private static LogHandler logger = new LogHandler("ZKMTransformer");
private Map<String, ClassGen> cgs = new HashMap<String, ClassGen>();
private Map<String, ArrayList<String>> zkStrings = new HashMap<String, ArrayList<String>>();
String JAR_NAME;
private List<Field> flowObstructors = new LinkedList<Field>();
private Field controlField = null;
private String controlClass = "";
public String getZKMString(String className, int index) {
return zkStrings.get(className).get(index);
}
public ZKMTransformer(String jarfile) throws Exception {
File jar = new File(jarfile);
JAR_NAME = jarfile;
JarFile jf = new JarFile(jar);
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry == null) {
break;
}
if (entry.getName().endsWith(".class")) {
ClassGen cg = new ClassGen(new ClassParser(jf.getInputStream(entry), entry.getName()).parse());
String className = entry.getName().replace(".class", "").replace("\\", "/").replace("/", ".");
cgs.put(className, cg);
} else {
NonClassEntries.add(entry, jf.getInputStream(entry));
}
}
logger.debug("Classes loaded from JAR");
jf.close();
}
public static boolean typeA(ClassGen cg) {
for (Method m : cg.getMethods()) {
if (m.getArgumentTypes().length == 1 && m.getArgumentTypes()[0].equals(Type.getType(char[].class))) {
return true;
}
}
return false;
}
public static char[] findKeyC(ClassGen cg) {
for (Method m : cg.getMethods()) {
if (m.getName().contains("clinit")) {
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionHandle[] handles = mg.getInstructionList().getInstructionHandles();
char[] keyAsChars = new char[5];
int found = 0;
for (int i = handles.length - 1; i > 0; i--) {
if (found < 5) {
//TODO: FIX THIS, THE LAST TWO CHARS FOR THE KEY ARE BEING FOUND PROPERLY BUT SAVED TO THE ARRAY WRONG
if ((handles[i - 1].getInstruction() instanceof BIPUSH)
&& ((handles[i].getInstruction() instanceof GOTO && ((GOTO) handles[i].getInstruction()).getTarget().getInstruction() instanceof IXOR)
|| handles[i].getInstruction() instanceof IXOR)) {
keyAsChars[found] = (char) ((BIPUSH) handles[i - 1].getInstruction()).getValue().intValue();
logger.debug(found + " found key char: " + (int) keyAsChars[found] + " line: " + i);
found++;
} else if ((handles[i - 1].getInstruction() instanceof ICONST)
&& ((handles[i].getInstruction() instanceof GOTO && ((GOTO) handles[i].getInstruction()).getTarget().getInstruction() instanceof IXOR)
|| handles[i].getInstruction() instanceof IXOR)) {
keyAsChars[found] = (char) ((ICONST) handles[i - 1].getInstruction()).getValue().intValue();
logger.debug(found + " found key char: " + (int) keyAsChars[found] + " line: " + i);
found++;
}
} else {
break;
}
}
char[] right = new char[5];
right[0] = keyAsChars[4];
right[1] = keyAsChars[3];
right[2] = keyAsChars[2];
right[3] = keyAsChars[1];
right[4] = keyAsChars[0];
for (char c : right) {
logger.debug("KeyChar: " + (int) c);
}
if (keyAsChars == new char[5]) {
logger.error("ZKM Key Method C Failed, please send in this file to be analyzed.");
return null;
} else {
logger.debug("ZKM Key = " + String.valueOf(right));
return right;
}
}
}
return null;
}
public static char[] findKeyB(ClassGen cg) {
return findKeyC(cg);
/*
for(Method m : cg.getMethods()) {
if(m.getName().contains("clinit")) {
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionHandle[] handles = mg.getInstructionList().getInstructionHandles();
char[] keyAsChars = new char[5];
int found = 0;
int ixor = -1;
for(int i = handles.length - 1; i < handles.length; i--){
if(handles[i].getInstruction() instanceof IXOR){
ixor = i;
break;
}
}
if(ixor == -1){
logger.error("ZKM Key Method B Failed, attempting method C");
return findKeyC(cg);
}
for(int i = ixor; i > 0; i--) {
if(found < 5) {
if((handles[i - 1].getInstruction() instanceof BIPUSH)
&& (handles[i].getInstruction() instanceof GOTO || handles[i].getInstruction() instanceof IXOR)) {
keyAsChars[found] = (char) ((BIPUSH) handles[i - 1].getInstruction()).getValue().intValue();
logger.debug("found key char: " + (char) found);
found++;
} else if((handles[i - 1].getInstruction() instanceof ICONST)
&& (handles[i].getInstruction() instanceof GOTO || handles[i].getInstruction() instanceof IXOR)) {
keyAsChars[found] = (char) ((ICONST) handles[i - 1].getInstruction()).getValue().intValue();
found++;
logger.debug("found key char: " + (char) found);
}
} else {
break;
}
}
char[] right = keyAsChars;
right[0] = keyAsChars[4];
right[1] = keyAsChars[3];
right[2] = keyAsChars[2];
right[3] = keyAsChars[1];
right[4] = keyAsChars[0];
if(keyAsChars == new char[5]) {
logger.error("ZKM Key Method B Failed, please send in this file to be analyzed.");
return null;
} else {
logger.debug("ZKM Key = " + String.valueOf(right));
return right;
}
}
}
return null; */
}
public static char[] findKey(ClassGen cg) {
if (typeA(cg)) {
System.out.println("Type A Found!");
for (Method m : cg.getMethods()) {
if (m.getArgumentTypes().length == 1 && m.getArgumentTypes()[0].equals(Type.getType(char[].class))) {
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionHandle[] handles = mg.getInstructionList().getInstructionHandles();
char[] keyAsChars = new char[5];
for (InstructionHandle handle : handles) {
if (handle.getInstruction() instanceof TABLESWITCH) {
TABLESWITCH xor = (TABLESWITCH) handle.getInstruction();
for (int a = 0; a < xor.getTargets().length; a++) {
Instruction target = xor.getTargets()[a].getInstruction();
if (target instanceof BIPUSH) {
keyAsChars[a] = (char) ((BIPUSH) target).getValue().intValue();
} else {
keyAsChars[a] = (char) ((ICONST) target).getValue().intValue();
}
}
Instruction target = xor.getTarget().getInstruction();
if (target instanceof BIPUSH) {
keyAsChars[4] = (char) ((BIPUSH) target).getValue().intValue();
} else {
keyAsChars[4] = (char) ((ICONST) target).getValue().intValue();
}
}
}
return keyAsChars;
}
}
} else {
for (Method m : cg.getMethods()) {
if (m.getName().contains("clinit")) {
MethodGen mg = new MethodGen(m, cg.getClassName(), cg.getConstantPool());
InstructionHandle[] handles = mg.getInstructionList().getInstructionHandles();
char[] keyAsChars;
for (InstructionHandle handle : handles) {
if (handle.getInstruction() instanceof TABLESWITCH) {
TABLESWITCH xor = (TABLESWITCH) handle.getInstruction();
keyAsChars = getKeyFromSwitch(xor, cg);
if (keyAsChars != null) {
return keyAsChars;
}
}
}
return findKeyB(cg);
}
}
}
return null;
}
private static char[] getKeyFromSwitch(TABLESWITCH xor, ClassGen cg) {
char[] keyAsChars = new char[5];
for (int a = 0; a < xor.getTargets().length; a++) {
Instruction target = xor.getTargets()[a].getInstruction();
if (GenericMethods.isNumber(target)) {
keyAsChars[a] = (char) GenericMethods.getValueOfNumber(target, cg.getConstantPool());
} else {
return null;
}
}
Instruction target = xor.getTarget().getInstruction();
if (target instanceof BIPUSH) {
keyAsChars[4] = (char) ((BIPUSH) target).getValue().intValue();
} else if (target instanceof ICONST) {
keyAsChars[4] = (char) ((ICONST) target).getValue().intValue();
} else {
return null;
}
return keyAsChars;
}
public static String decrypt(String encrypted, char[] key) {
char[] plainText = encrypted.toCharArray();
int plainTextLength = plainText.length;
int keyLength = key.length;
//
// encryption
char[] cryptoText = new char[plainTextLength];
for (int i = 0; i < plainTextLength; i++) {
cryptoText[i] = (char) (plainText[i] ^ key[i % keyLength]);
}
// finishing
return new String(cryptoText);
}
public void replaceStrings() throws TargetLostException {
for (ClassGen cg : cgs.values()) {
int replaced = 0;
for (Method method : cg.getMethods()) {
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null)
continue;
InstructionHandle[] handles = list.getInstructionHandles();
//if (!typeA(cg)) {
for (int i = 0; i < handles.length; i++) {
if ((handles[i].getInstruction() instanceof GETSTATIC)
&& ((handles[i + 1].getInstruction() instanceof BIPUSH)
|| (handles[i + 1].getInstruction() instanceof SIPUSH)
|| (handles[i + 1].getInstruction() instanceof ICONST))
&& (handles[i + 2].getInstruction() instanceof AALOAD)) {
int push;
if (handles[i + 1].getInstruction() instanceof BIPUSH) {
push = ((BIPUSH) handles[i + 1].getInstruction()).getValue().intValue();
} else if (handles[i + 1].getInstruction() instanceof SIPUSH) {
push = ((SIPUSH) handles[i + 1].getInstruction()).getValue().intValue();
} else {
push = ((ICONST) handles[i + 1].getInstruction()).getValue().intValue();
}
String decryptedString = getZKMString(cg.getClassName(), push);
int stringRef = cg.getConstantPool().addString(decryptedString);
LDC lc = new LDC(stringRef);
NOP nop = new NOP();
handles[i].setInstruction(lc);
handles[i + 1].setInstruction(nop);
handles[i + 2].setInstruction(nop);
replaced++;
}
}
mg.setInstructionList(list);
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
/*
} else {
logger.error("This type of ZKM is not currently supported for string deob!");
} */
}
if (replaced > 0) {
logger.debug("replaced " + replaced + " calls in class " + cg.getClassName());
}
}
}
public void removeOriginStrings() {
for (ClassGen cg : cgs.values()) {
for (Method method : cg.getMethods()) {
if (method.getName().contains("clinit")) {
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
InstructionHandle[] handles = list.getInstructionHandles();
int startLoc = -1;
int endLoc = -1;
for (int i = 0; i < handles.length; i++) {
if (((handles[i].getInstruction() instanceof BIPUSH) || (handles[i].getInstruction() instanceof SIPUSH) || (handles[i].getInstruction() instanceof ICONST))
&& (handles[i + 1].getInstruction() instanceof ANEWARRAY)) {
ANEWARRAY an = (ANEWARRAY) handles[i + 1].getInstruction();
ObjectType ty = an.getLoadClassType(cg.getConstantPool());
if (ty == null) continue;
String type = ty.toString();
if (type.equals("java.lang.String")) {
startLoc = i;
logger.debug("Start Location for <clinit> removal: " + startLoc);
}
}
if (startLoc >= 0) {
if (handles.length > (i + 2)) {
if (handles[i].getInstruction() instanceof POP
&& handles[i + 1].getInstruction() instanceof SWAP
&& handles[i + 2].getInstruction() instanceof TABLESWITCH) {
endLoc = i + 2;
logger.debug("End Location for <clinit> removal: " + endLoc);
break;
}
}
}
}
if ((startLoc >= 0) && (endLoc >= 0)) {
try {
list.delete(handles[startLoc], handles[endLoc]);
} catch (TargetLostException e) {
logger.error("Control flow obfuscation evident. Couldn't clear clinit");
//e.printStackTrace();
return;
}
logger.debug("NOPed " + (endLoc - startLoc) + " instructions from <clinit> in " + cg.getClassName());
list.setPositions();
mg.setInstructionList(list);
mg.setMaxLocals();
mg.setMaxStack();
cg.replaceMethod(method, mg.getMethod());
}
}
}
}
}
//TODO: It isn't finding the last string sometimes, so shit gets all fucked up and it ends up leaving a call to static{} and extra instructions
public void getStringsFromZKM() {
for (ClassGen cg : cgs.values()) {
char[] key = findKey(cg);
for (Method method : cg.getMethods()) {
MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionList list = mg.getInstructionList();
if (list == null) {
continue;
}
InstructionHandle[] handles = list.getInstructionHandles();
ArrayList<String> all = new ArrayList<String>();
if (method.getName().contains("clinit")) {
for (int i = 0; i < handles.length; i++) {
if (handles[i].getInstruction() instanceof LDC) {
LDC orig = ((LDC) handles[i].getInstruction());
if (!orig.getType(cg.getConstantPool()).getSignature().contains("String")) continue;
String enc = orig.getValue(cg.getConstantPool()).toString();
String dec = decrypt(enc, key);
all.add(dec);
logger.debug(cg.getClassName() + " -> " + dec);
}
}
zkStrings.put(cg.getClassName(), all);
logger.debug("Decrypted and stored " + all.size() + " strings from " + cg.getClassName());
}
}
}
}
private void locateObstructors() {
for (ClassGen cg : cgs.values()) {
for (Method method : cg.getMethods()) {
if (method.isAbstract() || method.isNative()) {
continue;
}
MethodGen mGen = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
InstructionFinder finder = new InstructionFinder(mGen.getInstructionList());
Iterator<InstructionHandle[]> matches = finder.search(
"GETSTATIC IFEQ ((ILOAD IFEQ ICONST GOTO ICONST)|(IINC ILOAD)) PUTSTATIC"
);
if (matches.hasNext()) {
InstructionHandle[] match = matches.next();
GETSTATIC gstatCtrlField = (GETSTATIC) match[0].getInstruction();
controlClass = gstatCtrlField.getName(cg.getConstantPool());
String fieldName = gstatCtrlField.getFieldName(cg.getConstantPool());
ClassGen ctrlClazz = cgs.get(controlClass);
controlField = ctrlClazz.containsField(fieldName);
}
}
}
if (controlField == null) {
logger.error("Couldn't Locate Control Field!");
return;
}
flowObstructors.add(controlField);
logger.debug("control field: " + controlClass + "." + controlField.getName() + " " + controlField.getSignature());
for (ClassGen cg : cgs.values()) {
final ConstantPoolGen cpg = cg.getConstantPool();
for (Method method : cg.getMethods()) {
if (method.isAbstract() || method.isNative()) {
continue;
}
MethodGen mGen = new MethodGen(method, cg.getClassName(), cpg);
InstructionFinder finder = new InstructionFinder(mGen.getInstructionList());
Iterator<InstructionHandle[]> matches = finder.search(
"(GETSTATIC|ILOAD) IFEQ (((ILOAD|GETSTATIC) IFEQ ICONST GOTO ICONST)|(IINC ILOAD)) PUTSTATIC",
new InstructionFinder.CodeConstraint() {
public boolean checkCode(InstructionHandle[] code) {
FieldInstruction ctrlFieldInstr = null;
if (code[0].getInstruction() instanceof GETSTATIC) {
ctrlFieldInstr = (FieldInstruction) code[0].getInstruction();
} else {
ctrlFieldInstr = (FieldInstruction) code[code.length - 2].getInstruction();
}
String className = ctrlFieldInstr.getName(cpg);
String fieldName = ctrlFieldInstr.getFieldName(cpg);
return className.equals(controlClass) && fieldName.equals(controlField.getName());
}
});
while (matches.hasNext()) {
InstructionHandle[] match = matches.next();
Instruction first = match[0].getInstruction();
ClassGen ctrlClazz;
Field flowObstructor = null;
if (first instanceof GETSTATIC) {
PUTSTATIC pstatCtrlField = (PUTSTATIC) match[match.length - 2].getInstruction();
String className = pstatCtrlField.getName(cg.getConstantPool());
String fieldName = pstatCtrlField.getFieldName(cg.getConstantPool());
ctrlClazz = cgs.get(className);
flowObstructor = ctrlClazz.containsField(fieldName);
} else {
ILOAD iLoad = (ILOAD) first;
int idx = iLoad.getIndex();
InstructionHandle iStoreHandle = finder.getInstructionList().getInstructionHandles()[1];
ISTORE iStore = (ISTORE) iStoreHandle.getInstruction();
assert idx == iStore.getIndex() && idx == mGen.getMaxLocals() - 1 : "expected " + idx
+ " found " + iStore.getIndex();
GETSTATIC gstatCtrlField = (GETSTATIC) iStoreHandle.getPrev().getInstruction();
String className = gstatCtrlField.getName(cg.getConstantPool());
String fieldName = gstatCtrlField.getFieldName(cg.getConstantPool());
ctrlClazz = cgs.get(className);
flowObstructor = ctrlClazz.containsField(fieldName);
}
if (!flowObstructors.contains(flowObstructor)) {
logger.debug("flow obstructor: " + ctrlClazz.getClassName() + "." + flowObstructor.getName()
+ " " + flowObstructor.getSignature());
flowObstructors.add(flowObstructor);
}
}
}
}
}
public void transform() {
logger.log("ZKM Deobfuscator");
logger.log("Starting Opaque Predicate Remover...");
locateObstructors();
opaqueTransformer();
logger.log("Starting String Encryption Removal...");
try {
logger.log("Starting ZKM String Grabber...");
getStringsFromZKM();
logger.log("Starting ZKM String Replacer...");
replaceStrings();
} catch (TargetLostException e) {
e.printStackTrace();
}
logger.log("Starting String Origin Removal...");
removeOriginStrings();
logger.log("Starting Unconditional Branch Remover...");
unconditionalBranchTransformer();
logger.log("Starting Exit Flow Corrector...");
exitFlowTransformer();
logger.log("Deobfuscation finished! Dumping jar...");
GenericMethods.dumpJar(JAR_NAME, cgs.values());
logger.log("Operation Completed.");
}
private InstructionHandle findIStore(InstructionHandle start, int idx) {
InstructionHandle ih = start;
while (ih != null) {
if (ih.getInstruction() instanceof ISTORE) {
if (((ISTORE) ih.getInstruction()).getIndex() == idx) {
return ih;
}
}
ih = ih.getNext();
}
return null;
}
public void unconditionalBranchTransformer() {
for (ClassGen cg : cgs.values()) {
for (Method method : cg.getMethods()) {
final MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
if (method.isAbstract() || method.isNative()) {
return;
}
final InstructionList list = mg.getInstructionList();
InstructionFinder finder = new InstructionFinder(list);
final ConstantPoolGen cpg = cg.getConstantPool();
int branchesSimplified = 0;
Iterator<InstructionHandle[]> matches = finder.search("IfInstruction");
while (matches.hasNext()) {
InstructionHandle ifHandle = matches.next()[0];
InstructionHandle target = ((BranchHandle) ifHandle).getTarget();
if (target.getInstruction() instanceof GOTO) {
branchesSimplified++;
((BranchHandle) ifHandle).setTarget(((BranchHandle) target).getTarget());
}
}
matches = finder.search("GOTO GOTO");
while (matches.hasNext()) {
InstructionHandle[] match = matches.next();
try {
list.delete(match[0]);
} catch (TargetLostException tlex) {
for (InstructionHandle target : tlex.getTargets()) {
for (InstructionTargeter targeter : target.getTargeters()) {
targeter.updateTarget(target, match[1]);
}
}
}
}
mg.setInstructionList(list);
mg.setMaxLocals();
mg.setMaxStack();
if (branchesSimplified > 0) {
logger.debug("simplified " + branchesSimplified + " unconditional branches");
cg.replaceMethod(method, mg.getMethod());
}
}
}
}
public void exitFlowTransformer() {
for (ClassGen cg : cgs.values()) {
int correct = 0;
for (Method method : cg.getMethods()) {
final MethodGen mgen = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
if (!method.isAbstract() && !method.isNative()) {
InstructionList list = mgen.getInstructionList();
InstructionFinder finder = new InstructionFinder(list);
CodeExceptionGen[] exceptionGens = mgen.getExceptionHandlers();
for (Iterator<InstructionHandle[]> matches = finder.search(
"ASTORE ALOAD (NEW DUP (PushInstruction InvokeInstruction)+ (ALOAD IfInstruction LDC GOTO LDC " +
"INVOKEVIRTUAL)? (PushInstruction InvokeInstruction)* InvokeInstruction+)?");
matches.hasNext();) {
/* thanks to popcorn89 */
InstructionHandle[] match = matches.next();
if (!(match[match.length - 1].getInstruction() instanceof ATHROW)) {
continue;
}
InstructionHandle astoreInstr = match[0];
InstructionHandle athrowInstr = match[match.length - 1];
InstructionHandle toRedirect = athrowInstr.getNext();
for (CodeExceptionGen exgen : exceptionGens) {
if (exgen.getHandlerPC().equals(astoreInstr)) {
mgen.removeExceptionHandler(exgen);
}
}
try {
list.delete(astoreInstr, athrowInstr);
} catch (TargetLostException tlex) {
if (athrowInstr == list.getEnd()) {
toRedirect = astoreInstr.getPrev();
}
if (toRedirect != null) {
for (InstructionHandle target : tlex.getTargets()) {
for (InstructionTargeter targeter : target.getTargeters()) {
targeter.updateTarget(target, toRedirect);
}
}
}
}
}
list.setPositions(true);
InstructionHandle lastInstr = list.getEnd();
InstructionHandle secondToLastInstr = lastInstr.getPrev();
if (secondToLastInstr != null && (lastInstr.getInstruction() instanceof RETURN)
&& (secondToLastInstr.getInstruction() instanceof RETURN)) {
try {
list.delete(secondToLastInstr);
} catch (TargetLostException tlex) {
for (InstructionHandle target : tlex.getTargets()) {
for (InstructionTargeter targeter : target.getTargeters()) {
targeter.updateTarget(target, lastInstr);
}
}
}
}
if (mgen.getMethod() != method) {
correct++;
logger.debug("corrected exit flow in " + cg.getClassName() + "." + mgen.getName() + mgen.getSignature());
mgen.setInstructionList(list);
mgen.setMaxLocals();
mgen.setMaxStack();
cg.replaceMethod(method, mgen.getMethod());
}
}
}
logger.debug("Corrected exit flow " + correct + " times in " + cg.getClassName());
}
}
public void opaqueTransformer() {
for (ClassGen cg : cgs.values()) {
for (Method method : cg.getMethods()) {
final MethodGen mg = new MethodGen(method, cg.getClassName(), cg.getConstantPool());
if (method.isAbstract() || method.isNative()) {
return;
}
final InstructionList list = mg.getInstructionList();
InstructionFinder finder = new InstructionFinder(list);
final ConstantPoolGen cpg = cg.getConstantPool();
int stripped = 0;
Iterator<InstructionHandle[]> matches = finder.search(
"(ILOAD|GETSTATIC) ((ISTORE)|(IFNE|IFEQ)) (((IINC ILOAD)|((ILOAD IFEQ)? ICONST GOTO ICONST)) PUTSTATIC)?",
code -> {
InstructionHandle ih = code[0];
if (code.length <= 3) {
if (ih.getInstruction() instanceof GETSTATIC) {
GETSTATIC gstat = (GETSTATIC) ih.getInstruction();
return flowObstructors.contains(cgs.get(gstat.getName(cpg))
.containsField(gstat.getFieldName(cpg)));
} else {
ILOAD iLoad = (ILOAD) ih.getInstruction();
int idx = iLoad.getIndex();
if (idx < mg.getArgumentTypes().length + (mg.isStatic() ? 0 : 1)) {
return false;
}
InstructionHandle storeHandle = findIStore(list.getStart(), idx);
if (storeHandle == null || !(storeHandle.getPrev().getInstruction() instanceof GETSTATIC)) {
return false;
}
GETSTATIC gstat = (GETSTATIC) storeHandle.getPrev().getInstruction();
return flowObstructors.contains(cgs.get(gstat.getName(cpg))
.containsField(gstat.getFieldName(cpg)));
}
} else {
GETSTATIC gstat = (GETSTATIC) ih.getInstruction();
ClassGen cp = cgs.get(gstat.getName(cpg));
Field fz = cp.containsField(gstat.getFieldName(cpg));
return cp != null && fz != null && controlField != null && controlField.equals(fz);
}
});
while (matches.hasNext()) {
List<InstructionHandle> toDelete = new LinkedList<InstructionHandle>();
InstructionHandle[] match = matches.next();
InstructionHandle ih = match[0];
InstructionHandle theBranch = match[1];
InstructionHandle lastInstr = match[match.length - 2];
InstructionHandle toRedirect = lastInstr.getNext();
if (toRedirect == null) {
break;
}
if (theBranch.getInstruction() instanceof IFEQ && match.length <= 3) {
toDelete.add(ih);
theBranch.setInstruction(new GOTO(((BranchHandle) theBranch).getTarget()));
} else {
try {
list.delete(ih, lastInstr);
} catch (TargetLostException tlex) {
for (InstructionHandle target : tlex.getTargets()) {
for (InstructionTargeter targeter : target.getTargeters()) {
logger.debug("redirected " + target + " to " + toRedirect + " in " + cg.getClassName() + "." + mg.getName() + mg.getSignature());
targeter.updateTarget(target, toRedirect);
}
}
}
}
stripped++;
for (InstructionHandle del : toDelete) {
try {
list.delete(del);
} catch (TargetLostException tlex) {
for (InstructionHandle target : tlex.getTargets()) {
for (InstructionTargeter targeter : target.getTargeters()) {
logger.debug("redirected " + target + " to " + (theBranch.getInstruction() instanceof IFEQ ? lastInstr : toRedirect) + " in " + cg.getClassName() + "." + mg.getName() + mg.getSignature());
targeter.updateTarget(target, theBranch.getInstruction() instanceof IFEQ ?
lastInstr : toRedirect);
}
}
}
}
}
if (stripped > 0) {
logger.debug("stripped " + stripped + " opaque predicates from " + cg.getClassName() + "." + mg.getName() + mg.getSignature());
mg.setInstructionList(list);
mg.setMaxStack();
mg.setMaxLocals();
cg.replaceMethod(method, mg.getMethod());
}
}
}
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/util/GenericClassLoader.java
================================================
package net.contra.jmd.util;
/**
* Created by IntelliJ IDEA.
* User: Eric
* Date: Dec 9, 2010
* Time: 4:23:08 AM
*/
public class GenericClassLoader extends ClassLoader {
public GenericClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> loadClass(String name, byte[] crap) {
//name = name.substring(0, name.lastIndexOf('.'));
Class c = null;
try {
//c = super.defineClass(crap, 0, crap.length);
c = super.defineClass(name, crap, 0, crap.length);
} catch (Exception e) {
return c;
}
super.resolveClass(c);
return c;
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/util/GenericMethods.java
================================================
package net.contra.jmd.util;
import org.apache.bcel.generic.*;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
public class GenericMethods {
//TODO: Get LDC_W into isInt and getValueOfInt
public static boolean isNumber(Instruction ins) {
return ins instanceof BIPUSH
|| ins instanceof SIPUSH
|| ins instanceof ICONST
|| ins instanceof LDC_W;
}
public static int getValueOfNumber(Instruction ins, ConstantPoolGen cpg) {
if (ins instanceof BIPUSH) {
return ((BIPUSH) ins).getValue().intValue();
} else if (ins instanceof SIPUSH) {
return ((SIPUSH) ins).getValue().intValue();
} else if (ins instanceof ICONST) {
return ((ICONST) ins).getValue().intValue();
} else if (ins instanceof LDC_W) {
LDC_W ldcw = (LDC_W) ins;
return Integer.valueOf(ldcw.getValue(cpg).toString());
} else {
return -1;
}
}
public static String getCallSignature(Instruction ins, ConstantPoolGen cp) {
if (ins instanceof INVOKESTATIC) {
INVOKESTATIC invst = (INVOKESTATIC) ins;
return invst.getSignature(cp);
} else if (ins instanceof INVOKEVIRTUAL) {
INVOKEVIRTUAL invst = (INVOKEVIRTUAL) ins;
return invst.getSignature(cp);
} else if (ins instanceof INVOKEINTERFACE) {
INVOKEINTERFACE invst = (INVOKEINTERFACE) ins;
return invst.getSignature(cp);
} else if (ins instanceof INVOKESPECIAL) {
INVOKESPECIAL invst = (INVOKESPECIAL) ins;
return invst.getSignature(cp);
} else {
return null;
}
}
public static Instruction getNewInvoke(Instruction ins, int index) {
if (ins instanceof INVOKESTATIC) {
INVOKESTATIC invst = (INVOKESTATIC) ins;
invst.setIndex(index);
return invst;
} else if (ins instanceof INVOKEVIRTUAL) {
INVOKEVIRTUAL invst = (INVOKEVIRTUAL) ins;
invst.setIndex(index);
return invst;
} else if (ins instanceof INVOKEINTERFACE) {
INVOKEINTERFACE invst = (INVOKEINTERFACE) ins;
invst.setIndex(index);
return invst;
} else if (ins instanceof INVOKESPECIAL) {
INVOKESPECIAL invst = (INVOKESPECIAL) ins;
invst.setIndex(index);
return invst;
} else {
return null;
}
}
public static String getCallReturnType(Instruction ins, ConstantPoolGen cp) {
if (ins instanceof INVOKESTATIC) {
INVOKESTATIC invst = (INVOKESTATIC) ins;
return invst.getReturnType(cp).toString();
} else if (ins instanceof INVOKEVIRTUAL) {
INVOKEVIRTUAL invst = (INVOKEVIRTUAL) ins;
return invst.getReturnType(cp).toString();
} else if (ins instanceof INVOKEINTERFACE) {
INVOKEINTERFACE invst = (INVOKEINTERFACE) ins;
return invst.getReturnType(cp).toString();
} else if (ins instanceof INVOKESPECIAL) {
INVOKESPECIAL invst = (INVOKESPECIAL) ins;
return invst.getReturnType(cp).toString();
} else {
return null;
}
}
public static String getCallClassName(Instruction ins, ConstantPoolGen cp) {
if (ins instanceof INVOKESTATIC) {
INVOKESTATIC invst = (INVOKESTATIC) ins;
return invst.getClassName(cp);
} else if (ins instanceof INVOKEVIRTUAL) {
INVOKEVIRTUAL invst = (INVOKEVIRTUAL) ins;
return invst.getClassName(cp);
} else if (ins instanceof INVOKEINTERFACE) {
INVOKEINTERFACE invst = (INVOKEINTERFACE) ins;
return invst.getClassName(cp);
} else if (ins instanceof INVOKESPECIAL) {
INVOKESPECIAL invst = (INVOKESPECIAL) ins;
return invst.getClassName(cp);
} else {
return null;
}
}
public static void dumpJar(String path, Collection<ClassGen> cgs) {
FileOutputStream os;
path = path.replace(".jar", "") + "-deob.jar";
try {
os = new FileOutputStream(new File(path));
} catch (FileNotFoundException fnfe) {
throw new RuntimeException("could not create file \"" + path + "\": " + fnfe);
}
JarOutputStream jos;
try {
jos = new JarOutputStream(os);
for (JarEntry jbe : NonClassEntries.entries) {
JarEntry destEntry = new JarEntry(jbe.getName());
byte[] bite = IOUtils.toByteArray(NonClassEntries.ins.get(jbe));
jos.putNextEntry(destEntry);
jos.write(bite);
jos.closeEntry();
}
for (ClassGen classIt : cgs) {
JarEntry classEntry = new JarEntry(classIt.getClassName().replace('.', '/') + ".class");
jos.putNextEntry(classEntry);
jos.write(classIt.getJavaClass().getBytes());
jos.closeEntry();
}
jos.closeEntry();
jos.close();
} catch (IOException ignored) {
}
}
public static String getCallMethodName(Instruction ins, ConstantPoolGen cp) {
if (ins instanceof INVOKESTATIC) {
INVOKESTATIC invst = (INVOKESTATIC) ins;
return invst.getMethodName(cp);
} else if (ins instanceof INVOKEVIRTUAL) {
INVOKEVIRTUAL invst = (INVOKEVIRTUAL) ins;
return invst.getMethodName(cp);
} else if (ins instanceof INVOKEINTERFACE) {
INVOKEINTERFACE invst = (INVOKEINTERFACE) ins;
return invst.getMethodName(cp);
} else if (ins instanceof INVOKESPECIAL) {
INVOKESPECIAL invst = (INVOKESPECIAL) ins;
return invst.getMethodName(cp);
} else {
return null;
}
}
public static Type[] getCallArgTypes(Instruction ins, ConstantPoolGen cp) {
if (ins instanceof INVOKESTATIC) {
INVOKESTATIC invst = (INVOKESTATIC) ins;
return invst.getArgumentTypes(cp);
} else if (ins instanceof INVOKEVIRTUAL) {
INVOKEVIRTUAL invst = (INVOKEVIRTUAL) ins;
return invst.getArgumentTypes(cp);
} else if (ins instanceof INVOKEINTERFACE) {
INVOKEINTERFACE invst = (INVOKEINTERFACE) ins;
return invst.getArgumentTypes(cp);
} else if (ins instanceof INVOKESPECIAL) {
INVOKESPECIAL invst = (INVOKESPECIAL) ins;
return invst.getArgumentTypes(cp);
} else {
return null;
}
}
public static boolean isCall(Instruction ins) {
return ins instanceof INVOKESTATIC
|| ins instanceof INVOKEVIRTUAL
|| ins instanceof INVOKEINTERFACE
|| ins instanceof INVOKESPECIAL;
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/util/HandleSearcher.java
================================================
package net.contra.jmd.util;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.LDC;
/**
* Created by IntelliJ IDEA.
* User: Eric
* Date: Nov 25, 2010
* Time: 10:16:26 PM
*/
public class HandleSearcher {
InstructionHandle[] handles;
ConstantPoolGen cpg;
public int index;
public void setPosition(int index) {
this.index = index;
}
public LDC previousLDC() {
for (; index >= 0; index--) {
if (handles[index].getInstruction() instanceof LDC) {
return (LDC) handles[index].getInstruction();
}
}
return null;
}
public INVOKESTATIC nextInvokeStatic(String className) {
for (; index < handles.length; index++) {
if (index > -1) {
if (handles[index].getInstruction() instanceof INVOKESTATIC) {
INVOKESTATIC methodCall = (INVOKESTATIC) handles[index].getInstruction();
if (methodCall.getClassName(cpg).equals(className)) {
return methodCall;
}
}
}
}
return null;
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/util/LogHandler.java
================================================
package net.contra.jmd.util;
public class LogHandler {
private String _className = "NoClass";
public LogHandler(String className) {
_className = className;
}
public void message(String msg) {
System.out.println(msg);
}
public void log(String msg) {
System.out.println("[" + _className + "]" + msg);
}
public void debug(String msg) {
System.out.println("[" + _className + "]" + "[DEBUG]" + msg);
}
public void error(String msg) {
System.out.println("[" + _className + "]" + "[ERROR]" + msg);
}
}
================================================
FILE: jmd-core/src/main/java/net/contra/jmd/util/NonClassEntries.java
================================================
package net.contra.jmd.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
public final class NonClassEntries {
public static ArrayList<JarEntry> entries = new ArrayList<JarEntry>();
public static Map<JarEntry, InputStream> ins = new HashMap<JarEntry, InputStream>();
private NonClassEntries() {
}
public static JarEntry getByName(String name) {
for (JarEntry e : entries) {
if (e.getName().equals(name)) {
return e;
}
}
return null;
}
public static void add(JarEntry entry, InputStream inputStream) {
entries.add(entry);
ins.put(entry, inputStream);
}
}
================================================
FILE: jmd-core/src/test/java/net/contra/jmd/transformers/dasho/DashOTransformerTest.java
================================================
package net.contra.jmd.transformers.dasho;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
public class DashOTransformerTest {
@Test(dataProvider = "inputData")
public void testDecrypt(String input, String outputExpected) throws Exception {
final String outputActual = DashOTransformer.decrypt(input);
assertEquals(outputActual, outputExpected);
}
@DataProvider
public Object[][] inputData() {
return new Object[][]{
{null, null},
{"<", ";"},
{"", ""}
};
}
}
================================================
FILE: jmd-gui/gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Nov 08 23:54:48 MSK 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip
================================================
FILE: jmd-gui/gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: jmd-gui/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: jmd-gui/src/main/java/net/contra/jmd/ConfigureApp.java
================================================
package net.contra.jmd;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;
public class ConfigureApp extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("JMD-GUI");
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(ConfigureApp.class.getResource("/view/configure.fxml"));
Pane rootLayout = loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
// empty
}
}
}
================================================
FILE: jmd-gui/src/main/resources/view/configure.fxml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="125.0"
prefWidth="325.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<ComboBox layoutX="81.0" layoutY="54.0" prefWidth="150.0"/>
<CheckBox layoutX="249.0" layoutY="58.0" mnemonicParsing="false" text="Debug?"/>
<Label layoutX="7.0" layoutY="58.0" text="Transformer:"/>
<TextField layoutX="82.0" layoutY="14.0"/>
<Label layoutX="21.0" layoutY="18.0" text="Input JAR:"/>
<Button layoutX="249.0" layoutY="14.0" mnemonicParsing="false" text="Choose..."/>
<Button layoutX="130.0" layoutY="87.0" mnemonicParsing="false" text="Run"/>
</AnchorPane>
================================================
FILE: settings.gradle
================================================
rootProject.name = 'jmd'
include ':jmd-cli', ':jmd-core', ':jmd-gui'
gitextract_nu5m95t5/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jmd-cli/ │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── src/ │ └── main/ │ └── java/ │ └── net/ │ └── contra/ │ └── jmd/ │ ├── Deobfuscator.java │ └── Version.java ├── jmd-core/ │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── net/ │ │ └── contra/ │ │ └── jmd/ │ │ ├── transformers/ │ │ │ ├── Transformer.java │ │ │ ├── allatori/ │ │ │ │ └── AllatoriTransformer.java │ │ │ ├── dasho/ │ │ │ │ └── DashOTransformer.java │ │ │ ├── generic/ │ │ │ │ ├── ForeignCallRemover.java │ │ │ │ ├── GenericStringDeobfuscator.java │ │ │ │ ├── Renamer.java │ │ │ │ ├── StackFixer.java │ │ │ │ ├── StringFixer.java │ │ │ │ ├── StringScanner.java │ │ │ │ └── TransformerTemplate.java │ │ │ ├── jshrink/ │ │ │ │ ├── JShrinkTransformer.java │ │ │ │ └── StoreHandler.java │ │ │ ├── smokescreen/ │ │ │ │ └── SmokeScreenTransformer.java │ │ │ └── zkm/ │ │ │ └── ZKMTransformer.java │ │ └── util/ │ │ ├── GenericClassLoader.java │ │ ├── GenericMethods.java │ │ ├── HandleSearcher.java │ │ ├── LogHandler.java │ │ └── NonClassEntries.java │ └── test/ │ └── java/ │ └── net/ │ └── contra/ │ └── jmd/ │ └── transformers/ │ └── dasho/ │ └── DashOTransformerTest.java ├── jmd-gui/ │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── src/ │ └── main/ │ ├── java/ │ │ └── net/ │ │ └── contra/ │ │ └── jmd/ │ │ └── ConfigureApp.java │ └── resources/ │ └── view/ │ └── configure.fxml └── settings.gradle
SYMBOL INDEX (123 symbols across 23 files)
FILE: jmd-cli/src/main/java/net/contra/jmd/Deobfuscator.java
class Deobfuscator (line 19) | public class Deobfuscator {
method main (line 25) | public static void main(String[] argv) throws Exception {
FILE: jmd-cli/src/main/java/net/contra/jmd/Version.java
class Version (line 10) | public final class Version {
method Version (line 14) | private Version() {
method getVersion (line 17) | public static String getVersion() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/Transformer.java
type Transformer (line 5) | public interface Transformer {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/allatori/AllatoriTransformer.java
class AllatoriTransformer (line 18) | public class AllatoriTransformer implements Transformer {
method AllatoriTransformer (line 25) | public AllatoriTransformer(String jarfile, boolean strong) throws Exce...
method isStringClass (line 52) | private boolean isStringClass(ClassGen cg) {
method isStringClassB (line 65) | private boolean isStringClassB(ClassGen cg) {
method decode (line 74) | public static String decode(String string) {
method decodeContext (line 97) | public static String decodeContext(String encrypted, String callingCla...
method transform (line 115) | public void transform() throws TargetLostException {
method replaceStrings (line 123) | public void replaceStrings() throws TargetLostException {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/dasho/DashOTransformer.java
class DashOTransformer (line 21) | public class DashOTransformer implements Transformer {
method DashOTransformer (line 28) | public DashOTransformer(String jarfile) throws Exception {
method decrypt (line 49) | public static String decrypt(String input) {
method isEmpty (line 71) | private static boolean isEmpty(final CharSequence cs) {
method setDecryptor (line 75) | public void setDecryptor() {
method removeStringEncryption (line 96) | public void removeStringEncryption() {
method dumpJar (line 131) | public void dumpJar(String path) {
method transform (line 163) | public void transform() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/ForeignCallRemover.java
class ForeignCallRemover (line 22) | public class ForeignCallRemover {
method ForeignCallRemover (line 28) | public ForeignCallRemover(String jarfile) throws Exception {
method isAuthClass (line 52) | public boolean isAuthClass(ClassGen cg) {
method dumpJar (line 63) | public void dumpJar(String path) {
method RemoveCalls (line 95) | public void RemoveCalls() {
method replaceCheckMethod (line 162) | public void replaceCheckMethod() {
method fixPOPs (line 182) | public void fixPOPs() {
method transform (line 210) | public void transform() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/GenericStringDeobfuscator.java
class GenericStringDeobfuscator (line 28) | public class GenericStringDeobfuscator {
method GenericStringDeobfuscator (line 33) | public GenericStringDeobfuscator(String jarfile) throws Exception {
method replaceStrings (line 53) | public void replaceStrings() {
method transform (line 128) | public void transform() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/Renamer.java
class Renamer (line 17) | public class Renamer {
method Renamer (line 24) | public Renamer(String jarfile) throws Exception {
method renameClasses (line 50) | public void renameClasses() {
method replaceMethodRefs (line 66) | public void replaceMethodRefs() {
method renameMethods (line 106) | public void renameMethods() {
method transform (line 145) | public void transform() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/StackFixer.java
class StackFixer (line 24) | public class StackFixer {
method StackFixer (line 29) | public StackFixer(String jarfile) throws Exception {
method fixStack (line 50) | public void fixStack() {
method transform (line 63) | public void transform() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/StringFixer.java
class StringFixer (line 22) | public class StringFixer {
method StringFixer (line 28) | public StringFixer(String jarfile) throws Exception {
method removeBASA (line 49) | public void removeBASA() {
method dumpJar (line 153) | public void dumpJar(String path) {
method transform (line 183) | public void transform() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/StringScanner.java
class StringScanner (line 16) | public class StringScanner {
method StringScanner (line 23) | public StringScanner(String jarfile, String scanstring, boolean replac...
method searchConstantPool (line 46) | public void searchConstantPool() {
method scan (line 84) | public void scan() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/generic/TransformerTemplate.java
class TransformerTemplate (line 10) | public class TransformerTemplate {
method transform (line 15) | public void transform() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/jshrink/JShrinkTransformer.java
class JShrinkTransformer (line 18) | public class JShrinkTransformer implements Transformer {
method JShrinkTransformer (line 24) | public JShrinkTransformer(String jarfile) throws Exception {
method isLoader (line 56) | public boolean isLoader(ClassGen cg) {
method replaceStrings (line 66) | public void replaceStrings() throws TargetLostException {
method transform (line 106) | public void transform() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/jshrink/StoreHandler.java
class StoreHandler (line 13) | public class StoreHandler {
method I (line 18) | public static synchronized String I(int paramInt) {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/smokescreen/SmokeScreenTransformer.java
class SmokeScreenTransformer (line 20) | public class SmokeScreenTransformer implements Transformer {
method getActualString (line 26) | public String getActualString(String className, int i1, int i2) {
method replaceStrings (line 30) | public void replaceStrings() {
method grabStrings (line 74) | public void grabStrings() {
method SmokeScreenTransformer (line 106) | public SmokeScreenTransformer(String jarfile) throws Exception {
method decrypt (line 129) | public static String decrypt(String encrypted, int myKey) {
method transform (line 141) | public void transform() {
method unconditionalBranchTransformer (line 158) | public void unconditionalBranchTransformer() {
method exitFlowTransformer (line 202) | public void exitFlowTransformer() {
FILE: jmd-core/src/main/java/net/contra/jmd/transformers/zkm/ZKMTransformer.java
class ZKMTransformer (line 16) | public class ZKMTransformer implements Transformer {
method getZKMString (line 25) | public String getZKMString(String className, int index) {
method ZKMTransformer (line 29) | public ZKMTransformer(String jarfile) throws Exception {
method typeA (line 51) | public static boolean typeA(ClassGen cg) {
method findKeyC (line 60) | public static char[] findKeyC(ClassGen cg) {
method findKeyB (line 109) | public static char[] findKeyB(ClassGen cg) {
method findKey (line 165) | public static char[] findKey(ClassGen cg) {
method getKeyFromSwitch (line 218) | private static char[] getKeyFromSwitch(TABLESWITCH xor, ClassGen cg) {
method decrypt (line 239) | public static String decrypt(String encrypted, char[] key) {
method replaceStrings (line 254) | public void replaceStrings() throws TargetLostException {
method removeOriginStrings (line 305) | public void removeOriginStrings() {
method getStringsFromZKM (line 359) | public void getStringsFromZKM() {
method locateObstructors (line 388) | private void locateObstructors() {
method transform (line 473) | public void transform() {
method findIStore (line 498) | private InstructionHandle findIStore(InstructionHandle start, int idx) {
method unconditionalBranchTransformer (line 511) | public void unconditionalBranchTransformer() {
method exitFlowTransformer (line 555) | public void exitFlowTransformer() {
method opaqueTransformer (line 625) | public void opaqueTransformer() {
FILE: jmd-core/src/main/java/net/contra/jmd/util/GenericClassLoader.java
class GenericClassLoader (line 9) | public class GenericClassLoader extends ClassLoader {
method GenericClassLoader (line 10) | public GenericClassLoader(ClassLoader parent) {
method loadClass (line 14) | public Class<?> loadClass(String name, byte[] crap) {
FILE: jmd-core/src/main/java/net/contra/jmd/util/GenericMethods.java
class GenericMethods (line 14) | public class GenericMethods {
method isNumber (line 16) | public static boolean isNumber(Instruction ins) {
method getValueOfNumber (line 23) | public static int getValueOfNumber(Instruction ins, ConstantPoolGen cp...
method getCallSignature (line 38) | public static String getCallSignature(Instruction ins, ConstantPoolGen...
method getNewInvoke (line 56) | public static Instruction getNewInvoke(Instruction ins, int index) {
method getCallReturnType (line 78) | public static String getCallReturnType(Instruction ins, ConstantPoolGe...
method getCallClassName (line 96) | public static String getCallClassName(Instruction ins, ConstantPoolGen...
method dumpJar (line 114) | public static void dumpJar(String path, Collection<ClassGen> cgs) {
method getCallMethodName (line 147) | public static String getCallMethodName(Instruction ins, ConstantPoolGe...
method getCallArgTypes (line 165) | public static Type[] getCallArgTypes(Instruction ins, ConstantPoolGen ...
method isCall (line 183) | public static boolean isCall(Instruction ins) {
FILE: jmd-core/src/main/java/net/contra/jmd/util/HandleSearcher.java
class HandleSearcher (line 14) | public class HandleSearcher {
method setPosition (line 19) | public void setPosition(int index) {
method previousLDC (line 23) | public LDC previousLDC() {
method nextInvokeStatic (line 35) | public INVOKESTATIC nextInvokeStatic(String className) {
FILE: jmd-core/src/main/java/net/contra/jmd/util/LogHandler.java
class LogHandler (line 3) | public class LogHandler {
method LogHandler (line 6) | public LogHandler(String className) {
method message (line 10) | public void message(String msg) {
method log (line 14) | public void log(String msg) {
method debug (line 18) | public void debug(String msg) {
method error (line 22) | public void error(String msg) {
FILE: jmd-core/src/main/java/net/contra/jmd/util/NonClassEntries.java
class NonClassEntries (line 11) | public final class NonClassEntries {
method NonClassEntries (line 15) | private NonClassEntries() {
method getByName (line 18) | public static JarEntry getByName(String name) {
method add (line 27) | public static void add(JarEntry entry, InputStream inputStream) {
FILE: jmd-core/src/test/java/net/contra/jmd/transformers/dasho/DashOTransformerTest.java
class DashOTransformerTest (line 8) | public class DashOTransformerTest {
method testDecrypt (line 10) | @Test(dataProvider = "inputData")
method inputData (line 17) | @DataProvider
FILE: jmd-gui/src/main/java/net/contra/jmd/ConfigureApp.java
class ConfigureApp (line 12) | public class ConfigureApp extends Application {
method main (line 14) | public static void main(String[] args) {
method start (line 18) | @Override
Condensed preview — 46 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (176K chars).
[
{
"path": ".gitignore",
"chars": 25,
"preview": ".gradle\n.idea\n*.iml\nbuild"
},
{
"path": ".travis.yml",
"chars": 89,
"preview": "language: java\njdk:\n - oraclejdk8\n\nafter_success:\n- ./gradlew jacocoTestReport coveralls"
},
{
"path": "LICENSE",
"chars": 1075,
"preview": "Copyright (c) 2011 Contra <contra@australia.edu> \n\nPermission is hereby granted, free of charge, to any person obtaining"
},
{
"path": "README.md",
"chars": 3126,
"preview": "**JMD is a general purpose Java bytecode deobfuscation tool**\n\n[![Build Status][travis-image]][travis-url] [![Coverage S"
},
{
"path": "build.gradle",
"chars": 3342,
"preview": "buildscript {\n ext {\n projectVersion = '1.6'\n\n gradleWrapperVersion = '3.1'\n\n daggerVersion = '2"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Tue Nov 08 23:54:47 MSK 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradlew",
"chars": 5242,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "gradlew.bat",
"chars": 2176,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "jmd-cli/gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Tue Nov 08 23:54:48 MSK 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "jmd-cli/gradlew",
"chars": 5242,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "jmd-cli/gradlew.bat",
"chars": 2176,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "jmd-cli/src/main/java/net/contra/jmd/Deobfuscator.java",
"chars": 4281,
"preview": "package net.contra.jmd;\n\nimport net.contra.jmd.transformers.allatori.AllatoriTransformer;\nimport net.contra.jmd.transfor"
},
{
"path": "jmd-cli/src/main/java/net/contra/jmd/Version.java",
"chars": 830,
"preview": "package net.contra.jmd;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.j"
},
{
"path": "jmd-core/gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Tue Nov 08 23:54:48 MSK 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "jmd-core/gradlew",
"chars": 5242,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "jmd-core/gradlew.bat",
"chars": 2176,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/Transformer.java",
"chars": 124,
"preview": "package net.contra.jmd.transformers;\n\nimport org.apache.bcel.generic.TargetLostException;\n\npublic interface Transformer "
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/allatori/AllatoriTransformer.java",
"chars": 6704,
"preview": "package net.contra.jmd.transformers.allatori;\n\nimport net.contra.jmd.transformers.Transformer;\nimport net.contra.jmd.uti"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/dasho/DashOTransformer.java",
"chars": 6780,
"preview": "package net.contra.jmd.transformers.dasho;\n\nimport net.contra.jmd.transformers.Transformer;\nimport net.contra.jmd.util.G"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/generic/ForeignCallRemover.java",
"chars": 9738,
"preview": "package net.contra.jmd.transformers.generic;\n\nimport net.contra.jmd.util.GenericMethods;\nimport net.contra.jmd.util.LogH"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/generic/GenericStringDeobfuscator.java",
"chars": 6734,
"preview": "/*\n\tTODO: Write a dynamic string decryptor like the one in SAE; Pattern is LDC INVOKESTATIC, \n\tgrab "
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/generic/Renamer.java",
"chars": 6855,
"preview": "package net.contra.jmd.transformers.generic;\n\nimport net.contra.jmd.util.GenericMethods;\nimport net.contra.jmd.util.LogH"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/generic/StackFixer.java",
"chars": 2319,
"preview": "package net.contra.jmd.transformers.generic;\n\nimport net.contra.jmd.util.GenericMethods;\nimport net.contra.jmd.util.LogH"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/generic/StringFixer.java",
"chars": 10084,
"preview": "package net.contra.jmd.transformers.generic;\n\nimport net.contra.jmd.util.GenericMethods;\nimport net.contra.jmd.util.LogH"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/generic/StringScanner.java",
"chars": 3648,
"preview": "package net.contra.jmd.transformers.generic;\n\nimport net.contra.jmd.util.LogHandler;\nimport net.contra.jmd.util.NonClass"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/generic/TransformerTemplate.java",
"chars": 664,
"preview": "package net.contra.jmd.transformers.generic;\n\nimport net.contra.jmd.util.GenericMethods;\nimport net.contra.jmd.util.LogH"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/jshrink/JShrinkTransformer.java",
"chars": 4865,
"preview": "package net.contra.jmd.transformers.jshrink;\n\nimport net.contra.jmd.transformers.Transformer;\nimport net.contra.jmd.util"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/jshrink/StoreHandler.java",
"chars": 1810,
"preview": "package net.contra.jmd.transformers.jshrink;\n\nimport net.contra.jmd.util.NonClassEntries;\n\nimport java.io.InputStream;\n\n"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/smokescreen/SmokeScreenTransformer.java",
"chars": 13153,
"preview": "package net.contra.jmd.transformers.smokescreen;\n\nimport net.contra.jmd.transformers.Transformer;\nimport net.contra.jmd."
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/transformers/zkm/ZKMTransformer.java",
"chars": 36568,
"preview": "package net.contra.jmd.transformers.zkm;\n\nimport net.contra.jmd.transformers.Transformer;\nimport net.contra.jmd.util.Gen"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/util/GenericClassLoader.java",
"chars": 656,
"preview": "package net.contra.jmd.util;\n\n/**\n * Created by IntelliJ IDEA.\n * User: Eric\n * Date: Dec 9, 2010\n * Time: 4:23:08 AM\n *"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/util/GenericMethods.java",
"chars": 7189,
"preview": "package net.contra.jmd.util;\n\nimport org.apache.bcel.generic.*;\nimport org.apache.commons.io.IOUtils;\n\nimport java.io.Fi"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/util/HandleSearcher.java",
"chars": 1273,
"preview": "package net.contra.jmd.util;\n\nimport org.apache.bcel.generic.ConstantPoolGen;\nimport org.apache.bcel.generic.INVOKESTATI"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/util/LogHandler.java",
"chars": 589,
"preview": "package net.contra.jmd.util;\n\npublic class LogHandler {\n private String _className = \"NoClass\";\n\n public LogHandle"
},
{
"path": "jmd-core/src/main/java/net/contra/jmd/util/NonClassEntries.java",
"chars": 828,
"preview": "package net.contra.jmd.util;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStre"
},
{
"path": "jmd-core/src/test/java/net/contra/jmd/transformers/dasho/DashOTransformerTest.java",
"chars": 644,
"preview": "package net.contra.jmd.transformers.dasho;\n\nimport org.testng.annotations.DataProvider;\nimport org.testng.annotations.Te"
},
{
"path": "jmd-gui/gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Tue Nov 08 23:54:48 MSK 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "jmd-gui/gradlew",
"chars": 5242,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "jmd-gui/gradlew.bat",
"chars": 2176,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "jmd-gui/src/main/java/net/contra/jmd/ConfigureApp.java",
"chars": 831,
"preview": "package net.contra.jmd;\n\nimport javafx.application.Application;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Scene"
},
{
"path": "jmd-gui/src/main/resources/view/configure.fxml",
"chars": 869,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.*?>\n<?import javafx.scene.control.*?>\n<?import java.lang.*"
},
{
"path": "settings.gradle",
"chars": 69,
"preview": "rootProject.name = 'jmd'\n\ninclude ':jmd-cli', ':jmd-core', ':jmd-gui'"
}
]
// ... and 4 more files (download for full content)
About this extraction
This page contains the full source code of the contra/JMD GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 46 files (162.5 KB), approximately 38.5k tokens, and a symbol index with 123 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.