|
|
@@ -22,8 +22,11 @@ import java.security.ProtectionDomain;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Base64;
|
|
|
import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
import java.util.zip.ZipFile;
|
|
|
|
|
|
@@ -35,32 +38,46 @@ import java.util.zip.ZipFile;
|
|
|
*/
|
|
|
public class Reverses {
|
|
|
|
|
|
- private static JavaagentCmdArgs javaagentCmdArgs = null;
|
|
|
+ private static JavaagentCmdArgs javaagentCmdArgs = null;
|
|
|
|
|
|
/**
|
|
|
* pre-main 入口函数
|
|
|
*
|
|
|
* @param args
|
|
|
- * 参数
|
|
|
+ * 参数
|
|
|
* @param instrumentation
|
|
|
- * This class provides services needed to instrumentation Java programming language code
|
|
|
+ * This class provides services needed to instrumentation Java programming language code
|
|
|
*/
|
|
|
public static void premain(String args, Instrumentation instrumentation) {
|
|
|
// javaagent指定的参数
|
|
|
if (javaagentCmdArgs == null) {
|
|
|
try {
|
|
|
javaagentCmdArgs = JavaagentCmdArgs.parseJavaagentCmdArgs(args);
|
|
|
- Logger.debug(Reverses.class, "Parse raw javaagent args [" + args + "] to javaagentCmdArgs -> " + javaagentCmdArgs);
|
|
|
+ Logger.debug(Reverses.class,
|
|
|
+ "Parse raw javaagent args [" + args + "] to javaagentCmdArgs -> " + javaagentCmdArgs);
|
|
|
} catch (Exception e) {
|
|
|
- Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
|
|
|
- System.exit(-1);
|
|
|
+ Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
|
|
|
+ exit(null);
|
|
|
}
|
|
|
// 是否启动debug,同步给Logger
|
|
|
Logger.ENABLE_DEBUG.set(javaagentCmdArgs.isDebug());
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ // 要忽略的解密处理逻辑的项目路径(ProtectionDomain.getCodeSource().getLocation().getPath())
|
|
|
+ Set<String> skipProjectPathPrefixSet = new HashSet<>();
|
|
|
+ String skipProjectPathPrefix = javaagentCmdArgs.getSkipProjectPathPrefix();
|
|
|
+ if (!StrUtil.isBlank(skipProjectPathPrefix)) {
|
|
|
+ String[] projectPathItem = skipProjectPathPrefix.split("___");
|
|
|
+ for (String projectPathPrefix : projectPathItem) {
|
|
|
+ if (!StrUtil.isBlank(projectPathPrefix)) {
|
|
|
+ skipProjectPathPrefixSet.add(projectPathPrefix.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Logger.debug(Reverses.class, "skipProjectPathPrefixSet -> " + skipProjectPathPrefixSet);
|
|
|
+
|
|
|
final AtomicBoolean firstExec = new AtomicBoolean(false);
|
|
|
-
|
|
|
+
|
|
|
// 在JVM加载class字节码之前,通过ClassFileTransformer修改字节码
|
|
|
if (instrumentation != null) {
|
|
|
/*
|
|
|
@@ -70,7 +87,8 @@ public class Reverses {
|
|
|
*/
|
|
|
instrumentation.addTransformer(new ClassFileTransformer() {
|
|
|
@Override
|
|
|
- public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
|
|
+ public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
|
|
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
|
|
if (className == null || protectionDomain == null || loader == null) {
|
|
|
return classfileBuffer;
|
|
|
}
|
|
|
@@ -80,40 +98,49 @@ public class Reverses {
|
|
|
if (StrUtil.isEmpty(projectPath)) {
|
|
|
return classfileBuffer;
|
|
|
}
|
|
|
+ for (String skipProjectPathPrefix : skipProjectPathPrefixSet) {
|
|
|
+ if (projectPath.startsWith(skipProjectPathPrefix)) {
|
|
|
+ return classfileBuffer;
|
|
|
+ }
|
|
|
+ }
|
|
|
className = className.replace("/", ".").replace("\\", ".");
|
|
|
// 本项目的印章
|
|
|
if (Cache.sealCache == null) {
|
|
|
try {
|
|
|
- byte[] sealByte = IOUtil.readFileFromWorkbenchRoot(new File(projectPath), Constant.SEAL_FILE);
|
|
|
+ byte[] sealByte = IOUtil.readFileFromWorkbenchRoot(new File(projectPath),
|
|
|
+ Constant.SEAL_FILE);
|
|
|
String sealContent = new String(sealByte, StandardCharsets.UTF_8);
|
|
|
if (StrUtil.isBlank(sealContent)) {
|
|
|
Logger.error(Reverses.class, "Obtain project seal fail.");
|
|
|
// 结束程序
|
|
|
- System.exit(-1);
|
|
|
+ exit(projectPath);
|
|
|
}
|
|
|
Logger.debug(Reverses.class, "seal of the project is -> " + sealContent);
|
|
|
Cache.sealCache = sealContent;
|
|
|
} catch (Exception e) {
|
|
|
Logger.error(Reverses.class, "Obtain project seal fail.");
|
|
|
Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
|
|
|
- System.exit(-1);
|
|
|
+ exit(projectPath);
|
|
|
for (int i = 0; i < Constant.TEN; i++) {
|
|
|
Logger.error(Reverses.class, "!!!!!!!!!!! class-winter Invalidation. !!!!!!!!!!!");
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/// ============================================ 1.校验启动时是否输入了加密时指定的jvm参数 2.处理non-class文件
|
|
|
final String inputPwd = javaagentCmdArgs.getPassword();
|
|
|
if (firstExec.compareAndSet(false, true)) {
|
|
|
// 1.校验启动时是否输入了加密时指定的jvm参数
|
|
|
try {
|
|
|
- byte[] jvmArgCheckBytes = IOUtil.readFileFromWorkbenchRoot(new File(projectPath), Constant.JVM_ARG_CHECK_FILE);
|
|
|
+ byte[] jvmArgCheckBytes = IOUtil.readFileFromWorkbenchRoot(new File(projectPath),
|
|
|
+ Constant.JVM_ARG_CHECK_FILE);
|
|
|
if (jvmArgCheckBytes == null) {
|
|
|
throw new IllegalStateException("jvmArgCheckBytes should not be null.");
|
|
|
}
|
|
|
- jvmArgCheckBytes = DecryptExecutor.decrypt(projectPath, null, Base64.getDecoder().decode(jvmArgCheckBytes), inputPwd == null ? null : inputPwd.toCharArray());
|
|
|
+ jvmArgCheckBytes = DecryptExecutor.decrypt(projectPath, null,
|
|
|
+ Base64.getDecoder().decode(jvmArgCheckBytes), inputPwd == null ? null :
|
|
|
+ inputPwd.toCharArray());
|
|
|
String jvmArgCheck = new String(jvmArgCheckBytes, StandardCharsets.UTF_8);
|
|
|
Logger.debug(Reverses.class, "jvmArgCheck is -> " + jvmArgCheck);
|
|
|
if (StrUtil.isBlank(jvmArgCheck)) {
|
|
|
@@ -140,7 +167,7 @@ public class Reverses {
|
|
|
} catch (Exception e) {
|
|
|
Logger.error(Reverses.class, "jvm-arg-check fail.");
|
|
|
Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
|
|
|
- System.exit(-1);
|
|
|
+ exit(projectPath);
|
|
|
// 上一步System.exit(-1)就退出程序了,照理说是不会走到下面这里的(,不过为了以防万一,这里打出提醒, class-winter失效)
|
|
|
for (int i = 0; i < Constant.TEN; i++) {
|
|
|
Logger.error(Reverses.class, "!!!!!!!!!!! class-winter Invalidation. !!!!!!!!!!!");
|
|
|
@@ -152,7 +179,8 @@ public class Reverses {
|
|
|
try {
|
|
|
// 解混淆 除了class文件外的其它文件
|
|
|
Map<String, Pair<byte[], byte[]>> resultMap =
|
|
|
- DecryptExecutor.unMaskNonClassFiles(projectPath, inputPwd == null ? null : inputPwd.toCharArray());
|
|
|
+ DecryptExecutor.unMaskNonClassFiles(projectPath, inputPwd == null ? null :
|
|
|
+ inputPwd.toCharArray());
|
|
|
Map<String, byte[]> tmpMap = new HashMap<>(16);
|
|
|
resultMap.forEach((k, v) -> tmpMap.put(k, v.getLeft()));
|
|
|
String finalProjectPath = projectPath;
|
|
|
@@ -168,7 +196,7 @@ public class Reverses {
|
|
|
} catch (Exception e) {
|
|
|
Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
|
|
|
Logger.error(Reverses.class, "Decrypt non-classes fail. ");
|
|
|
- System.exit(-1);
|
|
|
+ exit(projectPath);
|
|
|
// 上一步System.exit(-1)就退出程序了,照理说是不会走到下面这里的(,不过为了以防万一,这里打出提醒, class-winter失效)
|
|
|
for (int i = 0; i < Constant.TEN; i++) {
|
|
|
Logger.error(Reverses.class, "!!!!!!!!!!! class-winter Invalidation. !!!!!!!!!!!");
|
|
|
@@ -176,7 +204,7 @@ public class Reverses {
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/// ============================================ 处理class文件
|
|
|
// 判断是否应该解密
|
|
|
if (DecryptExecutor.checklistContain(projectPath, className) && DecryptExecutor.verifySeal(classfileBuffer)) {
|
|
|
@@ -185,14 +213,15 @@ public class Reverses {
|
|
|
Logger.debug(Reverses.class, "Decrypt class[" + className + "] start.");
|
|
|
classfileBuffer = DecryptExecutor.process(projectPath, null, className,
|
|
|
inputPwd == null ? null :
|
|
|
- inputPwd.toCharArray());
|
|
|
+ inputPwd.toCharArray());
|
|
|
Logger.debug(Reverses.class, "Decrypt class[" + className + "] end.");
|
|
|
return classfileBuffer;
|
|
|
} catch (Exception e) {
|
|
|
Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
|
|
|
Logger.error(Reverses.class, "Decrypt class[" + className + "] fail. "
|
|
|
- + (StrUtil.isEmpty(inputPwd) ? e.getMessage() : "Please ensure your password is correct."));
|
|
|
- System.exit(-1);
|
|
|
+ + (StrUtil.isEmpty(inputPwd) ? e.getMessage() : "Please ensure your password is "
|
|
|
+ + "correct."));
|
|
|
+ exit(projectPath);
|
|
|
// 上一步System.exit(-1)就退出程序了,照理说是不会走到下面这里的(,不过为了以防万一,这里打出提醒, class-winter失效)
|
|
|
for (int i = 0; i < Constant.TEN; i++) {
|
|
|
Logger.error(Reverses.class, "!!!!!!!!!!! class-winter Invalidation. !!!!!!!!!!!");
|
|
|
@@ -214,8 +243,8 @@ public class Reverses {
|
|
|
String lib = DecryptExecutor.parseLib(classWinterInfoDir);
|
|
|
Logger.error(Reverses.class, "Decrypt class[" + className + "] fail. \nPlease check:\n"
|
|
|
+ "\t1. Ensure 'Your lib " + lib + " need a input password ?'\n"
|
|
|
- + "\t2. Ensure 'Your lib "+ lib +"'s password is correct ?'");
|
|
|
- System.exit(-1);
|
|
|
+ + "\t2. Ensure 'Your lib " + lib + "'s password is correct ?'");
|
|
|
+ exit(projectPath);
|
|
|
// 上一步System.exit(-1)就退出程序了,照理说是不会走到下面这里的(,不过为了以防万一,这里打出提醒, class-winter失效)
|
|
|
for (int i = 0; i < Constant.TEN; i++) {
|
|
|
Logger.error(Reverses.class, "!!!!!!!!!!! class-winter Invalidation. !!!!!!!!!!!");
|
|
|
@@ -230,5 +259,18 @@ public class Reverses {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+ /**
|
|
|
+ * 退出程序前停几秒,防止以tomcat启动时,太快关闭界面,观察不到日志
|
|
|
+ */
|
|
|
+ private static void exit(String projectPath) {
|
|
|
+ try {
|
|
|
+ if (projectPath != null) {
|
|
|
+ Logger.error(Reverses.class, "Curr projectPath is -> " + projectPath);
|
|
|
+ }
|
|
|
+ TimeUnit.SECONDS.sleep(10);
|
|
|
+ } catch (InterruptedException interruptedException) {
|
|
|
+ }
|
|
|
+ System.exit(-1);
|
|
|
+ }
|
|
|
+
|
|
|
}
|