├── .gitignore ├── META-INF └── MANIFEST.MF ├── README.MD ├── asm.jar ├── config.yaml ├── input.jar ├── native.dll ├── native ├── Native.sln ├── Native │ ├── Native.vcxproj │ ├── Native.vcxproj.filters │ ├── Native.vcxproj.user │ ├── jvm.cpp │ ├── jvm.hpp │ ├── third_party │ │ ├── detour │ │ │ ├── detours.lib │ │ │ └── include │ │ │ │ ├── detours.h │ │ │ │ ├── detver.h │ │ │ │ └── syelog.h │ │ └── java │ │ │ ├── include │ │ │ ├── jni.h │ │ │ ├── jni_md.h │ │ │ └── jvmti.h │ │ │ └── jvm.lib │ ├── types.cpp │ └── types.hpp └── native │ └── dllmain.cpp ├── pom.xml └── src ├── main └── java │ ├── com │ └── cheatbreaker │ │ └── obf │ │ ├── Main.java │ │ ├── Obf.java │ │ ├── addons │ │ ├── IObfuscator.java │ │ └── skidfuscator │ │ │ └── SkidfuscatorAddon.java │ │ ├── transformer │ │ ├── Transformer.java │ │ ├── flow │ │ │ └── ExceptionTransformer.java │ │ ├── general │ │ │ └── StripTransformer.java │ │ ├── methods │ │ │ └── DynamicTransformer.java │ │ ├── misc │ │ │ ├── ChecksumTransformer.java │ │ │ ├── InlinerTransformer.java │ │ │ ├── PackerTransformer.java │ │ │ └── VariableTransformer.java │ │ ├── natives │ │ │ ├── CodeHiderTransformer.java │ │ │ └── ConstantPoolTransformer.java │ │ └── strings │ │ │ └── ToStringTransformer.java │ │ └── utils │ │ ├── asm │ │ ├── AsmUtils.java │ │ ├── ClassWrapper.java │ │ ├── ContextClassWriter.java │ │ └── NodeAccess.java │ │ ├── configuration │ │ ├── Configuration.java │ │ ├── ConfigurationOptions.java │ │ ├── ConfigurationSection.java │ │ ├── InvalidConfigurationException.java │ │ ├── MemoryConfiguration.java │ │ ├── MemoryConfigurationOptions.java │ │ ├── MemorySection.java │ │ ├── NumberConversions.java │ │ ├── file │ │ │ ├── FileConfiguration.java │ │ │ ├── FileConfigurationOptions.java │ │ │ ├── YamlConfiguration.java │ │ │ ├── YamlConfigurationOptions.java │ │ │ ├── YamlConstructor.java │ │ │ └── YamlRepresenter.java │ │ └── serialization │ │ │ ├── ConfigurationSerializable.java │ │ │ ├── ConfigurationSerialization.java │ │ │ ├── DelegateDeserialization.java │ │ │ └── SerializableAs.java │ │ ├── loader │ │ └── LoaderUtil.java │ │ ├── pair │ │ └── ClassMethodNode.java │ │ ├── samples │ │ └── DynamicInvoke.java │ │ └── tree │ │ ├── ClassTree.java │ │ └── HierarchyUtils.java │ └── vm │ └── NativeHandler.java └── test └── java └── com └── cheatbreaker └── obf └── tests └── DynamicInvokeTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | MC.jar 3 | MC.obf.jar 4 | *.iml 5 | target/ 6 | .vs/ 7 | x64/ -------------------------------------------------------------------------------- /META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.cheatbreaker.obf.Main 3 | 4 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Some Java Obfuscator 2 | 3 | A modified version of cheatbreaker-obf to test out some ideas I had 4 | 5 | ## Features 6 | 7 | Native obfuscators shouldn't be used for production 8 | 9 | * StripTransformer 10 | - `Strips debug information from classes, methods and fields` 11 | * ChecksumTransformer 12 | - `Inserts block of code inside configurable class which will check if the target method is modified, uses modified asm to check constant pool and method bytecode` 13 | * InlinerTransformer (Disabled) 14 | - `Tries to inline methods, does need massive improvements` 15 | * VariableTransformer 16 | - `Just converted local variables to java.lang.Object[]` 17 | * ConstantPoolTransformer (Unstable) 18 | - `Uses native method to modify constant pool and applies some simple number obfuscation` 19 | - `Uses basic xor encryption which shows the key it would be best to change it` 20 | * CodeHiderTransformer (Unstable) 21 | - `Uses natives to hide method bytecode from the code attribute` 22 | * PackerTransformer 23 | - `Packs classes into a single file which are loaded afterwards` 24 | * ToStringTransformer 25 | - `Saw something cool in a source obfuscator and decided to implement it, converts strins to new Object() { toString... return "String" }.toString()` 26 | * ExceptionTransformer 27 | - `Turns local storing to storing of exception objects` 28 | 29 | ## Disclaimers 30 | 31 | * CodeHider 32 | * won't go over exceptions 33 | * skips methodhandle and class ldc 34 | * skips synthetic methods 35 | * breaks on classes with generated methods 36 | * can break randomly due to constant pool not being updated when generating bytes 37 | 38 | ## Usage 39 | 40 | You use the obfuscator by creating a config file much like the one provided and parsing it through command line 41 | > java -jar obfuscator.jar --config config.yml 42 | 43 | ## Screenshots 44 | 45 | ### String Obfuscation 46 | 47 | String Obfuscation 48 | 49 | ### Variable Obfuscation 50 | 51 | Variable Obfuscation 52 | 53 | ### Inliner Obfuscation 54 | 55 | Inliner Obfuscation 56 | 57 | ### Checksum Obfuscation 58 | 59 | Checksum Obfuscation 60 | 61 | ### ConstantPool Obfuscation (Unstable) 62 | 63 | ConstantPool Obfuscation 64 | 65 | ### Packer Obfuscation 66 | 67 | Packer Obfuscation 68 | 69 | ### Code Hider Obfuscation (Unstable) 70 | 71 | CodeHider Obfuscation 72 | 73 | ### Exception Obfuscation 74 | 75 | Exception Obfuscation 76 | 77 | ## Credits 78 | 79 | * cheatbreaker-obf for base 80 | * most of utils methods aren't mine 81 | * yaml classes for config stolen from bukkit 82 | 83 | -------------------------------------------------------------------------------- /asm.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssheera/Some-Java-Obfuscator/8b0c8ff543a29e66e242314ab70a8cc9f3bbc68e/asm.jar -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | input: eval.jar 2 | output: output.jar 3 | libs: [] 4 | 5 | flow: 6 | exceptions: 7 | enabled: false 8 | excluded: [] 9 | included: [] 10 | iterations: 1 11 | general: 12 | strip: 13 | enabled: true 14 | excluded: [] 15 | included: [] 16 | iterations: 1 17 | strings: 18 | tostring: 19 | enabled: false 20 | excluded: [] 21 | included: [] 22 | iterations: 1 23 | methods: 24 | dynamic: 25 | enabled: true 26 | excluded: [] 27 | included: [] 28 | iterations: 1 29 | misc: 30 | vars: 31 | enabled: false 32 | excluded: [] 33 | included: [] 34 | iterations: 1 35 | inliner: 36 | enabled: false 37 | excluded: [] 38 | included: [] 39 | iterations: 1 40 | maxPasses: 1000 41 | remove-unused-methods: true 42 | checksum: 43 | enabled: false 44 | excluded: [] 45 | included: [] 46 | iterations: 1 47 | randomHolders: true 48 | holders: [] 49 | targets: 50 | - dev/sim0n/evaluator/Main 51 | reobf: true 52 | method-name: random 53 | reduced: false 54 | packer: 55 | enabled: false 56 | excluded: [] 57 | included: [] 58 | iterations: 1 59 | resource-name: '/' 60 | class-name: ~/ 61 | class-super-name: random 62 | reobf: true 63 | forge: true 64 | natives: 65 | constantpool: 66 | enabled: false 67 | excluded: [] 68 | included: [] 69 | iterations: 1 70 | codehider: 71 | enabled: false 72 | excluded: [] 73 | included: [] 74 | iterations: 1 -------------------------------------------------------------------------------- /input.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssheera/Some-Java-Obfuscator/8b0c8ff543a29e66e242314ab70a8cc9f3bbc68e/input.jar -------------------------------------------------------------------------------- /native.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssheera/Some-Java-Obfuscator/8b0c8ff543a29e66e242314ab70a8cc9f3bbc68e/native.dll -------------------------------------------------------------------------------- /native/Native.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34316.72 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Native", "Native\Native.vcxproj", "{77BEA700-2ECA-43CE-9185-BD827734BF04}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {77BEA700-2ECA-43CE-9185-BD827734BF04}.Debug|x64.ActiveCfg = Debug|x64 17 | {77BEA700-2ECA-43CE-9185-BD827734BF04}.Debug|x64.Build.0 = Debug|x64 18 | {77BEA700-2ECA-43CE-9185-BD827734BF04}.Debug|x86.ActiveCfg = Debug|Win32 19 | {77BEA700-2ECA-43CE-9185-BD827734BF04}.Debug|x86.Build.0 = Debug|Win32 20 | {77BEA700-2ECA-43CE-9185-BD827734BF04}.Release|x64.ActiveCfg = Release|x64 21 | {77BEA700-2ECA-43CE-9185-BD827734BF04}.Release|x64.Build.0 = Release|x64 22 | {77BEA700-2ECA-43CE-9185-BD827734BF04}.Release|x86.ActiveCfg = Release|Win32 23 | {77BEA700-2ECA-43CE-9185-BD827734BF04}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4C389A6D-BFA1-4EF7-A5E6-B9AA0E343523} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /native/Native/Native.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {77bea700-2eca-43ce-9185-bd827734bf04} 25 | Native 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | $(ProjectDir)third_party\detour\include;$(ProjectDir)third_party\java\include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 75 | $(ProjectDir)third_party\java;$(ProjectDir)third_party\detour;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) 76 | 77 | 78 | $(ProjectDir)third_party\detour\include;$(ProjectDir)third_party\java\include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 79 | $(ProjectDir)third_party\java;$(ProjectDir)third_party\detour;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) 80 | 81 | 82 | 83 | Level3 84 | true 85 | WIN32;_DEBUG;GENERICDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 86 | true 87 | Use 88 | pch.h 89 | 90 | 91 | Windows 92 | true 93 | false 94 | 95 | 96 | 97 | 98 | Level3 99 | true 100 | true 101 | true 102 | WIN32;NDEBUG;GENERICDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 103 | true 104 | Use 105 | pch.h 106 | 107 | 108 | Windows 109 | true 110 | true 111 | true 112 | false 113 | 114 | 115 | 116 | 117 | Level3 118 | true 119 | _DEBUG;GENERICDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 120 | true 121 | NotUsing 122 | pch.h 123 | stdcpp17 124 | 125 | 126 | Windows 127 | true 128 | false 129 | jvm.lib;%(AdditionalDependencies) 130 | 131 | 132 | 133 | 134 | Level3 135 | true 136 | true 137 | true 138 | NDEBUG;GENERICDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 139 | true 140 | NotUsing 141 | pch.h 142 | stdcpp17 143 | 144 | 145 | Windows 146 | true 147 | true 148 | true 149 | false 150 | jvm.lib;%(AdditionalDependencies) 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /native/Native/Native.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 源文件 20 | 21 | 22 | 源文件 23 | 24 | 25 | 源文件 26 | 27 | 28 | 29 | 30 | 头文件 31 | 32 | 33 | 头文件 34 | 35 | 36 | -------------------------------------------------------------------------------- /native/Native/Native.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /native/Native/jvm.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "jvm.hpp" 3 | #include 4 | 5 | 6 | #include 7 | 8 | #include 9 | 10 | /* VMTypes */ 11 | 12 | #define DumpSDK 13 | 14 | VMTypes::struct_entries_t VMTypes::struct_entries; 15 | VMTypes::type_entries_t VMTypes::type_entries; 16 | VMTypes::int_entries_t VMTypes::int_entries; 17 | VMTypes::long_entries_t VMTypes::long_entries; 18 | 19 | void VMTypes::init(VMStructEntry* vmstructs, VMTypeEntry* vmtypes, VMIntConstantEntry* vmints, VMLongConstantEntry* vmlongs) 20 | { 21 | for (int i = 0; vmstructs[i].fieldName != NULL; ++i) { 22 | auto s = &vmstructs[i]; 23 | #ifdef DumpSDK 24 | std::cout << "VMStructEntry: \n" 25 | "type: " << s->typeName << "\n" 26 | "field: " << s->fieldName << "\n" 27 | "static: " << (s->isStatic ? "true" : "false") << "\n"; 28 | if (s->isStatic) std::cout << 29 | "address: " << s->address << "\n"; 30 | std::cout << "offset: " << s->offset << "\n\n"; 31 | 32 | #endif // DumpSDK 33 | VMTypes::struct_entries[s->typeName][s->fieldName] = s; 34 | } 35 | 36 | for (int i = 0; vmtypes[i].typeName != NULL; ++i) { 37 | auto t = &vmtypes[i]; 38 | #ifdef DumpSDK 39 | std::cout << "VMType :" << t->typeName << "\nSize :" << t->size << "\n\n"; 40 | #endif // DumpSDK 41 | VMTypes::type_entries[t->typeName] = t; 42 | } 43 | 44 | 45 | for (int i = 0; vmints[i].name != NULL; ++i) { 46 | auto v = &vmints[i]; 47 | #ifdef DumpSDK 48 | std::cout << "VMInt :" << v->name << "\nValue :" << v->value << "\n\n"; 49 | #endif // DumpSDK 50 | VMTypes::int_entries[v->name] = v; 51 | } 52 | 53 | for (int i = 0; vmlongs[i].name != NULL; ++i) { 54 | auto l = &vmlongs[i]; 55 | #ifdef DumpSDK 56 | std::cout << "VMLong :" << l->name << "\nValue :" << l->value << "\n\n"; 57 | #endif // DumpSDK 58 | VMTypes::long_entries[l->name] = l; 59 | } 60 | } 61 | 62 | std::optional> VMTypes::findTypeFields(const char* typeName) 63 | { 64 | for (auto& m : VMTypes::struct_entries) { 65 | if (m.first == typeName) 66 | return m.second; 67 | } 68 | 69 | return std::nullopt; 70 | } 71 | 72 | std::optional VMTypes::findType(const char* typeName) 73 | { 74 | auto t = type_entries.find(typeName); 75 | if (t == type_entries.end()) 76 | return std::nullopt; 77 | 78 | return t->second; 79 | } 80 | 81 | /* VMType */ 82 | std::optional VMType::from_instance(const char* typeName, void* instance) 83 | { 84 | auto type = VMTypes::findType(typeName); 85 | if (!type.has_value()) 86 | return std::nullopt; 87 | 88 | auto fields = VMTypes::findTypeFields(typeName); 89 | if (!fields.has_value()) 90 | return std::nullopt; 91 | 92 | VMType vmtype; 93 | vmtype.instance = instance; 94 | vmtype.type_entry = type.value(); 95 | vmtype.fields = fields; 96 | 97 | return vmtype; 98 | } -------------------------------------------------------------------------------- /native/Native/jvm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #pragma warning(disable:26815) 3 | 4 | //thanks to github.com/rdbo/jnihook 5 | 6 | #ifndef JVM_HPP 7 | #define JVM_HPP 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* JVM definitions */ 15 | typedef struct { 16 | const char* typeName; 17 | const char* fieldName; 18 | const char* typeString; 19 | int32_t isStatic; 20 | uint64_t offset; 21 | void* address; 22 | } VMStructEntry; 23 | 24 | typedef struct { 25 | const char* typeName; 26 | const char* superclassName; 27 | int32_t isOopType; 28 | int32_t isIntegerType; 29 | int32_t isUnsigned; 30 | uint64_t size; 31 | } VMTypeEntry; 32 | 33 | typedef struct { 34 | const char* name; 35 | int32_t value; 36 | } VMIntConstantEntry; 37 | 38 | typedef struct { 39 | const char* name; 40 | uint64_t value; 41 | } VMLongConstantEntry; 42 | 43 | /* AccessFlags */ 44 | enum { 45 | JVM_ACC_NOT_C2_COMPILABLE = 0x02000000, 46 | JVM_ACC_NOT_C1_COMPILABLE = 0x04000000, 47 | JVM_ACC_NOT_C2_OSR_COMPILABLE = 0x08000000 48 | }; 49 | 50 | /* VTable Index */ 51 | enum VtableIndexFlag { 52 | itable_index_max = -10, 53 | pending_itable_index = -9, 54 | invalid_vtable_index = -4, 55 | garbage_vtable_index = -3, 56 | nonvirtual_vtable_index = -2 57 | }; 58 | 59 | /* Wrappers */ 60 | class VMTypes { 61 | public: 62 | typedef std::unordered_map struct_entry_t; 63 | typedef std::unordered_map struct_entries_t; 64 | typedef std::unordered_map type_entries_t; 65 | typedef std::unordered_map int_entries_t; 66 | typedef std::unordered_map long_entries_t; 67 | private: 68 | static struct_entries_t struct_entries; 69 | static type_entries_t type_entries; 70 | static int_entries_t int_entries; 71 | static long_entries_t long_entries; 72 | public: 73 | static void init(VMStructEntry* vmstructs, VMTypeEntry* vmtypes, VMIntConstantEntry* vmints, VMLongConstantEntry* vmlongs); 74 | static std::optional> findTypeFields(const char* typeName); 75 | static std::optional findType(const char* typeName); 76 | }; 77 | 78 | class VMType { 79 | private: 80 | VMTypeEntry* type_entry; 81 | std::optional> fields; 82 | void* instance; // pointer to instantiated VM type 83 | 84 | inline std::optional find_field_address(const char* fieldName) 85 | { 86 | auto tbl = fields.value().get(); 87 | auto entry = tbl.find(fieldName); 88 | if (entry == tbl.end()) 89 | return std::nullopt; 90 | 91 | auto field = entry->second; 92 | void* fieldAddress; 93 | if (field->isStatic) 94 | fieldAddress = (void*)field->address; 95 | else 96 | fieldAddress = (void*)((uint64_t)this->instance + field->offset); 97 | 98 | return fieldAddress; 99 | } 100 | 101 | public: 102 | // the following function will lookup the type in the 103 | // VMTypes. If it is found, return successful std::optional 104 | static std::optional from_instance(const char* typeName, void* instance); 105 | 106 | template 107 | std::optional get_field(const char* fieldName) 108 | { 109 | auto fieldAddress = find_field_address(fieldName); 110 | if (!fieldAddress.has_value()) 111 | return std::nullopt; 112 | 113 | return reinterpret_cast(fieldAddress.value()); 114 | } 115 | 116 | inline void* get_instance() 117 | { 118 | return this->instance; 119 | } 120 | 121 | uint64_t size() { 122 | return (uint64_t)this->type_entry->size; 123 | } 124 | }; 125 | 126 | #endif -------------------------------------------------------------------------------- /native/Native/third_party/detour/detours.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssheera/Some-Java-Obfuscator/8b0c8ff543a29e66e242314ab70a8cc9f3bbc68e/native/Native/third_party/detour/detours.lib -------------------------------------------------------------------------------- /native/Native/third_party/detour/include/detver.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Common version parameters. 4 | // 5 | // Microsoft Research Detours Package, Version 4.0.1 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved. 8 | // 9 | 10 | #define _USING_V110_SDK71_ 1 11 | #include "winver.h" 12 | #if 0 13 | #include 14 | #include 15 | #else 16 | #ifndef DETOURS_STRINGIFY 17 | #define DETOURS_STRINGIFY_(x) #x 18 | #define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) 19 | #endif 20 | 21 | #define VER_FILEFLAGSMASK 0x3fL 22 | #define VER_FILEFLAGS 0x0L 23 | #define VER_FILEOS 0x00040004L 24 | #define VER_FILETYPE 0x00000002L 25 | #define VER_FILESUBTYPE 0x00000000L 26 | #endif 27 | #define VER_DETOURS_BITS DETOURS_STRINGIFY(DETOURS_BITS) 28 | -------------------------------------------------------------------------------- /native/Native/third_party/detour/include/syelog.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Detours Test Program (syelog.h of syelog.lib) 4 | // 5 | // Microsoft Research Detours Package 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved. 8 | // 9 | #pragma once 10 | #ifndef _SYELOGD_H_ 11 | #define _SYELOGD_H_ 12 | #include 13 | 14 | #pragma pack(push, 1) 15 | #pragma warning(push) 16 | #pragma warning(disable: 4200) 17 | 18 | ////////////////////////////////////////////////////////////////////////////// 19 | // 20 | // 21 | #define SYELOG_PIPE_NAMEA "\\\\.\\pipe\\syelog" 22 | #define SYELOG_PIPE_NAMEW L"\\\\.\\pipe\\syelog" 23 | #ifdef UNICODE 24 | #define SYELOG_PIPE_NAME SYELOG_PIPE_NAMEW 25 | #else 26 | #define SYELOG_PIPE_NAME SYELOG_PIPE_NAMEA 27 | #endif 28 | 29 | ////////////////////////////////////////////////////////////////////////////// 30 | // 31 | #define SYELOG_MAXIMUM_MESSAGE 4086 // 4096 - sizeof(header stuff) 32 | 33 | typedef struct _SYELOG_MESSAGE 34 | { 35 | USHORT nBytes; 36 | BYTE nFacility; 37 | BYTE nSeverity; 38 | DWORD nProcessId; 39 | FILETIME ftOccurance; 40 | BOOL fTerminate; 41 | CHAR szMessage[SYELOG_MAXIMUM_MESSAGE]; 42 | } SYELOG_MESSAGE, *PSYELOG_MESSAGE; 43 | 44 | 45 | // Facility Codes. 46 | // 47 | #define SYELOG_FACILITY_KERNEL 0x10 // OS Kernel 48 | #define SYELOG_FACILITY_SECURITY 0x20 // OS Security 49 | #define SYELOG_FACILITY_LOGGING 0x30 // OS Logging-internal 50 | #define SYELOG_FACILITY_SERVICE 0x40 // User-mode system daemon 51 | #define SYELOG_FACILITY_APPLICATION 0x50 // User-mode application 52 | #define SYELOG_FACILITY_USER 0x60 // User self-generated. 53 | #define SYELOG_FACILITY_LOCAL0 0x70 // Locally defined. 54 | #define SYELOG_FACILITY_LOCAL1 0x71 // Locally defined. 55 | #define SYELOG_FACILITY_LOCAL2 0x72 // Locally defined. 56 | #define SYELOG_FACILITY_LOCAL3 0x73 // Locally defined. 57 | #define SYELOG_FACILITY_LOCAL4 0x74 // Locally defined. 58 | #define SYELOG_FACILITY_LOCAL5 0x75 // Locally defined. 59 | #define SYELOG_FACILITY_LOCAL6 0x76 // Locally defined. 60 | #define SYELOG_FACILITY_LOCAL7 0x77 // Locally defined. 61 | #define SYELOG_FACILITY_LOCAL8 0x78 // Locally defined. 62 | #define SYELOG_FACILITY_LOCAL9 0x79 // Locally defined. 63 | 64 | // Severity Codes. 65 | // 66 | #define SYELOG_SEVERITY_FATAL 0x00 // System is dead. 67 | #define SYELOG_SEVERITY_ALERT 0x10 // Take action immediately. 68 | #define SYELOG_SEVERITY_CRITICAL 0x20 // Critical condition. 69 | #define SYELOG_SEVERITY_ERROR 0x30 // Error 70 | #define SYELOG_SEVERITY_WARNING 0x40 // Warning 71 | #define SYELOG_SEVERITY_NOTICE 0x50 // Significant condition. 72 | #define SYELOG_SEVERITY_INFORMATION 0x60 // Informational 73 | #define SYELOG_SEVERITY_AUDIT_FAIL 0x66 // Audit Failed 74 | #define SYELOG_SEVERITY_AUDIT_PASS 0x67 // Audit Succeeeded 75 | #define SYELOG_SEVERITY_DEBUG 0x70 // Debugging 76 | 77 | // Logging Functions. 78 | // 79 | VOID SyelogOpen(PCSTR pszIdentifier, BYTE nFacility); 80 | VOID Syelog(BYTE nSeverity, PCSTR pszMsgf, ...); 81 | VOID SyelogV(BYTE nSeverity, PCSTR pszMsgf, va_list args); 82 | VOID SyelogClose(BOOL fTerminate); 83 | 84 | #pragma warning(pop) 85 | #pragma pack(pop) 86 | 87 | #endif // _SYELOGD_H_ 88 | // 89 | ///////////////////////////////////////////////////////////////// End of File. 90 | -------------------------------------------------------------------------------- /native/Native/third_party/java/include/jni_md.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | #ifndef _JAVASOFT_JNI_MD_H_ 27 | #define _JAVASOFT_JNI_MD_H_ 28 | 29 | #define JNIEXPORT __declspec(dllexport) 30 | #define JNIIMPORT __declspec(dllimport) 31 | #define JNICALL __stdcall 32 | 33 | typedef long jint; 34 | typedef __int64 jlong; 35 | typedef signed char jbyte; 36 | 37 | #endif /* !_JAVASOFT_JNI_MD_H_ */ 38 | -------------------------------------------------------------------------------- /native/Native/third_party/java/jvm.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssheera/Some-Java-Obfuscator/8b0c8ff543a29e66e242314ab70a8cc9f3bbc68e/native/Native/third_party/java/jvm.lib -------------------------------------------------------------------------------- /native/Native/types.cpp: -------------------------------------------------------------------------------- 1 | #include "types.hpp" 2 | 3 | 4 | 5 | std::string Symbol::to_string() 6 | { 7 | auto typeSymbol = VMType::from_instance("Symbol", this).value(); 8 | int16_t len = *typeSymbol.get_field("_length").value(); 9 | char* body = typeSymbol.get_field("_body").value(); 10 | 11 | return std::string(body, len); 12 | } 13 | 14 | void** ConstantPool::get_base() 15 | { 16 | if (!this) return nullptr; 17 | 18 | static VMTypeEntry* ConstantPool_entry = VMTypes::findType("ConstantPool").value(); 19 | if (!ConstantPool_entry) return nullptr; 20 | 21 | return (void**)((uint8_t*)this + ConstantPool_entry->size); 22 | } 23 | 24 | Array* ConstantPool::get_tags() 25 | { 26 | auto contatnPool = VMType::from_instance("ConstantPool", this).value(); 27 | auto tag = *contatnPool.get_field("_tags").value(); 28 | return reinterpret_cast*>(tag); 29 | } 30 | 31 | int ConstantPool::get_length() 32 | { 33 | if (!this) return 0; 34 | 35 | static VMStructEntry* length_entry = VMTypes::findTypeFields("ConstantPool").value().get()["_length"]; 36 | if (!length_entry) return 0; 37 | 38 | return *(int*)((uint8_t*)this + length_entry->offset); 39 | return 0; 40 | } 41 | 42 | ConstantPool* ConstMethod::get_constants() 43 | { 44 | if (!this) return nullptr; 45 | 46 | static VMStructEntry* _constants_entry = VMTypes::findTypeFields("ConstMethod").value().get()["_constants"]; 47 | if (!_constants_entry) return nullptr; 48 | 49 | return *(ConstantPool**)((uint8_t*)this + _constants_entry->offset); 50 | } 51 | 52 | unsigned short ConstMethod::get_code_size() 53 | { 54 | if (!this) return 0; 55 | 56 | static VMStructEntry* _code_size_entry = VMTypes::findTypeFields("ConstMethod").value().get()["_code_size"]; 57 | if (!_code_size_entry) return 0; 58 | 59 | return *(unsigned short*)((uint8_t*)this + _code_size_entry->offset); 60 | } 61 | 62 | unsigned short ConstMethod::get_name_index() 63 | { 64 | if (!this) return 0; 65 | 66 | static VMStructEntry* _name_index_entry = VMTypes::findTypeFields("ConstMethod").value().get()["_name_index"]; 67 | if (!_name_index_entry) return 0; 68 | 69 | return *(unsigned short*)((uint8_t*)this + _name_index_entry->offset); 70 | } 71 | 72 | unsigned short ConstMethod::get_signature_index() 73 | { 74 | if (!this) return 0; 75 | 76 | static VMStructEntry* _signature_index_entry = VMTypes::findTypeFields("ConstMethod").value().get()["_signature_index"]; 77 | if (!_signature_index_entry) return 0; 78 | 79 | return *(unsigned short*)((uint8_t*)this + _signature_index_entry->offset); 80 | } 81 | 82 | ConstMethod* Method::get_constMethod() 83 | { 84 | if (!this) return nullptr; 85 | 86 | static VMStructEntry* _constMethod_entry = VMTypes::findTypeFields("Method").value().get()["_constMethod"]; 87 | if (!_constMethod_entry) return nullptr; 88 | 89 | return *(ConstMethod**)((uint8_t*)this + _constMethod_entry->offset); 90 | } 91 | 92 | std::string Method::get_signature() 93 | { 94 | if (!this) return ""; 95 | 96 | ConstMethod* const_method = this->get_constMethod(); 97 | auto signature_index = const_method->get_signature_index(); 98 | ConstantPool* cp = const_method->get_constants(); 99 | Symbol** base = (Symbol**)cp->get_base(); 100 | return base[signature_index]->to_string(); 101 | } 102 | 103 | std::string Method::get_name() 104 | { 105 | if (!this) return ""; 106 | 107 | ConstMethod* const_method = this->get_constMethod(); 108 | auto signature_index = const_method->get_name_index(); 109 | ConstantPool* cp = const_method->get_constants(); 110 | Symbol** base = (Symbol**)cp->get_base(); 111 | return base[signature_index]->to_string(); 112 | } 113 | 114 | Array* Klass::get_methods() 115 | { 116 | if (!this) return nullptr; 117 | 118 | static VMStructEntry* _methods_entry = VMTypes::findTypeFields("InstanceKlass").value().get()["_methods"]; 119 | if (!_methods_entry) return nullptr; 120 | 121 | return *(Array**)((uint8_t*)this + _methods_entry->offset); 122 | } 123 | 124 | ConstantPool* Klass::get_constants() 125 | { 126 | if (!this) return nullptr; 127 | 128 | static VMStructEntry* _constants_entry = VMTypes::findTypeFields("InstanceKlass").value().get()["_constants"]; 129 | if (!_constants_entry) return nullptr; 130 | 131 | return *(ConstantPool**)((uint8_t*)this + _constants_entry->offset); 132 | } 133 | 134 | 135 | Symbol* Klass::get_name() 136 | { 137 | if (!this) return nullptr; 138 | 139 | static VMStructEntry* _name_entry = VMTypes::findTypeFields("Klass").value().get()["_name"]; 140 | if (!_name_entry) return nullptr; 141 | 142 | return *(Symbol**)((uint8_t*)this + _name_entry->offset); 143 | } 144 | 145 | Method* Klass::findMethod(const std::string& method_name, const std::string& method_sig) 146 | { 147 | auto _methods = get_methods(); 148 | auto _data = _methods->get_data(); 149 | auto _length = _methods->get_length(); 150 | for (int i = 0; i < _length; ++i) 151 | { 152 | auto method = _data[i]; 153 | auto cm = method->get_constMethod(); 154 | auto cp = cm->get_constants(); 155 | auto b = cp->get_base(); 156 | auto n = cm->get_name_index(); 157 | auto s = cm->get_signature_index(); 158 | auto symbol = (Symbol*)b[n]; 159 | auto symbol_sig = (Symbol*)b[s]; 160 | const auto& name = symbol->to_string(); 161 | const auto& signature = symbol_sig->to_string(); 162 | if (name == method_name && signature == method_sig) 163 | return method; 164 | } 165 | return nullptr; 166 | } 167 | 168 | Method* Klass::findMethod(const std::string& name_and_sign) 169 | { 170 | auto _methods = get_methods(); 171 | auto _data = _methods->get_data(); 172 | auto _length = _methods->get_length(); 173 | for (int i = 0; i < _length; ++i) 174 | { 175 | auto method = _data[i]; 176 | auto cm = method->get_constMethod(); 177 | auto cp = cm->get_constants(); 178 | auto b = cp->get_base(); 179 | auto n = cm->get_name_index(); 180 | auto s = cm->get_signature_index(); 181 | auto symbol = (Symbol*)b[n]; 182 | auto symbol_sig = (Symbol*)b[s]; 183 | const auto& comp = symbol->to_string() + symbol_sig->to_string(); 184 | if (comp == name_and_sign) 185 | return method; 186 | } 187 | return nullptr; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /native/Native/types.hpp: -------------------------------------------------------------------------------- 1 | #include "jvm.hpp" 2 | 3 | //thanks to github.com/Lefraudeur/RiptermsGhost/blob/master/HotSpot/HotSpot.cpp 4 | 5 | enum { 6 | JVM_CONSTANT_Utf8 = 1, 7 | JVM_CONSTANT_Unicode = 2, /* unused */ 8 | JVM_CONSTANT_Integer = 3, 9 | JVM_CONSTANT_Float = 4, 10 | JVM_CONSTANT_Long = 5, 11 | JVM_CONSTANT_Double = 6, 12 | JVM_CONSTANT_Class = 7, 13 | JVM_CONSTANT_String = 8, 14 | JVM_CONSTANT_Fieldref = 9, 15 | JVM_CONSTANT_Methodref = 10, 16 | JVM_CONSTANT_InterfaceMethodref = 11, 17 | JVM_CONSTANT_NameAndType = 12, 18 | JVM_CONSTANT_MethodHandle = 15, // JSR 292 19 | JVM_CONSTANT_MethodType = 16, // JSR 292 20 | JVM_CONSTANT_Dynamic = 17, 21 | JVM_CONSTANT_InvokeDynamic = 18 22 | }; 23 | 24 | class constantTag { 25 | public: 26 | constantTag(unsigned char tag) : _tag(tag) {} 27 | unsigned char _tag; 28 | public: 29 | bool is_klass() const { return _tag == JVM_CONSTANT_Class; } 30 | bool is_field() const { return _tag == JVM_CONSTANT_Fieldref; } 31 | bool is_method() const { return _tag == JVM_CONSTANT_Methodref; } 32 | bool is_interface_method() const { return _tag == JVM_CONSTANT_InterfaceMethodref; } 33 | bool is_string() const { return _tag == JVM_CONSTANT_String; } 34 | bool is_int() const { return _tag == JVM_CONSTANT_Integer; } 35 | bool is_float() const { return _tag == JVM_CONSTANT_Float; } 36 | bool is_long() const { return _tag == JVM_CONSTANT_Long; } 37 | bool is_double() const { return _tag == JVM_CONSTANT_Double; } 38 | bool is_name_and_type() const { return _tag == JVM_CONSTANT_NameAndType; } 39 | bool is_utf8() const { return _tag == JVM_CONSTANT_Utf8; } 40 | 41 | }; 42 | 43 | 44 | 45 | 46 | template 47 | class Array 48 | { 49 | public: 50 | int64_t get_length() { 51 | if (!this) return 0; 52 | if (sizeof(T) != 0x08) 53 | { 54 | static auto typeArray = VMTypes::findTypeFields("Array"); 55 | if (!typeArray.has_value()) return 0; 56 | static auto lengthEntry = typeArray.value().get()["_length"]; 57 | return (int64_t)*(int*)((uintptr_t)this + lengthEntry->offset); 58 | } 59 | else { 60 | static auto typeArray = VMTypes::findTypeFields("Array"); 61 | if (!typeArray.has_value()) return 0; 62 | static auto lengthEntry = typeArray.value().get()["_length"]; 63 | 64 | return *(int64_t*)((uintptr_t)this + lengthEntry->offset); 65 | } 66 | } 67 | 68 | T* get_data() { 69 | if (!this) return nullptr; 70 | if (sizeof(T) != 0x08) { 71 | static auto typeArray = VMTypes::findTypeFields("Array"); 72 | if (!typeArray.has_value()) return nullptr; 73 | static auto dataEntry = typeArray.value().get()["_data"]; 74 | return (T*)((uintptr_t)this + dataEntry->offset); 75 | } 76 | else { 77 | static auto typeArray = VMTypes::findTypeFields("Array"); 78 | if (!typeArray.has_value()) return nullptr; 79 | static auto dataEntry = typeArray.value().get()["_data"]; 80 | return (T*)((uintptr_t)this + dataEntry->offset); 81 | } 82 | 83 | } 84 | auto at(int i) -> T { 85 | if (i >= 0 && i < this->get_length()) 86 | return (T)(this->get_data()[i]); 87 | return (T)NULL; 88 | } 89 | 90 | auto is_empty() const -> bool { 91 | return get_length() == 0; 92 | } 93 | 94 | T* adr_at(const int i) { 95 | if (i >= 0 && i < this->get_length()) 96 | return &this->get_data()[i]; 97 | return nullptr; 98 | } 99 | }; 100 | 101 | class Symbol 102 | { 103 | public: 104 | std::string to_string(); 105 | }; 106 | 107 | 108 | class ConstantPool 109 | { 110 | public: 111 | void** get_base(); 112 | Array* get_tags(); 113 | int get_length(); 114 | 115 | }; 116 | class ConstMethod 117 | { 118 | public: 119 | ConstantPool* get_constants(); 120 | unsigned short get_code_size(); 121 | unsigned short get_name_index(); 122 | unsigned short get_signature_index(); 123 | }; 124 | 125 | class Method 126 | { 127 | public: 128 | ConstMethod* get_constMethod(); 129 | std::string get_signature(); 130 | std::string get_name(); 131 | }; 132 | 133 | class Klass 134 | { 135 | public: 136 | 137 | Symbol* get_name(); 138 | Method* findMethod(const std::string& method_name, const std::string& method_sig); 139 | Method* findMethod(const std::string& name_and_sign); 140 | Array* get_methods(); 141 | ConstantPool* get_constants(); 142 | }; -------------------------------------------------------------------------------- /native/native/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "jvm.hpp" 7 | #include "types.hpp" 8 | 9 | namespace Offsets { 10 | uintptr_t klassOffset = 0; 11 | } 12 | 13 | 14 | static jintArray JNICALL raw_1bytes(JNIEnv* env, jclass caller, jclass target, jstring method_name_and_sign_jstr) { 15 | 16 | auto klass = *reinterpret_cast(*(uintptr_t*)target + Offsets::klassOffset); 17 | 18 | auto method_name_and_sign = env->GetStringUTFChars(method_name_and_sign_jstr, 0); 19 | auto method = klass->findMethod(method_name_and_sign); 20 | if (!method) return 0; 21 | auto c_method = method->get_constMethod(); 22 | auto j = 0; 23 | 24 | auto code_size = c_method->get_code_size(); 25 | auto arr = env->NewIntArray(code_size); 26 | auto code_base = *reinterpret_cast(c_method + 1); 27 | while (j < code_size) { 28 | auto bcp = code_base + j; 29 | auto bci = *(int*)bcp & 0xff; 30 | env->SetIntArrayRegion(arr, j++, 1, reinterpret_cast(&bci)); 31 | } 32 | 33 | return arr; 34 | } 35 | 36 | static void JNICALL decryptConstantPool(JNIEnv* env, jclass cls, jclass target) { 37 | auto klass = reinterpret_cast(*(uintptr_t*)target + Offsets::klassOffset); 38 | auto cp = klass->get_constants(); 39 | auto tags = cp->get_tags(); 40 | auto cls_name = klass->get_name()->to_string(); 41 | int h = 0; 42 | if (cls_name.length() > 0) { 43 | 44 | for (int i = 0; i < cls_name.length(); i++) { 45 | h = 31 * h + (int)cls_name[i]; 46 | } 47 | } 48 | auto base = cp->get_base(); 49 | for (auto i = 0; i < tags->get_length(); i++) { 50 | auto tag = (constantTag)tags->at(i); 51 | if (tag.is_int()) { 52 | jint* val = (jint*)&base[i]; 53 | *val ^= h; 54 | *val += (h << 2); 55 | } 56 | else if (tag.is_long()) { 57 | jlong* val = (jlong*)&base[i]; 58 | *val += h; 59 | *val ^= (jlong(h) << 4); 60 | } 61 | else if (tag.is_double()) { 62 | jdouble* val = (jdouble*)&base[i]; 63 | *val = cbrt(*val); 64 | } 65 | else if (tag.is_float()) { 66 | jfloat* val = (jfloat*)&base[i]; 67 | *val = cbrtf(*val); 68 | } 69 | } 70 | return; 71 | } 72 | 73 | #define DEBUG 74 | 75 | #include 76 | extern "C" JNIIMPORT VMStructEntry * gHotSpotVMStructs; 77 | extern "C" JNIIMPORT VMTypeEntry * gHotSpotVMTypes; 78 | extern "C" JNIIMPORT VMIntConstantEntry * gHotSpotVMIntConstants; 79 | extern "C" JNIIMPORT VMLongConstantEntry * gHotSpotVMLongConstants; 80 | 81 | static bool InitOffsets() { 82 | auto java_lang_Class = VMTypes::findTypeFields("java_lang_Class"); 83 | if (!java_lang_Class.has_value()) return false; 84 | Offsets::klassOffset = *(jint*)java_lang_Class.value().get()["_klass_offset"]->address; 85 | #ifdef DEBUG 86 | std::cout << "KlassOffset : " << Offsets::klassOffset << std::endl; 87 | #endif // DEBUG 88 | 89 | } 90 | 91 | 92 | static bool Init(JNIEnv* env) { 93 | VMTypes::init(gHotSpotVMStructs, gHotSpotVMTypes,gHotSpotVMIntConstants,gHotSpotVMLongConstants); 94 | if (!InitOffsets()) return false; 95 | 96 | auto nativeHandler = env->FindClass("vm/NativeHandler"); 97 | if (!nativeHandler) return false; 98 | JNINativeMethod table[] = { 99 | {(char*)"decryptConstantPool", (char*)"(Ljava/lang/Class;)V", (void*)&decryptConstantPool}, 100 | {(char*)"raw_1bytes", (char*)"(Ljava/lang/Class;Ljava/lang/String;)[I", (void*)&raw_1bytes}, 101 | }; 102 | 103 | env->RegisterNatives(nativeHandler, table, sizeof(table) / sizeof(JNINativeMethod)); 104 | 105 | return true; 106 | } 107 | 108 | 109 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) 110 | { 111 | JNIEnv* env{}; 112 | auto err = vm->GetEnv((void**)&env, JNI_VERSION_1_8); 113 | if (err != JNI_OK || !env || !Init(env)) return JNI_EVERSION; 114 | return JNI_VERSION_1_8; 115 | } 116 | 117 | 118 | BOOL APIENTRY DllMain( HMODULE hModule, 119 | DWORD ul_reason_for_call, 120 | LPVOID lpReserved 121 | ) 122 | { 123 | switch (ul_reason_for_call) 124 | { 125 | case DLL_PROCESS_ATTACH: 126 | case DLL_THREAD_ATTACH: 127 | case DLL_THREAD_DETACH: 128 | case DLL_PROCESS_DETACH: 129 | break; 130 | } 131 | return TRUE; 132 | } 133 | 134 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.cheatbreaker 8 | obf 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | jitpack.io 19 | https://jitpack.io 20 | 21 | 22 | 23 | 24 | 25 | org.yaml 26 | snakeyaml 27 | 1.30 28 | 29 | 30 | com.google.guava 31 | guava 32 | 31.1-jre 33 | 34 | 35 | net.sf.jopt-simple 36 | jopt-simple 37 | 5.0.4 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.apache.commons 66 | commons-lang3 67 | 3.12.0 68 | 69 | 70 | org.apache.commons 71 | commons-collections4 72 | 4.1 73 | 74 | 75 | commons-io 76 | commons-io 77 | 2.11.0 78 | 79 | 80 | org.projectlombok 81 | lombok 82 | RELEASE 83 | compile 84 | 85 | 86 | org.junit.jupiter 87 | junit-jupiter 88 | RELEASE 89 | test 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/Main.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf; 26 | 27 | import com.cheatbreaker.obf.utils.configuration.file.YamlConfiguration; 28 | import joptsimple.OptionException; 29 | import joptsimple.OptionParser; 30 | import joptsimple.OptionSet; 31 | 32 | import java.io.File; 33 | 34 | public class Main { 35 | 36 | public static void main(String[] args) { 37 | 38 | System.out.println("Starting..."); 39 | 40 | OptionParser parser = new OptionParser(); 41 | parser.accepts("config").withRequiredArg().required().ofType(File.class); 42 | 43 | OptionSet options; 44 | 45 | try { 46 | options = parser.parse(args); 47 | } catch (OptionException ex) { 48 | System.out.println("Usage: obf --config "); 49 | System.out.println(ex.getMessage()); 50 | System.exit(1); 51 | return; 52 | } 53 | 54 | File configFile = (File) options.valueOf("config"); 55 | 56 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); 57 | 58 | try { 59 | new Obf(config); 60 | } catch (Exception ex) { 61 | ex.printStackTrace(); 62 | System.exit(1); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/Obf.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf; 2 | 3 | import com.cheatbreaker.obf.transformer.Transformer; 4 | import com.cheatbreaker.obf.transformer.flow.ExceptionTransformer; 5 | import com.cheatbreaker.obf.transformer.general.StripTransformer; 6 | import com.cheatbreaker.obf.transformer.methods.DynamicTransformer; 7 | import com.cheatbreaker.obf.transformer.misc.ChecksumTransformer; 8 | import com.cheatbreaker.obf.transformer.misc.PackerTransformer; 9 | import com.cheatbreaker.obf.transformer.misc.VariableTransformer; 10 | import com.cheatbreaker.obf.transformer.natives.CodeHiderTransformer; 11 | import com.cheatbreaker.obf.transformer.natives.ConstantPoolTransformer; 12 | import com.cheatbreaker.obf.transformer.strings.ToStringTransformer; 13 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 14 | import com.cheatbreaker.obf.utils.asm.ContextClassWriter; 15 | import com.cheatbreaker.obf.utils.configuration.file.YamlConfiguration; 16 | import com.cheatbreaker.obf.utils.loader.LoaderUtil; 17 | import com.cheatbreaker.obf.utils.tree.ClassTree; 18 | import lombok.Getter; 19 | import org.apache.commons.io.IOUtils; 20 | import org.objectweb.asm.ClassReader; 21 | import org.objectweb.asm.ClassWriter; 22 | import org.objectweb.asm.Opcodes; 23 | import sun.util.calendar.BaseCalendar; 24 | 25 | import java.io.*; 26 | import java.lang.reflect.Method; 27 | import java.time.Instant; 28 | import java.util.*; 29 | import java.util.concurrent.ThreadLocalRandom; 30 | import java.util.jar.JarEntry; 31 | import java.util.jar.JarFile; 32 | import java.util.jar.JarOutputStream; 33 | import java.util.jar.Manifest; 34 | import java.util.stream.Collectors; 35 | import java.util.zip.ZipEntry; 36 | 37 | public class Obf implements Opcodes { 38 | 39 | private final Map hierachy = new HashMap<>(); 40 | 41 | private Manifest manifest; 42 | 43 | @Getter 44 | private static Obf instance; 45 | 46 | private final ThreadLocalRandom random; 47 | private List classes = new ArrayList<>(); 48 | private final List libs = new ArrayList<>(65525); 49 | private final List transformers = new ArrayList<>(); 50 | private final YamlConfiguration config; 51 | private final HashMap resources = new HashMap<>(); 52 | private final HashMap generated = new HashMap<>(); 53 | 54 | @Getter 55 | private final LoaderUtil loader; 56 | 57 | public List getTransformers() { 58 | return transformers; 59 | } 60 | 61 | public Obf(YamlConfiguration configuration) throws Exception { 62 | 63 | this.config = configuration; 64 | this.loader = new LoaderUtil(Obf.class.getClassLoader()); 65 | 66 | File inputFile = new File(config.getString("input")); 67 | File outputFile = new File(config.getString("output")); 68 | List libs = config.getStringList("libs").stream().map(File::new).collect(Collectors.toList()); 69 | 70 | instance = this; 71 | loadJavaRuntime(); 72 | 73 | LinkedList libraryThreads = new LinkedList<>(); 74 | 75 | System.out.println("Loading libraries..."); 76 | 77 | for (String library : libraries) { 78 | libraryThreads.add(new Thread(() -> { 79 | try { 80 | loadJar(new File(library), true); 81 | } catch (Exception e) { 82 | e.printStackTrace(); 83 | } 84 | })); 85 | } 86 | 87 | for (File folder : libs) { 88 | for (File lib : walkFolder(folder)) { 89 | if (lib.getName().startsWith("rt")) { 90 | continue; 91 | } 92 | libraryThreads.add(new Thread(() -> { 93 | try { 94 | loadJar(lib, true); 95 | } catch (Exception e) { 96 | e.printStackTrace(); 97 | } 98 | })); 99 | } 100 | } 101 | 102 | for (Thread libraryThread : libraryThreads) { 103 | libraryThread.start(); 104 | } 105 | 106 | for (Thread libraryThread : libraryThreads) { 107 | libraryThread.join(); 108 | } 109 | 110 | System.out.println("Reading jar..."); 111 | 112 | loadJar(inputFile, false); 113 | 114 | random = ThreadLocalRandom.current(); 115 | 116 | System.out.println("Loading transformers..."); 117 | 118 | transformers.add(new StripTransformer(this)); 119 | // transformers.add(new InlinerTransformer(this)); 120 | transformers.add(new ExceptionTransformer(this)); 121 | transformers.add(new VariableTransformer(this)); 122 | transformers.add(new DynamicTransformer(this)); 123 | transformers.add(new ToStringTransformer(this)); 124 | transformers.add(new ChecksumTransformer(this)); 125 | transformers.add(new ConstantPoolTransformer(this)); 126 | transformers.add(new CodeHiderTransformer(this)); 127 | transformers.add(new PackerTransformer(this)); 128 | 129 | long start = System.currentTimeMillis(); 130 | 131 | try (JarOutputStream out = new JarOutputStream(new FileOutputStream(outputFile))) { 132 | 133 | System.out.println("Transforming classes..."); 134 | 135 | for (Transformer transformer : transformers) { 136 | if (!transformer.enabled) continue; 137 | new ArrayList<>(classes).forEach(transformer::run); 138 | } 139 | 140 | for (Transformer transformer : transformers) { 141 | if (!transformer.enabled) continue; 142 | try { 143 | transformer.getClass().getDeclaredMethod("after"); 144 | } catch (NoSuchMethodException e) { 145 | continue; 146 | } 147 | transformer.runAfter(); 148 | } 149 | 150 | // Write manifest 151 | if (manifest != null) { 152 | ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME); 153 | out.putNextEntry(e); 154 | manifest.write(new BufferedOutputStream(out)); 155 | out.closeEntry(); 156 | } 157 | 158 | System.out.println("Writing classes..."); 159 | 160 | for (ClassWrapper classNode : classes) { 161 | 162 | byte[] b = generated.getOrDefault(classNode.name, null); 163 | 164 | if (b != null && b.length == 0) continue; 165 | 166 | if (b == null) { 167 | ContextClassWriter writer = new ContextClassWriter(ClassWriter.COMPUTE_FRAMES); 168 | try { 169 | classNode.accept(writer); 170 | b = writer.toByteArray(); 171 | } catch (Exception ex) { 172 | System.out.println("Failed to compute frames for class: " + classNode.name + ", " + ex.getMessage()); 173 | writer = new ContextClassWriter(ClassWriter.COMPUTE_MAXS); 174 | classNode.accept(writer); 175 | b = writer.toByteArray(); 176 | } 177 | } 178 | 179 | if (b != null) { 180 | out.putNextEntry(new JarEntry(classNode.name + ".class")); 181 | out.write(b); 182 | } 183 | } 184 | 185 | System.out.println("Writing resources..."); 186 | resources.forEach((name, data) -> { 187 | try { 188 | if (name.equals(JarFile.MANIFEST_NAME)) 189 | return; 190 | out.putNextEntry(new JarEntry(name)); 191 | out.write(data); 192 | } catch (IOException e) { 193 | e.printStackTrace(); 194 | } 195 | }); 196 | 197 | out.close(); 198 | 199 | long difference = outputFile.length() - inputFile.length(); 200 | 201 | boolean compressed = difference < 0; 202 | Date epoch = new Date(0); 203 | Date elapsed = Date.from(Instant.ofEpochMilli(System.currentTimeMillis() - start)); 204 | 205 | StringBuilder time = new StringBuilder(); 206 | 207 | int dh = elapsed.getHours() - epoch.getHours(); 208 | int dm = elapsed.getMinutes() - epoch.getMinutes(); 209 | int ds = elapsed.getSeconds() - epoch.getSeconds(); 210 | 211 | if (dh > 0) 212 | time.append(dh).append("h "); 213 | if (dm > 0) 214 | time.append(dm).append("m "); 215 | if (ds > 0) 216 | time.append(ds).append("s "); 217 | Method normalize = Date.class.getDeclaredMethod("normalize"); 218 | normalize.setAccessible(true); 219 | BaseCalendar.Date date = (BaseCalendar.Date) normalize.invoke(elapsed); 220 | time.append(date.getMillis()).append("ms"); 221 | 222 | System.out.printf("Size: %.2fKB -> %.2fKB (%s%.2f%%)\n", 223 | inputFile.length() / 1024D, outputFile.length() / 1024D, compressed ? "-" : "+", (100D * Math.abs((double) difference) / (double) inputFile.length())); 224 | System.out.printf("Elapsed: %s\n", time); 225 | } 226 | } 227 | 228 | public HashMap getResources() { 229 | return resources; 230 | } 231 | 232 | private final Vector libraries = new Vector<>(); 233 | 234 | public void loadJavaRuntime() { 235 | String path = System.getProperty("sun.boot.class.path"); 236 | if (path != null) { 237 | String[] pathFiles = path.split(";"); 238 | for (String lib : pathFiles) { 239 | if (lib.endsWith(".jar")) { 240 | libraries.addElement(lib); 241 | } 242 | } 243 | } 244 | 245 | } 246 | 247 | public void loadJar(File inputFile, boolean lib) throws Exception { 248 | if (!inputFile.exists()) return; 249 | JarFile inputJar = new JarFile(inputFile); 250 | if (!lib) manifest = inputJar.getManifest(); 251 | for (Enumeration iter = inputJar.entries(); iter.hasMoreElements(); ) { 252 | JarEntry entry = iter.nextElement(); 253 | if (entry.isDirectory()) continue; 254 | try (InputStream in = inputJar.getInputStream(entry)) { 255 | byte[] bytes = IOUtils.toByteArray(in); 256 | if (entry.getName().endsWith(".class") || entry.getName().endsWith(".class/")) { 257 | ClassReader reader = new ClassReader(bytes); 258 | ClassWrapper classNode = new ClassWrapper(!lib); 259 | reader.accept(classNode, ClassReader.SKIP_FRAMES); 260 | if (lib) libs.add(classNode); 261 | else { 262 | classes.add(classNode); 263 | } 264 | loader.addClass(classNode.name, bytes); 265 | } else { 266 | if (!lib) resources.put(entry.getName(), bytes); 267 | } 268 | } 269 | } 270 | } 271 | 272 | private List walkFolder(File folder) { 273 | List files = new ArrayList<>(); 274 | if (!folder.isDirectory()) { 275 | if (folder.getName().endsWith(".jar")) 276 | files.add(folder); 277 | return files; 278 | } 279 | for (File file : folder.listFiles()) { 280 | if (file.isDirectory()) { 281 | files.addAll(walkFolder(file)); 282 | } else { 283 | if (file.getName().endsWith(".jar")) 284 | files.add(file); 285 | } 286 | } 287 | return files; 288 | } 289 | 290 | public void loadHierachy() { 291 | Set processed = new HashSet<>(); 292 | LinkedList toLoad = new LinkedList<>(this.classes); 293 | toLoad.addAll(libs); 294 | while (!toLoad.isEmpty()) { 295 | for (ClassWrapper toProcess : loadHierachy(toLoad.poll())) { 296 | if (processed.add(toProcess.name)) { 297 | toLoad.add(toProcess); 298 | } 299 | } 300 | } 301 | } 302 | 303 | public ClassTree getClassTree(String classNode) { 304 | ClassTree tree = hierachy.get(classNode); 305 | if (tree == null) { 306 | loadHierachyAll(assureLoaded(classNode)); 307 | return getClassTree(classNode); 308 | } 309 | return tree; 310 | } 311 | 312 | private ClassTree getOrCreateClassTree(String name) { 313 | return this.hierachy.computeIfAbsent(name, ClassTree::new); 314 | } 315 | 316 | public List loadHierachy(ClassWrapper specificNode) { 317 | if (specificNode.name.equals("java/lang/Object")) { 318 | return Collections.emptyList(); 319 | } 320 | List toProcess = new ArrayList<>(); 321 | 322 | ClassTree thisTree = getOrCreateClassTree(specificNode.name); 323 | ClassWrapper superClass; 324 | 325 | superClass = assureLoaded(specificNode.superName); 326 | 327 | if (superClass == null) { 328 | throw new IllegalArgumentException("Could not load " + specificNode.name); 329 | } 330 | 331 | ClassTree superTree = getOrCreateClassTree(superClass.name); 332 | superTree.subClasses.add(specificNode.name); 333 | thisTree.parentClasses.add(superClass.name); 334 | toProcess.add(superClass); 335 | 336 | for (String interfaceReference : specificNode.interfaces) { 337 | ClassWrapper interfaceNode = assureLoaded(interfaceReference); 338 | if (interfaceNode == null) { 339 | throw new IllegalArgumentException("Could not load " + interfaceReference); 340 | } 341 | ClassTree interfaceTree = getOrCreateClassTree(interfaceReference); 342 | interfaceTree.subClasses.add(specificNode.name); 343 | thisTree.parentClasses.add(interfaceReference); 344 | toProcess.add(interfaceNode); 345 | } 346 | return toProcess; 347 | } 348 | 349 | public void loadHierachyAll(ClassWrapper classNode) { 350 | Set processed = new HashSet<>(); 351 | LinkedList toLoad = new LinkedList<>(); 352 | toLoad.add(classNode); 353 | while (!toLoad.isEmpty()) { 354 | for (ClassWrapper toProcess : loadHierachy(toLoad.poll())) { 355 | if (processed.add(toProcess.name)) { 356 | toLoad.add(toProcess); 357 | } 358 | } 359 | } 360 | } 361 | 362 | public YamlConfiguration getConfig() { 363 | return config; 364 | } 365 | 366 | public void addResource(String name, byte[] data) { 367 | resources.put(name, data); 368 | } 369 | 370 | 371 | public ThreadLocalRandom getRandom() { 372 | return random; 373 | } 374 | 375 | public void setClasses(List classes) { 376 | this.classes = classes; 377 | } 378 | 379 | public List getClasses() { 380 | return classes; 381 | } 382 | 383 | public List getLibs() { 384 | return libs; 385 | } 386 | 387 | public void addClass(ClassWrapper classNode) { 388 | classes.add(classNode); 389 | } 390 | 391 | public ClassWrapper assureLoaded(String owner) { 392 | if (owner == null) return null; 393 | for (ClassWrapper classNode : classes) { 394 | if (classNode.name.equals(owner)) return classNode; 395 | } 396 | for (ClassWrapper classNode : libs) { 397 | if (classNode == null) continue; 398 | if (classNode.name.equals(owner)) return classNode; 399 | } 400 | return null; 401 | // throw new NoClassDefFoundError(owner); 402 | } 403 | 404 | public Manifest getManifest() { 405 | return manifest; 406 | } 407 | 408 | public void addGeneratedClass(String name, byte[] b) { 409 | generated.put(name, b); 410 | } 411 | 412 | public boolean isTransformerEnabled(Class transformer) { 413 | return transformers.stream().anyMatch(t -> t.getClass().equals(transformer) && t.enabled); 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/addons/IObfuscator.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.addons; 2 | 3 | import lombok.SneakyThrows; 4 | 5 | import java.io.File; 6 | import java.util.List; 7 | 8 | public interface IObfuscator { 9 | 10 | @SneakyThrows 11 | void transform(File input, File output, List includes); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/addons/skidfuscator/SkidfuscatorAddon.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.addons.skidfuscator; 2 | 3 | import com.cheatbreaker.obf.addons.IObfuscator; 4 | 5 | import java.io.File; 6 | import java.util.List; 7 | 8 | public class SkidfuscatorAddon implements IObfuscator { 9 | 10 | @Override 11 | public void transform(File input, File output, List includes) { 12 | StringBuilder args = new StringBuilder(); 13 | args.append("-ph ") 14 | .append("-li=") 15 | .append(includes.toString(), 1, includes.toString().length() - 1) 16 | .append(input.getAbsolutePath()) 17 | .append(" -o=") 18 | .append(output.getAbsolutePath()); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/Transformer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.transformer; 26 | 27 | import com.cheatbreaker.obf.Obf; 28 | import com.cheatbreaker.obf.transformer.natives.CodeHiderTransformer; 29 | import com.cheatbreaker.obf.transformer.natives.ConstantPoolTransformer; 30 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 31 | import com.cheatbreaker.obf.utils.configuration.ConfigurationSection; 32 | import com.cheatbreaker.obf.utils.pair.ClassMethodNode; 33 | import lombok.SneakyThrows; 34 | import org.apache.commons.io.IOUtils; 35 | import org.objectweb.asm.ClassReader; 36 | import org.objectweb.asm.Opcodes; 37 | 38 | import java.io.File; 39 | import java.io.FileInputStream; 40 | import java.util.ArrayList; 41 | import java.util.List; 42 | import java.util.Vector; 43 | import java.util.concurrent.ThreadLocalRandom; 44 | 45 | public abstract class Transformer implements Opcodes { 46 | 47 | protected final Obf obf; 48 | protected final ThreadLocalRandom random; 49 | protected final ConfigurationSection config; 50 | protected int iterations = 1; 51 | 52 | public static boolean loadedNative; 53 | 54 | protected Vector excluded = new Vector<>(); 55 | protected Vector included = new Vector<>(); 56 | public boolean enabled; 57 | public ClassMethodNode target; 58 | public boolean canBeIterated = true; 59 | 60 | public abstract String getSection(); 61 | 62 | public Transformer(Obf obf) { 63 | this.obf = obf; 64 | this.random = obf.getRandom(); 65 | this.config = obf.getConfig().getConfigurationSection(getSection()); 66 | 67 | this.enabled = config.getBoolean("enabled", true); 68 | this.excluded.addAll(config.getStringList("excluded")); 69 | this.included.addAll(config.getStringList("included")); 70 | this.iterations = config.getInt("iterations", 1); 71 | 72 | loadedNative = obf.getClasses().stream().anyMatch(c -> c.name.equals("vm/NativeHandler")); 73 | } 74 | 75 | 76 | @SneakyThrows 77 | public final void run(ClassWrapper classNode) { 78 | 79 | if (!enabled) return; 80 | for (String s : excluded) { 81 | if (classNode.name.startsWith(s)) return; 82 | } 83 | for (String s : included) { 84 | if (!classNode.name.startsWith(s)) return; 85 | } 86 | for (int i = 0; i < iterations; i++) { 87 | visit(classNode); 88 | } 89 | } 90 | 91 | protected void visit(ClassWrapper classNode) {} 92 | 93 | @SneakyThrows 94 | public final void runAfter() { 95 | if (!enabled) return; 96 | 97 | if (obf.isTransformerEnabled(ConstantPoolTransformer.class) || obf.isTransformerEnabled(CodeHiderTransformer.class)) { 98 | if (!loadedNative) { 99 | File file = new File("target\\classes\\vm\\NativeHandler.class"); 100 | byte[] b = IOUtils.toByteArray(new FileInputStream(file)); 101 | ClassReader cr = new ClassReader(b); 102 | ClassWrapper cw = new ClassWrapper(false); 103 | cr.accept(cw, ClassReader.SKIP_DEBUG); 104 | cw.name = "vm/NativeHandler"; 105 | cw.methods.removeIf(m -> m.name.startsWith("raw_")); 106 | obf.addClass(cw); 107 | loadedNative = true; 108 | } 109 | } 110 | 111 | List clone = new ArrayList<>(obf.getClasses()); 112 | 113 | for (ClassWrapper classNode : clone) { 114 | for (String s : excluded) { 115 | if (classNode.name.startsWith(s)) { 116 | obf.getClasses().remove(classNode); 117 | } 118 | } 119 | for (String s : included) { 120 | if (!classNode.name.startsWith(s)) { 121 | obf.getClasses().remove(classNode); 122 | } 123 | } 124 | } 125 | 126 | after(); 127 | 128 | obf.setClasses(clone); 129 | } 130 | 131 | protected void after() {} 132 | 133 | protected boolean nextBoolean(int i) { 134 | boolean ret = random.nextBoolean(); 135 | for (int j = 0; j < i; j++) { 136 | ret = random.nextBoolean() && ret; 137 | } 138 | return ret; 139 | } 140 | 141 | protected void error(String message, Object... args) { 142 | System.err.printf("[" + this.getClass().getSimpleName() + "] " + message + "\n", args); 143 | } 144 | 145 | protected void log(String message, Object... args) { 146 | System.out.printf("[" + this.getClass().getSimpleName() + "] " + message + "\n", args); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/flow/ExceptionTransformer.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.transformer.flow; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.transformer.Transformer; 5 | import com.cheatbreaker.obf.utils.asm.AsmUtils; 6 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 7 | import com.cheatbreaker.obf.utils.pair.ClassMethodNode; 8 | import org.apache.commons.lang3.RandomStringUtils; 9 | import org.objectweb.asm.FieldVisitor; 10 | import org.objectweb.asm.MethodVisitor; 11 | import org.objectweb.asm.Type; 12 | import org.objectweb.asm.tree.*; 13 | import org.objectweb.asm.tree.analysis.Analyzer; 14 | import org.objectweb.asm.tree.analysis.BasicInterpreter; 15 | import org.objectweb.asm.tree.analysis.BasicValue; 16 | import org.objectweb.asm.tree.analysis.Frame; 17 | 18 | import java.util.ArrayList; 19 | 20 | public class ExceptionTransformer extends Transformer { 21 | 22 | public static String handlerName; 23 | 24 | public ExceptionTransformer(Obf obf) { 25 | super(obf); 26 | } 27 | 28 | @Override 29 | public String getSection() { 30 | return "flow.exceptions"; 31 | } 32 | 33 | private void visitMethod(ClassWrapper classNode, MethodNode method) { 34 | ClassMethodNode cmn = new ClassMethodNode(classNode, method); 35 | if (target == null || target.equals(cmn)) { 36 | 37 | int amt = 0; 38 | 39 | for (AbstractInsnNode instruction : method.instructions) { 40 | if (instruction instanceof VarInsnNode) { 41 | if (instruction.getOpcode() >= ISTORE && instruction.getOpcode() <= ASTORE) { 42 | amt++; 43 | } 44 | } 45 | } 46 | 47 | if (AsmUtils.codeSize(method) + (amt * 15) >= AsmUtils.MAX_INSTRUCTIONS) return; 48 | 49 | Analyzer analyzer = new Analyzer<>(new BasicInterpreter()); 50 | 51 | Frame[] frames; 52 | 53 | try { 54 | frames = analyzer.analyzeAndComputeMaxs(classNode.name, method); 55 | } catch (Exception ex) { 56 | error("Failed to analyze method %s ", ex.getMessage()); 57 | return; 58 | } 59 | 60 | for (AbstractInsnNode instruction : method.instructions) { 61 | if (instruction instanceof VarInsnNode) { 62 | 63 | VarInsnNode var = (VarInsnNode) instruction; 64 | 65 | if (instruction.getOpcode() >= ISTORE && instruction.getOpcode() <= ASTORE) { 66 | 67 | Frame frame = frames[instruction.index]; 68 | if (frame == null || frame.getStackSize() > 1) 69 | continue; 70 | 71 | BasicValue value; 72 | Type type; 73 | 74 | value = frame.getStack(frame.getStackSize() - 1); 75 | 76 | type = value.getType(); 77 | 78 | if (value == BasicValue.UNINITIALIZED_VALUE || 79 | // type.getSort() == Type.OBJECT || 80 | type.getInternalName().equals("null") || 81 | type.getInternalName().equals("java/lang/Object")) { 82 | continue; 83 | } 84 | 85 | 86 | LabelNode start = new LabelNode(); 87 | LabelNode end = new LabelNode(); 88 | LabelNode handler = new LabelNode(); 89 | LabelNode finish = new LabelNode(); 90 | 91 | TryCatchBlockNode tryCatch = new TryCatchBlockNode(start, end, handler, handlerName); 92 | 93 | InsnList list = new InsnList(); 94 | list.add(start); 95 | AsmUtils.boxPrimitive(type.getDescriptor(), list); 96 | list.add(new TypeInsnNode(NEW, handlerName)); 97 | list.add(new InsnNode(SWAP)); 98 | list.add(new InsnNode(DUP2)); 99 | list.add(new InsnNode(POP)); 100 | list.add(new InsnNode(SWAP)); 101 | list.add(new MethodInsnNode(INVOKESPECIAL, handlerName, "", "(Ljava/lang/Object;)V", false)); 102 | list.add(new InsnNode(ATHROW)); 103 | list.add(new JumpInsnNode(GOTO, start)); 104 | list.add(handler); 105 | list.add(new FieldInsnNode(GETFIELD, handlerName, "o", "Ljava/lang/Object;")); 106 | AsmUtils.unboxPrimitive(type.getDescriptor(), list); 107 | list.add(end); 108 | list.add(new JumpInsnNode(GOTO, finish)); 109 | list.add(finish); 110 | list.add(instruction.clone(null)); 111 | 112 | method.instructions.insert(instruction, list); 113 | method.instructions.remove(instruction); 114 | 115 | method.tryCatchBlocks.add(tryCatch); 116 | 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | @Override 124 | protected void visit(ClassWrapper classNode) { 125 | 126 | while (handlerName == null) { 127 | for (ClassWrapper cn : new ArrayList<>(obf.getClasses())) { 128 | if (nextBoolean(5)) { 129 | handlerName = AsmUtils.parentName(cn.name) + RandomStringUtils.randomAlphabetic(3); 130 | if (obf.assureLoaded(handlerName) != null) { 131 | handlerName = null; 132 | } else { 133 | ClassWrapper handlerClass = new ClassWrapper(false); 134 | handlerClass.visit(V1_8, ACC_PUBLIC, handlerName, null, "java/lang/Throwable", null); 135 | FieldVisitor fv = handlerClass.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null); 136 | fv.visitEnd(); 137 | MethodVisitor mv = handlerClass.visitMethod(ACC_PUBLIC, 138 | "", "(Ljava/lang/Object;)V", null, null); 139 | mv.visitCode(); 140 | mv.visitVarInsn(ALOAD, 1); 141 | mv.visitVarInsn(ALOAD, 0); 142 | mv.visitInsn(DUP); 143 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Throwable", "", "()V", false); 144 | mv.visitInsn(SWAP); 145 | mv.visitFieldInsn(PUTFIELD, handlerName, "o", "Ljava/lang/Object;"); 146 | mv.visitInsn(RETURN); 147 | mv.visitMaxs(2, 2); 148 | mv.visitEnd(); 149 | obf.getClasses().add(handlerClass); 150 | } 151 | } 152 | } 153 | } 154 | 155 | for (MethodNode method : classNode.methods) { 156 | 157 | visitMethod(classNode, method); 158 | 159 | // List cachedTrys = new ArrayList<>(method.tryCatchBlocks); 160 | // AbstractInsnNode[] cachedInsns = method.instructions.toArray(); 161 | 162 | // try { 163 | // 164 | // visitMethod(classNode, method); 165 | // 166 | // Analyzer analyzer = new Analyzer<>(new BasicInterpreter()); 167 | // analyzer.analyzeAndComputeMaxs(classNode.name, method); 168 | // 169 | // } catch (Exception ex) { 170 | // // Failed to inline 171 | // error("Failed to obfuscate method %s.%s%s [%s]", classNode.name, method.name, method.desc, ex.getMessage()); 172 | //// 173 | // method.tryCatchBlocks = cachedTrys; 174 | // method.instructions.clear(); 175 | // for (AbstractInsnNode cachedInsn : cachedInsns) { 176 | // method.instructions.add(cachedInsn); 177 | // } 178 | // } 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/general/StripTransformer.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.transformer.general; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.transformer.Transformer; 5 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 6 | import org.objectweb.asm.tree.AbstractInsnNode; 7 | import org.objectweb.asm.tree.FieldNode; 8 | import org.objectweb.asm.tree.LineNumberNode; 9 | import org.objectweb.asm.tree.MethodNode; 10 | 11 | public class StripTransformer extends Transformer { 12 | 13 | public StripTransformer(Obf obf) { 14 | super(obf); 15 | } 16 | 17 | @Override 18 | public String getSection() { 19 | return "general.strip"; 20 | } 21 | 22 | @Override 23 | protected void after() { 24 | for (ClassWrapper classNode : obf.getClasses()) { 25 | for (MethodNode method : classNode.methods) { 26 | method.localVariables = null; 27 | method.parameters = null; 28 | method.signature = null; 29 | for (AbstractInsnNode instruction : method.instructions) { 30 | if (instruction instanceof LineNumberNode) { 31 | method.instructions.remove(instruction); 32 | } 33 | } 34 | } 35 | for (FieldNode field : classNode.fields) 36 | field.signature = null; 37 | classNode.signature = null; 38 | classNode.innerClasses.clear(); 39 | classNode.sourceFile = null; 40 | classNode.sourceDebug = null; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/methods/DynamicTransformer.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.transformer.methods; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.transformer.Transformer; 5 | import com.cheatbreaker.obf.utils.asm.AsmUtils; 6 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 7 | import com.cheatbreaker.obf.utils.asm.NodeAccess; 8 | import com.cheatbreaker.obf.utils.tree.HierarchyUtils; 9 | import org.apache.commons.lang3.RandomStringUtils; 10 | import org.objectweb.asm.Handle; 11 | import org.objectweb.asm.Label; 12 | import org.objectweb.asm.MethodVisitor; 13 | import org.objectweb.asm.Type; 14 | import org.objectweb.asm.tree.*; 15 | 16 | import java.lang.reflect.Modifier; 17 | import java.util.ArrayList; 18 | 19 | // Was testing something :) 20 | public class DynamicTransformer extends Transformer { 21 | 22 | private static String handlerName; 23 | 24 | public DynamicTransformer(Obf obf) { 25 | super(obf); 26 | } 27 | 28 | @Override 29 | public String getSection() { 30 | return "methods.dynamic"; 31 | } 32 | 33 | @Override 34 | protected void visit(ClassWrapper classNode) { 35 | 36 | while (handlerName == null) { 37 | for (ClassWrapper cn : new ArrayList<>(obf.getClasses())) { 38 | if (nextBoolean(5)) { 39 | handlerName = AsmUtils.parentName(cn.name) + RandomStringUtils.randomAlphabetic(3); 40 | if (obf.assureLoaded(handlerName) != null) { 41 | handlerName = null; 42 | } else { 43 | ClassWrapper handlerClass = new ClassWrapper(false); 44 | handlerClass.visit(V1_8, ACC_PUBLIC, handlerName, null, "java/lang/Object", null); 45 | 46 | MethodVisitor methodVisitor = handlerClass.visitMethod(ACC_PUBLIC, 47 | "", "()V", null, null); 48 | methodVisitor.visitCode(); 49 | methodVisitor.visitVarInsn(ALOAD, 0); 50 | methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V"); 51 | methodVisitor.visitInsn(RETURN); 52 | methodVisitor.visitEnd(); 53 | 54 | methodVisitor = handlerClass.visitMethod(ACC_PUBLIC | ACC_STATIC | ACC_VARARGS, 55 | "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); 56 | methodVisitor.visitCode(); 57 | Label label0 = new Label(); 58 | Label label1 = new Label(); 59 | Label label2 = new Label(); 60 | methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable"); 61 | methodVisitor.visitLabel(label0); 62 | methodVisitor.visitLineNumber(15, label0); 63 | methodVisitor.visitVarInsn(ALOAD, 0); 64 | methodVisitor.visitInsn(ICONST_3); 65 | methodVisitor.visitVarInsn(ALOAD, 0); 66 | methodVisitor.visitInsn(ICONST_3); 67 | methodVisitor.visitInsn(AALOAD); 68 | methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/invoke/MethodHandle"); 69 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invoke", "()Ljava/lang/String;", false); 70 | methodVisitor.visitInsn(AASTORE); 71 | Label label3 = new Label(); 72 | methodVisitor.visitLabel(label3); 73 | methodVisitor.visitLineNumber(16, label3); 74 | methodVisitor.visitVarInsn(ALOAD, 0); 75 | methodVisitor.visitInsn(ICONST_2); 76 | methodVisitor.visitVarInsn(ALOAD, 0); 77 | methodVisitor.visitInsn(ICONST_2); 78 | methodVisitor.visitInsn(AALOAD); 79 | methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/invoke/MethodHandle"); 80 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invoke", "()Ljava/lang/String;", false); 81 | methodVisitor.visitInsn(AASTORE); 82 | Label label4 = new Label(); 83 | methodVisitor.visitLabel(label4); 84 | methodVisitor.visitLineNumber(17, label4); 85 | methodVisitor.visitVarInsn(ALOAD, 0); 86 | methodVisitor.visitInsn(ICONST_4); 87 | methodVisitor.visitInsn(AALOAD); 88 | methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/invoke/MethodHandle"); 89 | methodVisitor.visitVarInsn(ALOAD, 0); 90 | methodVisitor.visitInsn(ICONST_5); 91 | methodVisitor.visitInsn(AALOAD); 92 | methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/invoke/MethodHandle"); 93 | methodVisitor.visitInsn(ICONST_4); 94 | methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Object"); 95 | methodVisitor.visitInsn(DUP); 96 | methodVisitor.visitInsn(ICONST_0); 97 | methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false); 98 | methodVisitor.visitInsn(AASTORE); 99 | methodVisitor.visitInsn(DUP); 100 | methodVisitor.visitInsn(ICONST_1); 101 | methodVisitor.visitVarInsn(ALOAD, 0); 102 | methodVisitor.visitInsn(ICONST_1); 103 | methodVisitor.visitInsn(AALOAD); 104 | methodVisitor.visitInsn(AASTORE); 105 | methodVisitor.visitInsn(DUP); 106 | methodVisitor.visitInsn(ICONST_2); 107 | methodVisitor.visitVarInsn(ALOAD, 0); 108 | methodVisitor.visitInsn(ICONST_2); 109 | methodVisitor.visitInsn(AALOAD); 110 | methodVisitor.visitInsn(AASTORE); 111 | methodVisitor.visitInsn(DUP); 112 | methodVisitor.visitInsn(ICONST_3); 113 | methodVisitor.visitVarInsn(ALOAD, 0); 114 | methodVisitor.visitInsn(ICONST_3); 115 | methodVisitor.visitInsn(AALOAD); 116 | Label label5 = new Label(); 117 | methodVisitor.visitLabel(label5); 118 | methodVisitor.visitLineNumber(18, label5); 119 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false); 120 | methodVisitor.visitVarInsn(ALOAD, 0); 121 | methodVisitor.visitInsn(ICONST_1); 122 | methodVisitor.visitInsn(AALOAD); 123 | methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Class"); 124 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false); 125 | methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", false); 126 | methodVisitor.visitInsn(AASTORE); 127 | Label label6 = new Label(); 128 | methodVisitor.visitLabel(label6); 129 | methodVisitor.visitLineNumber(17, label6); 130 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;", false); 131 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "bindTo", "(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false); 132 | methodVisitor.visitVarInsn(ALOAD, 0); 133 | methodVisitor.visitInsn(ICONST_0); 134 | methodVisitor.visitInsn(AALOAD); 135 | Label label7 = new Label(); 136 | methodVisitor.visitLabel(label7); 137 | methodVisitor.visitLineNumber(18, label7); 138 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invoke", "(Ljava/lang/Object;)Ljava/lang/Object;", false); 139 | methodVisitor.visitLabel(label1); 140 | methodVisitor.visitLineNumber(17, label1); 141 | methodVisitor.visitInsn(ARETURN); 142 | methodVisitor.visitLabel(label2); 143 | methodVisitor.visitLineNumber(12, label2); 144 | methodVisitor.visitVarInsn(ASTORE, 1); 145 | Label label8 = new Label(); 146 | methodVisitor.visitLabel(label8); 147 | methodVisitor.visitVarInsn(ALOAD, 1); 148 | methodVisitor.visitInsn(ATHROW); 149 | Label label9 = new Label(); 150 | methodVisitor.visitLabel(label9); 151 | methodVisitor.visitEnd(); 152 | 153 | obf.getClasses().add(handlerClass); 154 | } 155 | } 156 | } 157 | } 158 | 159 | for (MethodNode method : classNode.methods) { 160 | for (AbstractInsnNode instruction : method.instructions) { 161 | if (instruction instanceof MethodInsnNode) { 162 | MethodInsnNode node = (MethodInsnNode) instruction; 163 | 164 | ClassWrapper owner = obf.assureLoaded(node.owner); 165 | if (owner == null) continue; 166 | 167 | NodeAccess access = new NodeAccess(owner.access); 168 | if (!checkAccess(access, obf.assureLoaded(handlerName), owner)) continue; 169 | 170 | MethodNode target = AsmUtils.findMethodSuper(owner, node.name, node.desc); 171 | if (target == null) continue; 172 | 173 | access = new NodeAccess(target.access); 174 | if (!checkAccess(access, obf.assureLoaded(handlerName), owner)) continue; 175 | target.access = access.access; 176 | 177 | if (node.getOpcode() == INVOKESTATIC) { 178 | InsnList list = new InsnList(); 179 | Type[] args = Type.getArgumentTypes(node.desc); 180 | InsnList stack = storeStack(false, args); 181 | if (stack == null) continue; 182 | list.add(stack); 183 | 184 | list.add(AsmUtils.pushInt(6)); 185 | list.add(new TypeInsnNode(ANEWARRAY, "java/lang/Object")); 186 | list.add(new InsnNode(DUP_X1)); 187 | list.add(new InsnNode(SWAP)); 188 | list.add(AsmUtils.pushInt(0)); 189 | list.add(new InsnNode(SWAP)); 190 | list.add(new InsnNode(AASTORE)); 191 | 192 | list.add(new InsnNode(DUP)); 193 | list.add(new LdcInsnNode(Type.getType("L" + node.owner + ";"))); 194 | list.add(AsmUtils.pushInt(1)); 195 | list.add(new InsnNode(SWAP)); 196 | list.add(new InsnNode(AASTORE)); 197 | 198 | list.add(new InsnNode(DUP)); 199 | list.add(methodHandle(H_INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false)); 200 | list.add(new LdcInsnNode(node.name)); 201 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "bindTo", "(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false)); 202 | list.add(AsmUtils.pushInt(2)); 203 | list.add(new InsnNode(SWAP)); 204 | list.add(new InsnNode(AASTORE)); 205 | 206 | list.add(new InsnNode(DUP)); 207 | list.add(methodHandle(H_INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false)); 208 | list.add(new LdcInsnNode(node.desc)); 209 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "bindTo", "(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false)); 210 | list.add(AsmUtils.pushInt(3)); 211 | list.add(new InsnNode(SWAP)); 212 | list.add(new InsnNode(AASTORE)); 213 | 214 | list.add(new InsnNode(DUP)); 215 | list.add(methodHandle(H_INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;", false)); 216 | list.add(AsmUtils.pushInt(4)); 217 | list.add(new InsnNode(SWAP)); 218 | list.add(new InsnNode(AASTORE)); 219 | 220 | list.add(new InsnNode(DUP)); 221 | list.add(methodHandle(H_INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false)); 222 | list.add(AsmUtils.pushInt(5)); 223 | list.add(new InsnNode(SWAP)); 224 | list.add(new InsnNode(AASTORE)); 225 | 226 | list.add(new MethodInsnNode(INVOKESTATIC, handlerName, "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;")); 227 | Type returnType = Type.getReturnType(node.desc); 228 | if (returnType.equals(Type.VOID_TYPE)) { 229 | list.add(new InsnNode(POP)); 230 | } else { 231 | AsmUtils.unboxPrimitive(returnType.getDescriptor(), list); 232 | } 233 | 234 | method.instructions.insert(instruction, list); 235 | method.instructions.remove(instruction); 236 | } 237 | else if (node.getOpcode() == INVOKEVIRTUAL || 238 | node.getOpcode() == INVOKEINTERFACE) { 239 | InsnList list = new InsnList(); 240 | Type[] args = Type.getArgumentTypes(node.desc); 241 | 242 | InsnList stack = storeStack(true, args); 243 | if (stack == null) continue; 244 | list.add(stack); 245 | 246 | list.add(AsmUtils.pushInt(6)); 247 | list.add(new TypeInsnNode(ANEWARRAY, "java/lang/Object")); 248 | list.add(new InsnNode(DUP_X1)); 249 | list.add(new InsnNode(SWAP)); 250 | list.add(AsmUtils.pushInt(0)); 251 | list.add(new InsnNode(SWAP)); 252 | list.add(new InsnNode(AASTORE)); 253 | 254 | list.add(new InsnNode(DUP)); 255 | list.add(new LdcInsnNode(Type.getType("L" + node.owner + ";"))); 256 | list.add(AsmUtils.pushInt(1)); 257 | list.add(new InsnNode(SWAP)); 258 | list.add(new InsnNode(AASTORE)); 259 | 260 | list.add(new InsnNode(DUP)); 261 | list.add(methodHandle(H_INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false)); 262 | list.add(new LdcInsnNode(node.name)); 263 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "bindTo", "(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false)); 264 | list.add(AsmUtils.pushInt(2)); 265 | list.add(new InsnNode(SWAP)); 266 | list.add(new InsnNode(AASTORE)); 267 | 268 | list.add(new InsnNode(DUP)); 269 | list.add(methodHandle(H_INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false)); 270 | list.add(new LdcInsnNode(node.desc)); 271 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "bindTo", "(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false)); 272 | list.add(AsmUtils.pushInt(3)); 273 | list.add(new InsnNode(SWAP)); 274 | list.add(new InsnNode(AASTORE)); 275 | 276 | list.add(new InsnNode(DUP)); 277 | list.add(methodHandle(H_INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;", false)); 278 | list.add(AsmUtils.pushInt(4)); 279 | list.add(new InsnNode(SWAP)); 280 | list.add(new InsnNode(AASTORE)); 281 | 282 | list.add(new InsnNode(DUP)); 283 | list.add(methodHandle(H_INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findVirtual", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false)); 284 | list.add(AsmUtils.pushInt(5)); 285 | list.add(new InsnNode(SWAP)); 286 | list.add(new InsnNode(AASTORE)); 287 | 288 | list.add(new MethodInsnNode(INVOKESTATIC, handlerName, "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;")); 289 | Type returnType = Type.getReturnType(node.desc); 290 | if (returnType.equals(Type.VOID_TYPE)) { 291 | list.add(new InsnNode(POP)); 292 | } else { 293 | AsmUtils.unboxPrimitive(returnType.getDescriptor(), list); 294 | } 295 | 296 | method.instructions.insert(instruction, list); 297 | method.instructions.remove(instruction); 298 | } 299 | } 300 | } 301 | } 302 | 303 | } 304 | 305 | private InsnList methodHandle(int opcode, String owner, String name, String desc, boolean itf) { 306 | InsnList list = new InsnList(); 307 | list.add(new LdcInsnNode(new Handle(opcode, owner, name, desc, itf))); 308 | return list; 309 | } 310 | 311 | private boolean checkAccess(NodeAccess access, ClassWrapper classNode, ClassWrapper ownerClass) { 312 | 313 | int acc = access.access; 314 | if (Modifier.isPublic(acc)) 315 | return true; 316 | 317 | if (!Modifier.isPublic(acc) && classNode.name.equals(ownerClass.name)) 318 | return true; 319 | 320 | if (Modifier.isProtected(acc)) { 321 | String parent = ownerClass.name; 322 | if (HierarchyUtils.isAssignableFrom(parent, classNode.name)) { 323 | return true; 324 | } 325 | } 326 | 327 | if (!Modifier.isPrivate(acc) && (ACC_SYNTHETIC & acc) == 0) { 328 | String pkg1 = AsmUtils.parentName(classNode.name); 329 | String pkg2 = AsmUtils.parentName(ownerClass.name); 330 | return pkg1.equals(pkg2); 331 | } 332 | 333 | return false; 334 | } 335 | 336 | private InsnList storeStack(boolean virtual, Type[] types) { 337 | InsnList list = new InsnList(); 338 | 339 | Type[] args = new Type[types.length + (virtual ? 1 : 0)]; 340 | 341 | for (Type type : types) { 342 | if (type.getDescriptor().startsWith("[")) { 343 | String actual = type.getDescriptor().substring(type.getDescriptor().lastIndexOf('[') + 1); 344 | if (actual.equals("Ljava/lang/Object;")) { 345 | return null; 346 | } 347 | } 348 | } 349 | 350 | System.arraycopy(types, 0, args, virtual ? 1 : 0, types.length); 351 | 352 | if (virtual) { 353 | args[0] = Type.getType("Ljava/lang/Object;"); 354 | } 355 | 356 | list.add(AsmUtils.pushInt(args.length)); 357 | list.add(new TypeInsnNode(ANEWARRAY, "java/lang/Object")); 358 | 359 | for (int i = args.length - 1; i >= 0; i--) { 360 | Type arg = args[i]; 361 | InsnList sub = new InsnList(); 362 | if (arg.getSize() > 1) { 363 | sub.add(new InsnNode(DUP_X2)); 364 | sub.add(new InsnNode(DUP_X2)); 365 | sub.add(new InsnNode(POP)); 366 | sub.add(AsmUtils.pushInt(i)); 367 | sub.add(new InsnNode(DUP_X2)); 368 | sub.add(new InsnNode(POP)); 369 | } else { 370 | sub.add(new InsnNode(DUP_X1)); 371 | sub.add(new InsnNode(SWAP)); 372 | sub.add(AsmUtils.pushInt(i)); 373 | sub.add(new InsnNode(SWAP)); 374 | } 375 | AsmUtils.boxPrimitive(arg.getDescriptor(), sub); 376 | sub.add(new InsnNode(AASTORE)); 377 | list.add(sub); 378 | } 379 | 380 | return list; 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/misc/ChecksumTransformer.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.transformer.misc; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.transformer.Transformer; 5 | import com.cheatbreaker.obf.utils.asm.AsmUtils; 6 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 7 | import com.cheatbreaker.obf.utils.asm.ContextClassWriter; 8 | import com.cheatbreaker.obf.utils.pair.ClassMethodNode; 9 | import lombok.SneakyThrows; 10 | import org.apache.commons.lang3.RandomStringUtils; 11 | import org.objectweb.asm.ClassWriter; 12 | import org.objectweb.asm.Label; 13 | import org.objectweb.asm.Type; 14 | import org.objectweb.asm.tree.MethodInsnNode; 15 | import org.objectweb.asm.tree.MethodNode; 16 | import org.objectweb.asm.tree.analysis.Analyzer; 17 | import org.objectweb.asm.tree.analysis.BasicInterpreter; 18 | 19 | import java.io.ByteArrayInputStream; 20 | import java.io.ByteArrayOutputStream; 21 | import java.util.ArrayList; 22 | import java.util.Arrays; 23 | import java.util.Collections; 24 | import java.util.List; 25 | 26 | public class ChecksumTransformer extends Transformer { 27 | 28 | public ChecksumTransformer(Obf obf) { 29 | super(obf); 30 | 31 | this.targets = config.getStringList("targets"); 32 | this.reobfTarget = config.getBoolean("reobf", true); 33 | this.holders = config.getStringList("holders"); 34 | this.randomHolders = config.getBoolean("randomHolders", false); 35 | 36 | if (randomHolders) { 37 | this.holders = new ArrayList<>(); 38 | for (ClassWrapper classNode : obf.getClasses()) { 39 | if (targets.contains(classNode.name)) { 40 | continue; 41 | } 42 | holders.add(classNode.name); 43 | } 44 | } 45 | 46 | } 47 | 48 | private final List targets; 49 | private List holders; 50 | private final boolean reobfTarget; 51 | private final boolean randomHolders; 52 | 53 | @Override 54 | public String getSection() { 55 | return "misc.checksum"; 56 | } 57 | 58 | @SneakyThrows 59 | @Override 60 | protected void after() { 61 | 62 | if (targets.isEmpty() || holders.isEmpty()) return; 63 | 64 | if (!randomHolders) { 65 | for (String target : targets) { 66 | for (String holder : holders) { 67 | if (target.equals(holder)) { 68 | error("Target and holder are the same: %s", target); 69 | } 70 | } 71 | } 72 | } 73 | 74 | // log("Targets: %s", targets); 75 | // log("Holders: %s", randomHolders ? "All" : holders); 76 | 77 | List classNodes = new ArrayList<>(obf.getClasses()); 78 | Collections.shuffle(classNodes); 79 | 80 | boolean applied = false; 81 | 82 | for (ClassWrapper classNode : classNodes) { 83 | if (targets.contains(classNode.name)) { 84 | 85 | Collections.shuffle(holders); 86 | String holderName = holders.get(0); 87 | 88 | byte[] b; 89 | ContextClassWriter writer = new ContextClassWriter(ClassWriter.COMPUTE_FRAMES); 90 | classNode.accept(writer); 91 | b = writer.toByteArray(); 92 | 93 | obf.addGeneratedClass(classNode.name, b); 94 | 95 | ClassWrapper holder = obf.assureLoaded(holderName); 96 | if (holder == null) { 97 | error("Holder class not found: %s", holderName); 98 | } else { 99 | log("Applying checksum to %s inside of %s", classNode.name, holderName); 100 | applied = true; 101 | MethodNode checkMethod; 102 | String name = config.getString("method-name"); 103 | if (name.equals("random")) name = RandomStringUtils.random(10); 104 | String desc = "()V"; 105 | checkMethod = new MethodNode(ACC_PRIVATE | ACC_SYNTHETIC | ACC_STATIC, name, desc, null, null); 106 | checkMethod.visitCode(); 107 | 108 | Label label2 = new Label(); 109 | Label label3 = new Label(); 110 | 111 | if (config.getBoolean("reduced", false) || obf.isTransformerEnabled(PackerTransformer.class)) { 112 | 113 | log("Reduced checksum check due to packer"); 114 | 115 | checkMethod.visitLdcInsn(Type.getType("L" + holder.name + ";")); 116 | String s = "/" + classNode.name + ".class"; 117 | checkMethod.visitLdcInsn(s); 118 | 119 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;", false); 120 | checkMethod.visitJumpInsn(IFNONNULL, label3); 121 | checkMethod.visitJumpInsn(GOTO, label2); 122 | 123 | } else { 124 | 125 | int r1 = random.nextInt(); 126 | int r2 = random.nextInt(); 127 | 128 | int real = r1; 129 | 130 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 131 | ByteArrayInputStream bais = new ByteArrayInputStream(b); 132 | 133 | byte[] byArray = new byte[4096]; 134 | while (true) { 135 | int n = bais.read(byArray, 0, byArray.length); 136 | real ^= r2 ^ baos.size(); 137 | if (n == -1) break; 138 | baos.write(byArray, 0, n); 139 | } 140 | 141 | int bStart = writer.offsetCPStart; 142 | int bEnd = writer.offsetMethodEnd; 143 | 144 | byte[] b2 = new byte[bEnd - bStart]; 145 | System.arraycopy(b, bStart, b2, 0, b2.length); 146 | 147 | int hash = Arrays.hashCode(b2) ^ real; 148 | 149 | log("Hash: %d Start: %d End: %d", hash, bStart, bEnd); 150 | 151 | checkMethod.visitLdcInsn(r1); 152 | checkMethod.visitVarInsn(ISTORE, 5); 153 | 154 | checkMethod.visitLdcInsn(Type.getType("L" + holder.name + ";")); 155 | char[] s = ("/" + classNode.name + ".class").toCharArray(); 156 | checkMethod.visitTypeInsn(NEW, "java/lang/String"); 157 | checkMethod.visitInsn(DUP); 158 | 159 | checkMethod.visitLdcInsn(s.length); 160 | checkMethod.visitIntInsn(NEWARRAY, T_CHAR); 161 | for (int i = 0; i < s.length; i++) { 162 | checkMethod.visitInsn(DUP); 163 | checkMethod.instructions.add(AsmUtils.pushInt(i)); 164 | checkMethod.instructions.add(AsmUtils.pushInt(s[i] ^ r1)); 165 | checkMethod.visitVarInsn(ILOAD, 5); 166 | checkMethod.visitInsn(IXOR); 167 | checkMethod.visitInsn(I2C); 168 | checkMethod.visitInsn(CASTORE); 169 | } 170 | checkMethod.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([C)V", false); 171 | 172 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;", false); 173 | checkMethod.visitVarInsn(ASTORE, 1); 174 | 175 | checkMethod.visitVarInsn(ALOAD, 1); 176 | checkMethod.visitJumpInsn(IFNULL, label3); 177 | 178 | checkMethod.visitTypeInsn(NEW, "java/io/ByteArrayOutputStream"); 179 | checkMethod.visitInsn(DUP); 180 | checkMethod.visitMethodInsn(INVOKESPECIAL, "java/io/ByteArrayOutputStream", "", "()V", false); 181 | checkMethod.visitVarInsn(ASTORE, 2); 182 | checkMethod.visitIntInsn(SIPUSH, 4096); 183 | checkMethod.visitIntInsn(NEWARRAY, T_BYTE); 184 | checkMethod.visitVarInsn(ASTORE, 3); 185 | Label label0 = new Label(); 186 | checkMethod.visitLabel(label0); 187 | checkMethod.visitInsn(ICONST_M1); 188 | checkMethod.visitVarInsn(ALOAD, 1); 189 | checkMethod.visitVarInsn(ALOAD, 3); 190 | checkMethod.visitInsn(ICONST_0); 191 | checkMethod.visitVarInsn(ALOAD, 3); 192 | checkMethod.visitInsn(ARRAYLENGTH); 193 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/io/InputStream", "read", "([BII)I", false); 194 | 195 | checkMethod.visitVarInsn(ILOAD, 5); 196 | checkMethod.visitLdcInsn(r2); 197 | checkMethod.visitVarInsn(ALOAD, 2); 198 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/io/ByteArrayOutputStream", "size", "()I", false); 199 | checkMethod.visitInsn(IXOR); 200 | checkMethod.visitInsn(IXOR); 201 | 202 | checkMethod.visitVarInsn(ISTORE, 5); 203 | 204 | checkMethod.visitInsn(DUP); 205 | checkMethod.visitVarInsn(ISTORE, 4); 206 | Label label1 = new Label(); 207 | checkMethod.visitJumpInsn(IF_ICMPEQ, label1); 208 | checkMethod.visitVarInsn(ALOAD, 2); 209 | checkMethod.visitVarInsn(ALOAD, 3); 210 | checkMethod.visitInsn(ICONST_0); 211 | checkMethod.visitVarInsn(ILOAD, 4); 212 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/io/ByteArrayOutputStream", "write", "([BII)V", false); 213 | checkMethod.visitJumpInsn(GOTO, label0); 214 | checkMethod.visitLabel(label1); 215 | 216 | checkMethod.visitVarInsn(ALOAD, 2); 217 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/io/ByteArrayOutputStream", "toByteArray", "()[B", false); 218 | 219 | checkMethod.visitLdcInsn(bEnd - bStart); 220 | checkMethod.visitIntInsn(NEWARRAY, T_BYTE); 221 | checkMethod.visitVarInsn(ASTORE, 6); 222 | 223 | checkMethod.visitLdcInsn(bStart); 224 | checkMethod.visitVarInsn(ALOAD, 6); 225 | checkMethod.visitInsn(ICONST_0); 226 | checkMethod.visitVarInsn(ALOAD, 6); 227 | checkMethod.visitInsn(ARRAYLENGTH); 228 | checkMethod.visitMethodInsn(INVOKESTATIC, "java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", false); 229 | 230 | checkMethod.visitVarInsn(ALOAD, 6); 231 | 232 | checkMethod.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "hashCode", "([B)I", false); 233 | 234 | checkMethod.visitVarInsn(ILOAD, 5); 235 | checkMethod.visitLdcInsn(hash); 236 | checkMethod.visitInsn(IXOR); 237 | 238 | checkMethod.visitJumpInsn(IF_ICMPEQ, label2); 239 | 240 | } 241 | 242 | checkMethod.visitLabel(label3); 243 | 244 | checkMethod.visitMethodInsn(INVOKESTATIC, "sun/misc/Launcher", "getLauncher", "()Lsun/misc/Launcher;", false); 245 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "sun/misc/Launcher", "getClassLoader", "()Ljava/lang/ClassLoader;", false); 246 | checkMethod.visitInsn(DUP); 247 | checkMethod.visitVarInsn(ASTORE, 7); 248 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); 249 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getSuperclass", "()Ljava/lang/Class;", false); 250 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredFields", "()[Ljava/lang/reflect/Field;", false); 251 | checkMethod.visitInsn(ICONST_0); 252 | checkMethod.visitInsn(AALOAD); 253 | checkMethod.visitVarInsn(ASTORE, 8); 254 | checkMethod.visitVarInsn(ALOAD, 8); 255 | checkMethod.visitInsn(ICONST_1); 256 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Field", "setAccessible", "(Z)V", false); 257 | checkMethod.visitVarInsn(ALOAD, 8); 258 | checkMethod.visitVarInsn(ALOAD, 7); 259 | checkMethod.visitTypeInsn(NEW, "sun/misc/URLClassPath"); 260 | checkMethod.visitInsn(DUP); 261 | checkMethod.visitInsn(ICONST_0); 262 | checkMethod.visitTypeInsn(ANEWARRAY, "java/net/URL"); 263 | checkMethod.visitInsn(ACONST_NULL); 264 | checkMethod.visitMethodInsn(INVOKESPECIAL, "sun/misc/URLClassPath", "", "([Ljava/net/URL;Ljava/security/AccessControlContext;)V", false); 265 | checkMethod.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Field", "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", false); 266 | 267 | checkMethod.visitLabel(label2); 268 | 269 | checkMethod.visitInsn(RETURN); 270 | checkMethod.visitEnd(); 271 | 272 | holder.methods.add(checkMethod); 273 | 274 | Analyzer analyzer = new Analyzer<>(new BasicInterpreter()); 275 | analyzer.analyzeAndComputeMaxs(holder.name, checkMethod); 276 | 277 | MethodNode clinit = AsmUtils.getClinit(holder); 278 | clinit.instructions.insertBefore(clinit.instructions.getFirst(), new MethodInsnNode(INVOKESTATIC, holder.name, name, 279 | desc)); 280 | 281 | if (reobfTarget) { 282 | for (Transformer transformer : obf.getTransformers()) { 283 | // boolean old = transformer.enabled; 284 | // transformer.enabled = true; 285 | if (!transformer.canBeIterated) continue; 286 | transformer.target = new ClassMethodNode(holder, checkMethod); 287 | transformer.run(holder); 288 | transformer.target = null; 289 | // transformer.enabled = old; 290 | } 291 | } 292 | } 293 | 294 | break; 295 | } 296 | } 297 | 298 | if (!applied) { 299 | error("Could not find any targets and holders"); 300 | } 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/misc/VariableTransformer.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.transformer.misc; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.transformer.Transformer; 5 | import com.cheatbreaker.obf.utils.asm.AsmUtils; 6 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 7 | import com.cheatbreaker.obf.utils.pair.ClassMethodNode; 8 | import lombok.SneakyThrows; 9 | import org.objectweb.asm.Type; 10 | import org.objectweb.asm.tree.*; 11 | import org.objectweb.asm.tree.analysis.Analyzer; 12 | import org.objectweb.asm.tree.analysis.BasicInterpreter; 13 | import org.objectweb.asm.tree.analysis.BasicValue; 14 | import org.objectweb.asm.tree.analysis.Frame; 15 | 16 | import java.lang.reflect.Modifier; 17 | import java.util.*; 18 | 19 | public class VariableTransformer extends Transformer { 20 | 21 | public VariableTransformer(Obf obf) { 22 | super(obf); 23 | } 24 | 25 | @Override 26 | public String getSection() { 27 | return "misc.vars"; 28 | } 29 | 30 | @SneakyThrows 31 | @Override 32 | protected void visit(ClassWrapper classNode) { 33 | for (MethodNode method : classNode.methods) { 34 | ClassMethodNode cmn = new ClassMethodNode(classNode, method); 35 | if (target == null || target.equals(cmn)) { 36 | if (method.instructions.size() == 0) continue; 37 | if (AsmUtils.codeSize(method) * 2 >= 50000) continue; 38 | 39 | Analyzer analyzer = new Analyzer<>(new BasicInterpreter()); 40 | 41 | Frame[] frames; 42 | 43 | try { 44 | frames = analyzer.analyzeAndComputeMaxs(classNode.name, method); 45 | } catch (Exception ex) { 46 | error("Failed to analyze method %s ", ex.getMessage()); 47 | continue; 48 | } 49 | 50 | int nonLocals = 0; 51 | if (!Modifier.isStatic(method.access)) nonLocals++; 52 | 53 | nonLocals += Arrays.stream(Type.getArgumentTypes(method.desc)).mapToInt(Type::getSize).sum(); 54 | 55 | int amt = method.maxLocals - nonLocals; 56 | 57 | int arrayVar = method.maxLocals; 58 | 59 | Map varMap = new HashMap<>(); 60 | LinkedList failedVars = new LinkedList<>(); 61 | LinkedList potential = new LinkedList<>(); 62 | 63 | for (int i = 0; i < amt; i++) { 64 | potential.add(i); 65 | } 66 | 67 | Collections.shuffle(potential); 68 | 69 | for (AbstractInsnNode instruction : method.instructions) { 70 | if (instruction instanceof VarInsnNode) { 71 | VarInsnNode var = (VarInsnNode) instruction; 72 | 73 | if (failedVars.contains(var.var)) continue; 74 | 75 | if (var.var < nonLocals) 76 | continue; 77 | 78 | boolean load = var.getOpcode() >= ILOAD && var.getOpcode() <= ALOAD; 79 | Frame frame = frames[instruction.index + (load ? 1 : 0)]; 80 | if (frame == null) { 81 | failedVars.add(var.var); 82 | continue; 83 | } 84 | 85 | BasicValue value; 86 | Type type; 87 | 88 | value = frame.getStack(frame.getStackSize() - 1); 89 | 90 | type = value.getType(); 91 | 92 | if (value == BasicValue.UNINITIALIZED_VALUE || 93 | // type.getSort() == Type.OBJECT || 94 | type.getInternalName().equals("null") || 95 | type.getInternalName().equals("java/lang/Object")) { 96 | failedVars.add(var.var); 97 | continue; 98 | } 99 | 100 | if (!varMap.containsKey(var.var)) 101 | varMap.put(var.var, potential.pop()); 102 | 103 | InsnList list = new InsnList(); 104 | 105 | if (load) { 106 | list.add(new VarInsnNode(ALOAD, arrayVar)); 107 | list.add(AsmUtils.pushInt(varMap.get(var.var))); 108 | list.add(new InsnNode(AALOAD)); 109 | AsmUtils.unboxPrimitive(type.getDescriptor(), list); 110 | } else { 111 | AsmUtils.boxPrimitive(type.getDescriptor(), list); 112 | list.add(new VarInsnNode(ALOAD, arrayVar)); 113 | list.add(new InsnNode(SWAP)); 114 | list.add(AsmUtils.pushInt(varMap.get(var.var))); 115 | list.add(new InsnNode(SWAP)); 116 | list.add(new InsnNode(AASTORE)); 117 | } 118 | 119 | method.instructions.insertBefore(instruction, list); 120 | method.instructions.remove(instruction); 121 | } 122 | } 123 | 124 | for (AbstractInsnNode instruction : method.instructions) { 125 | if (instruction instanceof IincInsnNode) { 126 | IincInsnNode inc = (IincInsnNode) instruction; 127 | 128 | if (!varMap.containsKey(inc.var)) 129 | continue; 130 | 131 | int var = varMap.get(inc.var); 132 | InsnList list = new InsnList(); 133 | 134 | list.add(new VarInsnNode(ALOAD, arrayVar)); 135 | list.add(AsmUtils.pushInt(var)); 136 | list.add(new InsnNode(DUP2)); 137 | list.add(new InsnNode(AALOAD)); 138 | AsmUtils.unboxPrimitive("I", list); 139 | list.add(AsmUtils.pushInt(inc.incr)); 140 | list.add(new InsnNode(IADD)); 141 | AsmUtils.boxPrimitive("I", list); 142 | list.add(new InsnNode(AASTORE)); 143 | 144 | method.instructions.insertBefore(instruction, list); 145 | method.instructions.remove(instruction); 146 | } 147 | } 148 | 149 | if (amt > 0) { 150 | method.maxLocals++; 151 | InsnList start = new InsnList(); 152 | start.add(AsmUtils.pushInt(amt)); 153 | start.add(new TypeInsnNode(ANEWARRAY, "java/lang/Object")); 154 | start.add(new VarInsnNode(ASTORE, arrayVar)); 155 | method.instructions.insertBefore(method.instructions.getFirst(), start); 156 | } 157 | } 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/natives/CodeHiderTransformer.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.transformer.natives; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.transformer.Transformer; 5 | import com.cheatbreaker.obf.utils.asm.AsmUtils; 6 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 7 | import com.cheatbreaker.obf.utils.asm.ContextClassWriter; 8 | import lombok.SneakyThrows; 9 | import org.apache.commons.lang3.RandomStringUtils; 10 | import org.objectweb.asm.ClassWriter; 11 | import org.objectweb.asm.Handle; 12 | import org.objectweb.asm.Type; 13 | import org.objectweb.asm.tree.*; 14 | 15 | import java.util.*; 16 | 17 | public class CodeHiderTransformer extends Transformer { 18 | 19 | private final List vmExcluded = new ArrayList<>(); 20 | 21 | 22 | public CodeHiderTransformer(Obf obf) { 23 | super(obf); 24 | canBeIterated = false; 25 | } 26 | 27 | @Override 28 | public String getSection() { 29 | return "natives.codehider"; 30 | } 31 | 32 | @SneakyThrows 33 | @Override 34 | protected void after() { 35 | 36 | for (ClassWrapper classNode : obf.getClasses()) { 37 | if (classNode.name.equals("vm/NativeHandler")) continue; 38 | transform(classNode); 39 | } 40 | } 41 | 42 | @SneakyThrows 43 | public void transform(ClassWrapper classNode) { 44 | 45 | // if (!Modifier.isPublic(classNode.access)) return; 46 | 47 | vmExcluded.clear(); 48 | 49 | String _name = classNode.name + RandomStringUtils.randomNumeric(50); 50 | String name = classNode.name; 51 | String bytesCallName = "bytesCall$" + RandomStringUtils.randomNumeric(10); 52 | 53 | { 54 | ContextClassWriter cw = new ContextClassWriter(ClassWriter.COMPUTE_FRAMES); 55 | classNode.name = _name; 56 | 57 | Map cached = new HashMap<>(); 58 | 59 | for (MethodNode method : new ArrayList<>(classNode.methods)) { 60 | if (!safe(method)) { 61 | continue; 62 | } 63 | setup(classNode, method, name); 64 | } 65 | 66 | for (MethodNode method : classNode.methods) { 67 | if (!method.name.equals("")) continue; 68 | 69 | AbstractInsnNode[] instructions = method.instructions.toArray(); 70 | cached.put(method.name + method.desc, instructions); 71 | 72 | LabelNode endLabel = new LabelNode(); 73 | InsnList list = new InsnList(); 74 | 75 | list.add(AsmUtils.pushInt((short) random.nextInt())); 76 | list.add(new InsnNode(ICONST_0)); 77 | list.add(new InsnNode(IXOR)); 78 | list.add(new JumpInsnNode(IFNE, endLabel)); 79 | 80 | method.instructions.insert(list); 81 | method.instructions.add(endLabel); 82 | method.instructions.add(new InsnNode(RETURN)); 83 | 84 | } 85 | 86 | int old = classNode.access; 87 | classNode.access |= ACC_PUBLIC; 88 | 89 | MethodNode bytesCall = new MethodNode(ACC_PUBLIC | ACC_STATIC, 90 | bytesCallName, "(Ljava/lang/Class;Ljava/lang/String;)[I", null, null); 91 | bytesCall.instructions = new InsnList(); 92 | bytesCall.instructions.add(new VarInsnNode(ALOAD, 0)); 93 | bytesCall.instructions.add(new VarInsnNode(ALOAD, 1)); 94 | bytesCall.instructions.add(new MethodInsnNode(INVOKESTATIC, "vm/NativeHandler", "raw_bytes", 95 | "(Ljava/lang/Class;Ljava/lang/String;)[I")); 96 | bytesCall.instructions.add(new InsnNode(ARETURN)); 97 | bytesCall.maxStack = 10; 98 | bytesCall.maxLocals = 10; 99 | 100 | classNode.methods.add(bytesCall); 101 | 102 | classNode.accept(cw); 103 | 104 | classNode.access = old; 105 | classNode.name = name; 106 | classNode.methods.remove(bytesCall); 107 | 108 | for (MethodNode method : classNode.methods) { 109 | String id = method.name + method.desc; 110 | if (cached.containsKey(id)) { 111 | method.instructions.clear(); 112 | Arrays.stream(cached.get(id)).forEach(method.instructions::add); 113 | } 114 | } 115 | 116 | obf.getLoader().addClass(_name, cw.toByteArray()); 117 | 118 | } 119 | 120 | for (MethodNode method : new ArrayList<>(classNode.methods)) { 121 | if (!safe(method)) { 122 | vmExcluded.add(method.name + method.desc); 123 | continue; 124 | } 125 | 126 | log("%s.%s%s", classNode.name, method.name, method.desc); 127 | 128 | int[] bytes; 129 | 130 | // Class klass = obf.getLoader().loadClass(_name.replace('/', '.'), false); 131 | // bytes = NativeHandler.raw_bytes(klass, method.name + method.desc); 132 | // 133 | try { 134 | Class klass = obf.getLoader().loadClass(_name.replace('/', '.'), true); 135 | bytes = (int[]) klass.getMethod(bytesCallName, Class.class, String.class).invoke(null, klass, method.name + method.desc); 136 | } catch (Exception ex) { 137 | ex.printStackTrace(); 138 | vmExcluded.add(method.name + method.desc); 139 | continue; 140 | } 141 | 142 | registerMethod(classNode, method, 143 | bytes); 144 | } 145 | 146 | ContextClassWriter cw = new ContextClassWriter(ClassWriter.COMPUTE_FRAMES, true, vmExcluded); 147 | classNode.accept(cw); 148 | 149 | obf.addGeneratedClass(classNode.name, cw.toByteArray()); 150 | 151 | } 152 | 153 | public void setup(ClassWrapper classNode, MethodNode method, String realName) { 154 | 155 | MethodNode clinit = AsmUtils.getClinit(classNode); 156 | 157 | MethodNode methodNode = new MethodNode(ACC_STATIC, 158 | "setup$" + Math.abs(realName.hashCode() + classNode.superName.hashCode() + 159 | method.name.hashCode() + method.desc.hashCode()), "()V", null, null); 160 | 161 | { 162 | vmExcluded.add(methodNode.name + methodNode.desc); 163 | 164 | InsnList list = new InsnList(); 165 | list.add(new LdcInsnNode(Type.getType("L" + realName + ";"))); 166 | list.add(new LdcInsnNode(method.name + method.desc)); 167 | 168 | list.add(new InsnNode(ICONST_0)); 169 | list.add(new IntInsnNode(NEWARRAY, T_INT)); 170 | 171 | list.add(new MethodInsnNode(INVOKESTATIC, "vm/NativeHandler", "transformMethod", "(Ljava/lang/Class;Ljava/lang/String;[I)V", false)); 172 | 173 | list.add(new InsnNode(RETURN)); 174 | 175 | methodNode.instructions = list; 176 | 177 | classNode.methods.add(methodNode); 178 | } 179 | 180 | AbstractInsnNode start = null; 181 | 182 | for (AbstractInsnNode instruction : clinit.instructions) { 183 | if (instruction instanceof MethodInsnNode) { 184 | MethodInsnNode node = (MethodInsnNode) instruction; 185 | if (node.name.equals("decryptConstantPool")) { 186 | start = instruction; 187 | break; 188 | } 189 | } 190 | } 191 | 192 | if (start != null) clinit.instructions.insert(start, new MethodInsnNode(INVOKESTATIC, realName, 193 | methodNode.name, methodNode.desc, false)); 194 | else clinit.instructions.insert(new MethodInsnNode(INVOKESTATIC, realName, 195 | methodNode.name, methodNode.desc, false)); 196 | } 197 | 198 | public void registerMethod(ClassWrapper classNode, MethodNode method, int[] code) { 199 | 200 | InsnList list = new InsnList(); 201 | // list.add(new LdcInsnNode(Type.getType("L" + classNode.name + ";"))); 202 | // list.add(new LdcInsnNode(method.name + method.desc)); 203 | 204 | list.add(AsmUtils.pushInt(code.length)); 205 | list.add(new IntInsnNode(NEWARRAY, T_INT)); 206 | 207 | for (int i = 0; i < code.length; i++) { 208 | list.add(new InsnNode(DUP)); 209 | list.add(AsmUtils.pushInt(i)); 210 | list.add(AsmUtils.pushInt((byte) code[i])); 211 | list.add(new InsnNode(IASTORE)); 212 | } 213 | 214 | MethodNode mn = AsmUtils.findMethod(classNode, "setup$" + Math.abs(classNode.name.hashCode() + classNode.superName.hashCode() + 215 | method.name.hashCode() + method.desc.hashCode()), "()V"); 216 | for (AbstractInsnNode instruction : mn.instructions) { 217 | if (instruction instanceof IntInsnNode) { 218 | method.instructions.insert(instruction, list); 219 | method.instructions.remove(instruction.getPrevious()); 220 | method.instructions.remove(instruction); 221 | } 222 | } 223 | 224 | // list.add(new MethodInsnNode(INVOKESTATIC, "vm/NativeHandler", "transformMethod", "(Ljava/lang/Class;Ljava/lang/String;[I)V", false)); 225 | 226 | // String name = String.valueOf(classNode.name.hashCode() + classNode.methods.hashCode()); 227 | // 228 | // for (ClassWrapper cn : obf.getClasses()) { 229 | // if (cn.name.equals("vm/NativeHandler")) { 230 | // 231 | // if (cn.methods.stream().noneMatch(m -> m.name.equals(name))) { 232 | // MethodNode mn2 = new MethodNode(ACC_STATIC, name, "()V", null, null); 233 | // mn2.instructions.add(new InsnNode(RETURN)); 234 | // cn.methods.add(mn2); 235 | // 236 | // MethodNode clinit = AsmUtils.getClinit(cn); 237 | // clinit.instructions.insertBefore(clinit.instructions.getLast(), new MethodInsnNode(INVOKESTATIC, "vm/NativeHandler", name, 238 | // "()V", false)); 239 | // } 240 | // 241 | // MethodNode mn = cn.methods.stream().filter(m -> m.name.equals(name)).findFirst().orElse(null); 242 | // mn.instructions.insert(list); 243 | // 244 | // } 245 | // } 246 | } 247 | 248 | public boolean safe(MethodNode method) { 249 | if (method.name.equals("")) return false; 250 | if (method.name.equals("")) return false; 251 | if (method.instructions.size() == 0) return false; 252 | if (method.instructions.size() >= AsmUtils.MAX_INSTRUCTIONS/5) return false; 253 | if (method.tryCatchBlocks.size() > 0) return false; 254 | if ((method.access & ACC_SYNTHETIC) != 0) return false; 255 | for (AbstractInsnNode instruction : method.instructions) { 256 | if (instruction instanceof LdcInsnNode) { 257 | LdcInsnNode ldc = (LdcInsnNode) instruction; 258 | if (ldc.cst instanceof Handle || ldc.cst instanceof Type) return false; 259 | } else if (instruction instanceof InvokeDynamicInsnNode) { 260 | return false; 261 | } 262 | } 263 | return true; 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/natives/ConstantPoolTransformer.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.transformer.natives; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.transformer.Transformer; 5 | import com.cheatbreaker.obf.utils.asm.AsmUtils; 6 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 7 | import lombok.SneakyThrows; 8 | import org.objectweb.asm.Type; 9 | import org.objectweb.asm.tree.*; 10 | 11 | public class ConstantPoolTransformer extends Transformer { 12 | public ConstantPoolTransformer(Obf obf) { 13 | super(obf); 14 | canBeIterated = false; 15 | } 16 | 17 | @Override 18 | public String getSection() { 19 | return "natives.constantpool"; 20 | } 21 | 22 | @SneakyThrows 23 | @Override 24 | protected void after() { 25 | 26 | for (ClassWrapper classNode : obf.getClasses()) { 27 | if (classNode.name.equals("vm/NativeHandler")) continue; 28 | transform(classNode); 29 | } 30 | 31 | } 32 | 33 | void transform(ClassWrapper classNode) { 34 | InsnList list = new InsnList(); 35 | list.add(new LdcInsnNode(Type.getType("L" + classNode.name + ";"))); 36 | list.add(new MethodInsnNode(INVOKESTATIC, "vm/NativeHandler", "decryptConstantPool", "(Ljava/lang/Class;)V", false)); 37 | 38 | AsmUtils.getClinit(classNode).instructions.insert(list); 39 | 40 | int key = classNode.name.hashCode(); 41 | 42 | for (int i = 0; i < 2; i++) { 43 | for (MethodNode method : classNode.methods) { 44 | for (AbstractInsnNode instruction : method.instructions) { 45 | if (AsmUtils.isPushInt(instruction)) { 46 | int value = AsmUtils.getPushedInt(instruction); 47 | InsnList list2 = new InsnList(); 48 | int r1 = random.nextInt(); 49 | list2.add(new LdcInsnNode(r1)); 50 | list2.add(new LdcInsnNode(value ^ r1)); 51 | list2.add(new InsnNode(IXOR)); 52 | method.instructions.insert(instruction, list2); 53 | method.instructions.remove(instruction); 54 | } else if (AsmUtils.isPushLong(instruction)) { 55 | long value = AsmUtils.getPushedLong(instruction); 56 | InsnList list2 = new InsnList(); 57 | long r1 = random.nextLong(); 58 | list2.add(new LdcInsnNode(r1)); 59 | list2.add(new LdcInsnNode(value ^ r1)); 60 | list2.add(new InsnNode(LXOR)); 61 | method.instructions.insert(instruction, list2); 62 | method.instructions.remove(instruction); 63 | } 64 | } 65 | } 66 | } 67 | 68 | for (MethodNode method : classNode.methods) { 69 | for (AbstractInsnNode instruction : method.instructions) { 70 | if (AsmUtils.isPushInt(instruction)) { 71 | int value = AsmUtils.getPushedInt(instruction); 72 | value -= (key << 2); 73 | value ^= key; 74 | method.instructions.set(instruction, new LdcInsnNode(value)); 75 | } else if (AsmUtils.isPushLong(instruction)) { 76 | long value = AsmUtils.getPushedLong(instruction); 77 | value ^= ((long) key << 4); 78 | value -= key; 79 | method.instructions.set(instruction, new LdcInsnNode(value)); 80 | } else if (instruction instanceof LdcInsnNode) { 81 | Object value = ((LdcInsnNode) instruction).cst; 82 | if (value instanceof Float) { 83 | float f = (float) value; 84 | method.instructions.set(instruction, new LdcInsnNode(Math.pow(f, 3))); 85 | } else if (value instanceof Double) { 86 | double d = (double) value; 87 | method.instructions.set(instruction, new LdcInsnNode(Math.pow(d, 3))); 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/strings/ToStringTransformer.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.transformer.strings; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.transformer.Transformer; 5 | import com.cheatbreaker.obf.utils.asm.AsmUtils; 6 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 7 | import org.apache.commons.lang3.RandomStringUtils; 8 | import org.objectweb.asm.Label; 9 | import org.objectweb.asm.MethodVisitor; 10 | import org.objectweb.asm.tree.*; 11 | 12 | import java.util.Arrays; 13 | 14 | public class ToStringTransformer extends Transformer { 15 | public ToStringTransformer(Obf obf) { 16 | super(obf); 17 | } 18 | 19 | @Override 20 | public String getSection() { 21 | return "strings.tostring"; 22 | } 23 | 24 | @Override 25 | public void visit(ClassWrapper classNode) { 26 | 27 | for (MethodNode method : classNode.methods) { 28 | for (AbstractInsnNode instruction : method.instructions) { 29 | if (instruction instanceof LdcInsnNode && ((LdcInsnNode) instruction).cst instanceof String) { 30 | 31 | String string = (String) ((LdcInsnNode) instruction).cst; 32 | ClassWrapper cn = new ClassWrapper(true); 33 | cn.visit(V1_8, ACC_FINAL | ACC_SUPER, AsmUtils.parentName(classNode.name) + RandomStringUtils.randomAlphabetic(5), null, "java/lang/Object", null); 34 | 35 | MethodVisitor mn = cn.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null); 36 | mn.visitCode(); 37 | 38 | char[] keys = new char[string.length()]; 39 | char[] chars = new char[string.length()]; 40 | 41 | for (int i = 0; i < keys.length; i++) { 42 | keys[i] = (char) (string.charAt(i) ^ Arrays.hashCode(chars)); 43 | chars[i] = string.charAt(i); 44 | } 45 | 46 | mn.visitLdcInsn(keys.length); 47 | mn.visitIntInsn(NEWARRAY, T_CHAR); 48 | mn.visitVarInsn(ASTORE, 1); 49 | Label l = new Label(); 50 | mn.visitLabel(l); 51 | for (int i = 0; i < keys.length; i++) { 52 | mn.visitVarInsn(ALOAD, 1); 53 | mn.visitInsn(DUP); 54 | mn.visitLdcInsn(i); 55 | mn.visitLdcInsn((int) keys[i]); 56 | mn.visitVarInsn(ALOAD, 1); 57 | mn.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "hashCode", "([C)I", false); 58 | mn.visitInsn(I2C); 59 | mn.visitInsn(I2C); 60 | mn.visitInsn(IXOR); 61 | mn.visitInsn(CASTORE); 62 | mn.visitVarInsn(ASTORE, 1); 63 | } 64 | 65 | mn.visitTypeInsn(NEW, "java/lang/String"); 66 | mn.visitInsn(DUP); 67 | mn.visitVarInsn(ALOAD, 1); 68 | mn.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([C)V", false); 69 | 70 | mn.visitInsn(ARETURN); 71 | mn.visitEnd(); 72 | 73 | mn = cn.visitMethod(0, "", "()V", null, null); 74 | mn.visitCode(); 75 | mn.visitVarInsn(ALOAD, 0); 76 | mn.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); 77 | mn.visitInsn(RETURN); 78 | cn.visitOuterClass(classNode.name, method.name, method.desc); 79 | cn.visitEnd(); 80 | 81 | classNode.innerClasses.add(new InnerClassNode(cn.name, null, null, ACC_STATIC)); 82 | 83 | InsnList list = new InsnList(); 84 | list.add(new TypeInsnNode(NEW, cn.name)); 85 | list.add(new InsnNode(DUP)); 86 | list.add(new MethodInsnNode(INVOKESPECIAL, cn.name, "", "()V", false)); 87 | list.add(new MethodInsnNode(INVOKEVIRTUAL, cn.name, "toString", "()Ljava/lang/String;", false)); 88 | 89 | method.instructions.insertBefore(instruction, list); 90 | method.instructions.remove(instruction); 91 | 92 | obf.addClass(cn); 93 | 94 | } 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/asm/AsmUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.utils.asm; 26 | 27 | import com.cheatbreaker.obf.Obf; 28 | import org.objectweb.asm.Opcodes; 29 | import org.objectweb.asm.Type; 30 | import org.objectweb.asm.commons.CodeSizeEvaluator; 31 | import org.objectweb.asm.tree.*; 32 | import org.objectweb.asm.tree.analysis.Analyzer; 33 | import org.objectweb.asm.tree.analysis.AnalyzerException; 34 | import org.objectweb.asm.tree.analysis.SourceInterpreter; 35 | import org.objectweb.asm.tree.analysis.SourceValue; 36 | import org.objectweb.asm.util.Printer; 37 | import org.objectweb.asm.util.Textifier; 38 | import org.objectweb.asm.util.TraceMethodVisitor; 39 | 40 | import java.io.PrintWriter; 41 | import java.io.StringWriter; 42 | import java.util.ArrayList; 43 | import java.util.List; 44 | import java.util.Random; 45 | 46 | public class AsmUtils implements Opcodes{ 47 | 48 | public static final int MAX_INSTRUCTIONS = 0xFFFF; 49 | 50 | public static InsnList println(String message) { 51 | InsnList list = new InsnList(); 52 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")); 53 | list.add(new LdcInsnNode(message)); 54 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false)); 55 | return list; 56 | } 57 | 58 | public static boolean isPushInt(AbstractInsnNode insn) { 59 | int op = insn.getOpcode(); 60 | return (op >= ICONST_M1 && op <= ICONST_5) 61 | || op == BIPUSH 62 | || op == SIPUSH 63 | || (op == LDC && ((LdcInsnNode) insn).cst instanceof Integer); 64 | } 65 | 66 | public static int getPushedInt(AbstractInsnNode insn) { 67 | int op = insn.getOpcode(); 68 | if (op >= ICONST_M1 && op <= ICONST_5) { 69 | return op - ICONST_0; 70 | } 71 | if (op == BIPUSH || op == SIPUSH) { 72 | return ((IntInsnNode) insn).operand; 73 | } 74 | if (op == LDC) { 75 | Object cst = ((LdcInsnNode) insn).cst; 76 | if (cst instanceof Integer) { 77 | return (int) cst; 78 | } 79 | } 80 | throw new IllegalArgumentException("insn is not a push int instruction"); 81 | } 82 | 83 | public static MethodNode getClinit(ClassWrapper classNode) { 84 | for (MethodNode method : classNode.methods) { 85 | if (method.name.equals("")) { 86 | return method; 87 | } 88 | } 89 | MethodNode clinit = new MethodNode(ACC_STATIC, "", "()V", null, null); 90 | clinit.instructions = new InsnList(); 91 | clinit.instructions.add(new InsnNode(RETURN)); 92 | classNode.methods.add(clinit); 93 | return clinit; 94 | } 95 | 96 | public static AbstractInsnNode pushInt(int value) { 97 | if (value >= -1 && value <= 5) { 98 | return new InsnNode(ICONST_0 + value); 99 | } 100 | if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { 101 | return new IntInsnNode(BIPUSH, value); 102 | } 103 | if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { 104 | return new IntInsnNode(SIPUSH, value); 105 | } 106 | return new LdcInsnNode(value); 107 | } 108 | 109 | public static boolean isPushLong(AbstractInsnNode insn) { 110 | int op = insn.getOpcode(); 111 | return op == LCONST_0 112 | || op == LCONST_1 113 | || (op == LDC && ((LdcInsnNode) insn).cst instanceof Long); 114 | } 115 | 116 | public static long getPushedLong(AbstractInsnNode insn) { 117 | int op = insn.getOpcode(); 118 | if (op == LCONST_0) { 119 | return 0; 120 | } 121 | if (op == LCONST_1) { 122 | return 1; 123 | } 124 | if (op == LDC) { 125 | Object cst = ((LdcInsnNode) insn).cst; 126 | if (cst instanceof Long) { 127 | return (long) cst; 128 | } 129 | } 130 | throw new IllegalArgumentException("insn is not a push long instruction"); 131 | } 132 | 133 | public static AbstractInsnNode pushLong(long value) { 134 | if (value == 0) { 135 | return new InsnNode(LCONST_0); 136 | } 137 | if (value == 1) { 138 | return new InsnNode(LCONST_1); 139 | } 140 | return new LdcInsnNode(value); 141 | } 142 | 143 | public static int codeSize(MethodNode methodNode) { 144 | CodeSizeEvaluator evaluator = new CodeSizeEvaluator(null); 145 | methodNode.accept(evaluator); 146 | return evaluator.getMaxSize(); 147 | } 148 | 149 | public static void unboxPrimitive(String desc, InsnList list) { 150 | switch (desc) { 151 | case "I": 152 | list.add(new TypeInsnNode(CHECKCAST, "java/lang/Integer")); 153 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false)); 154 | break; 155 | case "Z": 156 | list.add(new TypeInsnNode(CHECKCAST, "java/lang/Boolean")); 157 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false)); 158 | break; 159 | case "B": 160 | list.add(new TypeInsnNode(CHECKCAST, "java/lang/Byte")); 161 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false)); 162 | break; 163 | case "C": 164 | list.add(new TypeInsnNode(CHECKCAST, "java/lang/Character")); 165 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false)); 166 | break; 167 | case "S": 168 | list.add(new TypeInsnNode(CHECKCAST, "java/lang/Short")); 169 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false)); 170 | break; 171 | case "J": 172 | list.add(new TypeInsnNode(CHECKCAST, "java/lang/Long")); 173 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false)); 174 | break; 175 | case "F": 176 | list.add(new TypeInsnNode(CHECKCAST, "java/lang/Float")); 177 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false)); 178 | break; 179 | case "D": 180 | list.add(new TypeInsnNode(CHECKCAST, "java/lang/Double")); 181 | list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false)); 182 | break; 183 | default: 184 | if (!desc.equals("Lnull;") && !desc.equals("Ljava/lang/Object;")) 185 | list.add(new TypeInsnNode(CHECKCAST, desc.startsWith("L") && desc.endsWith(";") ? 186 | desc.substring(1, desc.length() - 1) : desc)); 187 | } 188 | } 189 | 190 | public static void boxPrimitive(String desc, InsnList list) { 191 | switch (desc) { 192 | case "I": 193 | list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)); 194 | break; 195 | case "Z": 196 | list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false)); 197 | break; 198 | case "B": 199 | list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false)); 200 | break; 201 | case "C": 202 | list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false)); 203 | break; 204 | case "S": 205 | list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false)); 206 | break; 207 | case "J": 208 | list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false)); 209 | break; 210 | case "F": 211 | list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false)); 212 | break; 213 | case "D": 214 | list.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false)); 215 | break; 216 | default: 217 | if (!desc.equals("Lnull;") && !desc.equals("Ljava/lang/Object;")) { 218 | list.add(new TypeInsnNode(CHECKCAST, desc.startsWith("L") && desc.endsWith(";") ? 219 | desc.substring(1, desc.length() - 1) : desc)); 220 | } 221 | break; 222 | } 223 | } 224 | 225 | private static final Printer printer = new Textifier(); 226 | private static final TraceMethodVisitor methodPrinter = new TraceMethodVisitor(printer); 227 | public static String print(AbstractInsnNode insnNode) { 228 | if (insnNode == null) return "null"; 229 | insnNode.accept(methodPrinter); 230 | StringWriter sw = new StringWriter(); 231 | printer.print(new PrintWriter(sw)); 232 | printer.getText().clear(); 233 | return sw.toString().trim(); 234 | } 235 | 236 | public static FieldNode findField(Obf obf, String owner, String name, String desc) { 237 | ClassWrapper classNode = obf.assureLoaded(owner); 238 | if (classNode == null) return null; 239 | return findField(classNode, name, desc); 240 | } 241 | 242 | public static FieldNode findField(ClassWrapper classNode, String name, String desc) { 243 | for (FieldNode field : classNode.fields) { 244 | if (field.name.equals(name) && (desc == null || field.desc.equals(desc))) { 245 | return field; 246 | } 247 | } 248 | return null; 249 | } 250 | 251 | 252 | 253 | public static MethodNode findMethod(Obf obf, String owner, String name, String descriptor) { 254 | ClassWrapper classNode = obf.assureLoaded(owner); 255 | if (classNode == null) return null; 256 | return findMethod(classNode, name, descriptor); 257 | } 258 | 259 | public static MethodNode findMethod(ClassWrapper classNode, String name, String descriptor) { 260 | for (MethodNode method : classNode.methods) { 261 | if (method.name.equals(name) && (descriptor == null || method.desc.equals(descriptor))) { 262 | return method; 263 | } 264 | } 265 | return null; 266 | } 267 | 268 | public static LabelNode[] getLabels(MethodNode method) { 269 | List labels = new ArrayList<>(); 270 | for (AbstractInsnNode insnNode : method.instructions.toArray()) { 271 | if (insnNode instanceof LabelNode) { 272 | labels.add((LabelNode) insnNode); 273 | } 274 | } 275 | return labels.toArray(new LabelNode[0]); 276 | } 277 | 278 | public static InsnList iterate(InsnList instructions, AbstractInsnNode start, AbstractInsnNode end) { 279 | InsnList list = new InsnList(); 280 | boolean f = false; 281 | for (AbstractInsnNode instruction : instructions) { 282 | if (!f && instruction == start) { 283 | f = true; 284 | } 285 | if (f) { 286 | list.add(instruction); 287 | } 288 | if (instruction == end) { 289 | break; 290 | } 291 | } 292 | return list; 293 | } 294 | 295 | public static ClassWrapper clone(ClassWrapper classNode) { 296 | ClassWrapper c = new ClassWrapper(classNode.modify); 297 | classNode.accept(c); 298 | return c; 299 | } 300 | 301 | public static void boxClass(InsnList list, Type type) { 302 | switch (type.getDescriptor()) { 303 | case "I": 304 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;")); 305 | break; 306 | case "Z": 307 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;")); 308 | break; 309 | case "B": 310 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;")); 311 | break; 312 | case "C": 313 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;")); 314 | break; 315 | case "S": 316 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;")); 317 | break; 318 | case "J": 319 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;")); 320 | break; 321 | case "F": 322 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;")); 323 | break; 324 | case "D": 325 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;")); 326 | break; 327 | case "V": 328 | list.add(new FieldInsnNode(GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;")); 329 | break; 330 | default: 331 | list.add(new LdcInsnNode(type)); 332 | break; 333 | } 334 | } 335 | 336 | public static MethodNode createMethod(int access, String name, String desc) { 337 | MethodNode m = new MethodNode(access, name, desc, null, null); 338 | m.instructions = new InsnList(); 339 | return m; 340 | } 341 | 342 | public static void boxReturn(Type returnType, InsnList list) { 343 | Random r = new Random(); 344 | switch (returnType.getOpcode(IRETURN)) { 345 | case IRETURN: 346 | list.add(pushInt(r.nextInt())); 347 | break; 348 | case LRETURN: 349 | list.add(pushLong(r.nextLong())); 350 | break; 351 | case FRETURN: 352 | list.add(new LdcInsnNode(r.nextFloat())); 353 | break; 354 | case DRETURN: 355 | list.add(new LdcInsnNode(r.nextDouble())); 356 | break; 357 | case ARETURN: 358 | list.add(new InsnNode(ACONST_NULL)); 359 | break; 360 | case RETURN: 361 | break; 362 | default: 363 | throw new IllegalArgumentException("Unknown return type: " + returnType); 364 | } 365 | list.add(new InsnNode(returnType.getOpcode(IRETURN))); 366 | } 367 | 368 | public static String parentName(String name) { 369 | if (name.contains("/")) { 370 | return name.substring(0, name.lastIndexOf("/") + 1); 371 | } else { 372 | return ""; 373 | } 374 | } 375 | 376 | public static MethodNode findMethodSuper(ClassWrapper owner, String name, String desc) { 377 | ClassWrapper superWrapper = owner; 378 | while (superWrapper != null) { 379 | MethodNode m = AsmUtils.findMethod(superWrapper, name, desc); 380 | if (m != null) { 381 | return m; 382 | } 383 | if (superWrapper.superName == null || superWrapper.superName.isEmpty()) { 384 | break; 385 | } 386 | superWrapper = Obf.getInstance().assureLoaded(superWrapper.superName); 387 | } 388 | 389 | return null; 390 | } 391 | 392 | public static FieldNode findFieldSuper(ClassWrapper ownerClass, String name, String desc) { 393 | ClassWrapper superWrapper = ownerClass; 394 | while (superWrapper != null) { 395 | FieldNode m = AsmUtils.findField(superWrapper, name, desc); 396 | if (m != null) { 397 | return m; 398 | } 399 | if (superWrapper.superName == null || superWrapper.superName.isEmpty()) { 400 | break; 401 | } 402 | superWrapper = Obf.getInstance().assureLoaded(superWrapper.superName); 403 | } 404 | 405 | return null; 406 | } 407 | 408 | public static void preverify(ClassWrapper classNode, MethodNode method) { 409 | Analyzer analyzer = new Analyzer<>(new SourceInterpreter()); 410 | try { 411 | analyzer.analyzeAndComputeMaxs(classNode.name, method); 412 | } catch (AnalyzerException e) { 413 | System.out.println("Failed to preverify method: " + classNode.name + "." + method.name + method.desc); 414 | e.printStackTrace(); 415 | } 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/asm/ClassWrapper.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.asm; 2 | 3 | import org.objectweb.asm.ClassWriter; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | public class ClassWrapper extends ClassNode { 7 | 8 | public boolean modify; 9 | 10 | public ClassWrapper(boolean modify) { 11 | super(589824); 12 | this.modify = modify; 13 | } 14 | 15 | public ContextClassWriter createWriter() { 16 | return this.createWriter(ClassWriter.COMPUTE_FRAMES); 17 | } 18 | 19 | public ContextClassWriter createWriter(int flags) { 20 | ContextClassWriter cw = new ContextClassWriter(flags); 21 | this.accept(cw); 22 | return cw; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/asm/ContextClassWriter.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.asm; 2 | 3 | import com.cheatbreaker.obf.utils.tree.HierarchyUtils; 4 | import org.objectweb.asm.ClassWriter; 5 | 6 | import java.util.List; 7 | 8 | public class ContextClassWriter extends ClassWriter { 9 | 10 | public ContextClassWriter(int flags) { 11 | super(null, flags); 12 | } 13 | 14 | public ContextClassWriter(int flags, boolean b, List l) { 15 | super(null, flags, b, l); 16 | } 17 | 18 | protected String getCommonSuperClass(String type1, String type2) { 19 | return HierarchyUtils.getCommonSuperClass1(type1, type2); 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/asm/NodeAccess.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.asm; 2 | 3 | public class NodeAccess { 4 | 5 | public int access; 6 | 7 | public NodeAccess(final int access) { 8 | this.access = access; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration; 2 | 3 | import java.util.Map; 4 | 5 | public interface Configuration extends ConfigurationSection { 6 | void addDefault(String var1, Object var2); 7 | 8 | void addDefaults(Map var1); 9 | 10 | void addDefaults(Configuration var1); 11 | 12 | void setDefaults(Configuration var1); 13 | 14 | Configuration getDefaults(); 15 | 16 | ConfigurationOptions options(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/ConfigurationOptions.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration; 2 | 3 | public class ConfigurationOptions { 4 | private char pathSeparator = '.'; 5 | private boolean copyDefaults = false; 6 | private final Configuration configuration; 7 | 8 | protected ConfigurationOptions(Configuration configuration) { 9 | this.configuration = configuration; 10 | } 11 | 12 | public Configuration configuration() { 13 | return this.configuration; 14 | } 15 | 16 | public char pathSeparator() { 17 | return this.pathSeparator; 18 | } 19 | 20 | public ConfigurationOptions pathSeparator(char value) { 21 | this.pathSeparator = value; 22 | return this; 23 | } 24 | 25 | public boolean copyDefaults() { 26 | return this.copyDefaults; 27 | } 28 | 29 | public ConfigurationOptions copyDefaults(boolean value) { 30 | this.copyDefaults = value; 31 | return this; 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/ConfigurationSection.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration; 2 | 3 | 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | public interface ConfigurationSection { 9 | Set getKeys(boolean var1); 10 | 11 | Map getValues(boolean var1); 12 | 13 | boolean contains(String var1); 14 | 15 | boolean isSet(String var1); 16 | 17 | String getCurrentPath(); 18 | 19 | String getName(); 20 | 21 | Configuration getRoot(); 22 | 23 | ConfigurationSection getParent(); 24 | 25 | Object get(String var1); 26 | 27 | Object get(String var1, Object var2); 28 | 29 | void set(String var1, Object var2); 30 | 31 | ConfigurationSection createSection(String var1); 32 | 33 | ConfigurationSection createSection(String var1, Map var2); 34 | 35 | String getString(String var1); 36 | 37 | String getString(String var1, String var2); 38 | 39 | boolean isString(String var1); 40 | 41 | int getInt(String var1); 42 | 43 | int getInt(String var1, int var2); 44 | 45 | boolean isInt(String var1); 46 | 47 | boolean getBoolean(String var1); 48 | 49 | boolean getBoolean(String var1, boolean var2); 50 | 51 | boolean isBoolean(String var1); 52 | 53 | double getDouble(String var1); 54 | 55 | double getDouble(String var1, double var2); 56 | 57 | boolean isDouble(String var1); 58 | 59 | float getFloat(String var1); 60 | 61 | float getFloat(String var1, float var2); 62 | 63 | boolean isFloat(String var1); 64 | 65 | long getLong(String var1); 66 | 67 | long getLong(String var1, long var2); 68 | 69 | boolean isLong(String var1); 70 | 71 | List getList(String var1); 72 | 73 | List getList(String var1, List var2); 74 | 75 | boolean isList(String var1); 76 | 77 | List getStringList(String var1); 78 | 79 | List getIntegerList(String var1); 80 | 81 | List getBooleanList(String var1); 82 | 83 | List getDoubleList(String var1); 84 | 85 | List getFloatList(String var1); 86 | 87 | List getLongList(String var1); 88 | 89 | List getByteList(String var1); 90 | 91 | List getCharacterList(String var1); 92 | 93 | List getShortList(String var1); 94 | 95 | List> getMapList(String var1); 96 | 97 | ConfigurationSection getConfigurationSection(String var1); 98 | 99 | boolean isConfigurationSection(String var1); 100 | 101 | ConfigurationSection getDefaultSection(); 102 | 103 | void addDefault(String var1, Object var2); 104 | } -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/InvalidConfigurationException.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration; 2 | 3 | public class InvalidConfigurationException extends Exception { 4 | public InvalidConfigurationException() { 5 | } 6 | 7 | public InvalidConfigurationException(String msg) { 8 | super(msg); 9 | } 10 | 11 | public InvalidConfigurationException(Throwable cause) { 12 | super(cause); 13 | } 14 | 15 | public InvalidConfigurationException(String msg, Throwable cause) { 16 | super(msg, cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/MemoryConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration; 2 | 3 | 4 | import org.apache.commons.lang3.Validate; 5 | 6 | import java.util.Iterator; 7 | import java.util.Map; 8 | import java.util.Map.Entry; 9 | 10 | public class MemoryConfiguration extends MemorySection implements Configuration { 11 | protected Configuration defaults; 12 | protected MemoryConfigurationOptions options; 13 | 14 | public MemoryConfiguration() { 15 | } 16 | 17 | public MemoryConfiguration(Configuration defaults) { 18 | this.defaults = defaults; 19 | } 20 | 21 | public void addDefault(String path, Object value) { 22 | Validate.notNull(path, "Path may not be null"); 23 | if (this.defaults == null) { 24 | this.defaults = new MemoryConfiguration(); 25 | } 26 | 27 | this.defaults.set(path, value); 28 | } 29 | 30 | public void addDefaults(Map defaults) { 31 | Validate.notNull(defaults, "Defaults may not be null"); 32 | Iterator var2 = defaults.entrySet().iterator(); 33 | 34 | while(var2.hasNext()) { 35 | Entry entry = (Entry)var2.next(); 36 | this.addDefault((String)entry.getKey(), entry.getValue()); 37 | } 38 | 39 | } 40 | 41 | public void addDefaults(Configuration defaults) { 42 | Validate.notNull(defaults, "Defaults may not be null"); 43 | this.addDefaults(defaults.getValues(true)); 44 | } 45 | 46 | public void setDefaults(Configuration defaults) { 47 | Validate.notNull(defaults, "Defaults may not be null"); 48 | this.defaults = defaults; 49 | } 50 | 51 | public Configuration getDefaults() { 52 | return this.defaults; 53 | } 54 | 55 | public ConfigurationSection getParent() { 56 | return null; 57 | } 58 | 59 | public MemoryConfigurationOptions options() { 60 | if (this.options == null) { 61 | this.options = new MemoryConfigurationOptions(this); 62 | } 63 | 64 | return this.options; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/MemoryConfigurationOptions.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration; 2 | 3 | public class MemoryConfigurationOptions extends ConfigurationOptions { 4 | protected MemoryConfigurationOptions(MemoryConfiguration configuration) { 5 | super(configuration); 6 | } 7 | 8 | public MemoryConfiguration configuration() { 9 | return (MemoryConfiguration)super.configuration(); 10 | } 11 | 12 | public MemoryConfigurationOptions copyDefaults(boolean value) { 13 | super.copyDefaults(value); 14 | return this; 15 | } 16 | 17 | public MemoryConfigurationOptions pathSeparator(char value) { 18 | super.pathSeparator(value); 19 | return this; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/NumberConversions.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration; 2 | 3 | public final class NumberConversions { 4 | private NumberConversions() { 5 | } 6 | 7 | public static int floor(double num) { 8 | int floor = (int)num; 9 | return (double)floor == num ? floor : floor - (int)(Double.doubleToRawLongBits(num) >>> 63); 10 | } 11 | 12 | public static int ceil(double num) { 13 | int floor = (int)num; 14 | return (double)floor == num ? floor : floor + (int)(~Double.doubleToRawLongBits(num) >>> 63); 15 | } 16 | 17 | public static int round(double num) { 18 | return floor(num + 0.5D); 19 | } 20 | 21 | public static double square(double num) { 22 | return num * num; 23 | } 24 | 25 | public static int toInt(Object object) { 26 | if (object instanceof Number) { 27 | return ((Number)object).intValue(); 28 | } else { 29 | try { 30 | return Integer.valueOf(object.toString()); 31 | } catch (NumberFormatException var2) { 32 | } catch (NullPointerException var3) { 33 | } 34 | 35 | return 0; 36 | } 37 | } 38 | 39 | public static float toFloat(Object object) { 40 | if (object instanceof Number) { 41 | return ((Number)object).floatValue(); 42 | } else { 43 | try { 44 | return Float.valueOf(object.toString()); 45 | } catch (NumberFormatException var2) { 46 | } catch (NullPointerException var3) { 47 | } 48 | 49 | return 0.0F; 50 | } 51 | } 52 | 53 | public static double toDouble(Object object) { 54 | if (object instanceof Number) { 55 | return ((Number)object).doubleValue(); 56 | } else { 57 | try { 58 | return Double.valueOf(object.toString()); 59 | } catch (NumberFormatException var2) { 60 | } catch (NullPointerException var3) { 61 | } 62 | 63 | return 0.0D; 64 | } 65 | } 66 | 67 | public static long toLong(Object object) { 68 | if (object instanceof Number) { 69 | return ((Number)object).longValue(); 70 | } else { 71 | try { 72 | return Long.valueOf(object.toString()); 73 | } catch (NumberFormatException var2) { 74 | } catch (NullPointerException var3) { 75 | } 76 | 77 | return 0L; 78 | } 79 | } 80 | 81 | public static short toShort(Object object) { 82 | if (object instanceof Number) { 83 | return ((Number)object).shortValue(); 84 | } else { 85 | try { 86 | return Short.valueOf(object.toString()); 87 | } catch (NumberFormatException var2) { 88 | } catch (NullPointerException var3) { 89 | } 90 | 91 | return 0; 92 | } 93 | } 94 | 95 | public static byte toByte(Object object) { 96 | if (object instanceof Number) { 97 | return ((Number)object).byteValue(); 98 | } else { 99 | try { 100 | return Byte.valueOf(object.toString()); 101 | } catch (NumberFormatException var2) { 102 | } catch (NullPointerException var3) { 103 | } 104 | 105 | return 0; 106 | } 107 | } 108 | 109 | public static boolean isFinite(double d) { 110 | return Math.abs(d) <= 1.7976931348623157E308D; 111 | } 112 | 113 | public static boolean isFinite(float f) { 114 | return Math.abs(f) <= 3.4028235E38F; 115 | } 116 | 117 | public static void checkFinite(double d, String message) { 118 | if (!isFinite(d)) { 119 | throw new IllegalArgumentException(message); 120 | } 121 | } 122 | 123 | public static void checkFinite(float d, String message) { 124 | if (!isFinite(d)) { 125 | throw new IllegalArgumentException(message); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/file/FileConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.file; 2 | 3 | 4 | import com.cheatbreaker.obf.utils.configuration.Configuration; 5 | import com.cheatbreaker.obf.utils.configuration.InvalidConfigurationException; 6 | import com.cheatbreaker.obf.utils.configuration.MemoryConfiguration; 7 | import com.google.common.io.Files; 8 | import org.apache.commons.io.Charsets; 9 | import org.apache.commons.lang3.Validate; 10 | import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; 11 | 12 | import java.io.*; 13 | import java.nio.charset.Charset; 14 | 15 | public abstract class FileConfiguration extends MemoryConfiguration { 16 | /** @deprecated */ 17 | @Deprecated 18 | public static final boolean UTF8_OVERRIDE; 19 | /** @deprecated */ 20 | @Deprecated 21 | public static final boolean UTF_BIG; 22 | /** @deprecated */ 23 | @Deprecated 24 | public static final boolean SYSTEM_UTF; 25 | 26 | public FileConfiguration() { 27 | } 28 | 29 | public FileConfiguration(Configuration defaults) { 30 | super(defaults); 31 | } 32 | 33 | public void save(File file) throws IOException { 34 | Validate.notNull(file, "File cannot be null"); 35 | Files.createParentDirs(file); 36 | String data = this.saveToString(); 37 | OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), UTF8_OVERRIDE && !UTF_BIG ? Charsets.UTF_8 : Charset.defaultCharset()); 38 | 39 | try { 40 | writer.write(data); 41 | } finally { 42 | writer.close(); 43 | } 44 | 45 | } 46 | 47 | public void save(String file) throws IOException { 48 | Validate.notNull(file, "File cannot be null"); 49 | this.save(new File(file)); 50 | } 51 | 52 | public abstract String saveToString(); 53 | 54 | public void load(File file) throws FileNotFoundException, IOException, InvalidConfigurationException { 55 | Validate.notNull(file, "File cannot be null"); 56 | FileInputStream stream = new FileInputStream(file); 57 | this.load((Reader)(new InputStreamReader(stream, UTF8_OVERRIDE && !UTF_BIG ? Charsets.UTF_8 : Charset.defaultCharset()))); 58 | } 59 | 60 | /** @deprecated */ 61 | @Deprecated 62 | public void load(InputStream stream) throws IOException, InvalidConfigurationException { 63 | Validate.notNull(stream, "Stream cannot be null"); 64 | this.load((Reader)(new InputStreamReader(stream, UTF8_OVERRIDE ? Charsets.UTF_8 : Charset.defaultCharset()))); 65 | } 66 | 67 | public void load(Reader reader) throws IOException, InvalidConfigurationException { 68 | BufferedReader input = reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(reader); 69 | StringBuilder builder = new StringBuilder(); 70 | 71 | String line; 72 | try { 73 | while((line = input.readLine()) != null) { 74 | builder.append(line); 75 | builder.append('\n'); 76 | } 77 | } finally { 78 | input.close(); 79 | } 80 | 81 | this.loadFromString(builder.toString()); 82 | } 83 | 84 | public void load(String file) throws FileNotFoundException, IOException, InvalidConfigurationException { 85 | Validate.notNull(file, "File cannot be null"); 86 | this.load(new File(file)); 87 | } 88 | 89 | public abstract void loadFromString(String var1) throws InvalidConfigurationException; 90 | 91 | protected abstract String buildHeader(); 92 | 93 | public FileConfigurationOptions options() { 94 | if (this.options == null) { 95 | this.options = new FileConfigurationOptions(this); 96 | } 97 | 98 | return (FileConfigurationOptions)this.options; 99 | } 100 | 101 | static { 102 | byte[] testBytes = Base64Coder.decode("ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX4NCg=="); 103 | String testString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\n"; 104 | Charset defaultCharset = Charset.defaultCharset(); 105 | String resultString = new String(testBytes, defaultCharset); 106 | boolean trueUTF = defaultCharset.name().contains("UTF"); 107 | UTF8_OVERRIDE = !" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\n".equals(resultString) || defaultCharset.equals(Charset.forName("US-ASCII")); 108 | SYSTEM_UTF = trueUTF || UTF8_OVERRIDE; 109 | UTF_BIG = trueUTF && UTF8_OVERRIDE; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/file/FileConfigurationOptions.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.file; 2 | 3 | import com.cheatbreaker.obf.utils.configuration.MemoryConfiguration; 4 | import com.cheatbreaker.obf.utils.configuration.MemoryConfigurationOptions; 5 | 6 | public class FileConfigurationOptions extends MemoryConfigurationOptions { 7 | private String header = null; 8 | private boolean copyHeader = true; 9 | 10 | protected FileConfigurationOptions(MemoryConfiguration configuration) { 11 | super(configuration); 12 | } 13 | 14 | public FileConfiguration configuration() { 15 | return (FileConfiguration)super.configuration(); 16 | } 17 | 18 | public FileConfigurationOptions copyDefaults(boolean value) { 19 | super.copyDefaults(value); 20 | return this; 21 | } 22 | 23 | public FileConfigurationOptions pathSeparator(char value) { 24 | super.pathSeparator(value); 25 | return this; 26 | } 27 | 28 | public String header() { 29 | return this.header; 30 | } 31 | 32 | public FileConfigurationOptions header(String value) { 33 | this.header = value; 34 | return this; 35 | } 36 | 37 | public boolean copyHeader() { 38 | return this.copyHeader; 39 | } 40 | 41 | public FileConfigurationOptions copyHeader(boolean value) { 42 | this.copyHeader = value; 43 | return this; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/file/YamlConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.file; 2 | 3 | import com.cheatbreaker.obf.utils.configuration.Configuration; 4 | import com.cheatbreaker.obf.utils.configuration.ConfigurationSection; 5 | import com.cheatbreaker.obf.utils.configuration.InvalidConfigurationException; 6 | import org.apache.commons.lang3.Validate; 7 | import org.yaml.snakeyaml.DumperOptions; 8 | import org.yaml.snakeyaml.Yaml; 9 | import org.yaml.snakeyaml.error.YAMLException; 10 | import org.yaml.snakeyaml.representer.Representer; 11 | 12 | import java.io.*; 13 | import java.util.Iterator; 14 | import java.util.Map; 15 | 16 | public class YamlConfiguration extends FileConfiguration { 17 | protected static final String COMMENT_PREFIX = "# "; 18 | protected static final String BLANK_CONFIG = "{}\n"; 19 | private final DumperOptions yamlOptions = new DumperOptions(); 20 | private final Representer yamlRepresenter = new YamlRepresenter(); 21 | private final Yaml yaml; 22 | 23 | public YamlConfiguration() { 24 | this.yaml = new Yaml(new YamlConstructor(), this.yamlRepresenter, this.yamlOptions); 25 | } 26 | 27 | public String saveToString() { 28 | this.yamlOptions.setIndent(this.options().indent()); 29 | this.yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); 30 | this.yamlOptions.setAllowUnicode(SYSTEM_UTF); 31 | this.yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); 32 | String header = this.buildHeader(); 33 | String dump = this.yaml.dump(this.getValues(false)); 34 | if (dump.equals("{}\n")) { 35 | dump = ""; 36 | } 37 | 38 | return header + dump; 39 | } 40 | 41 | public void loadFromString(String contents) throws InvalidConfigurationException { 42 | Validate.notNull(contents, "Contents cannot be null"); 43 | 44 | Map input; 45 | try { 46 | input = (Map)this.yaml.load(contents); 47 | } catch (YAMLException var4) { 48 | throw new InvalidConfigurationException(var4); 49 | } catch (ClassCastException var5) { 50 | throw new InvalidConfigurationException("Top level is not a Map."); 51 | } 52 | 53 | String header = this.parseHeader(contents); 54 | if (header.length() > 0) { 55 | this.options().header(header); 56 | } 57 | 58 | if (input != null) { 59 | this.convertMapsToSections(input, this); 60 | } 61 | 62 | } 63 | 64 | protected void convertMapsToSections(Map input, ConfigurationSection section) { 65 | Iterator var3 = input.entrySet().iterator(); 66 | 67 | while(var3.hasNext()) { 68 | Map.Entry entry = (Map.Entry)var3.next(); 69 | String key = entry.getKey().toString(); 70 | Object value = entry.getValue(); 71 | if (value instanceof Map) { 72 | this.convertMapsToSections((Map)value, section.createSection(key)); 73 | } else { 74 | section.set(key, value); 75 | } 76 | } 77 | 78 | } 79 | 80 | protected String parseHeader(String input) { 81 | String[] lines = input.split("\r?\n", -1); 82 | StringBuilder result = new StringBuilder(); 83 | boolean readingHeader = true; 84 | boolean foundHeader = false; 85 | 86 | for(int i = 0; i < lines.length && readingHeader; ++i) { 87 | String line = lines[i]; 88 | if (line.startsWith("# ")) { 89 | if (i > 0) { 90 | result.append("\n"); 91 | } 92 | 93 | if (line.length() > "# ".length()) { 94 | result.append(line.substring("# ".length())); 95 | } 96 | 97 | foundHeader = true; 98 | } else if (foundHeader && line.length() == 0) { 99 | result.append("\n"); 100 | } else if (foundHeader) { 101 | readingHeader = false; 102 | } 103 | } 104 | 105 | return result.toString(); 106 | } 107 | 108 | protected String buildHeader() { 109 | String header = this.options().header(); 110 | if (this.options().copyHeader()) { 111 | Configuration def = this.getDefaults(); 112 | if (def != null && def instanceof FileConfiguration) { 113 | FileConfiguration filedefaults = (FileConfiguration)def; 114 | String defaultsHeader = filedefaults.buildHeader(); 115 | if (defaultsHeader != null && defaultsHeader.length() > 0) { 116 | return defaultsHeader; 117 | } 118 | } 119 | } 120 | 121 | if (header == null) { 122 | return ""; 123 | } else { 124 | StringBuilder builder = new StringBuilder(); 125 | String[] lines = header.split("\r?\n", -1); 126 | boolean startedHeader = false; 127 | 128 | for(int i = lines.length - 1; i >= 0; --i) { 129 | builder.insert(0, "\n"); 130 | if (startedHeader || lines[i].length() != 0) { 131 | builder.insert(0, lines[i]); 132 | builder.insert(0, "# "); 133 | startedHeader = true; 134 | } 135 | } 136 | 137 | return builder.toString(); 138 | } 139 | } 140 | 141 | public YamlConfigurationOptions options() { 142 | if (this.options == null) { 143 | this.options = new YamlConfigurationOptions(this); 144 | } 145 | 146 | return (YamlConfigurationOptions)this.options; 147 | } 148 | 149 | public static YamlConfiguration loadConfiguration(File file) { 150 | Validate.notNull(file, "File cannot be null"); 151 | YamlConfiguration config = new YamlConfiguration(); 152 | 153 | try { 154 | config.load(file); 155 | } catch (IOException | InvalidConfigurationException var3) { 156 | System.err.println("Cannot load configuration from " + file); 157 | var3.printStackTrace(); 158 | } 159 | 160 | return config; 161 | } 162 | 163 | /** @deprecated */ 164 | @Deprecated 165 | public static YamlConfiguration loadConfiguration(InputStream stream) { 166 | Validate.notNull(stream, "Stream cannot be null"); 167 | YamlConfiguration config = new YamlConfiguration(); 168 | 169 | try { 170 | config.load(stream); 171 | } catch (IOException | InvalidConfigurationException var3) { 172 | System.err.println("Cannot load configuration from stream"); 173 | var3.printStackTrace(); 174 | } 175 | 176 | return config; 177 | } 178 | 179 | public static YamlConfiguration loadConfiguration(Reader reader) { 180 | Validate.notNull(reader, "Stream cannot be null"); 181 | YamlConfiguration config = new YamlConfiguration(); 182 | 183 | try { 184 | config.load(reader); 185 | } catch (IOException | InvalidConfigurationException var3) { 186 | System.err.println("Cannot load configuration from stream"); 187 | var3.printStackTrace(); 188 | } 189 | 190 | return config; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/file/YamlConfigurationOptions.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.file; 2 | 3 | import org.apache.commons.lang3.Validate; 4 | 5 | public class YamlConfigurationOptions extends FileConfigurationOptions { 6 | private int indent = 2; 7 | 8 | protected YamlConfigurationOptions(YamlConfiguration configuration) { 9 | super(configuration); 10 | } 11 | 12 | public YamlConfiguration configuration() { 13 | return (YamlConfiguration)super.configuration(); 14 | } 15 | 16 | public YamlConfigurationOptions copyDefaults(boolean value) { 17 | super.copyDefaults(value); 18 | return this; 19 | } 20 | 21 | public YamlConfigurationOptions pathSeparator(char value) { 22 | super.pathSeparator(value); 23 | return this; 24 | } 25 | 26 | public YamlConfigurationOptions header(String value) { 27 | super.header(value); 28 | return this; 29 | } 30 | 31 | public YamlConfigurationOptions copyHeader(boolean value) { 32 | super.copyHeader(value); 33 | return this; 34 | } 35 | 36 | public int indent() { 37 | return this.indent; 38 | } 39 | 40 | public YamlConfigurationOptions indent(int value) { 41 | Validate.isTrue(value >= 2, "Indent must be at least 2 characters"); 42 | Validate.isTrue(value <= 9, "Indent cannot be greater than 9 characters"); 43 | this.indent = value; 44 | return this; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/file/YamlConstructor.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.file; 2 | 3 | import com.cheatbreaker.obf.utils.configuration.serialization.ConfigurationSerialization; 4 | import org.yaml.snakeyaml.constructor.SafeConstructor; 5 | import org.yaml.snakeyaml.error.YAMLException; 6 | import org.yaml.snakeyaml.nodes.Node; 7 | import org.yaml.snakeyaml.nodes.Tag; 8 | 9 | import java.util.Iterator; 10 | import java.util.LinkedHashMap; 11 | import java.util.Map; 12 | 13 | public class YamlConstructor extends SafeConstructor { 14 | public YamlConstructor() { 15 | this.yamlConstructors.put(Tag.MAP, new ConstructCustomObject()); 16 | } 17 | 18 | private class ConstructCustomObject extends ConstructYamlMap { 19 | private ConstructCustomObject() { 20 | super(); 21 | } 22 | 23 | public Object construct(Node node) { 24 | if (node.isTwoStepsConstruction()) { 25 | throw new YAMLException("Unexpected referential mapping structure. Node: " + node); 26 | } else { 27 | Map raw = (Map)super.construct(node); 28 | if (!raw.containsKey("==")) { 29 | return raw; 30 | } else { 31 | Map typed = new LinkedHashMap(raw.size()); 32 | Iterator var4 = raw.entrySet().iterator(); 33 | 34 | while(var4.hasNext()) { 35 | Map.Entry entry = (Map.Entry)var4.next(); 36 | typed.put(entry.getKey().toString(), entry.getValue()); 37 | } 38 | 39 | try { 40 | return ConfigurationSerialization.deserializeObject(typed); 41 | } catch (IllegalArgumentException var6) { 42 | throw new YAMLException("Could not deserialize object", var6); 43 | } 44 | } 45 | } 46 | } 47 | 48 | public void construct2ndStep(Node node, Object object) { 49 | throw new YAMLException("Unexpected referential mapping structure. Node: " + node); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/file/YamlRepresenter.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.file; 2 | 3 | import com.cheatbreaker.obf.utils.configuration.ConfigurationSection; 4 | import com.cheatbreaker.obf.utils.configuration.serialization.ConfigurationSerializable; 5 | import com.cheatbreaker.obf.utils.configuration.serialization.ConfigurationSerialization; 6 | import org.yaml.snakeyaml.nodes.Node; 7 | import org.yaml.snakeyaml.representer.Representer; 8 | 9 | import java.util.LinkedHashMap; 10 | import java.util.Map; 11 | 12 | public class YamlRepresenter extends Representer { 13 | public YamlRepresenter() { 14 | this.multiRepresenters.put(ConfigurationSection.class, new YamlRepresenter.RepresentConfigurationSection()); 15 | this.multiRepresenters.put(ConfigurationSerializable.class, new YamlRepresenter.RepresentConfigurationSerializable()); 16 | } 17 | 18 | private class RepresentConfigurationSection extends RepresentMap { 19 | private RepresentConfigurationSection() { 20 | super(); 21 | } 22 | 23 | public Node representData(Object data) { 24 | return super.representData(((ConfigurationSection)data).getValues(false)); 25 | } 26 | } 27 | 28 | private class RepresentConfigurationSerializable extends RepresentMap { 29 | private RepresentConfigurationSerializable() { 30 | super(); 31 | } 32 | 33 | public Node representData(Object data) { 34 | ConfigurationSerializable serializable = (ConfigurationSerializable)data; 35 | Map values = new LinkedHashMap(); 36 | values.put("==", ConfigurationSerialization.getAlias(serializable.getClass())); 37 | values.putAll(serializable.serialize()); 38 | return super.representData(values); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/serialization/ConfigurationSerializable.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.serialization; 2 | 3 | import java.util.Map; 4 | 5 | public interface ConfigurationSerializable { 6 | Map serialize(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/serialization/ConfigurationSerialization.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.serialization; 2 | 3 | 4 | import org.apache.commons.lang3.Validate; 5 | 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.lang.reflect.Modifier; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.logging.Level; 13 | import java.util.logging.Logger; 14 | 15 | public class ConfigurationSerialization { 16 | public static final String SERIALIZED_TYPE_KEY = "=="; 17 | private final Class clazz; 18 | private static Map> aliases = new HashMap(); 19 | 20 | protected ConfigurationSerialization(Class clazz) { 21 | this.clazz = clazz; 22 | } 23 | 24 | protected Method getMethod(String name, boolean isStatic) { 25 | try { 26 | Method method = this.clazz.getDeclaredMethod(name, Map.class); 27 | if (!ConfigurationSerializable.class.isAssignableFrom(method.getReturnType())) { 28 | return null; 29 | } else { 30 | return Modifier.isStatic(method.getModifiers()) != isStatic ? null : method; 31 | } 32 | } catch (NoSuchMethodException var4) { 33 | return null; 34 | } catch (SecurityException var5) { 35 | return null; 36 | } 37 | } 38 | 39 | protected Constructor getConstructor() { 40 | try { 41 | return this.clazz.getConstructor(Map.class); 42 | } catch (NoSuchMethodException var2) { 43 | return null; 44 | } catch (SecurityException var3) { 45 | return null; 46 | } 47 | } 48 | 49 | protected ConfigurationSerializable deserializeViaMethod(Method method, Map args) { 50 | try { 51 | ConfigurationSerializable result = (ConfigurationSerializable)method.invoke((Object)null, args); 52 | if (result != null) { 53 | return result; 54 | } 55 | 56 | Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call method '" + method.toString() + "' of " + this.clazz + " for deserialization: method returned null"); 57 | } catch (Throwable var4) { 58 | Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call method '" + method.toString() + "' of " + this.clazz + " for deserialization", var4 instanceof InvocationTargetException ? var4.getCause() : var4); 59 | } 60 | 61 | return null; 62 | } 63 | 64 | protected ConfigurationSerializable deserializeViaCtor(Constructor ctor, Map args) { 65 | try { 66 | return (ConfigurationSerializable)ctor.newInstance(args); 67 | } catch (Throwable var4) { 68 | Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call constructor '" + ctor.toString() + "' of " + this.clazz + " for deserialization", var4 instanceof InvocationTargetException ? var4.getCause() : var4); 69 | return null; 70 | } 71 | } 72 | 73 | public ConfigurationSerializable deserialize(Map args) { 74 | Validate.notNull(args, "Args must not be null"); 75 | ConfigurationSerializable result = null; 76 | Method method = null; 77 | if (result == null) { 78 | method = this.getMethod("deserialize", true); 79 | if (method != null) { 80 | result = this.deserializeViaMethod(method, args); 81 | } 82 | } 83 | 84 | if (result == null) { 85 | method = this.getMethod("valueOf", true); 86 | if (method != null) { 87 | result = this.deserializeViaMethod(method, args); 88 | } 89 | } 90 | 91 | if (result == null) { 92 | Constructor constructor = this.getConstructor(); 93 | if (constructor != null) { 94 | result = this.deserializeViaCtor(constructor, args); 95 | } 96 | } 97 | 98 | return result; 99 | } 100 | 101 | public static ConfigurationSerializable deserializeObject(Map args, Class clazz) { 102 | return (new ConfigurationSerialization(clazz)).deserialize(args); 103 | } 104 | 105 | public static ConfigurationSerializable deserializeObject(Map args) { 106 | Class clazz = null; 107 | if (args.containsKey("==")) { 108 | try { 109 | String alias = (String)args.get("=="); 110 | if (alias == null) { 111 | throw new IllegalArgumentException("Cannot have null alias"); 112 | } 113 | 114 | clazz = getClassByAlias(alias); 115 | if (clazz == null) { 116 | throw new IllegalArgumentException("Specified class does not exist ('" + alias + "')"); 117 | } 118 | } catch (ClassCastException var3) { 119 | var3.fillInStackTrace(); 120 | throw var3; 121 | } 122 | 123 | return (new ConfigurationSerialization(clazz)).deserialize(args); 124 | } else { 125 | throw new IllegalArgumentException("Args doesn't contain type key ('==')"); 126 | } 127 | } 128 | 129 | public static void registerClass(Class clazz) { 130 | DelegateDeserialization delegate = (DelegateDeserialization)clazz.getAnnotation(DelegateDeserialization.class); 131 | if (delegate == null) { 132 | registerClass(clazz, getAlias(clazz)); 133 | registerClass(clazz, clazz.getName()); 134 | } 135 | 136 | } 137 | 138 | public static void registerClass(Class clazz, String alias) { 139 | aliases.put(alias, clazz); 140 | } 141 | 142 | public static void unregisterClass(String alias) { 143 | aliases.remove(alias); 144 | } 145 | 146 | public static void unregisterClass(Class clazz) { 147 | while(aliases.values().remove(clazz)) { 148 | } 149 | 150 | } 151 | 152 | public static Class getClassByAlias(String alias) { 153 | return (Class)aliases.get(alias); 154 | } 155 | 156 | public static String getAlias(Class clazz) { 157 | DelegateDeserialization delegate = (DelegateDeserialization)clazz.getAnnotation(DelegateDeserialization.class); 158 | if (delegate != null) { 159 | if (delegate.value() != null && delegate.value() != clazz) { 160 | return getAlias(delegate.value()); 161 | } 162 | 163 | delegate = null; 164 | } 165 | 166 | if (delegate == null) { 167 | SerializableAs alias = (SerializableAs)clazz.getAnnotation(SerializableAs.class); 168 | if (alias != null && alias.value() != null) { 169 | return alias.value(); 170 | } 171 | } 172 | 173 | return clazz.getName(); 174 | } 175 | 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/serialization/DelegateDeserialization.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.serialization; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.TYPE}) 10 | public @interface DelegateDeserialization { 11 | Class value(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/configuration/serialization/SerializableAs.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.configuration.serialization; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.TYPE}) 10 | public @interface SerializableAs { 11 | String value(); 12 | } -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/loader/LoaderUtil.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.loader; 2 | 3 | import java.security.SecureClassLoader; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | public class LoaderUtil extends SecureClassLoader { 8 | 9 | Map bytes = new ConcurrentHashMap<>(); 10 | Map> classes = new ConcurrentHashMap<>(); 11 | 12 | public LoaderUtil(ClassLoader parent) { 13 | super(parent); 14 | } 15 | 16 | @Override 17 | public Class loadClass(String name, boolean resolve) { 18 | 19 | if (name.startsWith("java.")) { 20 | try { 21 | return super.loadClass(name, resolve); 22 | } catch (ClassNotFoundException e) { 23 | return null; 24 | } 25 | } 26 | 27 | if (bytes.containsKey(name)) { 28 | byte[] b = this.bytes.get(name); 29 | Class klass = this.defineClass(name, b, 0, b.length); 30 | if (resolve) resolveClass(klass); 31 | classes.put(name, klass); 32 | bytes.remove(name); 33 | } 34 | 35 | if (classes.containsKey(name)) { 36 | return classes.get(name); 37 | } 38 | 39 | try { 40 | return super.loadClass(name, resolve); 41 | } catch (ClassNotFoundException e) { 42 | return null; 43 | } 44 | } 45 | 46 | public void addClass(String name, byte[] bytes) { 47 | if (name.startsWith("java/lang")) return; 48 | this.bytes.put(name.replace("/", "."), bytes); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/pair/ClassMethodNode.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.pair; 2 | 3 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 4 | import org.objectweb.asm.tree.MethodNode; 5 | 6 | public class ClassMethodNode { 7 | 8 | private final ClassWrapper classNode; 9 | private final MethodNode methodNode; 10 | 11 | public ClassMethodNode(ClassWrapper classNode, MethodNode methodNode) { 12 | this.classNode = classNode; 13 | this.methodNode = methodNode; 14 | } 15 | 16 | public ClassWrapper getClassWrapper() { 17 | return classNode; 18 | } 19 | 20 | public MethodNode getMethodNode() { 21 | return methodNode; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return classNode.name + "." + methodNode.name + methodNode.desc; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object o) { 31 | if (this == o) return true; 32 | if (o == null || getClass() != o.getClass()) return false; 33 | return toString().equals(o.toString()); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return toString().hashCode(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/samples/DynamicInvoke.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.samples; 2 | 3 | import lombok.SneakyThrows; 4 | 5 | import java.lang.invoke.MethodHandle; 6 | import java.lang.invoke.MethodHandles; 7 | import java.lang.invoke.MethodType; 8 | 9 | public class DynamicInvoke { 10 | 11 | @SneakyThrows 12 | public static Object invoke(Object... v) { 13 | // public static Object invoke(Object[] args, Class klass, MethodHandle _name, MethodHandle _desc, MethodHandle invoke, MethodHandle type) { 14 | v[3] = (String) ((MethodHandle) v[3]).invoke(); 15 | v[2] = (String) ((MethodHandle) v[2]).invoke(); 16 | return ((MethodHandle) v[4]).bindTo(((MethodHandle) v[5]).invokeWithArguments(MethodHandles.publicLookup(), v[1], v[2], 17 | MethodType.fromMethodDescriptorString(v[3].toString(), ((Class) v[1]).getClassLoader()))).invoke(v[0]); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/tree/ClassTree.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.tree; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class ClassTree { 7 | public String thisClass; 8 | 9 | public Set subClasses = new HashSet<>(); 10 | public Set parentClasses = new HashSet<>(); 11 | 12 | public ClassTree(String thisClass) { 13 | this.thisClass = thisClass; 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/tree/HierarchyUtils.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.utils.tree; 2 | 3 | import com.cheatbreaker.obf.Obf; 4 | import com.cheatbreaker.obf.utils.asm.ClassWrapper; 5 | 6 | import java.lang.reflect.Modifier; 7 | import java.util.HashSet; 8 | import java.util.LinkedList; 9 | import java.util.Set; 10 | 11 | public class HierarchyUtils { 12 | 13 | private static Obf obf = Obf.getInstance(); 14 | 15 | public static String getCommonSuperClass0(String type1, String type2) { 16 | ClassWrapper first = obf.assureLoaded(type1); 17 | ClassWrapper second = obf.assureLoaded(type2); 18 | if (isAssignableFrom(type1, type2)) 19 | return type1; 20 | else 21 | if (isAssignableFrom(type2, type1)) 22 | return type2; 23 | else 24 | if (Modifier.isInterface(first.access) 25 | || Modifier.isInterface(second.access)) 26 | return "java/lang/Object"; 27 | else { 28 | do { 29 | type1 = first.superName; 30 | first = obf.assureLoaded(type1); 31 | } while (!isAssignableFrom(type1, type2)); 32 | return type1; 33 | } 34 | } 35 | 36 | public static String getCommonSuperClass1(String type1, String type2) { 37 | if ("java/lang/Object".equals(type1) || "java/lang/Object".equals(type2)) 38 | return "java/lang/Object"; 39 | String a = getCommonSuperClass0(type1, type2); 40 | String b = getCommonSuperClass0(type2, type1); 41 | if (!"java/lang/Object".equals(a)) 42 | return a; 43 | if (!"java/lang/Object".equals(b)) 44 | return b; 45 | ClassWrapper first = obf.assureLoaded(type1); 46 | ClassWrapper second = obf.assureLoaded(type2); 47 | return getCommonSuperClass1(first.superName, second.superName); 48 | } 49 | 50 | public static boolean isAssignableFrom(String type1, String type2) { 51 | if ("java/lang/Object".equals(type1)) 52 | return true; 53 | if (type1.equals(type2)) 54 | return true; 55 | obf.assureLoaded(type1); 56 | obf.assureLoaded(type2); 57 | ClassTree firstTree = obf.getClassTree(type1); 58 | Set allChilds1 = new HashSet<>(); 59 | LinkedList toProcess = new LinkedList<>(firstTree.subClasses); 60 | while (!toProcess.isEmpty()) { 61 | String s = toProcess.poll(); 62 | if (allChilds1.add(s)) { 63 | obf. assureLoaded(s); 64 | ClassTree tempTree = obf.getClassTree(s); 65 | toProcess.addAll(tempTree.subClasses); 66 | } 67 | } 68 | return allChilds1.contains(type2); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/vm/NativeHandler.java: -------------------------------------------------------------------------------- 1 | package vm; 2 | 3 | import java.io.File; 4 | 5 | public class NativeHandler { 6 | 7 | static { 8 | System.load(new File("native.dll").getAbsolutePath()); 9 | } 10 | 11 | /** 12 | * Decrypts constant pool contents 13 | *

