From a8c82176244cda2607b14a21c2f7e3a55e304c1f Mon Sep 17 00:00:00 2001 From: Loremaster <88497195+Loremas1er@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:28:22 +0700 Subject: [PATCH] Add files via upload --- mvnw | 316 ++++++ mvnw.cmd | 188 ++++ pom.xml | 189 ++++ src/main/java/module-info.java | 17 + .../colonialdeobfuscator/ClassModifier.java | 11 + .../colonialdeobfuscator/Main.java | 24 + .../colonialdeobfuscator/MainCtr.java | 190 ++++ .../transformers/AccessTransformer.java | 28 + .../transformers/BooleanTransformer.java | 23 + .../transformers/CleanTransformer.java | 94 ++ .../transformers/FixNumberTransformer.java | 61 ++ .../transformers/FlowTransformer.java | 42 + .../transformers/NumberTransformer.java | 117 +++ .../transformers/StringTransformer.java | 196 ++++ .../colonialdeobfuscator/utils/ASMHelper.java | 438 +++++++++ .../utils/MathHelper.java | 50 + .../colonialdeobfuscator/utils/Utils.java | 930 ++++++++++++++++++ src/main/resources/META-INF/MANIFEST.MF | 3 + .../colonialdeobfuscator/Main.fxml | 19 + target/classes/META-INF/MANIFEST.MF | 3 + target/classes/module-info.class | Bin 0 -> 664 bytes .../colonialdeobfuscator/ClassModifier.class | Bin 0 -> 383 bytes .../colonialdeobfuscator/Main.class | Bin 0 -> 1350 bytes .../colonialdeobfuscator/Main.fxml | 19 + .../colonialdeobfuscator/MainCtr.class | Bin 0 -> 9089 bytes .../transformers/AccessTransformer.class | Bin 0 -> 2486 bytes .../transformers/BooleanTransformer.class | Bin 0 -> 3314 bytes .../transformers/CleanTransformer.class | Bin 0 -> 4305 bytes .../transformers/FixNumberTransformer.class | Bin 0 -> 4217 bytes .../transformers/FlowTransformer.class | Bin 0 -> 3451 bytes .../transformers/NumberTransformer.class | Bin 0 -> 6290 bytes .../transformers/StringTransformer.class | Bin 0 -> 9401 bytes .../utils/ASMHelper.class | Bin 0 -> 18237 bytes .../utils/MathHelper.class | Bin 0 -> 1702 bytes .../colonialdeobfuscator/utils/Utils.class | Bin 0 -> 20691 bytes 35 files changed, 2958 insertions(+) create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/java/module-info.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/ClassModifier.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/Main.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/MainCtr.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/AccessTransformer.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/BooleanTransformer.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/CleanTransformer.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FixNumberTransformer.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FlowTransformer.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/NumberTransformer.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/StringTransformer.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/ASMHelper.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/MathHelper.java create mode 100644 src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/Utils.java create mode 100644 src/main/resources/META-INF/MANIFEST.MF create mode 100644 src/main/resources/ru/paimon/deobfuscator/colonialdeobfuscator/Main.fxml create mode 100644 target/classes/META-INF/MANIFEST.MF create mode 100644 target/classes/module-info.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/ClassModifier.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/Main.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/Main.fxml create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/MainCtr.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/AccessTransformer.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/BooleanTransformer.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/CleanTransformer.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FixNumberTransformer.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FlowTransformer.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/NumberTransformer.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/StringTransformer.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/utils/ASMHelper.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/utils/MathHelper.class create mode 100644 target/classes/ru/paimon/deobfuscator/colonialdeobfuscator/utils/Utils.class diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + 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 + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2b97c53 --- /dev/null +++ b/pom.xml @@ -0,0 +1,189 @@ + + + 4.0.0 + + ru.paimon.deobfuscator.colonialdeobfuscator + ColonialDeobfuscator + 1.0-SNAPSHOT + ColonialDeobfuscator + + + UTF-8 + 5.9.2 + + + + + org.openjfx + javafx-controls + 17.0.6 + + + org.openjfx + javafx-fxml + 17.0.6 + + + org.openjfx + javafx-web + 17.0.6 + + + org.controlsfx + controlsfx + 11.1.2 + + + com.dlsc.formsfx + formsfx-core + 11.6.0 + + + org.openjfx + * + + + + + net.synedra + validatorfx + 0.4.0 + + + org.openjfx + * + + + + + org.kordamp.bootstrapfx + bootstrapfx-core + 0.4.0 + + + eu.hansolo + tilesfx + 17.1.17 + + + org.openjfx + * + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.rationalityfrontline.workaround + jfoenix + 19.0.1 + + + org.openjfx + javafx-graphics + 17.0.6 + + + + org.ow2.asm + asm + 9.6 + + + + org.ow2.asm + asm-tree + 9.4 + + + org.ow2.asm + asm-analysis + 9.4 + + + commons-io + commons-io + 2.11.0 + + + org.ow2.asm + asm-util + 9.3 + + + org.jetbrains + annotations + 22.0.0 + compile + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 14 + 14 + + + + org.openjfx + javafx-maven-plugin + 0.0.8 + + + + default-cli + + + ru.paimon.deobfuscator.colonialdeobfuscator/ru.paimon.deobfuscator.colonialdeobfuscator.HelloApplication + + app + app + app + true + true + true + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.0 + + + package + + shade + + + + + Main + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..1382c61 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,17 @@ +module ru.paimon.deobfuscator.colonialdeobfuscator { + requires javafx.controls; + requires javafx.fxml; + + requires org.controlsfx.controls; + requires com.dlsc.formsfx; + requires net.synedra.validatorfx; + requires org.kordamp.bootstrapfx.core; + requires org.objectweb.asm; + requires org.objectweb.asm.tree; + requires org.objectweb.asm.tree.analysis; + requires org.objectweb.asm.util; + requires jdk.unsupported; + requires org.apache.commons.io; + opens ru.paimon.deobfuscator.colonialdeobfuscator to javafx.fxml; + exports ru.paimon.deobfuscator.colonialdeobfuscator; +} \ No newline at end of file diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/ClassModifier.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/ClassModifier.java new file mode 100644 index 0000000..3cb1cc1 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/ClassModifier.java @@ -0,0 +1,11 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator; + +import org.objectweb.asm.tree.ClassNode; + +import java.util.Random; + +public interface ClassModifier { + Random r = new Random(); + + void modify(ClassNode classNode); +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/Main.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/Main.java new file mode 100644 index 0000000..fc9ab09 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/Main.java @@ -0,0 +1,24 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import java.io.IOException; + +public class Main extends Application { + public static Stage stage; + + public void start(Stage stage) throws IOException { + this.stage = stage; + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("Main.fxml")); + Scene scene = new Scene(fxmlLoader.load(),600,400); + stage.setScene(scene); + stage.setTitle("Colonial Deobfuscator by Paimon [V1.0]"); + stage.show(); + } + public static void main(String[] args) { + launch(); + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/MainCtr.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/MainCtr.java new file mode 100644 index 0000000..10a3e9d --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/MainCtr.java @@ -0,0 +1,190 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator; + +import javafx.fxml.FXML; +import javafx.scene.control.TextField; +import javafx.stage.FileChooser; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; +import ru.paimon.deobfuscator.colonialdeobfuscator.transformers.*; + +import java.io.*; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; + +public class MainCtr { + private boolean str = false,flow = false,bool = false,num = false,accs = false; + private static JarOutputStream finalOutputStream = null; + public static Map ToAdd = new HashMap<>(); + private static JarOutputStream outputStream = null; + public static Map classes = new HashMap<>(); + @FXML + public TextField output_path; + @FXML + public TextField input_path; + @FXML + protected void onAddInputPathButtonClick(){ + final FileChooser fileChooser = new FileChooser(); + try{ + File file = fileChooser.showOpenDialog(Main.stage); + if (file.exists()) { + String uga = file.getAbsolutePath(); + input_path.setText(uga); + } + }catch (Exception ignored){} + } + @FXML + protected void onAddOutputPathButtonClick(){ + final FileChooser fileChooser = new FileChooser(); + try{ + File file = fileChooser.showOpenDialog(Main.stage); + if (file.exists()) { + String uga = file.getAbsolutePath(); + output_path.setText(uga); + } + }catch (Exception ignored){} + } + @FXML + protected void onStringTransformerButtonClick(){ + str = !str; + } + @FXML + protected void onBooleanTransformerButtonClick(){ + bool = !bool; + } + @FXML + protected void onFlowTransformerButtonClick(){ + flow = !flow; + } + @FXML + protected void onNumberTransformerButtonClick(){ + num = !num; + } + @FXML + protected void onAccessTransformerButtonClick(){ + accs = !accs; + } + @FXML + protected void onDeobfuscateButtonClick() throws IOException { + File file = new File(input_path.getText()); + if(file.exists()){ + JarFile jarFile = new JarFile(file); + jarFile.stream().forEach((entry) -> { + try { + if (entry.getName().endsWith(".class")) { + ClassReader classReader = new ClassReader(jarFile.getInputStream(entry)); + ClassNode classNode = new ClassNode(); + classReader.accept(classNode, 2); + classes.put(classNode.name, classNode); + } else if (!entry.isDirectory()) { + finalOutputStream.putNextEntry(new ZipEntry(entry.getName())); + finalOutputStream.write(toByteArray(jarFile.getInputStream(entry))); + finalOutputStream.closeEntry(); + } + } catch (Exception ignored) {} + + }); + outputStream = new JarOutputStream(new FileOutputStream(output_path.getText())); + parseInput(input_path.getText()); + classes.values().forEach((classNode) -> { + for (ClassModifier m : modules()) { + m.modify(classNode); + } + + }); + classes.putAll(ToAdd); + classes.values().forEach((classNode) -> { + ClassWriter classWriter = new ClassWriter(1); + + try { + classNode.accept(classWriter); + JarEntry jarEntry = new JarEntry(classNode.name.concat(".class")); + outputStream.putNextEntry(jarEntry); + outputStream.write(classWriter.toByteArray()); + } catch (Exception var3) { + var3.printStackTrace(); + } + + }); + outputStream.setComment("https://discord.paimonsoft.xyz/"); + outputStream.setComment("https://discord.paimonsoft.xyz/"); + outputStream.close(); + } + } + public List modules() { + List modifier = new ArrayList<>(List.of()); + if(flow){ + modifier.add(new FlowTransformer()); + } + if(num){ + modifier.add(new NumberTransformer()); + } + if(bool){ + modifier.add(new BooleanTransformer()); + } + if(num && bool){ + modifier.add(new NumberTransformer()); + } + if(str){ + modifier.add(new StringTransformer()); + } + if(accs){ + modifier.add(new AccessTransformer()); + } + modifier.add(new CleanTransformer()); + if(num){ + modifier.add(new FixNumberTransformer()); + } + return modifier; + } + private static void parseInput(String input) { + JarFile jarFile = null; + + try { + jarFile = new JarFile(input); + } catch (IOException var5) { + var5.printStackTrace(); + } + + JarFile finalJarFile = jarFile; + jarFile.stream().forEach((entry) -> { + try { + if (entry.getName().endsWith(".class")) { + ClassReader classReader = new ClassReader(finalJarFile.getInputStream(entry)); + ClassNode classNode = new ClassNode(); + classReader.accept(classNode, 2); + classes.put(classNode.name, classNode); + } else if (!entry.isDirectory()) { + outputStream.putNextEntry(new ZipEntry(entry.getName())); + outputStream.write(toByteArray(finalJarFile.getInputStream(entry))); + outputStream.closeEntry(); + } + } catch (Exception var4) { + var4.printStackTrace(); + } + + }); + + try { + jarFile.close(); + } catch (IOException var4) { + var4.printStackTrace(); + } + + } + private static byte[] toByteArray(InputStream inputStream) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte['\uffff']; + + int length; + while((length = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + } + + outputStream.flush(); + return outputStream.toByteArray(); + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/AccessTransformer.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/AccessTransformer.java new file mode 100644 index 0000000..2b24ca5 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/AccessTransformer.java @@ -0,0 +1,28 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.transformers; + +import org.objectweb.asm.tree.ClassNode; +import ru.paimon.deobfuscator.colonialdeobfuscator.ClassModifier; +import ru.paimon.deobfuscator.colonialdeobfuscator.utils.ASMHelper; + +public class AccessTransformer extends ASMHelper implements ClassModifier { + @Override + public void modify(ClassNode classNode) { + if (isAccess(classNode.access, ACC_SYNTHETIC)) { + classNode.access &= ~ACC_SYNTHETIC; + } + + classNode.fields.stream() + .filter(node -> isAccess(node.access, ACC_SYNTHETIC)) + .forEach(node -> node.access &= ~ACC_SYNTHETIC); + + classNode.methods.forEach(methodNode -> { + if (isAccess(methodNode.access, ACC_SYNTHETIC)) { + methodNode.access &= ~ACC_SYNTHETIC; + } + + if (isAccess(methodNode.access, ACC_BRIDGE)) { + methodNode.access &= ~ACC_BRIDGE; + } + }); + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/BooleanTransformer.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/BooleanTransformer.java new file mode 100644 index 0000000..73c06b4 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/BooleanTransformer.java @@ -0,0 +1,23 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.transformers; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodInsnNode; +import ru.paimon.deobfuscator.colonialdeobfuscator.ClassModifier; +import ru.paimon.deobfuscator.colonialdeobfuscator.utils.ASMHelper; + +import java.util.Arrays; + +public class BooleanTransformer extends ASMHelper implements ClassModifier { + @Override + public void modify(ClassNode classNode) { + classNode.methods.removeIf(methodNode -> methodNode.name.startsWith("ColonialObfuscator_")); + classNode.methods.forEach(methodNode -> + Arrays.stream(methodNode.instructions.toArray()) + .forEach(insnNode -> { + if (isMethodStartWith(insnNode, classNode.name, "ColonialObfuscator_") + && insnNode.getOpcode() == INVOKESTATIC && ((MethodInsnNode) insnNode).desc.equals("(I)I")) { + methodNode.instructions.remove(insnNode); + } + })); + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/CleanTransformer.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/CleanTransformer.java new file mode 100644 index 0000000..3068ba6 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/CleanTransformer.java @@ -0,0 +1,94 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.transformers; + +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodNode; +import ru.paimon.deobfuscator.colonialdeobfuscator.ClassModifier; +import ru.paimon.deobfuscator.colonialdeobfuscator.utils.ASMHelper; + +import java.util.Arrays; + +public class CleanTransformer extends ASMHelper implements ClassModifier { + @Override + public void modify(ClassNode classNode) { + classNode.methods.forEach(this::transformNormally); + } + private void transformNormally(MethodNode methodNode) { + boolean modified; + do { + modified = false; + for (AbstractInsnNode node : methodNode.instructions.toArray()) { + switch (node.getOpcode()) { + case POP -> { + int type = check(node.getPrevious()); + if (type == 1) { + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.remove(node); + modified = true; + } + break; + } + case POP2 -> { + int type = check(node.getPrevious()); + if (type == 1 && check(node.getPrevious().getPrevious()) == 1) { + methodNode.instructions.remove(node.getPrevious().getPrevious()); + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.remove(node); + modified = true; + } else if (type == 2) { + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.remove(node); + modified = true; + } + break; + } + case DUP -> { + int type = check(node.getPrevious()); + if (type == 1) { + methodNode.instructions.insert(node.getPrevious(), node.getPrevious().clone(null)); + methodNode.instructions.remove(node); + modified = true; + } + break; + } + case DUP_X1, DUP2_X1, DUP2_X2, DUP_X2 -> { + break; + } + case DUP2 -> { + int type = check(node.getPrevious()); + if (type == 2) { + methodNode.instructions.insert(node.getPrevious(), node.getPrevious().clone(null)); + methodNode.instructions.remove(node); + modified = true; + } + break; + } + case SWAP -> { + int firstType = check(node.getPrevious().getPrevious()); + int secondType = check(node.getPrevious()); + + if (secondType == 1 && firstType == 1) { + AbstractInsnNode cloned = node.getPrevious().getPrevious(); + + methodNode.instructions.remove(node.getPrevious().getPrevious()); + methodNode.instructions.set(node, cloned.clone(null)); + modified = true; + } + break; + } + } + } + } while (modified); + } + + private int check(AbstractInsnNode node) { + if (isLong(node) || isDouble(node)) { + return 2; + } else if (isInteger(node) || isFloat(node) || node instanceof LdcInsnNode) { + return 1; + } + + return 0; + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FixNumberTransformer.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FixNumberTransformer.java new file mode 100644 index 0000000..d1ba3d7 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FixNumberTransformer.java @@ -0,0 +1,61 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.transformers; + +import org.objectweb.asm.tree.*; +import ru.paimon.deobfuscator.colonialdeobfuscator.ClassModifier; +import ru.paimon.deobfuscator.colonialdeobfuscator.utils.ASMHelper; + +import java.util.Arrays; +import java.util.HashMap; + +public class FixNumberTransformer extends ASMHelper implements ClassModifier { + @Override + public void modify(ClassNode classNode) { + HashMap vars = new HashMap<>(); + classNode.methods.forEach(methodNode -> + Arrays.stream(methodNode.instructions.toArray()) + .forEach(insnNode -> { + if (isInteger(insnNode) && insnNode.getNext().getOpcode() == IRETURN) { + if (getInteger(insnNode) % 2 == -1 || getInteger(insnNode) % 2 == 0) { + methodNode.instructions.set(insnNode, new InsnNode(ICONST_1)); + } else if (getInteger(insnNode) % 2 == 1) { + methodNode.instructions.set(insnNode, new InsnNode(ICONST_0)); + } + } else if (isInteger(insnNode) && insnNode.getNext().getOpcode() == INVOKEVIRTUAL) { + if (((MethodInsnNode) insnNode.getNext()).desc.contains("Z")) { + if (getInteger(insnNode) % 2 == -1 || getInteger(insnNode) % 2 == 0) { + methodNode.instructions.set(insnNode, new InsnNode(ICONST_1)); + } else if (getInteger(insnNode) % 2 == 1) { + methodNode.instructions.set(insnNode, new InsnNode(ICONST_0)); + } + } + }/* else if (insnNode.getPrevious() != null && insnNode.getPrevious().getOpcode() == ALOAD && isInteger(insnNode) && insnNode.getNext().getOpcode() == AALOAD) { + if (vars.get(((VarInsnNode) insnNode.getPrevious()).var) != null) { + int finalDec = getInteger(insnNode) ^ vars.get(((VarInsnNode) insnNode.getPrevious()).var); + int opcode = 9999; + switch (finalDec) { + case 1: + opcode = ICONST_0; + case 2: + opcode = ICONST_1; + case 3: + opcode = ICONST_2; + case 4: + opcode = ICONST_3; + case 5: + opcode = ICONST_4; + case 6: + opcode = ICONST_5; + } + if (opcode != 9999) { + methodNode.instructions.set(insnNode, new InsnNode(opcode)); + } else { + methodNode.instructions.set(insnNode, new IntInsnNode(BIPUSH, finalDec)); + } + } else { + vars.put(((VarInsnNode) insnNode.getPrevious()).var, getInteger(insnNode)); + methodNode.instructions.set(insnNode, new InsnNode(ICONST_0)); + } + }*/ + })); + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FlowTransformer.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FlowTransformer.java new file mode 100644 index 0000000..ab52713 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/FlowTransformer.java @@ -0,0 +1,42 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.transformers; + +import org.objectweb.asm.tree.ClassNode; +import ru.paimon.deobfuscator.colonialdeobfuscator.ClassModifier; +import ru.paimon.deobfuscator.colonialdeobfuscator.utils.ASMHelper; + +import java.util.Arrays; + +public class FlowTransformer extends ASMHelper implements ClassModifier { + @Override + public void modify(ClassNode classNode) { + classNode.methods.stream() + .filter(methodNode -> !methodNode.name.startsWith("<")) + .forEach(methodNode -> { + Arrays.stream(methodNode.instructions.toArray()) + .filter(insnNode -> insnNode.getOpcode() == DUP || insnNode.getOpcode() == POP || insnNode.getOpcode() == SWAP || insnNode.getOpcode() == FSUB || insnNode.getOpcode() == ISUB || insnNode.getOpcode() == DSUB || insnNode.getOpcode() == ATHROW) + .forEach(insnNode -> { + while (insnNode.getPrevious() != null + && insnNode.getPrevious().getPrevious() != null + && insnNode.getOpcode() == DUP + && insnNode.getPrevious().getOpcode() == INVOKEVIRTUAL + && insnNode.getPrevious().getPrevious().getOpcode() == LDC) { + + methodNode.instructions.remove(insnNode.getPrevious().getPrevious()); + methodNode.instructions.remove(insnNode.getPrevious()); + + if (insnNode.getNext().getOpcode() == POP2) { + + methodNode.instructions.remove(insnNode.getNext()); + + } else if (insnNode.getNext().getOpcode() == POP) { + + methodNode.instructions.remove(insnNode.getNext()); + methodNode.instructions.remove(insnNode.getNext()); + + } + methodNode.instructions.remove(insnNode); + } + }); + }); + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/NumberTransformer.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/NumberTransformer.java new file mode 100644 index 0000000..ea560e4 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/NumberTransformer.java @@ -0,0 +1,117 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.transformers; + +import org.objectweb.asm.tree.*; +import ru.paimon.deobfuscator.colonialdeobfuscator.ClassModifier; +import ru.paimon.deobfuscator.colonialdeobfuscator.utils.ASMHelper; +import ru.paimon.deobfuscator.colonialdeobfuscator.utils.MathHelper; + +public class NumberTransformer extends ASMHelper implements ClassModifier { + private final boolean extended = false; + @Override + public void modify(ClassNode classNode) { + classNode.methods.forEach(methodNode -> transform(classNode, methodNode)); + } + public void transform(ClassNode classNode, MethodNode methodNode) { + boolean modified; + do { + modified = false; + + for (AbstractInsnNode node : methodNode.instructions.toArray()) { + if (isString(node) + && node.getNext() instanceof MethodInsnNode + && ((MethodInsnNode) node.getNext()).name.equals("length") + && ((MethodInsnNode) node.getNext()).owner.equals("java/lang/String")) { + + methodNode.instructions.remove(node.getNext()); + methodNode.instructions.set(node, getNumber(getString(node).length())); + modified = true; + } else if (node.getOpcode() == INEG || node.getOpcode() == LNEG) { + if (isInteger(node.getPrevious())) { + int number = -getInteger(node.getPrevious()); + + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.set(node, getNumber(number)); + modified = true; + } else if (isLong(node.getPrevious())) { + long number = -getLong(node.getPrevious()); + + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.set(node, getNumber(number)); + modified = true; + } + } else if ((node.getOpcode() >= IADD && node.getOpcode() <= LXOR)) { + if (isInteger(node.getPrevious().getPrevious()) && isInteger(node.getPrevious())) { + int first = getInteger(node.getPrevious().getPrevious()); + int second = getInteger(node.getPrevious()); + + Integer product = MathHelper.doMath(node.getOpcode(), first, second); + if (product != null) { + methodNode.instructions.remove(node.getPrevious().getPrevious()); + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.set(node, getNumber(product)); + modified = true; + } + } else if (isLong(node.getPrevious().getPrevious()) && isLong(node.getPrevious())) { + long first = getLong(node.getPrevious().getPrevious()); + long second = getLong(node.getPrevious()); + + Long product = MathHelper.doMath(node.getOpcode(), first, second); + if (product != null) { + methodNode.instructions.remove(node.getPrevious().getPrevious()); + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.set(node, getNumber(product)); + modified = true; + } + } else if ((isLong(node.getPrevious().getPrevious()) && isInteger(node.getPrevious()))) { + long first = getLong(node.getPrevious().getPrevious()); + long second = getInteger(node.getPrevious()); + + Long product = MathHelper.doMath(node.getOpcode(), first, second); + if (product != null) { + methodNode.instructions.remove(node.getPrevious().getPrevious()); + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.set(node, getNumber(product)); + modified = true; + } + } else if ((isInteger(node.getPrevious().getPrevious()) && isLong(node.getPrevious()))) { + long first = getInteger(node.getPrevious().getPrevious()); + long second = getLong(node.getPrevious()); + + Long product = MathHelper.doMath(node.getOpcode(), first, second); + if (product != null) { + methodNode.instructions.remove(node.getPrevious().getPrevious()); + methodNode.instructions.remove(node.getPrevious()); + methodNode.instructions.set(node, getNumber(product)); + modified = true; + } + } + } else if (extended && (isLong(node) && isLong(node.getNext()) && node.getNext().getNext().getOpcode() == LCMP)) { + int result = Long.compare(getLong(node), getLong(node.getNext())); + + methodNode.instructions.remove(node.getNext().getNext()); + methodNode.instructions.remove(node.getNext()); + + methodNode.instructions.set(node, getNumber(result)); + } else if (extended && (node instanceof FieldInsnNode && ((FieldInsnNode) node).desc.equals("J") && node.getOpcode() == GETSTATIC + && isLong(node.getNext()) && node.getNext().getNext().getOpcode() == LCMP)) { + + int result = Long.compare(getFieldValue(classNode, ((FieldInsnNode) node).name), getLong(node.getNext())); + + methodNode.instructions.remove(node.getNext().getNext()); + methodNode.instructions.remove(node.getNext()); + + methodNode.instructions.set(node, getNumber(result)); + } + } + } while (modified); + } + + private long getFieldValue(ClassNode classNode, String name) { + return classNode.fields.stream() + .filter(fieldNode -> fieldNode.name.equals(name)) + .filter(fieldNode -> fieldNode.value != null) + .map(fieldNode -> (long) fieldNode.value) + .findFirst() + .orElse(0L); + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/StringTransformer.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/StringTransformer.java new file mode 100644 index 0000000..8784e01 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/transformers/StringTransformer.java @@ -0,0 +1,196 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.transformers; + +import org.objectweb.asm.tree.*; +import ru.paimon.deobfuscator.colonialdeobfuscator.ClassModifier; +import ru.paimon.deobfuscator.colonialdeobfuscator.utils.ASMHelper; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +public class StringTransformer extends ASMHelper implements ClassModifier { + private static char[] chars; + @Override + public void modify(ClassNode classNode) { + classNode.methods.stream().filter(methodNode -> methodNode.name.equals("")).forEach(methodNode -> { + try { + char[] chars1 = getChars(classNode); + if(chars1 != null){ + if(!getSwitchCases(methodNode).equals("Not Found!")){ + int[] switchCases = new int[7]; + AtomicInteger i = new AtomicInteger(0); + methodNode.instructions.forEach(insn -> { + if(insn.getOpcode() == BIPUSH && insn.getNext().getOpcode() == GOTO){ + //LOGGER.atError().log(methodNode.name+": "+getNumber(insn)); + switchCases[i.getAndIncrement()] = (int)getNumber(insn); + } else if(insn.getOpcode() == BIPUSH && insn.getNext().getOpcode() != IREM){ + //LOGGER.atError().log(methodNode.name+": "+getNumber(insn)); + switchCases[i.getAndIncrement()] = (int)getNumber(insn); + } + }); + + boolean mod; + + if(!modifedStringEnc(classNode)){ + decStep1(chars1,switchCases); + } else { + decStep2(chars1,switchCases); + } + mod = modifedStringEnc(classNode); + //LOGGER.atInfo().log(classNode.name+") Mod:"+mod); + //ClassNodeName = classNode.name; + + classNode.methods.forEach(methodNodee -> { + for (AbstractInsnNode node : methodNodee.instructions.toArray()) { + if (isInteger(node) + && isInteger(node.getNext()) + && isString(node.getNext().getNext()) + && isInteger(node.getNext().getNext().getNext()) + && isInteger(node.getNext().getNext().getNext().getNext()) + && node.getNext().getNext().getNext().getNext().getNext().getOpcode() == INVOKESTATIC + && ((MethodInsnNode)node.getNext().getNext().getNext().getNext().getNext()).desc.equals("(IILjava/lang/String;II)Ljava/lang/String;")) { + int one = getInteger(node); + int two = getInteger(node.getNext()); + int three = getInteger(node.getNext().getNext().getNext()); + int four = getInteger(node.getNext().getNext().getNext().getNext()); + String enc = getString(node.getNext().getNext()); + + String deobfedString = decStepFinal(one,two,enc,three,four,mod); + + methodNodee.instructions.remove(node.getNext().getNext().getNext().getNext().getNext()); + methodNodee.instructions.remove(node.getNext().getNext().getNext().getNext()); + methodNodee.instructions.remove(node.getNext().getNext().getNext()); + methodNodee.instructions.remove(node.getNext().getNext()); + methodNodee.instructions.remove(node.getNext()); + methodNodee.instructions.set(node, new LdcInsnNode(deobfedString)); + + } + } + }); + } + } + } catch (Exception e) { + //System.out.println("Error in class " + classNode.name); + } + }); + } + private String getSwitchCases(MethodNode methodNode){ + int[] switchCases = new int[7]; + AtomicInteger i = new AtomicInteger(0); + methodNode.instructions.forEach(insn -> { + if(insn.getOpcode() == BIPUSH && insn.getNext().getOpcode() == GOTO){ + //LOGGER.atError().log(methodNode.name+": "+getNumber(insn)); + switchCases[i.getAndIncrement()] = (int)getNumber(insn); + } else if(insn.getOpcode() == BIPUSH && insn.getNext().getOpcode() != IREM){ + //LOGGER.atError().log(methodNode.name+": "+getNumber(insn)); + switchCases[i.getAndIncrement()] = (int)getNumber(insn); + } + }); + if(i.get() == 7){ + return "Found"; + } else { + return "Not Found!"; + } + } + private char[] getChars(ClassNode classNode) { + // Get the method + final String[] chars = {""}; + MethodNode methodNode = classNode.methods.stream().filter(methodNode1 -> methodNode1.name.equals("")).findFirst().orElse(null); + assert methodNode != null; + methodNode.instructions.forEach(insn -> { + if(isString(insn) && insn.getNext().getOpcode() == ICONST_M1){ + chars[0] = new String(getString(insn).getBytes(),StandardCharsets.UTF_8); + //LOGGER.atError().log(getString(insn)); + } + }); + + if(chars[0] != null){ + return chars[0].toCharArray(); + } else { + return null; + } + } + private void decStep2(char[] encryptedData, int[] switchCases){ + int var10003 = encryptedData.length; + int var0 = 0; + while(true) { + if (var10003 <= var0) { + String var5 = (new String(encryptedData)).intern(); + chars = var5.toCharArray(); + return; + } + + char var10005 = encryptedData[var0]; + byte var10006 = switch (var0 % 7) { + case 0 -> (byte) switchCases[0]; + case 1 -> (byte) switchCases[1]; + case 2 -> (byte) switchCases[2]; + case 3 -> (byte) switchCases[3]; + case 4 -> (byte) switchCases[4]; + case 5 -> (byte) switchCases[5]; + default -> (byte) switchCases[6]; + }; + + encryptedData[var0] = (char)(var10005 ^ var10006); + ++var0; + } + } + private void decStep1(char[] encryptedData, int[] switchCases){ + for(int var0 = 0; encryptedData.length > var0; ++var0) { + char var10005 = encryptedData[var0]; + byte var10006 = switch (var0 % 7) { + case 0 -> (byte) switchCases[0]; + case 1 -> (byte) switchCases[1]; + case 2 -> (byte) switchCases[2]; + case 3 -> (byte) switchCases[3]; + case 4 -> (byte) switchCases[4]; + case 5 -> (byte) switchCases[5]; + default -> (byte) switchCases[6]; + }; + encryptedData[var0] = (char)(var10005 ^ var10006); + } + String var5 = (new String(encryptedData)).intern(); + chars = var5.toCharArray(); + } + public static String decStepFinal(int var0, int var1, String var2, int var3, int var4, boolean mod) { + StringBuilder var5 = new StringBuilder(); + int var6 = 0; + char[] var10; + int var9 = (var10 = var2.toCharArray()).length; + + if(!mod){ + for(int var8 = 0; var8 < var9; ++var8) { + char var7 = var10[var8]; + var5.append((char)(var7 ^ chars[var6 % chars.length] ^ var0 ^ var3 + var6 ^ var1 ^ var4)); + ++var6; + } + return var5.toString(); + } else { + for(int var8 = 0; var8 < var9; ++var8) { + char var7 = var10[var8]; + var5.append((char)(var7 ^ chars[var6 % chars.length] ^ var0 ^ XOR2(var3, var6) ^ var1 ^ var4)); + ++var6; + } + return var5.toString(); + } + } + private static int XOR2(int var0, int var1) { + return ((var0 | var1) << 1) + ~(var0 ^ var1) + 1; + } + private boolean modifedStringEnc(ClassNode classNode){ + int[] mod = {0}; + classNode.methods.forEach(methodNode -> { + Arrays.stream(methodNode.instructions.toArray()) + .forEach(insn -> { + if(insn.getOpcode() == ILOAD + && insn.getNext().getOpcode() == ILOAD + && insn.getNext().getNext().getOpcode() == INVOKESTATIC){ + if(((MethodInsnNode)insn.getNext().getNext()).desc.equals("(II)I")){ + mod[0] = 1; + } + } + }); + }); + return mod[0] == 1; + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/ASMHelper.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/ASMHelper.java new file mode 100644 index 0000000..8b18a98 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/ASMHelper.java @@ -0,0 +1,438 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.utils; + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.tree.analysis.Frame; +import org.objectweb.asm.tree.analysis.SourceInterpreter; +import org.objectweb.asm.tree.analysis.SourceValue; + +import java.util.*; +import java.util.function.Predicate; + +/** + * Used: https://github.com/ItzSomebody/radon/blob/master/src/main/java/me/itzsomebody/radon/utils/ASMUtils.java + */ +public class ASMHelper implements Opcodes { + + public static boolean isString(AbstractInsnNode node) { + return node instanceof LdcInsnNode && ((LdcInsnNode) node).cst instanceof String; + } + + public static boolean isType(AbstractInsnNode node) { + return node instanceof LdcInsnNode && ((LdcInsnNode) node).cst instanceof Type; + } + + public static boolean isInteger(AbstractInsnNode node) { + if (node == null) + return false; + + int opcode = node.getOpcode(); + return ((opcode >= ICONST_M1 && opcode <= ICONST_5) + || opcode == BIPUSH + || opcode == SIPUSH + || (node instanceof LdcInsnNode + && ((LdcInsnNode) node).cst instanceof Integer)); + } + + public static boolean isLong(AbstractInsnNode node) { + if (node == null) + return false; + + int opcode = node.getOpcode(); + return (opcode == LCONST_0 + || opcode == LCONST_1 + || (node instanceof LdcInsnNode + && ((LdcInsnNode) node).cst instanceof Long)); + } + + public static boolean isFloat(AbstractInsnNode node) { + int opcode = node.getOpcode(); + return (opcode >= FCONST_0 && opcode <= FCONST_2) + || (node instanceof LdcInsnNode && ((LdcInsnNode) node).cst instanceof Float); + } + + public static boolean isDouble(AbstractInsnNode node) { + if(node != null){ + int opcode = node.getOpcode(); + return (opcode >= DCONST_0 && opcode <= DCONST_1) + || (node instanceof LdcInsnNode && ((LdcInsnNode) node).cst instanceof Double); + } else { + return false; + } + } + + public static boolean isNumber(AbstractInsnNode node) { + return node != null && (isInteger(node) || isLong(node) || isFloat(node) || isDouble(node)); + } + + public static AbstractInsnNode getObjectList(MethodNode method) + { + List instrs = new ArrayList<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + { + if(ain.getOpcode() == -1) + continue; + instrs.add(ain); + if(instrs.size() >= 7) + break; + } + return instrs.get(6); + } + + public static boolean isOtherInitMethod(ClassNode classNode, MethodNode method, FieldInsnNode fieldInsn) + { + List instrs = new ArrayList<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + { + if(ain.getOpcode() == -1) + continue; + instrs.add(ain); + if(instrs.size() >= 3) + break; + } + if(instrs.size() < 3) + return false; + if(instrs.get(0).getOpcode() == Opcodes.GETSTATIC && ((FieldInsnNode)instrs.get(0)).desc.equals(fieldInsn.desc) + && ((FieldInsnNode)instrs.get(0)).name.equals(fieldInsn.name) + && ((FieldInsnNode)instrs.get(0)).owner.equals(fieldInsn.owner) + && instrs.get(1).getOpcode() == Opcodes.DUP + && Utils.isInteger(instrs.get(2))) + return true; + return false; + } + + public static AbstractInsnNode getFirstIndex(MethodNode method) + { + List instrs = new ArrayList<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + { + if(ain.getOpcode() == -1) + continue; + instrs.add(ain); + if(instrs.size() >= 3) + break; + } + return instrs.get(2); + } + + public static boolean isInitMethod(ClassNode classNode, MethodNode method) + { + List instrs = new ArrayList<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + { + if(ain.getOpcode() == -1) + continue; + instrs.add(ain); + if(instrs.size() >= 7) + break; + } + if(instrs.size() < 7) + return false; + int firstNum = -1; + if(Utils.isInteger(instrs.get(0))) + firstNum = Utils.getIntValue(instrs.get(0)); + if(firstNum == -1) + return false; + if(instrs.get(1).getOpcode() == Opcodes.ANEWARRAY && ((TypeInsnNode)instrs.get(1)).desc.equals("java/lang/String") + && instrs.get(2).getOpcode() == Opcodes.PUTSTATIC && ((FieldInsnNode)instrs.get(2)).owner.equals(classNode.name) + && Utils.isInteger(instrs.get(3)) && Utils.getIntValue(instrs.get(3)) == firstNum + && instrs.get(4).getOpcode() == Opcodes.ANEWARRAY && ((TypeInsnNode)instrs.get(4)).desc.equals("java/lang/Object") + && instrs.get(5).getOpcode() == Opcodes.DUP + && instrs.get(6).getOpcode() == Opcodes.PUTSTATIC && ((FieldInsnNode)instrs.get(6)).owner.equals(classNode.name)) + return true; + if(instrs.get(1).getOpcode() == Opcodes.NEWARRAY + && instrs.get(2).getOpcode() == Opcodes.PUTSTATIC && ((FieldInsnNode)instrs.get(2)).owner.equals(classNode.name) + && Utils.isInteger(instrs.get(3)) && Utils.getIntValue(instrs.get(3)) == firstNum + && instrs.get(4).getOpcode() == Opcodes.ANEWARRAY && ((TypeInsnNode)instrs.get(4)).desc.equals("java/lang/Object") + && instrs.get(5).getOpcode() == Opcodes.DUP + && instrs.get(6).getOpcode() == Opcodes.PUTSTATIC && ((FieldInsnNode)instrs.get(6)).owner.equals(classNode.name)) + return true; + return false; + } + + public static String getString(AbstractInsnNode node) { + return (String) ((LdcInsnNode) node).cst; + } + + public static Type getType(AbstractInsnNode node) { + return (Type) ((LdcInsnNode) node).cst; + } + + public static int getInteger(AbstractInsnNode node) { + int opcode = node.getOpcode(); + + if (opcode >= ICONST_M1 && opcode <= ICONST_5) { + return opcode - 3; + } else if (node instanceof IntInsnNode && node.getOpcode() != NEWARRAY) { + return ((IntInsnNode) node).operand; + } else if (node instanceof LdcInsnNode + && ((LdcInsnNode) node).cst instanceof Integer) { + return (Integer) ((LdcInsnNode) node).cst; + } + + throw new IllegalArgumentException(); + } + + public static long getLong(AbstractInsnNode node) { + int opcode = node.getOpcode(); + + if (opcode >= LCONST_0 && opcode <= LCONST_1) { + return opcode - 9; + } else if (node instanceof LdcInsnNode + && ((LdcInsnNode) node).cst instanceof Long) { + return (Long) ((LdcInsnNode) node).cst; + } + + throw new IllegalArgumentException(); + } + + public static float getFloat(AbstractInsnNode node) { + int opcode = node.getOpcode(); + + if (opcode >= FCONST_0 && opcode <= FCONST_2) { + return opcode - 11; + } else if (node instanceof LdcInsnNode + && ((LdcInsnNode) node).cst instanceof Float) { + return (Float) ((LdcInsnNode) node).cst; + } + + throw new IllegalArgumentException(); + } + + public static double getDouble(AbstractInsnNode node) { + int opcode = node.getOpcode(); + + if (opcode >= DCONST_0 && opcode <= DCONST_1) { + return opcode - 14; + } else if (node instanceof LdcInsnNode + && ((LdcInsnNode) node).cst instanceof Double) { + return (Double) ((LdcInsnNode) node).cst; + } + + throw new IllegalArgumentException(); + } + + public static Number getNumber(AbstractInsnNode node) { + if (isInteger(node)) { + return getInteger(node); + } else if (isLong(node)) { + return getLong(node); + } else if (isDouble(node)) { + return getDouble(node); + } else if (isFloat(node)) { + return getFloat(node); + } + + throw new IllegalArgumentException(); + } + + public static AbstractInsnNode getNumber(int number) { + if (number >= -1 && number <= 5) { + return new InsnNode(number + 3); + } else if (number >= -128 && number <= 127) { + return new IntInsnNode(BIPUSH, number); + } else if (number >= -32768 && number <= 32767) { + return new IntInsnNode(SIPUSH, number); + } else { + return new LdcInsnNode(number); + } + } + + public static AbstractInsnNode getNumber(long number) { + if (number >= 0 && number <= 1) { + return new InsnNode((int) (number + 9)); + } else { + return new LdcInsnNode(number); + } + } + + public static AbstractInsnNode getNumber(float number) { + if (number == 0 || number == 1 || number == 2) { + return new InsnNode((int) (number + 11)); + } else { + return new LdcInsnNode(number); + } + } + + public static AbstractInsnNode getNumber(double number) { + if (number == 0 || number == 1) { + return new InsnNode((int) (number + 14)); + } else { + return new LdcInsnNode(number); + } + } + + public static AbstractInsnNode getNumber(Number number) { + if (number instanceof Integer || number instanceof Byte || number instanceof Short) { + return getNumber(number.intValue()); + } else if (number instanceof Long) { + return getNumber(number.longValue()); + } else if (number instanceof Float) { + return getNumber(number.floatValue()); + } else if (number instanceof Double) { + return getNumber(number.doubleValue()); + } + + throw new IllegalArgumentException(); + } + + public static void visitNumber(MethodVisitor methodVisitor, int number) { + if (number >= -1 && number <= 5) { + methodVisitor.visitInsn(number + 3); + } else if (number >= -128 && number <= 127) { + methodVisitor.visitIntInsn(BIPUSH, number); + } else if (number >= -32768 && number <= 32767) { + methodVisitor.visitIntInsn(SIPUSH, number); + } else { + methodVisitor.visitLdcInsn(number); + } + } + + public static void visitNumber(MethodVisitor methodVisitor, long number) { + if (number >= 0 && number <= 1) { + methodVisitor.visitInsn((int) (number + 9)); + } else { + methodVisitor.visitLdcInsn(number); + } + } + + public static boolean isNumberOperator(AbstractInsnNode node) { + return node != null && (node.getOpcode() >= IADD && node.getOpcode() <= LXOR) + && node.getOpcode() != INEG; + } + + public static boolean isAccess(int access, int opcode) { + return (access & opcode) != 0; + } + + public static Optional findMethod(ClassNode classNode, MethodInsnNode methodInsnNode) { + return classNode == null || classNode.methods == null ? Optional.empty() : classNode.methods.stream() + .filter(methodNode -> methodNode.name.equals(methodInsnNode.name)) + .filter(methodNode -> methodNode.desc.equals(methodInsnNode.desc)) + .findFirst(); + } + + public static Optional findMethod(ClassNode classNode, Predicate predicate) { + return classNode.methods == null ? Optional.empty() : classNode.methods.stream() + .filter(predicate) + .findFirst(); + } + + public static Optional findField(ClassNode classNode, Predicate predicate) { + return classNode.methods == null ? Optional.empty() : classNode.fields.stream() + .filter(predicate) + .findFirst(); + } + + public static Optional findClInit(ClassNode classNode) { + return findMethod(classNode, methodNode -> methodNode.name.equals("")); + } + + public static List getInstructionsBetween(AbstractInsnNode start, AbstractInsnNode end) { + return getInstructionsBetween(start, end, true, true); + } + + public static List getInstructionsBetween(AbstractInsnNode start, AbstractInsnNode end, boolean includeStart, boolean includeEnd) { + List instructions = new ArrayList<>(); + + if (includeStart) + instructions.add(start); + + while ((start = start.getNext()) != null && start != end) { + instructions.add(start); + } + + if (includeEnd) + instructions.add(end); + + return instructions; + } + + /* + WTF i dad djud? + */ + public static boolean isSingleIf(JumpInsnNode node) { + return (node.getOpcode() >= IFEQ && node.getOpcode() <= IFLE) || (node.getOpcode() == IFNULL || node.getOpcode() == IFNONNULL); + } + + public static boolean isDoubleIf(JumpInsnNode node) { + return node.getOpcode() >= IF_ICMPEQ && node.getOpcode() <= IF_ICMPLE; + } + + public static boolean check(AbstractInsnNode node, int opcode) { + return node != null && node.getOpcode() == opcode; + } + + public static boolean check(AbstractInsnNode node, Class clazz) { + return node != null && node.getClass().equals(clazz); + } + + public static boolean check(AbstractInsnNode node, AbstractInsnNode other) { + return node != null && node.equals(other); + } + + public static boolean check(AbstractInsnNode node, Predicate predicate) { + return node != null && predicate.test(node); + } + + //Idk xd + public static boolean check(AbstractInsnNode node, Predicate... predicates) { + return node != null && Arrays.stream(predicates) + .filter(predicate -> predicate.test(node)) + .count() == predicates.length; + } + + public static boolean isMethod(AbstractInsnNode node, String owner) { + return node instanceof MethodInsnNode + && ((MethodInsnNode) node).name.equals(owner); + } + + public static boolean isMethod(AbstractInsnNode node, String owner, String name) { + return node instanceof MethodInsnNode + && ((MethodInsnNode) node).owner.equals(owner) + && ((MethodInsnNode) node).name.equals(name); + } + + public static boolean isMethod(AbstractInsnNode node, String owner, String name, String desc) { + return node instanceof MethodInsnNode + && ((MethodInsnNode) node).owner.equals(owner) + && ((MethodInsnNode) node).name.equals(name) + && ((MethodInsnNode) node).desc.equals(desc); + } + + public static boolean isMethod(AbstractInsnNode node, String owner, String name, String desc, int opcode) { + return node instanceof MethodInsnNode && node.getOpcode() == opcode + && ((MethodInsnNode) node).owner.equals(owner) + && ((MethodInsnNode) node).name.equals(name) + && ((MethodInsnNode) node).desc.equals(desc); + } + + public static boolean isMethodStartWith(AbstractInsnNode node, String owner, String startWith) { + return node instanceof MethodInsnNode + && ((MethodInsnNode) node).owner.equals(owner) + && ((MethodInsnNode) node).name.startsWith(startWith); + } + + public static Map> analyzeSource(ClassNode classNode, MethodNode methodNode) { + try { + Map> frames = new HashMap<>(); + Frame[] framesArray = new Analyzer<>(new SourceInterpreter()).analyze(classNode.name, methodNode); + for (int i = 0; i < framesArray.length; i++) { + frames.put(methodNode.instructions.get(i), framesArray[i]); + } + return frames; + } catch (Exception e) { + return null; + } + } + + public static MethodNode copyMethod(MethodNode methodNode) { + MethodNode copyMethod = new MethodNode(methodNode.access, methodNode.name, methodNode.desc, methodNode.signature, methodNode.exceptions.toArray(new String[0])); + methodNode.accept(copyMethod); + + return copyMethod; + } +} \ No newline at end of file diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/MathHelper.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/MathHelper.java new file mode 100644 index 0000000..869c855 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/MathHelper.java @@ -0,0 +1,50 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.utils; + +import org.objectweb.asm.Opcodes; + +import java.util.regex.Pattern; + +/* + TODO: Fix boilerplate + */ +public class MathHelper implements Opcodes { + + public static final Pattern INTEGER_PATTERN = Pattern.compile("^-?\\d+$"); + + public static Integer doMath(int opcode, int first, int second) { + return switch (opcode) { + case IADD -> first + second; + case ISUB -> first - second; + case IMUL -> first * second; + case IDIV -> first / opcode; + case IREM -> first % second; + case ISHL -> first << second; + case ISHR -> first >> second; + case IUSHR -> first >>> second; + case IAND -> first & second; + case IOR -> first | second; + case IXOR -> first ^ second; + case INEG -> //Idk why i put it here + -first; + default -> null; + }; + } + + public static Long doMath(int opcode, long first, long second) { + return switch (opcode) { + case LADD -> first + second; + case LSUB -> first - second; + case LMUL -> first * second; + case LDIV -> first / opcode; + case LREM -> first % second; + case LSHL -> first << second; + case LSHR -> first >> second; + case LUSHR -> first >>> second; + case LAND -> first & second; + case LOR -> first | second; + case LXOR -> first ^ second; + case LNEG -> -first; //Idk why i put it here + default -> null; + }; + } +} diff --git a/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/Utils.java b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/Utils.java new file mode 100644 index 0000000..8d1c548 --- /dev/null +++ b/src/main/java/ru/paimon/deobfuscator/colonialdeobfuscator/utils/Utils.java @@ -0,0 +1,930 @@ +package ru.paimon.deobfuscator.colonialdeobfuscator.utils; + +import org.apache.commons.io.IOUtils; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.util.Printer; +import org.objectweb.asm.util.Textifier; +import org.objectweb.asm.util.TraceMethodVisitor; +import sun.misc.Unsafe; + +import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class Utils { + private static final Printer printer = new Textifier(); + private static final TraceMethodVisitor methodPrinter; + private static Unsafe unsafe; + + public static boolean isInstruction(AbstractInsnNode node) { + return !(node instanceof LineNumberNode) && !(node instanceof FrameNode) && !(node instanceof LabelNode); + } + + public static boolean notAbstractOrNative(MethodNode methodNode) { + return !Modifier.isNative(methodNode.access) && !Modifier.isAbstract(methodNode.access); + } + + public static AbstractInsnNode getNextFollowGoto(AbstractInsnNode node) { + Object next; + for(next = node.getNext(); next instanceof LabelNode || next instanceof LineNumberNode || next instanceof FrameNode; next = ((AbstractInsnNode)next).getNext()) { + } + + if (((AbstractInsnNode)next).getOpcode() == 167) { + JumpInsnNode cast = (JumpInsnNode)next; + + for(next = cast.label; !isInstruction((AbstractInsnNode)next); next = ((AbstractInsnNode)next).getNext()) { + } + } + + return (AbstractInsnNode)next; + } + + public static AbstractInsnNode getNext(AbstractInsnNode node, int amount) { + for(int i = 0; i < amount; ++i) { + node = getNext(node); + } + + return node; + } + + public static AbstractInsnNode getNext(AbstractInsnNode node) { + AbstractInsnNode next; + for(next = node.getNext(); !isInstruction(next); next = next.getNext()) { + } + + return next; + } + + public static AbstractInsnNode getPrevious(AbstractInsnNode node, int amount) { + for(int i = 0; i < amount; ++i) { + node = getPrevious(node); + } + + return node; + } + + public static AbstractInsnNode getPrevious(AbstractInsnNode node) { + AbstractInsnNode prev; + for(prev = node.getPrevious(); !isInstruction(prev); prev = prev.getPrevious()) { + } + + return prev; + } + + public static int iconstToInt(int opcode) { + int operand = Integer.MIN_VALUE; + switch(opcode) { + case 2: + operand = -1; + break; + case 3: + operand = 0; + break; + case 4: + operand = 1; + break; + case 5: + operand = 2; + break; + case 6: + operand = 3; + break; + case 7: + operand = 4; + break; + case 8: + operand = 5; + } + + return operand; + } + + public static MethodNode getMethodNode(ClassNode start, String methodName, String methodDesc, Map dictionary) { + MethodNode targetMethod = null; + LinkedList haystack = new LinkedList(); + haystack.add(start); + + while(targetMethod == null && !haystack.isEmpty()) { + ClassNode needle = (ClassNode)haystack.poll(); + targetMethod = (MethodNode)needle.methods.stream().filter((imn) -> { + return imn.name.equals(methodName) && imn.desc.equals(methodDesc); + }).findFirst().orElse((MethodNode) null); + if (targetMethod == null && !needle.name.equals("java/lang/Object")) { + Iterator var7 = needle.interfaces.iterator(); + + while(var7.hasNext()) { + String intf = (String)var7.next(); + ClassNode intfNode = (ClassNode)dictionary.get(intf); + if (intfNode == null) { + throw new IllegalArgumentException("Class not found: " + intf); + } + + haystack.add(intfNode); + } + + String superName = needle.superName; + needle = (ClassNode)dictionary.get(needle.superName); + if (needle == null) { + throw new IllegalArgumentException("Class not found: " + superName); + } + + haystack.add(needle); + } + } + + return targetMethod; + } + + public static long copy(InputStream from, OutputStream to) throws IOException { + byte[] buf = new byte[4096]; + long total = 0L; + + while(true) { + int r = from.read(buf); + if (r == -1) { + return total; + } + + to.write(buf, 0, r); + total += (long)r; + } + } + + public static String descFromTypes(Type[] types) { + StringBuilder descBuilder = new StringBuilder("("); + Type[] var2 = types; + int var3 = types.length; + + for(int var4 = 0; var4 < var3; ++var4) { + Type type = var2[var4]; + descBuilder.append(type.getDescriptor()); + } + + descBuilder.append(")"); + return descBuilder.toString(); + } + + public static void sneakyThrow(Throwable t) { + sneakyThrow0(t); + } + + private static void sneakyThrow0(Throwable t) throws T { + try { + throw t; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + public static String prettyprint(AbstractInsnNode insnNode) { + if (insnNode == null) { + return "null"; + } else { + insnNode.accept(methodPrinter); + StringWriter sw = new StringWriter(); + printer.print(new PrintWriter(sw)); + printer.getText().clear(); + return sw.toString().trim(); + } + } + + public static boolean isTerminating(AbstractInsnNode next) { + switch(next.getOpcode()) { + case 167: + case 170: + case 171: + case 172: + case 173: + case 174: + case 175: + case 176: + case 177: + case 191: + return true; + case 168: + case 169: + case 178: + case 179: + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + case 186: + case 187: + case 188: + case 189: + case 190: + default: + return false; + } + } + + public static boolean willPushToStack(int opcode) { + switch(opcode) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 11: + case 12: + case 13: + case 16: + case 17: + case 18: + case 21: + case 22: + case 23: + case 24: + case 25: + case 178: + return true; + default: + return false; + } + } + + public static Unsafe getUnsafe() { + try { + initializeUnsafe(); + } catch (Exception var1) { + var1.printStackTrace(); + } + + return unsafe; + } + + public static Object allocateInstance(Class t) { + try { + return getUnsafe().allocateInstance(t); + } catch (InstantiationException var2) { + var2.printStackTrace(); + return null; + } + } + + private static void initializeUnsafe() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + if (unsafe == null) { + Constructor ctor = Unsafe.class.getDeclaredConstructor(); + ctor.setAccessible(true); + unsafe = (Unsafe)ctor.newInstance(); + } + + } + + public static boolean isNumberV2(int type,char var4) { + byte var2 = -1; + while(type > 0) { + // + } + + switch(var2) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + return true; + default: + return false; + } + } + + public static boolean isNumber(String type) { + byte var2 = -1; + switch(type.hashCode()) { + case 66: + if (type.equals("B")) { + var2 = 2; + } + case 67: + case 69: + case 71: + case 72: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + default: + break; + case 68: + if (type.equals("D")) { + var2 = 4; + } + break; + case 70: + if (type.equals("F")) { + var2 = 5; + } + break; + case 73: + if (type.equals("I")) { + var2 = 0; + } + break; + case 74: + if (type.equals("J")) { + var2 = 3; + } + break; + case 83: + if (type.equals("S")) { + var2 = 1; + } + } + + switch(var2) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + return true; + default: + return false; + } + } + + public static boolean canReturnDigit(String type) { + byte var2 = -1; + switch(type.hashCode()) { + case 66: + if (type.equals("B")) { + var2 = 2; + } + break; + case 67: + if (type.equals("C")) { + var2 = 5; + } + break; + case 73: + if (type.equals("I")) { + var2 = 0; + } + break; + case 74: + if (type.equals("J")) { + var2 = 3; + } + break; + case 83: + if (type.equals("S")) { + var2 = 1; + } + break; + case 90: + if (type.equals("Z")) { + var2 = 4; + } + } + + switch(var2) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + return true; + default: + return false; + } + } + + public static boolean isFloat(String type) { + byte var2 = -1; + switch(type.hashCode()) { + case 68: + if (type.equals("D")) { + var2 = 1; + } + break; + case 70: + if (type.equals("F")) { + var2 = 0; + } + } + + switch(var2) { + case 0: + case 1: + return true; + default: + return false; + } + } + + public static InsnList copyInsnList(InsnList original) { + InsnList newInsnList = new InsnList(); + + for(AbstractInsnNode insn = original.getFirst(); insn != null; insn = insn.getNext()) { + newInsnList.add(insn); + } + + return newInsnList; + } + + public static InsnList cloneInsnList(InsnList original) { + InsnList newInsnList = new InsnList(); + Map labels = new HashMap(); + + AbstractInsnNode insn; + for(insn = original.getFirst(); insn != null; insn = insn.getNext()) { + if (insn instanceof LabelNode) { + labels.put((LabelNode)insn, new LabelNode()); + } + } + + for(insn = original.getFirst(); insn != null; insn = insn.getNext()) { + newInsnList.add(insn.clone(labels)); + } + + return newInsnList; + } + + public static AbstractInsnNode getIntInsn(int number) { + if (number >= -1 && number <= 5) { + return new InsnNode(number + 3); + } else if (number >= -128 && number <= 127) { + return new IntInsnNode(16, number); + } else { + return (AbstractInsnNode)(number >= -32768 && number <= 32767 ? new IntInsnNode(17, number) : new LdcInsnNode(number)); + } + } + + public static AbstractInsnNode getLongInsn(long number) { + return (AbstractInsnNode)(number >= 0L && number <= 1L ? new InsnNode((int)(number + 9L)) : new LdcInsnNode(number)); + } + + public static AbstractInsnNode getFloatInsn(float number) { + return (AbstractInsnNode)(number >= 0.0F && number <= 2.0F ? new InsnNode((int)(number + 11.0F)) : new LdcInsnNode(number)); + } + + public static AbstractInsnNode getDoubleInsn(double number) { + return (AbstractInsnNode)(number >= 0.0D && number <= 1.0D ? new InsnNode((int)(number + 14.0D)) : new LdcInsnNode(number)); + } + + public static void printClass(ClassNode classNode) { + System.out.println(classNode.name + '\n'); + classNode.methods.forEach((methodNode) -> { + System.out.println(methodNode.name + " " + methodNode.desc); + + for(int i = 0; i < methodNode.instructions.size(); ++i) { + System.out.printf("%s: %s \n", i, prettyprint(methodNode.instructions.get(i))); + } + + }); + } + + public static boolean isInteger(AbstractInsnNode ain) { + if (ain == null) { + return false; + } else if ((ain.getOpcode() < 2 || ain.getOpcode() > 8) && ain.getOpcode() != 17 && ain.getOpcode() != 16) { + if (ain instanceof LdcInsnNode) { + LdcInsnNode ldc = (LdcInsnNode)ain; + if (ldc.cst instanceof Integer) { + return true; + } + } + + return false; + } else { + return true; + } + } + + public static int getIntValue(AbstractInsnNode node) { + if (node.getOpcode() >= 2 && node.getOpcode() <= 8) { + return node.getOpcode() - 3; + } else if (node.getOpcode() != 17 && node.getOpcode() != 16) { + if (node instanceof LdcInsnNode) { + LdcInsnNode ldc = (LdcInsnNode)node; + if (ldc.cst instanceof Integer) { + return (Integer)ldc.cst; + } + } + + return 0; + } else { + return ((IntInsnNode)node).operand; + } + } + + public static boolean isLong(AbstractInsnNode ain) { + if (ain == null) { + return false; + } else if (ain.getOpcode() != 9 && ain.getOpcode() != 10) { + if (ain instanceof LdcInsnNode) { + LdcInsnNode ldc = (LdcInsnNode)ain; + if (ldc.cst instanceof Long) { + return true; + } + } + + return false; + } else { + return true; + } + } + + public static long getLongValue(AbstractInsnNode node) { + if (node.getOpcode() >= 9 && node.getOpcode() <= 10) { + return (long)(node.getOpcode() - 9); + } else { + if (node instanceof LdcInsnNode) { + LdcInsnNode ldc = (LdcInsnNode)node; + if (ldc.cst instanceof Long) { + return (Long)ldc.cst; + } + } + + return 0L; + } + } + + public static List loadBytes(File input) { + List result = new ArrayList(); + Throwable var3; + if (input.getName().endsWith(".jar")) { + try { + ZipFile zipIn = new ZipFile(input); + var3 = null; + + try { + Enumeration e = zipIn.entries(); + + while(e.hasMoreElements()) { + ZipEntry next = (ZipEntry)e.nextElement(); + if (next.getName().endsWith(".class")) { + try { + InputStream in = zipIn.getInputStream(next); + Throwable var7 = null; + + try { + result.add(IOUtils.toByteArray(in)); + } catch (Throwable var60) { + var7 = var60; + throw var60; + } finally { + if (in != null) { + if (var7 != null) { + try { + in.close(); + } catch (Throwable var59) { + var7.addSuppressed(var59); + } + } else { + in.close(); + } + } + + } + } catch (IllegalArgumentException var65) { + System.out.println("Could not parse " + next.getName() + " (is it a class?)"); + } + } + } + } catch (Throwable var66) { + var3 = var66; + throw var66; + } finally { + if (zipIn != null) { + if (var3 != null) { + try { + zipIn.close(); + } catch (Throwable var58) { + var3.addSuppressed(var58); + } + } else { + zipIn.close(); + } + } + + } + } catch (IOException var68) { + var68.printStackTrace(System.out); + } + } else if (input.getName().endsWith(".class")) { + try { + InputStream in = new FileInputStream(input); + var3 = null; + + try { + result.add(IOUtils.toByteArray(in)); + } catch (Throwable var61) { + var3 = var61; + throw var61; + } finally { + if (in != null) { + if (var3 != null) { + try { + in.close(); + } catch (Throwable var57) { + var3.addSuppressed(var57); + } + } else { + in.close(); + } + } + + } + } catch (Throwable var63) { + System.out.println("Could not parse " + input.getName() + " (is it a class?)"); + } + } + + return result; + } + + public static Map generateCloneMap(InsnList list) { + Map result = new HashMap(); + list.iterator().forEachRemaining((insn) -> { + if (insn instanceof LabelNode) { + result.put((LabelNode)insn, new LabelNode()); + } + + }); + return result; + } + + public static int getPullValue(AbstractInsnNode ain, boolean includeShifts) { + int var5; + switch(ain.getOpcode()) { + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + return 2; + case 54: + case 56: + case 58: + return 1; + case 55: + case 57: + return 2; + case 59: + case 60: + case 61: + case 62: + case 63: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 132: + case 145: + case 146: + case 147: + case 167: + case 168: + case 169: + case 177: + case 178: + case 187: + case 196: + default: + break; + case 79: + case 81: + case 83: + case 84: + case 85: + case 86: + return 3; + case 80: + case 82: + return 4; + case 87: + return 1; + case 88: + return 2; + case 89: + if (includeShifts) { + return 1; + } + break; + case 90: + if (includeShifts) { + return 1; + } + break; + case 91: + if (includeShifts) { + return 1; + } + break; + case 92: + if (includeShifts) { + return 2; + } + break; + case 93: + if (includeShifts) { + return 2; + } + break; + case 94: + if (includeShifts) { + return 2; + } + break; + case 95: + if (includeShifts) { + return 2; + } + break; + case 96: + case 100: + case 104: + case 108: + case 112: + case 120: + case 122: + case 124: + case 126: + case 128: + case 130: + return 2; + case 97: + case 101: + case 105: + case 109: + case 113: + case 127: + case 129: + case 131: + case 148: + return 4; + case 98: + case 102: + case 106: + case 110: + case 114: + case 149: + case 150: + return 2; + case 99: + case 103: + case 107: + case 111: + case 115: + case 151: + case 152: + return 4; + case 116: + case 118: + return 1; + case 117: + case 119: + return 2; + case 121: + case 123: + case 125: + return 3; + case 133: + case 134: + case 135: + return 1; + case 136: + case 137: + case 138: + return 2; + case 139: + case 140: + case 141: + return 1; + case 142: + case 143: + case 144: + return 2; + case 153: + case 154: + case 155: + case 156: + case 157: + case 158: + return 1; + case 159: + case 160: + case 161: + case 162: + case 163: + case 164: + case 165: + case 166: + return 2; + case 170: + case 171: + return 1; + case 172: + case 174: + case 176: + return 1; + case 173: + case 175: + return 2; + case 179: + if (Type.getType(((FieldInsnNode)ain).desc).getSort() != 7 && Type.getType(((FieldInsnNode)ain).desc).getSort() != 8) { + return 1; + } + + return 2; + case 180: + return 1; + case 181: + if (Type.getType(((FieldInsnNode)ain).desc).getSort() != 7 && Type.getType(((FieldInsnNode)ain).desc).getSort() != 8) { + return 2; + } + + return 3; + case 182: + case 183: + case 184: + case 185: + int args = 0; + if (ain.getOpcode() != 184) { + ++args; + } + + Type[] var8 = Type.getArgumentTypes(((MethodInsnNode)ain).desc); + int var9 = var8.length; + + for(var5 = 0; var5 < var9; ++var5) { + Type t = var8[var5]; + if (t.getSort() != 7 && t.getSort() != 8) { + ++args; + } else { + args += 2; + } + } + + return args; + case 186: + int args1 = 0; + Type[] var4 = Type.getArgumentTypes(((InvokeDynamicInsnNode)ain).desc); + var5 = var4.length; + + for(int var6 = 0; var6 < var5; ++var6) { + Type t = var4[var6]; + if (t.getSort() != 7 && t.getSort() != 8) { + ++args1; + } else { + args1 += 2; + } + } + + return args1; + case 188: + case 189: + case 190: + case 191: + case 192: + case 193: + case 194: + case 195: + case 198: + case 199: + return 1; + case 197: + return ((MultiANewArrayInsnNode)ain).dims; + } + + return 0; + } + + static { + methodPrinter = new TraceMethodVisitor(printer); + } +} diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..4df3ca0 --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: ru.paimon.deobfuscator.colonialdeobfuscator.Main + diff --git a/src/main/resources/ru/paimon/deobfuscator/colonialdeobfuscator/Main.fxml b/src/main/resources/ru/paimon/deobfuscator/colonialdeobfuscator/Main.fxml new file mode 100644 index 0000000..4fd51d5 --- /dev/null +++ b/src/main/resources/ru/paimon/deobfuscator/colonialdeobfuscator/Main.fxml @@ -0,0 +1,19 @@ + + + + + + + + +