├── .gitattributes ├── .gitignore ├── LICENSE.MD ├── README.md ├── asmplus ├── build.gradle └── src │ └── main │ └── java │ └── me │ └── exeos │ └── asmplus │ ├── JarLoader.java │ ├── codegen │ ├── block │ │ └── CodeBlock.java │ ├── code │ │ └── Jump.java │ ├── lookupswitch │ │ ├── LookupSwitchGenerator.java │ │ └── SwitchCase.java │ └── proxyjump │ │ └── ProxyJump.java │ ├── descarg │ └── DescArg.java │ ├── pattern │ ├── PatternParts.java │ ├── PatternScanner.java │ └── result │ │ ├── ClassResult.java │ │ ├── InsnResult.java │ │ └── MethodResult.java │ ├── remapper │ └── mapping │ │ ├── ClassMember.java │ │ └── Remapper.java │ └── utils │ ├── ASMUtils.java │ ├── RandomUtil.java │ └── StreamUtils.java ├── gradle ├── libs.versions.toml └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | replay_pid* 25 | 26 | # manuel 27 | /.idea/ 28 | /target/ 29 | /out/ 30 | *.iml 31 | *.json 32 | /.gradle/ 33 | /asmplus/build/ 34 | -------------------------------------------------------------------------------- /LICENSE.MD: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Exeos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Recontinued! 2 | 3 | # ASMPlus 4 | Utils etc for object web asm 5 | -------------------------------------------------------------------------------- /asmplus/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * This generated file contains a sample Java library project to get you started. 5 | * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.7/userguide/building_java_projects.html in the Gradle documentation. 6 | */ 7 | 8 | plugins { 9 | id 'java-library' 10 | id 'maven-publish' 11 | } 12 | 13 | repositories { 14 | mavenCentral() 15 | maven { 16 | url 'https://stianloader.org/maven' 17 | } 18 | } 19 | 20 | dependencies { 21 | testImplementation libs.junit 22 | 23 | // This dependency is used internally, and not exposed to consumers on their own compile classpath. 24 | implementation libs.guava 25 | 26 | // asm 27 | implementation 'org.ow2.asm:asm:9.7' 28 | implementation 'org.ow2.asm:asm-analysis:9.7' 29 | implementation 'org.ow2.asm:asm-commons:9.7' 30 | implementation 'org.ow2.asm:asm-tree:9.7' 31 | implementation 'org.ow2.asm:asm-util:9.7' 32 | 33 | implementation 'org.stianloader:stianloader-remapper:0.1.0-a20240426' 34 | } 35 | 36 | // Apply a specific Java toolchain to ease working on different environments. 37 | java { 38 | toolchain { 39 | languageVersion = JavaLanguageVersion.of(8) 40 | } 41 | } 42 | 43 | // Task to generate sources jar 44 | task sourcesJar(type: Jar) { 45 | from sourceSets.main.allSource 46 | archiveClassifier.set('sources') 47 | } 48 | 49 | publishing { 50 | publications { 51 | mavenJava(MavenPublication) { 52 | from components.java 53 | 54 | groupId = 'me.exeos' 55 | artifactId = 'asmplus' 56 | version = '1.0.0' 57 | 58 | artifact sourcesJar 59 | } 60 | } 61 | repositories { 62 | maven { 63 | name = 'local' 64 | url = uri('file://' + new File(System.getProperty('user.home'), '.m2/repository').absolutePath) 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/JarLoader.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus; 2 | 3 | import me.exeos.asmplus.utils.StreamUtils; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.ClassWriter; 6 | import org.objectweb.asm.tree.ClassNode; 7 | 8 | import java.io.File; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.math.BigInteger; 13 | import java.nio.charset.StandardCharsets; 14 | import java.nio.file.Files; 15 | import java.nio.file.Paths; 16 | import java.nio.file.attribute.BasicFileAttributeView; 17 | import java.nio.file.attribute.BasicFileAttributes; 18 | import java.nio.file.attribute.FileTime; 19 | import java.util.Enumeration; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.jar.JarEntry; 23 | import java.util.jar.JarFile; 24 | import java.util.jar.JarOutputStream; 25 | 26 | public class JarLoader { 27 | 28 | public HashMap classes = new HashMap<>(); 29 | public HashMap resources = new HashMap<>(); 30 | 31 | protected FileTime creationTime; 32 | protected FileTime lastModifiedTime; 33 | 34 | /** 35 | * Load all classes and files of jar to HashMaps in form of: 36 | * for classes & 37 | * for files. 38 | * 39 | * @param inputPath Path to ajar file 40 | * @throws IOException 41 | */ 42 | 43 | public void load(String inputPath) throws IOException { 44 | JarFile jarIn = new JarFile(inputPath); 45 | Enumeration entries = jarIn.entries(); 46 | JarEntry entry; 47 | 48 | while (entries.hasMoreElements()) { 49 | entry = entries.nextElement(); 50 | if (!entry.isDirectory()) { 51 | InputStream stream = jarIn.getInputStream(entry); 52 | byte[] entryBytes = StreamUtils.readAllBytes(stream); 53 | 54 | if (isClass(entryBytes) && entry.getName().endsWith(".class")) { 55 | ClassReader classReader = new ClassReader(entryBytes); 56 | ClassNode classNode = new ClassNode(); 57 | 58 | classReader.accept(classNode, ClassReader.SKIP_FRAMES + ClassReader.SKIP_DEBUG); 59 | classes.put(classNode.name, classNode); 60 | } else { 61 | resources.put(entry.getName(), entryBytes); 62 | } 63 | } 64 | } 65 | 66 | BasicFileAttributes attributes = Files.getFileAttributeView(Paths.get(inputPath), BasicFileAttributeView.class).readAttributes(); 67 | creationTime = attributes.creationTime(); 68 | lastModifiedTime = attributes.lastModifiedTime(); 69 | } 70 | 71 | /** 72 | * Export mapped jar back to a jar file 73 | * 74 | * @param outputPath Desired path for jar output 75 | * @throws IOException 76 | */ 77 | 78 | public void export(String outputPath) throws Exception { 79 | new File(new File(outputPath).getParent()).mkdirs(); 80 | JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(outputPath)); 81 | for (Map.Entry entry : classes.entrySet()) { 82 | ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); 83 | entry.getValue().accept(classWriter); 84 | 85 | writeJarEntry(jarOut, entry.getKey() + ".class", classWriter.toByteArray()); 86 | } 87 | 88 | for (Map.Entry e : resources.entrySet()) { 89 | writeJarEntry(jarOut, e.getKey(), e.getValue()); 90 | } 91 | 92 | jarOut.finish(); 93 | 94 | BasicFileAttributeView attributeView = Files.getFileAttributeView(Paths.get(outputPath), BasicFileAttributeView.class); 95 | attributeView.setTimes(lastModifiedTime, null, creationTime); 96 | } 97 | 98 | public void putClass(byte[] classBytes) { 99 | ClassReader classReader = new ClassReader(classBytes); 100 | ClassNode classNode = new ClassNode(); 101 | classReader.accept(classNode, ClassReader.SKIP_DEBUG); 102 | 103 | classes.put(classNode.name, classNode); 104 | } 105 | 106 | protected void writeJarEntry(JarOutputStream outputStream, String name, byte[] bytes) throws IOException { 107 | JarEntry entry = new JarEntry(new String(name.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8)); 108 | 109 | if (creationTime != null) 110 | entry.setCreationTime(creationTime); 111 | if (lastModifiedTime != null) 112 | entry.setLastModifiedTime(lastModifiedTime); 113 | 114 | entry.setSize(bytes.length); 115 | 116 | outputStream.putNextEntry(entry); 117 | outputStream.write(bytes); 118 | outputStream.closeEntry(); 119 | } 120 | 121 | protected boolean isClass(byte[] file) { 122 | if (file.length < 4) 123 | return false; 124 | return new BigInteger(1, new byte[] { file[0], file[1], file[2], file[3] }).intValue() == -889275714; 125 | } 126 | } -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/codegen/block/CodeBlock.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.codegen.block; 2 | 3 | import me.exeos.asmplus.codegen.code.Jump; 4 | import me.exeos.asmplus.utils.ASMUtils; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.tree.AbstractInsnNode; 7 | import org.objectweb.asm.tree.InsnNode; 8 | import org.objectweb.asm.tree.LabelNode; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class CodeBlock { 14 | 15 | private LabelNode entry = new LabelNode(); 16 | public final ArrayList code = new ArrayList<>(); 17 | 18 | private CodeBlock prev; 19 | private CodeBlock next; 20 | 21 | public CodeBlock(CodeBlock next, CodeBlock prev) { 22 | this.next = next; 23 | this.prev = prev; 24 | } 25 | 26 | public CodeBlock() { 27 | this(null, null); 28 | } 29 | 30 | public ArrayList genBlockCode() { 31 | return genBlockCode(null, false); 32 | } 33 | 34 | public ArrayList genBlockCode(Jump jumpTemplate) { 35 | return genBlockCode(jumpTemplate, false); 36 | } 37 | 38 | public ArrayList genBlockCode(boolean encapsulate) { 39 | return genBlockCode(null, encapsulate); 40 | } 41 | 42 | public ArrayList genBlockCode(Jump jumpTemplateE, boolean encapsulate) { 43 | ArrayList blockCode = new ArrayList<>(); 44 | LabelNode exit = null; 45 | 46 | /* Jump to block exit, so you can only access block with entry */ 47 | if (encapsulate) { 48 | exit = new LabelNode(); 49 | blockCode.addAll(ASMUtils.getJumpInsns(exit)); 50 | } 51 | 52 | blockCode.add(entry); 53 | blockCode.addAll(code); 54 | if (next != null) { 55 | blockCode.addAll(ASMUtils.getJumpInsns(Opcodes.GOTO, next.entry)); 56 | } 57 | 58 | if (encapsulate) { 59 | blockCode.add(exit); 60 | blockCode.add(new InsnNode(Opcodes.NOP)); 61 | } 62 | 63 | return blockCode; 64 | } 65 | 66 | /** 67 | * Add list of instructions to block's code 68 | * @param insns List of instructions to add 69 | * @return Instance of current CodeBlock 70 | */ 71 | public CodeBlock add(List insns) { 72 | if (insns == null) { 73 | throw new IllegalArgumentException("insns can't be null"); 74 | } 75 | code.addAll(insns); 76 | 77 | return this; 78 | } 79 | 80 | /** 81 | * Add instruction 82 | * @param insnNode instruction to add 83 | * @return Instance of current CodeBlock 84 | */ 85 | public CodeBlock add(AbstractInsnNode insnNode) { 86 | if (insnNode == null) { 87 | throw new IllegalArgumentException("insnNode can't be null"); 88 | } 89 | code.add(insnNode); 90 | 91 | return this; 92 | } 93 | 94 | /* getter and setter for prev and next block */ 95 | 96 | /** 97 | * Set the next entry of the CodeBlock. NOTE: Entry MUST be a new Entry, not existing anywhere you are trying to add block to 98 | * @return Instance of CodeBlock 99 | */ 100 | public CodeBlock entry(LabelNode newEntry) { 101 | if (newEntry == null) { 102 | throw new IllegalArgumentException("Entry of CodeBlock can't be null"); 103 | } 104 | this.entry = newEntry; 105 | 106 | return this; 107 | } 108 | 109 | public LabelNode entry() { 110 | return entry; 111 | } 112 | 113 | public CodeBlock next() { 114 | return next; 115 | } 116 | 117 | /** 118 | * Set the next CodeBlock of this CodeBlock 119 | * @return Instance of next CodeBlock 120 | */ 121 | public CodeBlock next(CodeBlock next) { 122 | if (next == null) { 123 | throw new IllegalArgumentException("next can't be null"); 124 | } 125 | this.next = next; 126 | 127 | return next; 128 | } 129 | 130 | public CodeBlock prev() { 131 | return prev; 132 | } 133 | 134 | /** 135 | * Set the prev CodeBlock of this CodeBlock 136 | * @return Instance of prev CodeBlock 137 | */ 138 | public CodeBlock prev(CodeBlock prev) { 139 | if (prev == null) { 140 | throw new IllegalArgumentException("prev can't be null"); 141 | } 142 | this.prev = prev; 143 | 144 | return prev; 145 | } 146 | } -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/codegen/code/Jump.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.codegen.code; 2 | 3 | 4 | import org.objectweb.asm.Opcodes; 5 | import org.objectweb.asm.tree.AbstractInsnNode; 6 | import org.objectweb.asm.tree.JumpInsnNode; 7 | import org.objectweb.asm.tree.LabelNode; 8 | 9 | import java.util.ArrayList; 10 | 11 | public class Jump { 12 | 13 | public final ArrayList condition; 14 | private int opcode; 15 | private LabelNode label = null; 16 | 17 | public Jump(ArrayList condition, int opcode, LabelNode label) { 18 | this.condition = condition; 19 | setOpcode(opcode); 20 | this.label = label; 21 | } 22 | 23 | public Jump(int opcode, LabelNode label) { 24 | this(new ArrayList<>(), opcode, label); 25 | } 26 | 27 | public Jump(LabelNode label) { 28 | this(Opcodes.GOTO, label); 29 | } 30 | 31 | public Jump() { 32 | this(null); 33 | } 34 | 35 | /** 36 | * combine condition and jump and return 37 | * @return List of instructions required for the jump 38 | * */ 39 | public ArrayList getJump() { 40 | if (label == null) { 41 | throw new IllegalStateException("Jump label can't be null"); 42 | } 43 | ArrayList combine = new ArrayList<>(); 44 | combine.addAll(condition); 45 | combine.add(new JumpInsnNode(opcode, label)); 46 | 47 | return combine; 48 | } 49 | 50 | /** 51 | * Add an Instruction to the conditions 52 | * @param insnNode Instruction to be added to conditions 53 | * */ 54 | public void add(AbstractInsnNode insnNode) { 55 | condition.add(insnNode); 56 | } 57 | 58 | /** 59 | * Set the jump opcode 60 | * @param opcode new jump opcode 61 | * @return Instance of current Object 62 | */ 63 | public Jump setOpcode(int opcode) { 64 | if (opcode < Opcodes.IFEQ || opcode > Opcodes.JSR) { 65 | throw new IllegalArgumentException("Opcode must be a valid jump opcode"); 66 | } 67 | this.opcode = opcode; 68 | 69 | return this; 70 | } 71 | 72 | /** 73 | * Set the jump target label 74 | * @param label new label 75 | * @return Instance of current Object 76 | */ 77 | public Jump setLabel(LabelNode label) { 78 | if (label == null) { 79 | throw new IllegalArgumentException("Label can't be null"); 80 | } 81 | this.label = label; 82 | 83 | return this; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/codegen/lookupswitch/LookupSwitchGenerator.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.codegen.lookupswitch; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.tree.*; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | 10 | public class LookupSwitchGenerator implements Opcodes { 11 | 12 | private final List cases; 13 | 14 | private final SwitchCase dfltCase; 15 | private final LabelNode switchEnd; 16 | 17 | public LookupSwitchGenerator(List cases) { 18 | this(cases, null); 19 | } 20 | 21 | private LookupSwitchGenerator(List cases, SwitchCase dfltCase) { 22 | this.cases = cases; 23 | this.dfltCase = dfltCase; 24 | switchEnd = new LabelNode(); 25 | 26 | this.cases.sort(Comparator.comparingInt(o -> o.key)); 27 | } 28 | 29 | /** 30 | * Generate a new lookup switch 31 | * @return Lookup switch instructions 32 | */ 33 | public List gen() { 34 | List instructions = new ArrayList<>(); 35 | 36 | instructions.add(new LookupSwitchInsnNode(dfltCase != null ? dfltCase.caseStart : switchEnd, getKeys(), getLabels())); 37 | for (SwitchCase switchCase : cases) { 38 | instructions.add(switchCase.caseStart); 39 | instructions.addAll(switchCase.instructions); 40 | instructions.add(new JumpInsnNode(GOTO, switchEnd)); 41 | } 42 | if (dfltCase != null) { 43 | instructions.add(dfltCase.caseStart); 44 | instructions.addAll(dfltCase.instructions); 45 | instructions.add(new JumpInsnNode(GOTO, switchEnd)); 46 | } 47 | instructions.add(switchEnd); 48 | 49 | return instructions; 50 | } 51 | 52 | protected int[] getKeys() { 53 | int[] keys = new int[cases.size()]; 54 | 55 | for (int i = 0; i < cases.size(); i++) { 56 | keys[i] = cases.get(i).key; 57 | } 58 | 59 | return keys; 60 | } 61 | 62 | protected LabelNode[] getLabels() { 63 | LabelNode[] labels = new LabelNode[cases.size()]; 64 | 65 | for (int i = 0; i < cases.size(); i++) { 66 | labels[i] = cases.get(i).caseStart; 67 | } 68 | 69 | return labels; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/codegen/lookupswitch/SwitchCase.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.codegen.lookupswitch; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.LabelNode; 5 | 6 | import java.util.List; 7 | 8 | public class SwitchCase { 9 | 10 | public final int key; 11 | 12 | public final LabelNode caseStart; 13 | public final List instructions; 14 | 15 | public SwitchCase(Object key, List instructions) { 16 | this.instructions = instructions; 17 | caseStart = new LabelNode(); 18 | 19 | switch (key.getClass().getName()) { 20 | case "java.lang.Integer": 21 | case "java.lang.Byte": 22 | case "java.lang.Short": 23 | case "java.lang.Character": 24 | this.key = (int) key; 25 | break; 26 | case "java.lang.String": 27 | this.key = key.hashCode(); 28 | break; 29 | case "java.lang.Enum": 30 | this.key = ((Enum) key).ordinal(); 31 | break; 32 | default: 33 | throw new IllegalStateException("Key must be type of: char, byte, short, int, String or enum"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/codegen/proxyjump/ProxyJump.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.codegen.proxyjump; 2 | 3 | import me.exeos.asmplus.codegen.block.CodeBlock; 4 | import me.exeos.asmplus.utils.ASMUtils; 5 | import org.objectweb.asm.tree.AbstractInsnNode; 6 | import org.objectweb.asm.tree.LabelNode; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class ProxyJump { 12 | 13 | public final LabelNode entry = new LabelNode(); 14 | public final LabelNode destination; 15 | 16 | public ProxyJump(LabelNode destination) { 17 | this.destination = destination; 18 | } 19 | 20 | public List proxyCode() { 21 | ArrayList insns = new ArrayList<>(); 22 | LabelNode exit = new LabelNode(); 23 | 24 | insns.addAll(ASMUtils.getJumpInsns(exit)); 25 | insns.addAll(new CodeBlock() 26 | .entry(entry) 27 | .add(ASMUtils.getJumpInsns(new LabelNode())) // This label will be replaced but is needed by ASMUtils 28 | .genBlockCode(ASMUtils.getJump(new LabelNode()), false)); 29 | insns.add(exit); 30 | 31 | return insns; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/descarg/DescArg.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.descarg; 2 | 3 | public class DescArg { 4 | 5 | public final String arg; 6 | public final boolean isObject; 7 | 8 | public DescArg(String arg, boolean isObject) { 9 | this.arg = arg; 10 | this.isObject = isObject; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/pattern/PatternParts.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.pattern; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | 5 | public interface PatternParts { 6 | 7 | /** 8 | * Use if part is unknown 9 | */ 10 | int P_ANY = -2; 11 | /** 12 | * Will skip to part in pattern after itself 13 | */ 14 | int P_SKIPTO = -3; 15 | /** 16 | * Any number push NOTE: this does not include method invocations 17 | */ 18 | int P_NUMBER = -4; 19 | int P_STRING = -5; 20 | int P_VALUE = -6; 21 | 22 | 23 | /** 24 | * Because the PatternScanner doesn't support Objects it uses these ints 25 | */ 26 | int INSN = AbstractInsnNode.INSN + 300; 27 | int INT_INSN = AbstractInsnNode.INT_INSN + 300; 28 | int VAR_INSN = AbstractInsnNode.VAR_INSN + 300; 29 | int TYPE_INSN = AbstractInsnNode.TYPE_INSN + 300; 30 | int FIELD_INSN = AbstractInsnNode.FIELD_INSN + 300; 31 | int METHOD_INSN = AbstractInsnNode.METHOD_INSN + 300; 32 | int INVOKE_DYNAMIC_INSN = AbstractInsnNode.INVOKE_DYNAMIC_INSN + 300; 33 | int JUMP_INSN = AbstractInsnNode.JUMP_INSN + 300; 34 | int LABEL = AbstractInsnNode.LABEL + 300; 35 | int LDC_INSN = AbstractInsnNode.LDC_INSN + 300; 36 | int IINC_INSN = AbstractInsnNode.IINC_INSN + 300; 37 | int TABLESWITCH_INSN = AbstractInsnNode.TABLESWITCH_INSN + 300; 38 | int LOOKUPSWITCH_INSN = AbstractInsnNode.LOOKUPSWITCH_INSN + 300; 39 | int MULTIANEWARRAY_INSN = AbstractInsnNode.MULTIANEWARRAY_INSN + 300; 40 | int FRAME = AbstractInsnNode.FRAME + 300; 41 | int LINE = AbstractInsnNode.LINE + 300; 42 | } -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/pattern/PatternScanner.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.pattern; 2 | 3 | import me.exeos.asmplus.pattern.result.ClassResult; 4 | import me.exeos.asmplus.pattern.result.InsnResult; 5 | import me.exeos.asmplus.pattern.result.MethodResult; 6 | import me.exeos.asmplus.utils.ASMUtils; 7 | import org.objectweb.asm.Opcodes; 8 | import org.objectweb.asm.tree.AbstractInsnNode; 9 | import org.objectweb.asm.tree.ClassNode; 10 | import org.objectweb.asm.tree.MethodNode; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | public class PatternScanner implements PatternParts, Opcodes { 17 | 18 | /** 19 | * Pattern of opcodes to be looked after 20 | */ 21 | private int[] pattern = null; 22 | 23 | /** 24 | * 25 | * @param pattern Pattern of Opcodes to look after, use -1 for: labels, lines (& frames which get skipped by default) and use -2 for unknown 26 | */ 27 | public PatternScanner(int[] pattern) { 28 | this.pattern = pattern; 29 | } 30 | 31 | public PatternScanner() {} 32 | 33 | /** 34 | * @param archive Classes to be scanned 35 | * @return Classes the pattern was found in with array of methods and patterns in those. 36 | * Check out the "result" package for more context. 37 | */ 38 | public List scanArchive(List archive) { 39 | List results = new ArrayList<>(); 40 | 41 | for (ClassNode classNode : archive) { 42 | ClassResult result = scanClass(classNode); 43 | 44 | if (result != null) 45 | results.add(result); 46 | } 47 | 48 | return results; 49 | } 50 | 51 | /** 52 | * @param classNode Class to be scanned 53 | * @return Class the pattern was found in, array of methods and patterns in those. 54 | * Check out the "result" package for more context. 55 | */ 56 | public ClassResult scanClass(ClassNode classNode) { 57 | List results = new ArrayList<>(); 58 | 59 | for (MethodNode methodNode : classNode.methods) { 60 | List foundPatterns = scanMethod(methodNode); 61 | 62 | if (!foundPatterns.isEmpty()) 63 | results.add(new MethodResult(methodNode, foundPatterns)); 64 | } 65 | 66 | return results.isEmpty() ? null : new ClassResult(classNode, results); 67 | } 68 | 69 | /** 70 | * @param methodNode Method to be scanned 71 | * @return The patterns found 72 | */ 73 | public List scanMethod(MethodNode methodNode) { 74 | if (pattern == null) 75 | return null; 76 | 77 | List foundPatterns = new ArrayList<>(); 78 | 79 | for (AbstractInsnNode first : methodNode.instructions) { 80 | AbstractInsnNode last = ASMUtils.getNext(first, pattern.length - 1); 81 | 82 | if (last == null) 83 | break; 84 | 85 | /* Speeding up the process */ 86 | if (!containsSkip() && (!match(first, pattern[0]) || !match(last, pattern[pattern.length - 1]))) 87 | continue; 88 | 89 | boolean match = true; 90 | Integer toSkip = null; 91 | 92 | List foundPattern = new ArrayList<>(); 93 | 94 | int currentInsn = 0; 95 | int currentPattern = 0; 96 | while (currentPattern <= pattern.length - 1) { 97 | AbstractInsnNode next = ASMUtils.getNext(first, currentInsn); 98 | if (next == null) { 99 | match = false; 100 | break; 101 | } 102 | /* Handling P_SKIPTO */ 103 | if (toSkip == null && pattern[currentPattern] == P_SKIPTO) { 104 | toSkip = pattern[currentPattern + 1]; 105 | continue; 106 | } 107 | /* increasing i here because if pattern[patternIndex] == P_SKIPTO "next" should also get added */ 108 | currentInsn++; 109 | 110 | if (toSkip != null) { 111 | if (match(next, toSkip)) { 112 | currentPattern++; 113 | toSkip = null; 114 | } else { 115 | foundPattern.add(next); 116 | continue; 117 | } 118 | } 119 | 120 | if (!match(next, pattern[currentPattern])) { 121 | match = false; 122 | break; 123 | } else 124 | foundPattern.add(next); 125 | 126 | currentPattern++; 127 | } 128 | 129 | if (match) 130 | foundPatterns.add(new InsnResult(foundPattern.toArray(new AbstractInsnNode[0]))); 131 | } 132 | 133 | return foundPatterns; 134 | } 135 | 136 | public void setPattern(int[] pattern) { 137 | this.pattern = pattern; 138 | } 139 | 140 | private boolean containsSkip() { 141 | return Arrays.asList(Arrays.stream(pattern).boxed().toArray(Integer[]::new)).contains(P_SKIPTO); 142 | } 143 | 144 | protected boolean match(AbstractInsnNode toCheck, int part) { 145 | return part == P_ANY || (part == P_NUMBER && ASMUtils.isNumberPush(toCheck)) || (part == P_STRING && ASMUtils.isString(toCheck)) || 146 | (part == P_VALUE && ASMUtils.isValuePush(toCheck)) || toCheck.getOpcode() == part || toCheck.getType() == part - 300; 147 | } 148 | } -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/pattern/result/ClassResult.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.pattern.result; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | 5 | import java.util.List; 6 | 7 | public class ClassResult { 8 | 9 | public final ClassNode classNode; 10 | public final List methodResults; 11 | 12 | public ClassResult(ClassNode classNode, List methodResults) { 13 | this.classNode = classNode; 14 | this.methodResults = methodResults; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/pattern/result/InsnResult.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.pattern.result; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | 5 | public class InsnResult { 6 | 7 | public InsnResult(AbstractInsnNode[] pattern) { 8 | this.pattern = pattern; 9 | } 10 | 11 | public final AbstractInsnNode[] pattern; 12 | 13 | public AbstractInsnNode getFirst() { 14 | return pattern[0]; 15 | } 16 | 17 | public AbstractInsnNode getLast() { 18 | return pattern[pattern.length - 1]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/pattern/result/MethodResult.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.pattern.result; 2 | 3 | import org.objectweb.asm.tree.MethodNode; 4 | 5 | import java.util.List; 6 | 7 | public class MethodResult { 8 | 9 | public final MethodNode methodNode; 10 | public final List foundPatterns; 11 | 12 | public MethodResult(MethodNode methodNode, List foundPatterns) { 13 | this.methodNode = methodNode; 14 | this.foundPatterns = foundPatterns; 15 | } 16 | } -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/remapper/mapping/ClassMember.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.remapper.mapping; 2 | 3 | public class ClassMember { 4 | 5 | private final String owner; 6 | private final String name; 7 | private final String desc; 8 | 9 | public ClassMember(String owner, String name, String desc) { 10 | this.owner = owner; 11 | this.name = name; 12 | this.desc = desc; 13 | } 14 | 15 | public boolean equals(String owner, String name, String desc) { 16 | return this.owner.equals(owner) && this.name.equals(name) && this.desc.equals(desc); 17 | } 18 | 19 | @Override 20 | public boolean equals(Object obj) { 21 | if (obj == this) { 22 | return true; 23 | } 24 | 25 | if (obj == null || obj.getClass() != this.getClass()) { 26 | return false; 27 | } 28 | 29 | return ((ClassMember) obj).equals(owner, name, desc); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/remapper/mapping/Remapper.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.remapper.mapping; 2 | 3 | import me.exeos.asmplus.JarLoader; 4 | 5 | import java.util.HashMap; 6 | 7 | public class Remapper { 8 | 9 | /** 10 | * All maps below follow this concept: 11 | * K = original V = new 12 | * K will be used for lookup purposes 13 | */ 14 | 15 | private final HashMap classMap = new HashMap<>(); 16 | private final HashMap fieldMap = new HashMap<>(); 17 | private final HashMap methodMap = new HashMap<>(); 18 | 19 | public void mapClass(String name, String newName) { 20 | classMap.put(name, newName); 21 | } 22 | 23 | public void mapField(String owner, String name, String desc, String newName) { 24 | mapField(new ClassMember(owner, name, desc), newName); 25 | } 26 | 27 | public void mapField(ClassMember classMember, String newName) { 28 | fieldMap.put(classMember, newName); 29 | } 30 | 31 | public void mepMethod(String owner, String name, String desc, String newName) { 32 | mepMethod(new ClassMember(owner, name, desc), newName); 33 | } 34 | 35 | public void mepMethod(ClassMember classMember, String newName) { 36 | methodMap.put(classMember, newName); 37 | } 38 | 39 | public void applyMappings(JarLoader jarLoader) { 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/utils/ASMUtils.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.utils; 2 | 3 | import me.exeos.asmplus.JarLoader; 4 | import me.exeos.asmplus.codegen.code.Jump; 5 | import me.exeos.asmplus.descarg.DescArg; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.tree.*; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | public class ASMUtils implements Opcodes { 15 | 16 | private static JarLoader currentJar = null; 17 | 18 | public static void setJar(JarLoader jar) { 19 | currentJar = jar; 20 | } 21 | 22 | /* ___ START: Actions ___*/ 23 | 24 | public static void removeDebugInfos(ClassNode classNode) { 25 | classNode.sourceFile = null; 26 | classNode.sourceDebug = null; 27 | } 28 | 29 | public static void removeDebugInfos(MethodNode methodNode) { 30 | methodNode.localVariables = null; 31 | methodNode.parameters = null; 32 | } 33 | 34 | public static void addInstructions(AbstractInsnNode[] add, MethodNode from) { 35 | removeInstructions(Arrays.asList(add), from); 36 | } 37 | 38 | public static void addInstructions(List add, MethodNode from) { 39 | for (AbstractInsnNode toAdd : add) { 40 | from.instructions.add(toAdd); 41 | } 42 | } 43 | 44 | public static void removeInstructions(AbstractInsnNode[] remove, MethodNode from) { 45 | removeInstructions(Arrays.asList(remove), from); 46 | } 47 | 48 | /** 49 | * This may look stupid but is necessary to safely remove instructions 50 | */ 51 | public static void removeInstructions(List remove, MethodNode from) { 52 | for (AbstractInsnNode toRemove : remove) { 53 | if (isPresent(toRemove, from)) 54 | from.instructions.remove(toRemove); 55 | } 56 | } 57 | 58 | public static void setOpcode(AbstractInsnNode insnNode, int opcode) throws IllegalAccessException, NoSuchFieldException { 59 | Field ocField = AbstractInsnNode.class.getDeclaredField("opcode"); 60 | ocField.setAccessible(true); 61 | ocField.set(insnNode, opcode); 62 | } 63 | /* ___ END: Actions ___ */ 64 | 65 | /* ___ START: get x by / based on y ___ */ 66 | 67 | public static ClassNode getClass(String name) { 68 | if (currentJar == null) 69 | throw new IllegalStateException("Current jar can't be null"); 70 | 71 | return getClass(currentJar, name); 72 | } 73 | 74 | public static ClassNode getClass(JarLoader jar, String name) { 75 | return jar.classes.get(name); 76 | } 77 | 78 | public static FieldNode getField(FieldInsnNode fieldInsn) { 79 | return getField(fieldInsn.owner, fieldInsn.name, fieldInsn.desc); 80 | } 81 | 82 | public static FieldNode getField(String owner, String name, String desc) { 83 | return getField(owner, name, desc, null); 84 | } 85 | 86 | public static FieldNode getField(String owner, String name, String desc, String signature) { 87 | if (currentJar == null) 88 | throw new IllegalStateException("Current jar can't be null"); 89 | 90 | return getField(currentJar, owner, name, desc, signature); 91 | } 92 | 93 | public static FieldNode getField(JarLoader jar, String owner, String name, String desc) { 94 | return getField(jar, owner, name, desc, null); 95 | } 96 | 97 | public static FieldNode getField(JarLoader jar, String owner, String name, String desc, String signature) { 98 | ClassNode classNode = getClass(owner); 99 | if (classNode == null) 100 | throw new IllegalStateException("Owner of field can't be null"); 101 | else 102 | return getField(classNode, name, desc, signature); 103 | } 104 | 105 | public static FieldNode getField(ClassNode classNode, String name, String desc, String signature) { 106 | for (FieldNode fieldNode : classNode.fields) { 107 | if (fieldNode.name.equals(name) && fieldNode.desc.equals(desc) && (signature == null || fieldNode.signature.equals(signature))) 108 | return fieldNode; 109 | } 110 | 111 | return null; 112 | } 113 | 114 | public static MethodNode getMethod(ClassNode owner, String name, String desc) { 115 | return getMethod(owner, null, name, desc, null); 116 | } 117 | 118 | public static MethodNode getMethod(ClassNode owner, Integer access, String name, String desc) { 119 | return getMethod(owner, access, name, desc, null); 120 | } 121 | 122 | public static MethodNode getMethod(ClassNode owner, String name, String desc, String signature) { 123 | return getMethod(owner, null, name, desc, signature); 124 | } 125 | 126 | public static MethodNode getMethod(ClassNode owner, Integer access, String name, String desc, String signature) { 127 | for (MethodNode methodNode : owner.methods) { 128 | if (methodNode.name.equals(name) && methodNode.desc.equals(desc) && (access == null || access == methodNode.access) && 129 | (signature == null || signature.equals(methodNode.signature))) 130 | return methodNode; 131 | } 132 | 133 | return null; 134 | } 135 | 136 | /** 137 | * @return array of args from method. coming from " (BIZLme/exeos/Main;S)V " this may look like: 138 | * B | false 139 | * I | false 140 | * Z | false 141 | * me/exeos/Main | true 142 | * S | false 143 | */ 144 | public static DescArg[] getMethodArgs(MethodNode methodNode) { 145 | return getMethodArgs(methodNode.desc); 146 | } 147 | 148 | public static DescArg[] getMethodArgs(String methodDesc) { 149 | ArrayList args = new ArrayList<>(); 150 | 151 | boolean phrasing = false; 152 | boolean phrasingArg = false; 153 | StringBuilder argBuilder = new StringBuilder(); 154 | 155 | for (char c : methodDesc.toCharArray()) { 156 | if (c == '(') { 157 | phrasing = true; 158 | continue; 159 | } 160 | if (c == ')') { 161 | break; 162 | } 163 | if (!phrasing) { 164 | continue; 165 | } 166 | 167 | if (c == 'L') { 168 | phrasingArg = true; 169 | continue; 170 | } 171 | if (c == ';') { 172 | args.add(new DescArg(argBuilder.toString(), true)); 173 | argBuilder = new StringBuilder(); 174 | phrasingArg = false; 175 | continue; 176 | } 177 | if (phrasingArg) { 178 | argBuilder.append(c); 179 | } else 180 | args.add(new DescArg(String.valueOf(c), false)); 181 | } 182 | 183 | return args.toArray(args.toArray(new DescArg[0])); 184 | } 185 | 186 | /** 187 | * This is here because InsnList is stupid and buggy 188 | * @return converted to List 189 | */ 190 | 191 | public static List convertToJList(InsnList insnList) { 192 | List converted = new ArrayList<>(); 193 | for (AbstractInsnNode insnNode : insnList) { 194 | converted.add(insnNode); 195 | } 196 | 197 | return converted; 198 | } 199 | 200 | /** 201 | * This is here because InsnList is stupid and buggy 202 | * @return converted to InsnList 203 | */ 204 | 205 | public static InsnList convertToIList(List list) { 206 | InsnList converted = new InsnList(); 207 | for (AbstractInsnNode insnNode : list) { 208 | converted.add(insnNode); 209 | } 210 | 211 | return converted; 212 | } 213 | 214 | /** 215 | * Get x next insn after @current insn 216 | * 217 | * @param current InsnNode to start stepping from. 218 | * @param steps Steps to take from start 219 | * @return x next insn after current 220 | */ 221 | public static AbstractInsnNode getNext(AbstractInsnNode current, int steps) { 222 | for (int i = 0; i < steps; i++) { 223 | if (current.getNext() == null) 224 | return null; 225 | 226 | current = current.getNext(); 227 | } 228 | return current; 229 | } 230 | 231 | /** 232 | * Get x prev insn after @current insn 233 | * 234 | * @param current InsnNode to start stepping from. 235 | * @param steps Steps to revert from start 236 | * @return x prev insn after current or null if not able to reach 237 | */ 238 | public static AbstractInsnNode getPrev(AbstractInsnNode current, int steps) { 239 | for (int i = 0; i < steps; i++) { 240 | if (current.getPrevious() == null) 241 | return null; 242 | 243 | current = current.getPrevious(); 244 | } 245 | return current; 246 | } 247 | 248 | /** 249 | * @return Random label from method 250 | */ 251 | public static LabelNode getRandomLabel(MethodNode from) { 252 | List labels = getMethodLabels(from); 253 | 254 | if (labels.isEmpty()) 255 | return null; 256 | else 257 | return labels.get(RandomUtil.getInt(0, labels.size() - 1)); 258 | } 259 | 260 | public static List getMethodLabels(MethodNode from) { 261 | List labels = new ArrayList<>(); 262 | for (AbstractInsnNode insnNode : from.instructions) { 263 | if (insnNode instanceof LabelNode) { 264 | labels.add((LabelNode) insnNode); 265 | } 266 | } 267 | 268 | return labels; 269 | } 270 | 271 | public static AbstractInsnNode getMethodEnd(MethodNode from) { 272 | if (from.instructions.size() == 0) 273 | return null; 274 | 275 | AbstractInsnNode end = from.instructions.get(from.instructions.size() - 1); 276 | if (end != null) { 277 | while (end.getOpcode() < IRETURN || end.getOpcode() > RETURN) { 278 | end = end.getPrevious(); 279 | 280 | if (end == null) 281 | break; 282 | } 283 | } 284 | 285 | return end; 286 | } 287 | 288 | /* ___ START: value by insn ___ */ 289 | 290 | public static Object getValue(AbstractInsnNode insnNode) { 291 | if (isValuePush(insnNode)) { 292 | if (isIntPush(insnNode)) 293 | return getIntValue(insnNode); 294 | if (isBytePush(insnNode)) 295 | return getByteValue(insnNode); 296 | if (isShortPush(insnNode)) 297 | return getShortValue(insnNode); 298 | if (isLongPush(insnNode)) 299 | return getLongValue(insnNode); 300 | if (isFloatPush(insnNode)) 301 | return getFloatValue(insnNode); 302 | if (isDoublePush(insnNode)) 303 | return getDoubleValue(insnNode); 304 | if (isString(insnNode)) 305 | return getStringValue(insnNode); 306 | } 307 | throw new IllegalStateException("Instruction does not push a value"); 308 | } 309 | 310 | public static int getIntValue(AbstractInsnNode insnNode) { 311 | if (isIConstPush(insnNode.getOpcode())) 312 | return insnNode.getOpcode() - 3; 313 | 314 | switch (insnNode.getOpcode()) { 315 | case BIPUSH: 316 | case SIPUSH: 317 | return ((IntInsnNode) insnNode).operand; 318 | case LDC: 319 | if (insnNode instanceof LdcInsnNode) { 320 | LdcInsnNode ldcInsn = (LdcInsnNode) insnNode; 321 | if (ldcInsn.cst instanceof Integer) { 322 | return (int)ldcInsn.cst; 323 | } 324 | } 325 | throw new IllegalStateException("Instruction doesn't represent int"); 326 | default: 327 | throw new IllegalStateException("Instruction doesn't represent int"); 328 | } 329 | } 330 | 331 | public static byte getByteValue(AbstractInsnNode insnNode) { 332 | if (isIConstPush(insnNode.getOpcode())) 333 | return (byte) (insnNode.getOpcode() - 3); 334 | 335 | if (insnNode.getOpcode() == BIPUSH) 336 | return (byte) ((IntInsnNode) insnNode).operand; 337 | 338 | throw new IllegalStateException("Instruction doesn't represent byte"); 339 | } 340 | 341 | public static short getShortValue(AbstractInsnNode insnNode) { 342 | if (isIConstPush(insnNode.getOpcode())) 343 | return (short) (insnNode.getOpcode() - 3); 344 | 345 | if (insnNode.getOpcode() == SIPUSH) 346 | return (short) ((IntInsnNode) insnNode).operand; 347 | 348 | throw new IllegalStateException("Instruction doesn't represent short"); 349 | } 350 | 351 | public static long getLongValue(AbstractInsnNode insnNode) { 352 | if (isLConstPush(insnNode.getOpcode())) 353 | return insnNode.getOpcode() - 9; 354 | 355 | if (insnNode instanceof LdcInsnNode) { 356 | LdcInsnNode ldcInsn = (LdcInsnNode) insnNode; 357 | if (ldcInsn.cst instanceof Long) { 358 | return (long) ldcInsn.cst; 359 | } 360 | } 361 | 362 | throw new IllegalStateException("Instruction doesn't represent long"); 363 | } 364 | 365 | public static double getDoubleValue(AbstractInsnNode insnNode) { 366 | if (isDConstPush(insnNode.getOpcode())) 367 | return insnNode.getOpcode() - 14; 368 | 369 | if (insnNode instanceof LdcInsnNode) { 370 | LdcInsnNode ldcInsn = (LdcInsnNode) insnNode; 371 | if (ldcInsn.cst instanceof Double) { 372 | return (double) ldcInsn.cst; 373 | } 374 | } 375 | 376 | throw new IllegalStateException("Instruction doesn't represent long"); 377 | } 378 | 379 | public static float getFloatValue(AbstractInsnNode insnNode) { 380 | if (isFConstPush(insnNode.getOpcode())) 381 | return insnNode.getOpcode() - 11; 382 | 383 | if (insnNode instanceof LdcInsnNode) { 384 | LdcInsnNode ldcInsn = (LdcInsnNode) insnNode; 385 | if (ldcInsn.cst instanceof Float) { 386 | return (float) ldcInsn.cst; 387 | } 388 | } 389 | 390 | throw new IllegalStateException("Instruction doesn't represent long"); 391 | } 392 | 393 | public static String getStringValue(AbstractInsnNode insnNode) { 394 | if (insnNode instanceof LdcInsnNode) { 395 | LdcInsnNode ldcInsn = (LdcInsnNode) insnNode; 396 | if (ldcInsn.cst instanceof String) { 397 | return (String) ldcInsn.cst; 398 | } 399 | } 400 | 401 | throw new IllegalStateException("Instruction doesn't represent long"); 402 | } 403 | /* ___ END: value by insn ___*/ 404 | 405 | /* ___ START: value pushes___ */ 406 | 407 | public static AbstractInsnNode getValuePush(Object value) { 408 | switch (value.getClass().getSimpleName()) { 409 | case "Byte": 410 | return getBytePush((byte) value); 411 | case "Integer": 412 | return getIntPush((int) value); 413 | case "Long": 414 | return getLongPush((long) value); 415 | case "Short": 416 | return getShortPush((short) value); 417 | case "Float": 418 | return getFloatPush((float) value); 419 | case "Double": 420 | return getDoublePush((double) value); 421 | case "String": 422 | return new LdcInsnNode(value); 423 | default: 424 | throw new IllegalStateException("Unexpected value type: " + value.getClass().getSimpleName()); 425 | } 426 | } 427 | 428 | /** 429 | * @param value Int to be pushed. MUST BE IN BOUND OF: -1 TO +5 ! 430 | * @return Insn pushing the int 431 | */ 432 | public static AbstractInsnNode getIConstPush(int value) { 433 | if (value < -1 || value > 5) 434 | throw new IllegalStateException("Value: " + value + " isn't in required bound: -1 to +5"); 435 | 436 | return new InsnNode(ICONST_0 + value); 437 | } 438 | 439 | /** 440 | * @param value Int to be pushed. MUST BE EITHER 0 or 1! 441 | * @return Insn pushing the int 442 | */ 443 | public static AbstractInsnNode getLConstPush(long value) { 444 | if (value != 0 && value != 1) 445 | throw new IllegalStateException("Invalid value: " + value + ". Value must be 0 or 1"); 446 | 447 | return new InsnNode(LCONST_0 + (int) value); 448 | } 449 | 450 | /** 451 | * @param value Int to be pushed. MUST BE IN BOUND OF: 0 TO 2 ! 452 | * @return Insn pushing the int 453 | */ 454 | public static AbstractInsnNode getFConstPush(float value) { 455 | if (value < 0 || value > 2) 456 | throw new IllegalStateException("Invalid value: " + value + ". Value must be 0 or 1"); 457 | 458 | return new InsnNode(FCONST_0 + (int) value); 459 | } 460 | /** 461 | * @param value Int to be pushed. MUST BE EITHER 0 or 1! 462 | * @return Insn pushing the int 463 | */ 464 | public static AbstractInsnNode getDConstPush(double value) { 465 | if (value != 0 && value != 1) 466 | throw new IllegalStateException("Invalid value: " + value + ". Value must be 0 or 1"); 467 | 468 | return new InsnNode(DCONST_0 + (int) value); 469 | } 470 | 471 | public static AbstractInsnNode getBytePush(byte value) { 472 | if (isIConstPush(ICONST_0 + value)) 473 | return getIConstPush(value); 474 | 475 | return new IntInsnNode(BIPUSH, value); 476 | } 477 | 478 | public static AbstractInsnNode getIntPush(int value) { 479 | if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) 480 | return getShortPush((short) value); 481 | 482 | /* value is exceeds bound of byte and short */ 483 | return new LdcInsnNode(value); 484 | } 485 | 486 | public static AbstractInsnNode getShortPush(short value) { 487 | if (isIConstPush(ICONST_0 + value)) 488 | return getIConstPush(value); 489 | 490 | if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) 491 | return new IntInsnNode(BIPUSH, value); 492 | 493 | return new IntInsnNode(SIPUSH, value); 494 | } 495 | 496 | public static AbstractInsnNode getLongPush(long value) { 497 | if (value == 0 || value == 1) 498 | return getLConstPush(value); 499 | 500 | return new LdcInsnNode(value); 501 | } 502 | 503 | public static AbstractInsnNode getFloatPush(float value) { 504 | if (value >= 0 && value <= 2) 505 | return getFConstPush(value); 506 | 507 | return new LdcInsnNode(value); 508 | } 509 | 510 | public static AbstractInsnNode getDoublePush(double value) { 511 | if (value == 0 || value == 2) 512 | return getDConstPush(value); 513 | 514 | return new LdcInsnNode(value); 515 | } 516 | /* ___ END: value pushes ___ */ 517 | 518 | 519 | /* ___ START: jumps ___ */ 520 | 521 | /** 522 | * @param to Label to jump to 523 | * @return List for a random jump 524 | */ 525 | public static List getJumpInsns(LabelNode to) { 526 | return getJumpInsns(RandomUtil.getInt(IFEQ, IF_ICMPLT), to); 527 | } 528 | 529 | /** 530 | * @param jumpOpcode Jump insn to use 531 | * @param to Label to jump to 532 | * @return List for jump 533 | */ 534 | public static List getJumpInsns(int jumpOpcode, LabelNode to) { 535 | return getJump(jumpOpcode, to).getJump(); 536 | } 537 | 538 | /** 539 | * @param to Label to jump to 540 | * @return List for a random jump 541 | */ 542 | public static Jump getJump(LabelNode to) { 543 | return getJump(RandomUtil.getInt(IFEQ, IF_ICMPLT), to); 544 | } 545 | 546 | /** 547 | * @param jumpOpcode Jump insn to use 548 | * @param to Label to jump to 549 | * @return List for jump 550 | */ 551 | public static Jump getJump(int jumpOpcode, LabelNode to) { 552 | Jump jump = new Jump(); 553 | switch (jumpOpcode) { 554 | /* val == 0 */ 555 | case IFEQ: 556 | jump.add(new InsnNode(ICONST_0)); 557 | break; 558 | /* val != 0 */ 559 | case IFNE: 560 | jump.add(new InsnNode(ICONST_1)); 561 | break; 562 | /* val < 0 */ 563 | case IFLT: 564 | jump.add(getIntPush(RandomUtil.getInt(Integer.MIN_VALUE, -1))); 565 | break; 566 | /* val >= 0 */ 567 | case IFGE: 568 | jump.add(getIntPush(RandomUtil.getInt(0, Integer.MAX_VALUE))); 569 | break; 570 | /* val > 0 */ 571 | case IFGT: 572 | jump.add(getIntPush(RandomUtil.getInt(1, Integer.MAX_VALUE))); 573 | break; 574 | /* val <= 0 */ 575 | case IFLE: 576 | jump.add(getIntPush(RandomUtil.getInt(Integer.MIN_VALUE, 0))); 577 | break; 578 | /* int0 == int1 */ 579 | case IF_ICMPEQ: 580 | jump.add(getIntPush(RandomUtil.getInt(Integer.MIN_VALUE, Integer.MAX_VALUE))); 581 | jump.add(new InsnNode(DUP)); 582 | break; 583 | /* int0 != int1 */ 584 | case IF_ICMPNE: 585 | jump.add(getIntPush(RandomUtil.getInt(Integer.MIN_VALUE, 0))); 586 | jump.add(getIntPush(RandomUtil.getInt(1, Integer.MAX_VALUE))); 587 | break; 588 | /* int0 < int 1*/ 589 | case IF_ICMPLT: 590 | { 591 | int less = RandomUtil.getInt(Integer.MIN_VALUE, Integer.MAX_VALUE - 10); 592 | 593 | jump.add(getIntPush(RandomUtil.getInt(less + 1, Integer.MAX_VALUE))); 594 | jump.add(getIntPush(less)); 595 | } 596 | break; 597 | /* int0 >= int1 */ 598 | case IF_ICMPGE: // bis hier 599 | { 600 | int more = RandomUtil.getInt(0, Integer.MAX_VALUE); 601 | 602 | jump.add(getIntPush(more)); 603 | jump.add(getIntPush(RandomUtil.getInt(Integer.MIN_VALUE, more))); 604 | } 605 | break; 606 | /* int0 > int1 */ 607 | case IF_ICMPGT: 608 | { 609 | int more = RandomUtil.getInt(Integer.MIN_VALUE + 10, Integer.MAX_VALUE); 610 | 611 | jump.add(getIntPush(RandomUtil.getInt(Integer.MIN_VALUE, more - 1))); 612 | jump.add(getIntPush(more)); 613 | } 614 | break; 615 | /* int0 <= int1 */ 616 | case IF_ICMPLE: 617 | { 618 | int less = RandomUtil.getInt(Integer.MIN_VALUE, Integer.MAX_VALUE - 1); 619 | 620 | jump.add(getIntPush(less)); 621 | jump.add(getIntPush(RandomUtil.getInt(less, Integer.MAX_VALUE))); 622 | } 623 | break; 624 | /* object0 == object1 */ 625 | case IF_ACMPEQ: 626 | jump.add(new TypeInsnNode(NEW, "java/lang/String")); 627 | jump.add(new InsnNode(DUP)); 628 | break; 629 | /* object0 != object1 */ 630 | case IF_ACMPNE: 631 | jump.add(new TypeInsnNode(NEW, "java/lang/String")); 632 | jump.add(new TypeInsnNode(NEW, "java/lang/Integer")); 633 | break; 634 | /* direct jump to label */ 635 | case GOTO: 636 | /* Goto is the default opcode of jump */ 637 | break; 638 | default: 639 | System.out.println("This branch should never be reached. Opcode: " + jumpOpcode); 640 | } 641 | 642 | return jump.setOpcode(jumpOpcode).setLabel(to); 643 | } 644 | 645 | /* ___ END: jumps ___ */ 646 | 647 | /** 648 | * @param debugMessage String in LDC 649 | * @return LDC Insn with debugMessage,which gets poped right after 650 | */ 651 | 652 | public static List getDebugInsn(String debugMessage) { 653 | ArrayList insnNodes = new ArrayList<>(); 654 | insnNodes.add(new LdcInsnNode(debugMessage)); 655 | insnNodes.add(new InsnNode(POP)); 656 | 657 | return insnNodes; 658 | } 659 | 660 | public static List getCheckCastMessage(String message) { 661 | return getCheckCastMessage(message, true); 662 | } 663 | 664 | public static List getCheckCastMessage(String message, boolean pop) { 665 | ArrayList insns = new ArrayList<>(); 666 | 667 | insns.add(new InsnNode(ACONST_NULL)); 668 | insns.add(new TypeInsnNode(CHECKCAST, "L" + message.replace(" ", "") + ";")); 669 | if (pop) 670 | insns.add(new InsnNode(POP)); 671 | 672 | return insns; 673 | } 674 | 675 | public static int getOppositeJumpCode(int opcode) { 676 | if (opcode < IFEQ || opcode > IF_ACMPNE) { 677 | throw new IllegalStateException("Opcode must be between IFEQ and IF_ACMPNE"); 678 | } 679 | 680 | int inc = 1; 681 | if (opcode % 2 == 0) { 682 | inc = -1; 683 | } 684 | 685 | return opcode + inc; 686 | } 687 | 688 | /* ___ END: get x by / based on y ___ */ 689 | 690 | /** 691 | * @return New clean method returning the same type as arg methodNode 692 | */ 693 | public static MethodNode getCleanMethod(MethodNode methodNode) { 694 | AbstractInsnNode end = getMethodEnd(methodNode); 695 | if (end == null) 696 | throw new IllegalArgumentException("Invalid method: " + methodNode.name); 697 | 698 | methodNode.instructions.clear(); 699 | methodNode.tryCatchBlocks.clear(); 700 | methodNode.localVariables.clear(); 701 | methodNode.maxLocals = 0; 702 | methodNode.maxStack = 1; 703 | 704 | List clean = new ArrayList<>(); 705 | 706 | if (end.getOpcode() != RETURN) { 707 | switch (end.getOpcode()) { 708 | case IRETURN: 709 | clean.add(getIConstPush(0)); 710 | break; 711 | case LRETURN: 712 | clean.add(getLConstPush(0)); 713 | break; 714 | case FRETURN: 715 | clean.add(getFConstPush(0)); 716 | break; 717 | case DRETURN: 718 | clean.add(getDConstPush(0)); 719 | break; 720 | case ARETURN: 721 | clean.add(new InsnNode(ACONST_NULL)); 722 | break; 723 | default: throw new IllegalArgumentException("Invalid return type: " + end.getOpcode()); 724 | } 725 | } 726 | clean.add(end); 727 | addInstructions(clean, methodNode); 728 | 729 | return methodNode; 730 | } 731 | 732 | /** 733 | * Returns if insn is present in method, this can be used to avoid concurrent-modification exceptions or nullpointer exceptions when removing insns 734 | * @param insnNode insn to check for 735 | * @param in method that should contai insn 736 | * @return present or nut 737 | */ 738 | public static boolean isPresent(AbstractInsnNode insnNode, MethodNode in) { 739 | return in.instructions.indexOf(insnNode) >= 0; 740 | } 741 | 742 | /* ___ START: checks & conditions ___ */ 743 | 744 | public static boolean isValuePush(AbstractInsnNode insnNode) { 745 | return insnNode instanceof LdcInsnNode || insnNode instanceof TypeInsnNode || isNumberPush(insnNode); 746 | } 747 | 748 | /* ___STAR: num check start___*/ 749 | public static boolean isNumberPush(AbstractInsnNode insnNode) { 750 | return isNumConstPush(insnNode.getOpcode()) || 751 | isDoublePush(insnNode) || 752 | isBytePush(insnNode) || 753 | isFloatPush(insnNode) || 754 | isIntPush(insnNode) || 755 | isLongPush(insnNode) || 756 | isShortPush(insnNode); 757 | } 758 | 759 | /** 760 | * @param opcode Insn's opcode 761 | * @return if insn pushes a CONST and if that CONST is a number 762 | */ 763 | public static boolean isNumConstPush(int opcode) { 764 | return isDConstPush(opcode) || 765 | isFConstPush(opcode) || 766 | isIConstPush(opcode) || 767 | isLConstPush(opcode); 768 | } 769 | 770 | public static boolean isDConstPush(int opcode) { 771 | return opcode == DCONST_0 || opcode == DCONST_1; 772 | } 773 | 774 | public static boolean isFConstPush(int opcode) { 775 | return opcode >= FCONST_0 && opcode <= FCONST_2; 776 | } 777 | 778 | public static boolean isIConstPush(int opcode) { 779 | return opcode >= ICONST_M1 && opcode <= ICONST_5; 780 | } 781 | 782 | public static boolean isLConstPush(int opcode) { 783 | return opcode == LCONST_0 || opcode == LCONST_1; 784 | } 785 | 786 | public static boolean isBytePush(AbstractInsnNode insnNode) { 787 | return isIConstPush(insnNode.getOpcode()) || insnNode.getOpcode() == BIPUSH; 788 | } 789 | 790 | public static boolean isDoublePush(AbstractInsnNode insnNode) { 791 | return isDConstPush(insnNode.getOpcode()) || (insnNode instanceof LdcInsnNode && ((LdcInsnNode) insnNode).cst instanceof Double); 792 | } 793 | 794 | public static boolean isFloatPush(AbstractInsnNode insnNode) { 795 | return isFConstPush(insnNode.getOpcode()) || 796 | (insnNode instanceof LdcInsnNode && ((LdcInsnNode) insnNode).cst instanceof Float); 797 | } 798 | 799 | public static boolean isIntPush(AbstractInsnNode insnNode) { 800 | return isIConstPush(insnNode.getOpcode()) || insnNode instanceof IntInsnNode || 801 | (insnNode instanceof LdcInsnNode && ((LdcInsnNode) insnNode).cst instanceof Integer); 802 | } 803 | 804 | public static boolean isLongPush(AbstractInsnNode insnNode) { 805 | return isLConstPush(insnNode.getOpcode()) || 806 | (insnNode instanceof LdcInsnNode && ((LdcInsnNode) insnNode).cst instanceof Long); 807 | } 808 | 809 | public static boolean isShortPush(AbstractInsnNode insnNode) { 810 | return isIConstPush(insnNode.getOpcode()) || 811 | (insnNode instanceof IntInsnNode && ((IntInsnNode) insnNode).operand >= Short.MIN_VALUE && ((IntInsnNode) insnNode).operand <= Short.MAX_VALUE); 812 | } 813 | /* ___ END: num check ___*/ 814 | 815 | public static boolean isString(AbstractInsnNode insnNode) { 816 | return insnNode instanceof LdcInsnNode && ((LdcInsnNode) insnNode).cst instanceof String; 817 | } 818 | 819 | public static boolean isJumpOrCondition(AbstractInsnNode insnNode) { 820 | return insnNode.getOpcode() >= IFEQ && insnNode.getOpcode() <= GOTO; 821 | } 822 | 823 | /* ___ END: checks & conditions ___ */ 824 | } 825 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/utils/RandomUtil.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.utils; 2 | 3 | import java.security.SecureRandom; 4 | 5 | public class RandomUtil { 6 | 7 | 8 | public static int getInt() { 9 | return getInt(Integer.MIN_VALUE, Integer.MAX_VALUE); 10 | } 11 | 12 | public static int getInt(int min, int max) { 13 | if (min == max) 14 | return min; 15 | 16 | if (min > max) { 17 | throw new IllegalArgumentException("Max must be greater than min"); 18 | } 19 | 20 | return (int) ((Math.random() * (max - min)) + min); 21 | } 22 | 23 | public static boolean chance(int percentage) { 24 | if (percentage < 0 || percentage > 100) { 25 | throw new IllegalArgumentException("Percentage must be between 0 - 100"); 26 | } 27 | return percentage <= getInt(0, 100); 28 | } 29 | 30 | public static byte[] getBytes(int length) { 31 | byte[] randomBytes = new byte[length]; 32 | SecureRandom secureRandom = new SecureRandom(); 33 | secureRandom.nextBytes(randomBytes); 34 | return randomBytes; 35 | } 36 | 37 | public static String getString(int length) { 38 | StringBuilder builder = new StringBuilder(); 39 | for (int i = 0; i < length; i++) { 40 | builder.append((char) (getInt(Character.MIN_VALUE, Character.MAX_VALUE))); 41 | } 42 | return builder.toString(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /asmplus/src/main/java/me/exeos/asmplus/utils/StreamUtils.java: -------------------------------------------------------------------------------- 1 | package me.exeos.asmplus.utils; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | public class StreamUtils { 8 | 9 | public static byte[] readAllBytes(InputStream inputStream) throws IOException { 10 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 11 | byte[] buffer = new byte[1024]; 12 | 13 | int bytesRead; 14 | while ((bytesRead = inputStream.read(buffer)) != -1) { 15 | outputStream.write(buffer, 0, bytesRead); 16 | } 17 | 18 | return outputStream.toByteArray(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | # This file was generated by the Gradle 'init' task. 2 | # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format 3 | 4 | [versions] 5 | commons-math3 = "3.6.1" 6 | guava = "32.1.3-jre" 7 | junit = "4.13.2" 8 | 9 | [libraries] 10 | commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } 11 | guava = { module = "com.google.guava:guava", version.ref = "guava" } 12 | junit = { module = "junit:junit", version.ref = "junit" } 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.7/userguide/multi_project_builds.html in the Gradle documentation. 6 | */ 7 | 8 | plugins { 9 | // Apply the foojay-resolver plugin to allow automatic download of JDKs 10 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' 11 | } 12 | 13 | rootProject.name = 'ASMPlus' 14 | include('asmplus') 15 | --------------------------------------------------------------------------------