14 | * - Ints 15 | * - Longs 16 | * 17 | * @param klass The class whose constant pool is going to be decrypted 18 | */ 19 | public static native void decryptConstantPool(Class klass); 20 | 21 | /** 22 | * Replaces the bytecode 23 | * 24 | * @param klass The class which holds the method 25 | * @param method Method getting its bytecode replaced 26 | * @param bytecode New bytecode 27 | */ 28 | public static native void transformMethod(Class klass, String method, int[] bytecode); 29 | 30 | public static native int[] raw_bytes(Class var0, String var1); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/cheatbreaker/obf/tests/DynamicInvokeTest.java: -------------------------------------------------------------------------------- 1 | package com.cheatbreaker.obf.tests; 2 | 3 | import com.cheatbreaker.obf.utils.samples.DynamicInvoke; 4 | import lombok.SneakyThrows; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.lang.invoke.MethodHandle; 8 | import java.lang.invoke.MethodHandles; 9 | import java.lang.invoke.MethodType; 10 | 11 | public class DynamicInvokeTest { 12 | 13 | @SneakyThrows 14 | @Test 15 | void test() { 16 | 17 | MethodHandles.Lookup lookup = MethodHandles.publicLookup(); 18 | 19 | DynamicInvoke.invoke(new Object[] { 10 }, 20 | DynamicInvokeTest.class, 21 | lookup.findStatic(String.class, "valueOf", MethodType.methodType(String.class, Object.class)).bindTo("test2"), 22 | lookup.findStatic(String.class, "valueOf", MethodType.methodType(String.class, Object.class)).bindTo("(I)V"), 23 | lookup.findVirtual(MethodHandle.class, "invokeWithArguments", MethodType.methodType(Object.class, Object[].class)), 24 | lookup.findVirtual(MethodHandles.Lookup.class, "findStatic", MethodType.methodType(MethodHandle.class, Class.class, String.class, MethodType.class))); 25 | } 26 | 27 | public static void test2(int i) { 28 | System.out.println(i); 29 | } 30 | } 31 | --------------------------------------------------------------------------------