Skip to content
This repository was archived by the owner on Jan 2, 2023. It is now read-only.

Commit 953b5ae

Browse files
authored
Merge pull request #15 from Enaium/develop
Develop
2 parents 5006e82 + e0c70d2 commit 953b5ae

28 files changed

+980
-113
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# JavaOctetEditor
22

3-
![](https://user-images.githubusercontent.com/32991121/186328031-4bf5cddd-3926-4a8b-b675-10da8839b016.png)
4-
![](https://user-images.githubusercontent.com/32991121/186328036-7ab4de70-592b-4b9a-a48b-48c847f178c0.png)
3+
![Snipaste_2022-08-30_09-02-03](https://user-images.githubusercontent.com/32991121/187325154-249f6d48-a22e-4472-951a-bb4f69301802.png)
4+
![Snipaste_2022-08-30_09-02-22](https://user-images.githubusercontent.com/32991121/187325158-37d291fa-daeb-445d-9418-472254a32ea6.png)

build.gradle

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import java.text.SimpleDateFormat
2+
import org.gradle.internal.jvm.Jvm
23

34
plugins {
45
id 'java'
@@ -7,7 +8,7 @@ plugins {
78
}
89

910
group 'cn.enaium'
10-
version '1.0.1'
11+
version '1.1.0'
1112

1213
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
1314

@@ -18,7 +19,11 @@ jar {
1819
"Implementation-Title": "${project.name}",
1920
"Implementation-Version": "${project.version}",
2021
"Implementation-Vendor": "${project.group}",
21-
"Implementation-Timestamp": new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
22+
"Implementation-Timestamp": new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),
23+
"Premain-Class": "cn.enaium.joe.Agent",
24+
"Agent-Class": "cn.enaium.joe.Agent",
25+
"Can-Redefine-Classes": true,
26+
"Can-Retransform-Classes": true
2227
)
2328
}
2429

@@ -45,9 +50,10 @@ dependencies {
4550
implementation 'org.quiltmc:quiltflower:1.8.1'
4651
implementation 'org.javassist:javassist:3.29.0-GA'
4752
implementation 'com.google.code.gson:gson:2.9.0'
48-
implementation 'org.tinylog:tinylog-api:2.5.0'
49-
implementation 'org.tinylog:tinylog-impl:2.5.0'
53+
implementation 'org.tinylog:tinylog:1.3.6'
5054
implementation 'com.github.FabricMC:mapping-io:597f0722d6'
55+
56+
compileOnly files(Jvm.current().getToolsJar())
5157
}
5258

5359
test {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2022 Enaium
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package cn.enaium.joe;
18+
19+
import cn.enaium.joe.jar.Jar;
20+
import cn.enaium.joe.util.ReflectUtil;
21+
import org.objectweb.asm.ClassReader;
22+
import org.objectweb.asm.tree.ClassNode;
23+
24+
import java.io.IOException;
25+
import java.lang.instrument.Instrumentation;
26+
import java.lang.reflect.InvocationTargetException;
27+
import java.lang.reflect.Method;
28+
import java.net.URL;
29+
import java.net.URLClassLoader;
30+
31+
/**
32+
* @author Enaium
33+
* @since 1.1.0
34+
*/
35+
public class Agent {
36+
public static void premain(String agentArgs, Instrumentation inst) throws Exception {
37+
agent(agentArgs, inst);
38+
}
39+
40+
public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
41+
agent(agentArgs, inst);
42+
}
43+
44+
private static void agent(String agentArgs, Instrumentation inst) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
45+
URLClassLoader loader = new URLClassLoader(new URL[]{Agent.class.getProtectionDomain().getCodeSource().getLocation()}, ClassLoader.getSystemClassLoader().getParent());
46+
Class<?> main = loader.loadClass("cn.enaium.joe.Main");
47+
Method agent = ReflectUtil.getMethod(main, "agent", Instrumentation.class);
48+
agent.invoke(null, inst);
49+
}
50+
}

src/main/java/cn/enaium/joe/JavaOctetEditor.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626
import cn.enaium.joe.task.TaskManager;
2727
import cn.enaium.joe.util.LangUtil;
2828
import cn.enaium.joe.util.MessageUtil;
29+
import cn.enaium.joe.util.ReflectUtil;
30+
import com.sun.tools.attach.VirtualMachine;
2931
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
3032
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
33+
import org.pmw.tinylog.Logger;
3134

3235
import javax.swing.*;
3336
import java.awt.*;
@@ -77,6 +80,13 @@ public void run() {
7780
window.setJMenuBar(new JMenuBar() {{
7881
add(new FileMenu());
7982
add(new SearchMenu());
83+
84+
AttachMenu attachMenu = new AttachMenu() {{
85+
if (!ReflectUtil.classHas("com.sun.tools.attach.VirtualMachine")) {
86+
setEnabled(false);
87+
}
88+
}};
89+
add(attachMenu);
8090
add(new MappingMenu());
8191
add(new ConfigMenu());
8292
add(new HelpMenu());

src/main/java/cn/enaium/joe/Main.java

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,85 @@
1616

1717
package cn.enaium.joe;
1818

19+
import cn.enaium.joe.jar.Jar;
20+
import cn.enaium.joe.util.IOUtil;
21+
import cn.enaium.joe.util.MessageUtil;
22+
import cn.enaium.joe.util.ReflectUtil;
1923
import com.formdev.flatlaf.FlatDarkLaf;
20-
import org.tinylog.Logger;
24+
import org.objectweb.asm.ClassReader;
25+
import org.objectweb.asm.tree.ClassNode;
26+
import org.pmw.tinylog.Configurator;
27+
import org.pmw.tinylog.Logger;
28+
import org.pmw.tinylog.writers.ConsoleWriter;
29+
import org.pmw.tinylog.writers.FileWriter;
30+
31+
import java.io.File;
32+
import java.io.IOException;
33+
import java.lang.instrument.Instrumentation;
34+
import java.lang.reflect.InvocationTargetException;
35+
import java.lang.reflect.Method;
36+
import java.net.MalformedURLException;
37+
import java.net.URL;
38+
import java.net.URLClassLoader;
39+
import java.nio.file.Files;
40+
import java.nio.file.Path;
41+
import java.nio.file.Paths;
42+
import java.util.Objects;
2143

2244
/**
2345
* @author Enaium
2446
*/
2547
public final class Main {
2648
public static void main(String[] args) {
49+
loadTools();
50+
launch();
51+
}
52+
53+
54+
private static void agent(Instrumentation inst) throws IOException {
55+
launch();
56+
Jar jar = new Jar();
57+
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
58+
String name = allLoadedClass.getName().replace('.', '/');
59+
if (name.contains("$$") || name.contains("[") || !inst.isModifiableClass(allLoadedClass)
60+
|| allLoadedClass.getClassLoader() == Main.class.getClassLoader()
61+
|| name.matches("(java|sun|javax|com.sun|jdk|javafx).+")) {
62+
continue;
63+
}
64+
65+
ClassNode classNode = new ClassNode();
66+
ClassReader classReader = new ClassReader(IOUtil.getBytes(Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResourceAsStream(name + ".class"))));
67+
classReader.accept(classNode, 0);
68+
jar.classes.put(allLoadedClass.getName(), classNode);
69+
}
70+
JavaOctetEditor.getInstance().setJar(jar);
71+
}
72+
73+
private static void launch() {
74+
Configurator.currentConfig().writer(new ConsoleWriter(), "[{date: HH:mm:ss.SSS}] {level} > {message}").addWriter(new FileWriter("latest.log"), "[{date: HH:mm:ss.SSS}] {level} > {message}").activate();
75+
2776
Logger.info("DIR:{}", System.getProperty("user.dir"));
2877
FlatDarkLaf.setup();
2978
new JavaOctetEditor().run();
3079
}
80+
81+
private static void loadTools() {
82+
if (!ReflectUtil.classHas("com.sun.tools.attach.VirtualMachine")) {
83+
Path toolsPath = Paths.get("lib", "tools.jar");
84+
Path jrePath = Paths.get(System.getProperty("java.home"));
85+
Path tool = jrePath.getParent().resolve(toolsPath);
86+
if (Files.notExists(tool)) {
87+
Logger.warn("Please use jdk to run");
88+
return;
89+
}
90+
try {
91+
Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
92+
addURL.setAccessible(true);
93+
addURL.invoke(Main.class.getClassLoader(), tool.toUri().toURL());
94+
} catch (NoSuchMethodException | MalformedURLException | InvocationTargetException |
95+
IllegalAccessException e) {
96+
MessageUtil.error(e);
97+
}
98+
}
99+
}
31100
}

src/main/java/cn/enaium/joe/config/ConfigManager.java

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
import cn.enaium.joe.config.extend.ApplicationConfig;
2020
import cn.enaium.joe.config.extend.CFRConfig;
21-
import cn.enaium.joe.config.value.Value;
21+
import cn.enaium.joe.config.extend.FernFlowerConfig;
22+
import cn.enaium.joe.config.extend.ProcyonConfig;
23+
import cn.enaium.joe.config.value.*;
2224
import cn.enaium.joe.util.MessageUtil;
2325
import cn.enaium.joe.util.ReflectUtil;
2426
import com.google.gson.*;
@@ -29,19 +31,20 @@
2931
import java.lang.reflect.Field;
3032
import java.nio.charset.StandardCharsets;
3133
import java.nio.file.Files;
32-
import java.util.HashMap;
33-
import java.util.Map;
34+
import java.util.*;
3435

3536
/**
3637
* @author Enaium
3738
* @since 0.7.0
3839
*/
3940
public class ConfigManager {
40-
private final Map<Class<? extends Config>, Config> configMap = new HashMap<>();
41+
private final Map<Class<? extends Config>, Config> configMap = new LinkedHashMap<>();
4142

4243
public ConfigManager() {
4344
setByClass(new ApplicationConfig());
4445
setByClass(new CFRConfig());
46+
setByClass(new FernFlowerConfig());
47+
setByClass(new ProcyonConfig());
4548
}
4649

4750
@SuppressWarnings("unchecked")
@@ -70,7 +73,7 @@ public Map<String, String> getConfigMap(Class<? extends Config> config) {
7073
if (o instanceof Value<?>) {
7174
Object value = ((Value<?>) o).getValue();
7275
if (value != null) {
73-
map.put(((Value<?>) o).getName(), value.toString());
76+
map.put(declaredField.getName(), value.toString());
7477
}
7578
}
7679
} catch (IllegalAccessException e) {
@@ -92,34 +95,48 @@ public void load() {
9295
try {
9396
File file = new File(System.getProperty("."), config.getName() + ".json");
9497
if (file.exists()) {
95-
//Step.1 get json object
9698
JsonObject jsonObject = gson().fromJson(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8), JsonObject.class);
9799

98-
//Step.2 get all field of the config
99100
for (Field configField : klass.getDeclaredFields()) {
100101
configField.setAccessible(true);
101102
if (!jsonObject.has(configField.getName())) {
102103
continue;
103104
}
104105

105-
//Step.3 deserialize
106-
Object setting = gson().fromJson(jsonObject.get(configField.getName()).toString(), configField.getType());
106+
if (!jsonObject.has(configField.getName())) {
107+
continue;
108+
}
107109

108-
//Step.3.1 get all field of the setting
109-
for (Field settingField : setting.getClass().getDeclaredFields()) {
110-
settingField.setAccessible(true);
110+
if (!jsonObject.get(configField.getName()).getAsJsonObject().has("value")) {
111+
continue;
112+
}
111113

112-
if (settingField.isAnnotationPresent(Expose.class)) {
113-
if (!settingField.getAnnotation(Expose.class).deserialize()) {
114-
Field declaredField = ReflectUtil.getField(setting.getClass(), settingField.getName());
115-
//Step.3.2 use the value from config to set the value of setting
116-
declaredField.set(setting, ReflectUtil.getFieldValue(ReflectUtil.getFieldValue(config, configField.getName()), settingField.getName()));
114+
JsonElement valueJsonElement = jsonObject.get(configField.getName()).getAsJsonObject().get("value");
115+
116+
Object valueObject = configField.get(config);
117+
if (valueObject instanceof Value<?>) {
118+
Value<?> value = (Value<?>) valueObject;
119+
if (value instanceof EnableValue) {
120+
((EnableValue) value).setValue(valueJsonElement.getAsBoolean());
121+
} else if (value instanceof IntegerValue) {
122+
((IntegerValue) value).setValue(valueJsonElement.getAsInt());
123+
} else if (value instanceof ModeValue) {
124+
ModeValue modeValue = (ModeValue) value;
125+
if (modeValue.getMode().contains(valueJsonElement.getAsString())) {
126+
modeValue.setValue(valueJsonElement.getAsString());
127+
} else {
128+
modeValue.setValue(modeValue.getMode().get(0));
129+
}
130+
} else if (value instanceof StringSetValue) {
131+
Set<String> strings = new HashSet<>();
132+
for (JsonElement jsonElement : valueJsonElement.getAsJsonArray()) {
133+
strings.add(jsonElement.getAsString());
117134
}
135+
((StringSetValue) value).setValue(strings);
136+
} else if (value instanceof StringValue) {
137+
((StringValue) value).setValue(valueJsonElement.getAsString());
118138
}
119139
}
120-
121-
//Step.4 use the value from step 3 to set the value of config
122-
configField.set(config, setting);
123140
}
124141
}
125142
} catch (Throwable e) {

src/main/java/cn/enaium/joe/config/extend/ApplicationConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
public class ApplicationConfig extends Config {
3333
public ModeValue decompilerMode = new ModeValue("Decompiler", "CFR", "Java Decompiler", Arrays.asList("CFR", "Procyon", "FernFlower"));
3434
public ModeValue language = new ModeValue("Language", "System", "UI language", Arrays.asList("System", "zh_CN", "en_US"));
35+
public ModeValue packagePresentation = new ModeValue("Package Presentation", "Hierarchical", "Package Mode", Arrays.asList("Flat", "Hierarchical"));
3536
@NoUI
3637
public final StringSetValue loadRecent = new StringSetValue("Load Recent", new HashSet<>(), "");
3738

0 commit comments

Comments
 (0)