├── README.md ├── XUnion ├── 4.0.0.cs ├── App.config ├── Deobfuscators │ └── 4.0.0.cs ├── Program.cs ├── Unpackers │ └── 4.0.0.cs ├── Updater.cs ├── Utils.cs ├── XORManager.cs ├── XUnion.csproj └── dnlibUtils.cs └── XUnionPE ├── App.config ├── Decompiler.cs ├── PEUtils.cs ├── Program.cs ├── Utils.cs └── XUnionPE.csproj /README.md: -------------------------------------------------------------------------------- 1 | # GruMinion 2 | Full Deobfuscator for PEUnion 4.0.0 (.NET & PE32) 3 | 4 | # Notes 5 |
 6 | - This is my first deobfuscator for non-MSIL apps (messy ahh code)
 7 | - Currently in beta
 8 | 
9 | 10 | # Info 11 | (stub deobfuscation were made just for fun :)) 12 | 13 | Protection Name | Supported | Details 14 | ------------- | ------------- | ------------- 15 | PE32 Stub | Yes (Padding included) | Compression support soon 16 | .NET Stub | Yes | Fully works 17 | PE32 Stub Deobfuscation | No | 18 | .NET Stub Deobfuscation | Yes | You likely won't need it 19 | 20 | # Credits 21 | 9ee1 - Capstone.NET (Engine to decompile x86 Assembly Code (XUnionPE))
22 | 0xd4d - dnlib (Engine to decompile MSIL Code (XUnion))
23 | bytecode77 - PEUnion 24 | -------------------------------------------------------------------------------- /XUnion/4.0.0.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using dnlib.DotNet; 9 | using dnlib.DotNet.Emit; 10 | 11 | namespace XUnion 12 | { 13 | public class v4 14 | { 15 | public class SharedItems 16 | { 17 | public SharedItems(string Path) 18 | { 19 | path = Path; 20 | RawFile = File.ReadAllBytes(path); 21 | asm = ModuleDefMD.Load(RawFile); 22 | app = Assembly.Load(RawFile); 23 | } 24 | 25 | public bool Stage2Only, 26 | isCompressed; 27 | 28 | public string path; 29 | public byte[] RawFile, 30 | Stage2RawFile, 31 | OutputRawFile; 32 | 33 | public XORManager XORMng { get; set; } 34 | 35 | public ModuleDefMD asm, 36 | Stage2asm; 37 | 38 | public Assembly app, 39 | Stage2app; 40 | 41 | public MethodDef UnXorMethod_Main, 42 | UnXorMethod_IntPart, 43 | UnXorMethod_SelectPart, 44 | ResourceDecrypt, 45 | GZipStage2Method; 46 | } 47 | 48 | public XUnion.Deobfuscators.v4 Stage1Deobfuscator { get; set; } 49 | public XUnion.Deobfuscators.v4 Stage2Deobfuscator { get; set; } 50 | public XUnion.Unpackers.v4 Unpacker { get; set; } 51 | public XUnion.Unpackers.v4.Stage2 Stage2 { get; set; } 52 | 53 | public SharedItems Shared; 54 | 55 | public v4(ModuleDefMD asm, bool skipInit = false, bool isStage2ASM = false) { Shared = new SharedItems(asm.Location); if (!skipInit) { if (isStage2ASM) { Shared.Stage2Only = true; InitializeStage2(); } else { InitializeStage1(); } } } 56 | public v4(string path, bool skipInit = false, bool isStage2ASM = false) { Shared = new SharedItems(path); if (!skipInit) { if (isStage2ASM) { Shared.Stage2Only = true; InitializeStage2(); } else { InitializeStage1(); } } } 57 | 58 | public byte[] Unpack() { return Shared.OutputRawFile; } 59 | public void Unpack(string path) { File.WriteAllBytes(path, Shared.OutputRawFile); } 60 | 61 | private void IdentifyMethods (ModuleDefMD asm) { IdentifyMethods(asm, ref Shared); } 62 | private void InitializeXORManager(ModuleDefMD asm) { Shared.XORMng = InitializeXORManager(asm, ref Shared); } 63 | 64 | private void InitializeStage1() 65 | { 66 | IdentifyMethods(Shared.asm); // Sets Shared.UnXorMethod_Main, Shared.UnXorMethod_IntPart, Shared.UnXorMethod_SelectPart 67 | InitializeXORManager(Shared.asm); // Sets Shared.xor 68 | 69 | Unpacker = new XUnion.Unpackers.v4(ref Shared); 70 | 71 | Stage1Deobfuscator = new XUnion.Deobfuscators.v4(ref Shared, Shared.asm); 72 | //Stage1Deobfuscator.FixRenaming(); 73 | 74 | Unpacker.GetStage2ASM(); // Sets Shared.Stage2asm, Shared.Stage2app & Shared.Stage2RawFile 75 | InitializeStage2(); 76 | } 77 | private void InitializeStage2() 78 | { 79 | if (Shared.Stage2Only) 80 | { 81 | Shared.Stage2asm = Shared.asm; 82 | Shared.Stage2app = Assembly.Load(Shared.RawFile); 83 | IdentifyMethods(Shared.Stage2asm); 84 | InitializeXORManager(Shared.Stage2asm); 85 | } 86 | 87 | Stage2Deobfuscator = new XUnion.Deobfuscators.v4(ref Shared, Shared.Stage2asm); 88 | //Stage2Deobfuscator.FixRenaming(); 89 | 90 | Stage2 = new Unpackers.v4.Stage2(ref Shared); 91 | 92 | Stage2.GetResDecryptMethod(); // Sets Shared.ResourceDecrypt 93 | Stage2.IsCompressed(); // Sets Shared.isCompressed and Shared.GZipStage2Method if found/detected 94 | Stage2.Fix(); // Sets Shared.OutputRawFile 95 | } 96 | 97 | private static ModuleDefMD IdentifyMethods(ModuleDefMD asm, ref SharedItems Shared) 98 | { 99 | ModuleDefMD resultAsm = asm; 100 | bool XorMethodFound = false; 101 | 102 | for (int x = 0; x < resultAsm.EntryPoint.DeclaringType.Methods.Count; x++) 103 | { 104 | if (XorMethodFound) { break; } 105 | 106 | MethodDef methods = resultAsm.EntryPoint.DeclaringType.Methods[x]; 107 | if (!methods.HasBody) { continue; } 108 | 109 | Instruction[] insts = methods.Body.Instructions.ToArray(); 110 | 111 | try 112 | { 113 | // Detects if current method is Int32 UnXor Function (int[2] -> int) 114 | if (insts[0].IsLdarg() && 115 | insts[1].IsLdarg() && 116 | insts[2].IsLdcI4() && 117 | insts[3].OpCode == OpCodes.Xor && 118 | insts[4].OpCode == OpCodes.Xor && 119 | insts[5].OpCode == OpCodes.Ret) 120 | { 121 | Shared.UnXorMethod_IntPart = methods; if (Shared.UnXorMethod_Main != null) { XorMethodFound = true; } continue; 122 | } 123 | 124 | // Detects if current method is GZipDecompression 125 | if (insts[0].OpCode == OpCodes.Newobj && 126 | insts[3].OpCode == OpCodes.Newobj && 127 | insts[5].OpCode == OpCodes.Newobj && 128 | insts[9].OpCode == OpCodes.Callvirt && 129 | insts[9].Operand is MemberRef && 130 | Utils.CompareNamespaceMemberRef(insts[9], typeof(Stream), "CopyTo", new Type[] { typeof(Stream) })) 131 | { 132 | methods.Parameters[0].Name = "compressedData"; 133 | methods.Name = "DecompressProtectedApp"; 134 | continue; 135 | } 136 | 137 | // Detects if current method is UInt16 UnXor Function (uint16 -> string) 138 | if (insts[10].Operand != null && 139 | insts[14].Operand != null && 140 | insts[7].IsLdcI4() && 141 | insts[8].IsLdcI4() && 142 | insts[10].Operand is MethodSpec && 143 | insts[14].Operand is MethodSpec && 144 | Utils.CompareNamespaceMethodSpec(insts[10], typeof(Enumerable), "Skip")) 145 | { 146 | Shared.UnXorMethod_Main = methods; if (Shared.UnXorMethod_IntPart != null) { XorMethodFound = true; } continue; 147 | } 148 | } 149 | catch { } 150 | } 151 | 152 | // Getting the foreach Linq loop (hidden method) present in the UInt16 UnXor method 153 | for (int x = 0; x < resultAsm.EntryPoint.DeclaringType.NestedTypes.Count; x++) 154 | { 155 | TypeDef nestedType = resultAsm.EntryPoint.DeclaringType.NestedTypes[x]; 156 | if (nestedType.IsDelegate || !nestedType.HasFields) { continue; } 157 | for (int x_ = 0; x_ < nestedType.Methods.Count; x_++) 158 | { 159 | MethodDef nestedMethod = nestedType.Methods[x_]; 160 | if (!nestedMethod.HasBody || nestedMethod.IsConstructor) { continue; } 161 | Instruction[] insts = nestedMethod.Body.Instructions.ToArray(); 162 | if (insts[3].IsLdcI4() && 163 | insts[4].IsLdcI4() && 164 | insts[5].OpCode == OpCodes.Call && 165 | insts[5].Operand is MethodDef && 166 | insts[6].OpCode == OpCodes.Ldelem_U2 && 167 | insts[7].OpCode == OpCodes.Xor && 168 | insts[8].OpCode == OpCodes.Conv_U2) 169 | { 170 | Shared.UnXorMethod_SelectPart = nestedMethod; return resultAsm; 171 | } 172 | } 173 | } 174 | return null; 175 | } 176 | private static XORManager InitializeXORManager(ModuleDefMD asm, ref SharedItems Shared) 177 | { 178 | int[] MainUnXorValues = new int[4] 179 | { 180 | Shared.UnXorMethod_Main.Body.Instructions[7].GetLdcI4Value(), 181 | Shared.UnXorMethod_Main.Body.Instructions[8].GetLdcI4Value(), 182 | Shared.UnXorMethod_SelectPart.Body.Instructions[3].GetLdcI4Value(), 183 | Shared.UnXorMethod_SelectPart.Body.Instructions[4].GetLdcI4Value() 184 | }; 185 | 186 | int IntXorValue = Shared.UnXorMethod_IntPart.Body.Instructions[2].GetLdcI4Value(); 187 | 188 | return new XORManager(XORManager.ToInt(MainUnXorValues[0], MainUnXorValues[1], IntXorValue), 189 | XORManager.ToInt(MainUnXorValues[2], MainUnXorValues[3], IntXorValue), 190 | IntXorValue); 191 | } 192 | 193 | public static bool IsStage2ASM(ModuleDefMD asm) { return asm.EntryPoint.DeclaringType.Fields.Count == 2 ? true : false; } 194 | } 195 | } -------------------------------------------------------------------------------- /XUnion/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /XUnion/Deobfuscators/4.0.0.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using dnlib.DotNet; 11 | using dnlib.DotNet.Emit; 12 | 13 | namespace XUnion.Deobfuscators 14 | { 15 | public class v4 16 | { 17 | private XUnion.v4.SharedItems SharedItems; 18 | private XORManager xor; 19 | private ModuleDefMD asm; 20 | 21 | public int XORFixCount, XORStringFixCount, RenameFixCount, DelegateFixCount; 22 | 23 | public v4(ModuleDefMD module, XORManager xorMng) 24 | { 25 | asm = module; 26 | xor = xorMng; 27 | } 28 | public v4(ref XUnion.v4.SharedItems sharedItems, ModuleDefMD module) 29 | { 30 | SharedItems = sharedItems; 31 | xor = sharedItems.XORMng; 32 | asm = module; 33 | } 34 | 35 | public void Deobfuscate() 36 | { 37 | FixRenaming(); 38 | FixXOR(); 39 | FixDelegates(); 40 | } 41 | 42 | public void FixRenaming() // Renames Methods 43 | { 44 | asm.EntryPoint.DeclaringType.Name = "Program"; 45 | asm.EntryPoint.Parameters[0].Name = "args"; 46 | RenameFixCount += 2; 47 | 48 | if (SharedItems.ResourceDecrypt != null) 49 | { 50 | SharedItems.ResourceDecrypt.Name = "GetProtectedAppStream"; 51 | SharedItems.ResourceDecrypt.Parameters[0].Name = "resourceName"; 52 | RenameFixCount += 2; 53 | } 54 | 55 | List typesToCleanUp = new List(); 56 | typesToCleanUp.AddRange(asm.Types); 57 | for (int x = 0; x < asm.EntryPoint.DeclaringType.NestedTypes.Count; x++) 58 | { 59 | TypeDef nestedType = asm.EntryPoint.DeclaringType.NestedTypes[x]; 60 | if (nestedType.IsDelegate) { continue; } 61 | typesToCleanUp.Add(nestedType); 62 | } 63 | 64 | for (int x_type = 0; x_type < typesToCleanUp.Count; x_type++) 65 | { 66 | TypeDef type = typesToCleanUp[x_type]; 67 | int delegateCount = 0; 68 | 69 | for (int x_nestedTypes = 0; x_nestedTypes < type.NestedTypes.Count; x_nestedTypes++) 70 | { 71 | TypeDef nestedType = type.NestedTypes[x_nestedTypes]; 72 | if (nestedType.IsDelegate) { nestedType.Name = "Delegate_" + delegateCount++; RenameFixCount++; continue; } 73 | } 74 | 75 | for (int x_method = 0; x_method < type.Methods.Count; x_method++) 76 | { 77 | MethodDef methods = type.Methods[x_method]; 78 | 79 | if (methods.HasImplMap) 80 | { 81 | methods.Name = methods.ImplMap.Module.FullName.Split('.')[0] + "." + methods.ImplMap.Name; 82 | switch (methods.Name) 83 | { 84 | case "kernel32.GetProcAddress": methods.Parameters[0].Name = "hModule"; 85 | methods.Parameters[1].Name = "lpProcName" ; RenameFixCount += 2; break; 86 | case "kernel32.LoadLibraryA": methods.Parameters[0].Name = "lpLibFileName"; RenameFixCount++ ; break; 87 | } 88 | continue; 89 | } 90 | 91 | if (!methods.HasBody) { continue; } 92 | Instruction[] insts = methods.Body.Instructions.ToArray(); 93 | try 94 | { 95 | if ((insts[0].OpCode == OpCodes.Ldarga_S || insts[0].OpCode == OpCodes.Ldarga) && 96 | (insts[2].OpCode == OpCodes.Ldarga_S || insts[2].OpCode == OpCodes.Ldarga) && 97 | insts[4].OpCode == OpCodes.Call && 98 | insts[4].Operand is MethodSpec && 99 | insts[5].OpCode == OpCodes.Ret && 100 | Utils.CompareNamespaceMethodSpec(insts[4], typeof(Marshal), "GetDelegateForFunctionPointer", new Type[] { typeof(IntPtr) })) 101 | { 102 | methods.GenericParameters[0].Name = "TDelegate"; 103 | methods.Parameters[0].Name = "moduleName"; 104 | methods.Parameters[1].Name = "procName"; 105 | methods.Name = "GetDelegateForPtr"; 106 | RenameFixCount += 4; 107 | continue; 108 | } 109 | 110 | if (insts[0].IsLdarg() && 111 | insts[1].IsLdarg() && 112 | insts[2].IsLdcI4() && 113 | insts[3].OpCode == OpCodes.Xor && 114 | insts[4].OpCode == OpCodes.Xor && 115 | insts[5].OpCode == OpCodes.Ret) 116 | { 117 | methods.Name = "Int32Xor"; 118 | methods.Parameters[0].Name = "value1"; 119 | methods.Parameters[1].Name = "value2"; 120 | RenameFixCount += 3; 121 | continue; 122 | } 123 | 124 | if (insts[0].OpCode == OpCodes.Newobj && 125 | insts[3].OpCode == OpCodes.Newobj && 126 | insts[5].OpCode == OpCodes.Newobj && 127 | insts[9].OpCode == OpCodes.Callvirt && 128 | insts[9].Operand is MemberRef && 129 | Utils.CompareNamespaceMemberRef(insts[9], typeof(Stream), "CopyTo", new Type[] { typeof(Stream) })) 130 | { 131 | methods.Parameters[0].Name = "compressedData"; 132 | methods.Name = "DecompressProtectedApp"; 133 | continue; 134 | } 135 | 136 | if (insts[7].IsLdcI4() && 137 | insts[8].IsLdcI4() && 138 | insts[10].Operand != null && 139 | insts[10].Operand is MethodSpec && 140 | insts[14].Operand != null && 141 | insts[14].Operand is MethodSpec && 142 | Utils.CompareNamespaceMethodSpec(insts[10], typeof(Enumerable), "Skip")) 143 | { 144 | methods.Name = "UnXorToString"; 145 | methods.Parameters[0].Name = "data"; 146 | RenameFixCount += 2; 147 | continue; 148 | } 149 | 150 | if (insts[15].OpCode == OpCodes.Callvirt && 151 | insts[15].Operand is MemberRef && 152 | insts[12].OpCode == OpCodes.Callvirt && 153 | insts[12].Operand is MemberRef && 154 | insts[6].OpCode == OpCodes.Ldftn && 155 | insts[6].Operand is MethodDef && 156 | Utils.CompareNamespaceMemberRef(insts[15], typeof(Thread), "Start", new Type[] { }) && 157 | asm.EntryPoint.DeclaringType.NestedTypes.Contains(((MethodDef)insts[6].Operand).DeclaringType)) 158 | { 159 | MethodDef nestedMethod = ((MethodDef)insts[6].Operand); 160 | if (nestedMethod.Body.Instructions[3].OpCode == OpCodes.Callvirt && 161 | nestedMethod.Body.Instructions[3].Operand is MemberRef && 162 | nestedMethod.Body.Instructions[10].OpCode == OpCodes.Ldsfld && 163 | nestedMethod.Body.Instructions[10].Operand is FieldDef && 164 | nestedMethod.Body.Instructions[13].OpCode == OpCodes.Callvirt && 165 | nestedMethod.Body.Instructions[13].Operand is MemberRef && 166 | Utils.CompareNamespaceMemberRef(nestedMethod.Body.Instructions[3], typeof(Assembly), "get_EntryPoint", new Type[] { }) && 167 | Utils.CompareNamespaceMemberRef(nestedMethod.Body.Instructions[13], typeof(MethodBase), "Invoke", new Type[] { typeof(object), typeof(object[]) })) 168 | { 169 | methods.Name = "InvokeProtectedApp"; 170 | methods.Parameters[0].Name = "rawProtectedApp"; 171 | RenameFixCount += 2; 172 | continue; 173 | } 174 | } 175 | } 176 | catch { } 177 | 178 | for (int x = 0; x < insts.Length; x++) 179 | { 180 | if (insts[x].OpCode == OpCodes.Callvirt && 181 | insts[x].Operand is MethodDef && 182 | ((MethodDef)insts[x].Operand).DeclaringType.IsDelegate) 183 | { 184 | methods.Name = "InitializeDelegates"; 185 | RenameFixCount++; 186 | break; 187 | } 188 | 189 | if (insts[x].OpCode == OpCodes.Stsfld && 190 | insts[x].Operand is FieldDef && 191 | insts[x - 1].OpCode == OpCodes.Call && 192 | insts[x - 1].Operand is MethodSpec && 193 | insts[x - 7].OpCode == OpCodes.Stsfld && 194 | insts[x - 7].Operand is FieldDef && 195 | Utils.CompareNamespaceMethodSpec(insts[x - 1], typeof(Enumerable), "ToArray") && 196 | Utils.CompareNamespaceMethodSpec(insts[x - 2], typeof(Enumerable), "Skip")) 197 | { 198 | ((FieldDef)insts[x].Operand).Name = "protectedAppArgs"; 199 | ((FieldDef)insts[x - 7].Operand).Name = "PEunionArgs"; 200 | RenameFixCount += 2; 201 | break; 202 | } 203 | } 204 | } 205 | } 206 | } 207 | public void FixXOR() // Fixes XOR strings & integers 208 | { 209 | List typesToCleanUp = new List(); 210 | typesToCleanUp.AddRange(asm.Types); 211 | for (int x = 0; x < asm.EntryPoint.DeclaringType.NestedTypes.Count; x++) 212 | { 213 | TypeDef nestedType = asm.EntryPoint.DeclaringType.NestedTypes[x]; 214 | if (nestedType.IsDelegate) { continue; } 215 | typesToCleanUp.Add(nestedType); 216 | } 217 | 218 | for (int x_type = 0; x_type < typesToCleanUp.Count; x_type++) 219 | { 220 | TypeDef type = typesToCleanUp[x_type]; 221 | for (int x_method = 0; x_method < type.Methods.Count; x_method++) 222 | { 223 | MethodDef methods = type.Methods[x_method]; 224 | if (!methods.HasBody) { continue; } 225 | for (int x = 0; x < methods.Body.Instructions.Count; x++) 226 | { 227 | Instruction[] insts = methods.Body.Instructions.ToArray(); 228 | Instruction inst = insts[x]; 229 | if (inst.OpCode == OpCodes.Newarr && 230 | inst.Operand is TypeRef && 231 | ((TypeRef)inst.Operand).FullName == typeof(ushort).FullName) 232 | { 233 | Local arrLoc = insts[x + 1].GetLocal(methods.Body.Variables); 234 | MSILArray arr = MSILArray.ParseArray(insts, x - 1); 235 | methods.Body.Instructions[x - 1].OpCode = OpCodes.Ldstr; 236 | methods.Body.Instructions[x - 1].Operand = xor.Compute(Utils.ToUInt16(arr.ToArray())); 237 | for (int i = 0; i < arr.RawCount; i++) { methods.Body.Instructions.RemoveAt(x); } 238 | methods.Body.Variables.Remove(arrLoc); 239 | inst = methods.Body.Instructions[x]; 240 | if (inst.OpCode == OpCodes.Call && 241 | inst.Operand is MethodDef && 242 | ((MethodDef)inst.Operand).Name == "UnXorToString") { methods.Body.Instructions.RemoveAt(x); } 243 | XORStringFixCount++; 244 | continue; 245 | } 246 | 247 | if (inst.OpCode == OpCodes.Call && 248 | inst.Operand is MethodDef && 249 | ((MethodDef)inst.Operand).Name == "Int32Xor" && 250 | insts[x - 2].IsLdcI4() && 251 | insts[x - 1].IsLdcI4()) 252 | { 253 | methods.Body.Instructions[x - 2].Operand = xor.ToInt(insts[x - 2].GetLdcI4Value(), insts[x - 1].GetLdcI4Value()); 254 | methods.Body.Instructions.RemoveAt(x - 1); 255 | methods.Body.Instructions.RemoveAt(x - 1); 256 | XORFixCount++; 257 | continue; 258 | } 259 | } 260 | } 261 | } 262 | } 263 | public void FixDelegates() // Removes unused delegates and renames used ones 264 | { 265 | for (int x_method = 0; x_method < asm.EntryPoint.DeclaringType.Methods.Count; x_method++) 266 | { 267 | MethodDef methods = asm.EntryPoint.DeclaringType.Methods[x_method]; 268 | if (methods.Name != "InitializeDelegates") { continue; } 269 | 270 | Instruction[] insts = methods.Body.Instructions.ToArray(); 271 | for (int x = 0; x < insts.Length; x++) 272 | { 273 | Instruction inst = insts[x]; 274 | 275 | if (inst.OpCode == OpCodes.Call && 276 | inst.Operand is MethodSpec && 277 | ((MethodSpec)inst.Operand).Name == "GetDelegateForPtr" && 278 | insts[x + 1].IsStloc() && 279 | insts[x - 2].OpCode == OpCodes.Ldstr && // Requires FixXOR 280 | insts[x - 1].OpCode == OpCodes.Ldstr) // Requires FixXOR 281 | { 282 | Local tempLoc = insts[x + 1].GetLocal(methods.Body.Variables); 283 | ITypeDefOrRef delType; 284 | switch (insts[x - 1].Operand.ToString()) 285 | { 286 | case "SetErrorMode": // kernel32.SetErrorMode 287 | case "VirtualAllocExNuma": // kernel32.VirtualAllocExNuma 288 | if (isLocalTypeDelegate(tempLoc, out delType)) { delType.Name = /*insts[x - 2].Operand.ToString().Split('.')[0] + "." + */ insts[x - 1].Operand.ToString(); } 289 | DelegateFixCount++; 290 | break; 291 | } 292 | } 293 | } 294 | } 295 | 296 | for (int x_nestedTypes = 0; x_nestedTypes < asm.EntryPoint.DeclaringType.NestedTypes.Count; x_nestedTypes++) 297 | { 298 | TypeDef nestedType = asm.EntryPoint.DeclaringType.NestedTypes[x_nestedTypes]; 299 | if (nestedType.IsDelegate) 300 | { 301 | switch (nestedType.Name) 302 | { 303 | case "SetErrorMode": 304 | for (int x_method = 0; x_method < nestedType.Methods.Count; x_method++) 305 | { 306 | MethodDef method = nestedType.Methods[x_method]; 307 | switch (method.Name) 308 | { 309 | case "Invoke": 310 | case "BeginInvoke": 311 | int offset = (method.Parameters[0].MethodSigIndex == -2) ? 1 : 0; 312 | method.Parameters[offset++].Name = "uMode"; break; 313 | } 314 | } 315 | break; 316 | case "VirtualAllocExNuma": 317 | for (int x_method = 0; x_method < nestedType.Methods.Count; x_method++) 318 | { 319 | MethodDef method = nestedType.Methods[x_method]; 320 | switch (method.Name) 321 | { 322 | case "Invoke": 323 | case "BeginInvoke": 324 | int offset = (method.Parameters[0].MethodSigIndex == -2) ? 1 : 0; 325 | method.Parameters[offset++].Name = "hProcess"; 326 | method.Parameters[offset++].Name = "IpAddress"; 327 | method.Parameters[offset++].Name = "dwSize"; 328 | method.Parameters[offset++].Name = "flAllocationType"; 329 | method.Parameters[offset++].Name = "flProtect"; 330 | method.Parameters[offset++].Name = "nndPreferred"; 331 | break; 332 | } 333 | } 334 | break; 335 | default: 336 | asm.EntryPoint.DeclaringType.NestedTypes.RemoveAt(x_nestedTypes); 337 | x_nestedTypes--; 338 | break; 339 | } 340 | } 341 | } 342 | } 343 | 344 | private bool isLocalTypeDelegate(Local loc, out ITypeDefOrRef delegateType) 345 | { 346 | delegateType = null; 347 | if (loc.Type is TypeSig && ((TypeSig)loc.Type).IsTypeDefOrRef) 348 | { 349 | ITypeDefOrRef del = ((TypeSig)loc.Type).ToTypeDefOrRef(); 350 | if (((TypeDef)del).IsDelegate) { delegateType = del; return true; } 351 | } 352 | return false; 353 | } 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /XUnion/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Forms; 9 | using dnlib.DotNet; 10 | 11 | namespace XUnion 12 | { 13 | class Program 14 | { 15 | private static bool _stg2Export = false, _fixXor = true, _fixNames = true, _fixDelegates = true; 16 | private static ConsoleColor back1 = ConsoleColor.Gray, back2 = ConsoleColor.DarkGray; 17 | 18 | static int SelectMode(int defaultSelect = 0) 19 | { 20 | int selection = defaultSelect; 21 | bool noStg2 = v4.IsStage2ASM(asm); 22 | SetColorsFromPosition(); 23 | while (true) 24 | { 25 | string[] options = new string[] 26 | { 27 | "Unpack", 28 | "Deobfuscate", 29 | "", 30 | "Extract Stage 2" 31 | }; 32 | for (int x = 0; x < options.Length; x++) 33 | { 34 | switch (x) 35 | { 36 | case 2: continue; 37 | case 3: if (noStg2) { continue; } break; 38 | } 39 | Console.SetCursorPosition(1, x+2); 40 | if (x == selection) { Console.Write(" > " + options[x] + "\n"); } 41 | else { Console.Write(" " + options[x] + "\n"); } 42 | } 43 | ConsoleKeyInfo key = Console.ReadKey(); 44 | switch (key.Key) 45 | { 46 | case ConsoleKey.UpArrow: if (selection > 0) { if (selection - 1 == 2) { selection -= 2; } else { selection--; } } break; 47 | case ConsoleKey.DownArrow: if (selection <= (noStg2 ? 0 : options.Length - 2)) { if (selection + 1 == 2) { selection += 2; } else { selection++; } } break; 48 | case ConsoleKey.Enter: return selection; 49 | } 50 | } 51 | } 52 | 53 | // Working but scrapped since each deobfuscation layer requires each other. 54 | /*static int[] SelectDeobMode(int defaultSelect = 0) 55 | { 56 | int selection = -1; 57 | bool clear = true; 58 | Console.BackgroundColor = Console.CursorLeft < Console.WindowWidth / 2 ? back2 : back1; 59 | Console.ForegroundColor = Console.CursorLeft < Console.WindowWidth / 2 ? back1 : back2; 60 | while (true) 61 | { 62 | if (clear) 63 | { 64 | for (int y = 5; y < 10; y++) 65 | { 66 | Console.SetCursorPosition(2, y); 67 | Console.Write(" "); 68 | } 69 | if (selection != -1) { return new int[] { 0, selection }; } 70 | clear = false; 71 | selection = defaultSelect; 72 | } 73 | string[] options = new string[] 74 | { 75 | "%OPT%Fix XORs", 76 | "%OPT%Fix Names", 77 | "%OPT%Remove Delegates", 78 | "", 79 | "Start" 80 | }; 81 | bool[] optionsValues = new bool[3] { _fixXor, _fixNames, _fixDelegates }; 82 | for (int x = 0; x < options.Length; x++) 83 | { 84 | if (options[x] == "") { continue; } 85 | Console.SetCursorPosition(2, x + 5); 86 | ConsoleColor ogFore = Console.ForegroundColor; 87 | if (options[x].StartsWith("%OPT%")) 88 | { 89 | if (optionsValues[x]) 90 | { 91 | //if (selection == x) { Console.Write("> "); } else { Console.Write(" "); } 92 | if (selection == x) { Console.ForegroundColor = ConsoleColor.Black; } 93 | Console.Write("["); 94 | if (selection == x) { Console.ForegroundColor = ConsoleColor.Magenta; } else { Console.ForegroundColor = ConsoleColor.Green; } 95 | Console.Write("x"); 96 | if (selection == x) { Console.ForegroundColor = ConsoleColor.Black; } else { Console.ForegroundColor = ogFore; } 97 | Console.Write("] "); 98 | } 99 | else 100 | { 101 | if (selection == x) { Console.ForegroundColor = ConsoleColor.Black; } 102 | Console.Write("[ ] "); 103 | } 104 | Console.ForegroundColor = ogFore; 105 | Console.Write(options[x].Replace("%OPT%", null) + "\n"); 106 | } 107 | else 108 | { 109 | if (x == selection) { Console.Write(" > " + options[x] + "\n"); } 110 | else { Console.Write(" " + options[x] + "\n"); } 111 | } 112 | } 113 | ConsoleKeyInfo key = Console.ReadKey(); 114 | switch (key.Key) 115 | { 116 | case ConsoleKey.UpArrow: if (selection > 0) { if (selection - 1 == 3) { selection -= 2; } else { selection--; } } break; 117 | case ConsoleKey.DownArrow: if (selection <= options.Length - 2) { if (selection + 1 == 3) { selection += 2; } else { selection++; } } break; 118 | case ConsoleKey.Backspace: clear = true; continue; 119 | case ConsoleKey.Enter: 120 | if (options[selection].StartsWith("%OPT%")) 121 | { 122 | switch (selection) 123 | { 124 | case 0: _fixXor = !_fixXor; break; 125 | case 1: _fixNames = !_fixNames; break; 126 | case 2: _fixDelegates = !_fixDelegates; break; 127 | default: return new int[] { 1, selection }; 128 | } 129 | } 130 | break; 131 | } 132 | } 133 | }*/ 134 | 135 | static void InitConsole(bool clear = false) 136 | { 137 | Console.CursorVisible = false; 138 | SetColorsFromPosition(); 139 | drawOnBottomCorner(' '); // no scrollbars 140 | if (clear) { Console.Clear(); } 141 | } 142 | 143 | /*static readonly decimal version = 1.00m; 144 | static readonly string appText = "XUnion v" + version + " - misonothx"; 145 | 146 | private static void DrawAppText(ConsoleColor fore = ConsoleColor.Gray, ConsoleColor back = ConsoleColor.Blue) 147 | { 148 | Console.ForegroundColor = fore; Console.BackgroundColor = back; 149 | int topTextIndex = 0; 150 | for (int x = 0; x < Console.BufferWidth; x++) 151 | { 152 | char ch = x < (Console.BufferWidth / 2) - (appText.Length / 2) ? ' ' : (topTextIndex < appText.Length ? appText[topTextIndex++] : ' '); 153 | Console.Write(ch); 154 | } 155 | } 156 | private static void DrawSideFrame(ConsoleColor color1 = ConsoleColor.Blue, ConsoleColor color2 = ConsoleColor.Cyan) 157 | { 158 | for (int y = 0; y < Console.WindowHeight - 1; y++) 159 | { 160 | if (y % 2 == 1) { Console.BackgroundColor = color1; } 161 | else { Console.BackgroundColor = color2; } 162 | Console.SetCursorPosition(Console.WindowWidth - 1, y); 163 | Console.Write(' '); 164 | Console.SetCursorPosition(0, y); 165 | Console.Write(' '); 166 | } 167 | Console.SetCursorPosition(0, Console.WindowHeight - 1); 168 | Console.BackgroundColor = color1; 169 | for (int x = 0; x < Console.BufferWidth; x++) { Console.Write(' '); } 170 | }*/ 171 | 172 | private static void AlternateColors() 173 | { 174 | Console.BackgroundColor = Console.BackgroundColor == ConsoleColor.Gray ? ConsoleColor.DarkGray : ConsoleColor.Gray; 175 | Console.ForegroundColor = Console.BackgroundColor == ConsoleColor.Gray ? ConsoleColor.DarkGray : ConsoleColor.Gray; 176 | } 177 | private static void SetColorsFromPosition() 178 | { 179 | Console.BackgroundColor = Console.CursorLeft < Console.WindowWidth / 2 ? back2 : back1; 180 | Console.ForegroundColor = Console.CursorLeft < Console.WindowWidth / 2 ? back1 : back2; 181 | } 182 | 183 | private static void DrawSide(int sideNum) 184 | { 185 | for (int x = 0; x < Console.WindowWidth; x++) 186 | { 187 | SetColorsFromPosition(); 188 | for (int y = 0; y < Console.WindowHeight; y++) 189 | { 190 | Console.SetCursorPosition(x, y); 191 | Console.Write(' '); 192 | } 193 | } 194 | drawOnBottomCorner('▓'); 195 | } 196 | 197 | private static void DrawLayout(int activeSide) 198 | { 199 | for (int x = 0; x < Console.WindowWidth; x++) 200 | { 201 | SetColorsFromPosition(); 202 | if (x == (Console.WindowWidth / 2) - 1) { AlternateColors(); } 203 | for (int y = 0; y < Console.WindowHeight; y++) 204 | { 205 | Console.SetCursorPosition(x, y); 206 | if (activeSide == 1 && x < (Console.WindowWidth / 2)) { Console.Write(' '); } 207 | else 208 | { 209 | if (activeSide == 2 && x > (Console.WindowWidth / 2)) { Console.Write(' '); } 210 | Console.Write('▓'); 211 | } 212 | } 213 | } 214 | drawOnBottomCorner('▓'); 215 | /*Console.SetCursorPosition(0, 0); 216 | Console.BackgroundColor = back1; 217 | int drawHeight = 1; 218 | for (int x = 0; x < Console.WindowWidth-1; x++) 219 | { 220 | if (x == (Console.WindowWidth / 2) - 3 || 221 | x == (Console.WindowWidth / 2) + 3) 222 | { 223 | Console.ForegroundColor = x > (Console.WindowWidth/2) + 1?back1:back2; 224 | for (int y = 1; y < Console.BufferHeight - drawHeight; y++) 225 | { 226 | Console.SetCursorPosition(x, y); 227 | Console.Write('│'); 228 | } 229 | Console.SetCursorPosition(x, x == (Console.WindowWidth / 2) + 3 ? Console.BufferHeight - drawHeight : 0); 230 | } 231 | 232 | if (x == Console.WindowWidth / 2) 233 | { 234 | for (int y = 0; y < Console.BufferHeight - drawHeight; y++) 235 | { 236 | Console.BackgroundColor = y%2==1 ? back1 : back2; 237 | Console.SetCursorPosition(x, y); 238 | Console.Write(' '); 239 | } 240 | Console.SetCursorPosition(x, Console.BufferHeight - drawHeight); 241 | } 242 | Console.BackgroundColor = x >= Console.BufferWidth / 2?back2:back1; 243 | Console.Write(' '); 244 | }*/ 245 | } 246 | 247 | private static void drawOnBottomCorner(char ch) 248 | { 249 | Console.SetBufferSize(Console.WindowWidth+1, Console.WindowHeight); 250 | Console.SetCursorPosition(Console.WindowWidth - 1, Console.WindowHeight - 1); 251 | Console.Write(ch); 252 | Console.SetBufferSize(Console.WindowWidth, Console.WindowHeight); 253 | } 254 | 255 | static ModuleDefMD asm; 256 | static v4 mng; 257 | 258 | static void CheckForUpdates() 259 | { 260 | if (!Updater.HasInternetConnection()) { return; } 261 | Updater update = new Updater(Updater.GetUpdate()); 262 | string text = "New update available!\n\nCurrent Version: " + Updater.CurrentVersion + "\nLatest Version: " + update.LatestVersion + "\n\nChangelog:\n\n"; 263 | foreach (string cl_text in update.ChangeLog) { text += cl_text + "\n"; } 264 | text += "\nDownload now?"; 265 | if (!update.IsRunningLatest()) 266 | { 267 | if (MessageBox.Show(text, "Update Available!", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information) == DialogResult.Yes) 268 | { 269 | update.DownloadUpdate(); 270 | Application.Exit(); 271 | } 272 | } 273 | } 274 | 275 | public static string path; // used by Updater.cs 276 | 277 | [STAThread] 278 | static void Main(string[] args) 279 | { 280 | path = Path.GetFullPath(args[0]); 281 | Console.SetWindowSize(50, 15); 282 | InitConsole(true); 283 | Console.SetCursorPosition(1, 1); 284 | CheckForUpdates(); 285 | Console.WriteLine("Loading '" + Path.GetFileName(args[0]) + "'..."); 286 | Console.WriteLine(); 287 | try { ModuleDefMD tempAsm = ModuleDefMD.Load(Path.GetFullPath(args[0])); } 288 | catch (BadImageFormatException) 289 | { 290 | Console.WriteLine(" Native executable detected!"); 291 | Console.WriteLine(" Loading 'XUnionPE.exe'..."); 292 | Process.Start("XUnionPE.exe", '"' + Path.GetFullPath(args[0]) + '"').WaitForExit(); 293 | return; 294 | } 295 | //DrawSideFrame(); 296 | //DrawAppText(); 297 | asm = ModuleDefMD.Load(Path.GetFullPath(args[0])); 298 | mng = new v4(asm, false, v4.IsStage2ASM(asm)); 299 | int mode = 0/*, deobHover = 0*/; 300 | SaveFileDialog sfd = new SaveFileDialog(); 301 | bool exit = false; 302 | Console.Clear(); 303 | DrawLayout(1); 304 | drawOnBottomCorner(' '); 305 | while (!exit) 306 | { 307 | bool selectModeBackspace = false; 308 | mode = SelectMode(mode); 309 | switch (mode) 310 | { 311 | case 0: 312 | if (sfd.ShowDialog() == DialogResult.OK) 313 | { 314 | Console.Clear(); 315 | Console.SetCursorPosition(0, 1); 316 | Console.WriteLine(" Unpacking '" + Path.GetFileName(mng.Shared.path) + "'..."); 317 | mng.Unpack(sfd.FileName); 318 | Console.WriteLine("Successfully unpacked '" + Path.GetFileName(mng.Shared.path) + "'!"); 319 | exit = true; 320 | } 321 | break; 322 | case 1: 323 | if (sfd.ShowDialog() == DialogResult.OK) 324 | { 325 | Deobfuscators.v4 deob = null; 326 | if (mng.Shared.Stage2Only) 327 | { 328 | mng.Stage2Deobfuscator.Deobfuscate(); 329 | deob = mng.Stage2Deobfuscator; 330 | } 331 | else 332 | { 333 | mng.Stage1Deobfuscator.Deobfuscate(); 334 | deob = mng.Stage1Deobfuscator; 335 | } 336 | Console.Clear(); 337 | Console.SetCursorPosition(0, 1); 338 | Console.WriteLine(" Deobfuscating '" + Path.GetFileName(mng.Shared.path) + "'..."); 339 | Console.WriteLine(); 340 | //Console.SetCursorPosition((Console.BufferWidth / 2) + 2, 2); 341 | Console.ForegroundColor = ConsoleColor.White; 342 | Console.WriteLine(" " + deob.DelegateFixCount + " Delegates Removed"); 343 | Console.WriteLine(" " + deob.XORStringFixCount + " Strings Fixed"); 344 | Console.WriteLine(" " + deob.XORFixCount + " Numbers Fixed"); 345 | Console.WriteLine(" " + deob.RenameFixCount + " Elements Renamed"); 346 | Console.WriteLine(); 347 | Console.WriteLine(" '" + Path.GetFileName(mng.Shared.path) + "' Successfully Deobfuscated!"); 348 | Console.ForegroundColor = ConsoleColor.Gray; 349 | Console.WriteLine(" Saving Deobfuscated File..."); 350 | mng.Shared.asm.Write(sfd.FileName); 351 | Console.ForegroundColor = ConsoleColor.White; 352 | Console.WriteLine(); 353 | Console.WriteLine(" Deobfuscated File Successfully Saved!"); 354 | exit = true; 355 | } 356 | break; 357 | case 3: 358 | if (sfd.ShowDialog() == DialogResult.OK) 359 | { 360 | Console.Clear(); 361 | Console.SetCursorPosition(0, 1); 362 | Console.Write(" Exporting Stage 2... (" + mng.Shared.Stage2RawFile.Length + " bytes)"); 363 | File.WriteAllBytes(sfd.FileName, mng.Shared.Stage2RawFile); 364 | Console.ForegroundColor = ConsoleColor.White; 365 | Console.Write(" Stage 2 Exported!"); 366 | exit = true; 367 | } 368 | break; 369 | } 370 | if (selectModeBackspace) { continue; } 371 | } 372 | 373 | /*ModuleDefMD asm = ModuleDefMD.Load(Path.GetFullPath(args[0])); 374 | v4 mng = new v4(asm, false, v4.IsStage2ASM(asm)); 375 | mng.Stage1Deobfuscator.Deobfuscate(); 376 | mng.Stage2Deobfuscator.Deobfuscate(); 377 | mng.Shared.asm.Write(Path.GetFileNameWithoutExtension(args[0]) + "-stg1-unpk_XUnion" + Path.GetExtension(args[0])); 378 | mng.Shared.Stage2asm.Write(Path.GetFileNameWithoutExtension(args[0]) + "-stg2-unpk_XUnion" + Path.GetExtension(args[0])); 379 | File.WriteAllBytes(Path.GetFileNameWithoutExtension(args[0]) + "-app-unpk_XUnion" + Path.GetExtension(args[0]), mng.Shared.OutputRawFile);*/ 380 | Console.BackgroundColor = ConsoleColor.DarkGreen; 381 | Console.ForegroundColor = ConsoleColor.White; 382 | Console.SetCursorPosition(0, Console.BufferHeight - 1); 383 | for (int x = 0; x < Console.BufferWidth - 1; x++) { Console.Write(' '); } 384 | drawOnBottomCorner(' '); 385 | Console.SetCursorPosition(2, Console.BufferHeight - 1); 386 | Console.Write("Press any key to exit..."); 387 | Console.ReadKey(); 388 | } 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /XUnion/Unpackers/4.0.0.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using dnlib.DotNet.Emit; 7 | using dnlib.DotNet; 8 | using dnlib; 9 | using System.IO; 10 | using System.Reflection; 11 | using System.Resources; 12 | using System.IO.Compression; 13 | 14 | namespace XUnion.Unpackers 15 | { 16 | public class v4 17 | { 18 | public class Stage2 19 | { 20 | private XUnion.v4.SharedItems SharedItems; 21 | 22 | public Stage2(ref XUnion.v4.SharedItems sharedItems) { SharedItems = sharedItems; } 23 | 24 | public void GetResDecryptMethod() 25 | { 26 | for (int x = 0; x < SharedItems.Stage2asm.EntryPoint.DeclaringType.Methods.Count; x++) 27 | { 28 | MethodDef methods = SharedItems.Stage2asm.EntryPoint.DeclaringType.Methods[x]; 29 | if (!methods.HasBody) { continue; } 30 | Instruction[] insts = methods.Body.Instructions.ToArray(); 31 | for (int x_inst = 0; x_inst < insts.Length; x_inst++) 32 | { 33 | Instruction inst = insts[x_inst]; 34 | try 35 | { 36 | if (inst.OpCode == OpCodes.Call && 37 | inst.Operand is MemberRef && 38 | insts[x_inst - 4].OpCode == OpCodes.Call && 39 | insts[x_inst - 8].OpCode == OpCodes.Call && 40 | insts[x_inst - 4].Operand is MethodDef && 41 | insts[x_inst - 8].Operand is MethodDef && 42 | Utils.CompareNamespaceMemberRef(inst, typeof(Buffer), "BlockCopy", new Type[] { typeof(Array), typeof(int), typeof(Array), typeof(int), typeof(int) })) 43 | { 44 | MethodDef intPart = ((MethodDef)insts[x_inst - 4].Operand); 45 | 46 | if (intPart.Body.Instructions[0].IsLdarg() && 47 | intPart.Body.Instructions[1].IsLdarg() && 48 | intPart.Body.Instructions[2].IsLdcI4() && 49 | intPart.Body.Instructions[3].OpCode == OpCodes.Xor && 50 | intPart.Body.Instructions[4].OpCode == OpCodes.Xor && 51 | intPart.Body.Instructions[5].OpCode == OpCodes.Ret) 52 | { 53 | SharedItems.ResourceDecrypt = methods; 54 | return; 55 | } 56 | } 57 | } 58 | catch { } 59 | } 60 | } 61 | throw new Exception("Could not find ResourceDecryption Function!"); 62 | } 63 | public int[] GetBufferValues() 64 | { 65 | int[] result = new int[4]; 66 | Instruction[] insts = SharedItems.ResourceDecrypt.Body.Instructions.ToArray(); 67 | 68 | for (int x_inst = 0; x_inst < insts.Length; x_inst++) 69 | { 70 | Instruction inst = insts[x_inst]; 71 | if (inst.OpCode == OpCodes.Call && 72 | inst.Operand is MethodDef && 73 | inst.Operand == SharedItems.UnXorMethod_IntPart) 74 | { 75 | try 76 | { 77 | if (insts[x_inst - 4].OpCode == OpCodes.Callvirt && 78 | insts[x_inst + 2].OpCode == OpCodes.Callvirt && 79 | insts[x_inst - 4].Operand is MemberRef && 80 | insts[x_inst + 2].Operand is MemberRef && 81 | ((MemberRef)insts[x_inst - 2].Operand).DeclaringType.FullName == typeof(Assembly).FullName + "." + typeof(Assembly).GetMethod("GetManifestResourceName").Name && 82 | ((MemberRef)insts[x_inst - 4].Operand).DeclaringType.FullName == typeof(Assembly).FullName + "." + typeof(Assembly).GetMethod("GetManifestResourceStream", new Type[] { typeof(string) }).Name) 83 | { 84 | result[0] = SharedItems.XORMng.ToInt(insts[x_inst - 2].GetLdcI4Value(), insts[x_inst - 1].GetLdcI4Value()); // Index of resource name 85 | } 86 | 87 | if (insts[x_inst + 1].OpCode == OpCodes.Sub && 88 | insts[x_inst + 2].OpCode == OpCodes.Newarr && 89 | insts[x_inst + 2].Operand is TypeRef && 90 | ((TypeRef)insts[x_inst + 2].Operand).FullName == typeof(byte).FullName) 91 | { 92 | result[1] = SharedItems.XORMng.ToInt(insts[x_inst - 2].GetLdcI4Value(), insts[x_inst - 1].GetLdcI4Value()); // Size of output array 93 | } 94 | 95 | if (insts[x_inst - 6].OpCode == OpCodes.Sub && 96 | insts[x_inst - 5].OpCode == OpCodes.Newarr && 97 | insts[x_inst - 5].Operand is TypeRef && 98 | ((TypeRef)insts[x_inst - 5].Operand).FullName == typeof(byte).FullName) 99 | { 100 | result[2] = SharedItems.XORMng.ToInt(insts[x_inst - 2].GetLdcI4Value(), insts[x_inst - 1].GetLdcI4Value()); // Buffer.BlockCopy startIndex 101 | } 102 | 103 | if (insts[x_inst - 10].OpCode == OpCodes.Sub && 104 | insts[x_inst - 9].OpCode == OpCodes.Newarr && 105 | insts[x_inst - 9].Operand is TypeRef && 106 | ((TypeRef)insts[x_inst - 5].Operand).FullName == typeof(byte).FullName) 107 | { 108 | result[3] = SharedItems.XORMng.ToInt(insts[x_inst - 2].GetLdcI4Value(), insts[x_inst - 1].GetLdcI4Value()); // Buffer.BlockCopy count 109 | } 110 | } 111 | catch { } 112 | } 113 | } 114 | 115 | return result; 116 | } 117 | public void IsCompressed() 118 | { 119 | for (int x_methods = 0; x_methods < SharedItems.Stage2asm.EntryPoint.DeclaringType.Methods.Count; x_methods++) 120 | { 121 | MethodDef methods = SharedItems.Stage2asm.EntryPoint.DeclaringType.Methods[x_methods]; 122 | if (!methods.HasBody) { continue; } 123 | Instruction[] insts = methods.Body.Instructions.ToArray(); 124 | try 125 | { 126 | if (insts[0].OpCode == OpCodes.Newobj && 127 | insts[3].OpCode == OpCodes.Newobj && 128 | insts[5].OpCode == OpCodes.Newobj && 129 | insts[9].OpCode == OpCodes.Callvirt && 130 | insts[9].Operand is MemberRef && 131 | Utils.CompareNamespaceMemberRef(insts[9], typeof(Stream), "CopyTo", new Type[] { typeof(Stream) })) 132 | { 133 | SharedItems.isCompressed = true; 134 | SharedItems.GZipStage2Method = methods; 135 | return; 136 | } 137 | } catch { } 138 | } 139 | } 140 | 141 | public void Fix() 142 | { 143 | MSILArray resNameArr = null; 144 | 145 | Instruction[] insts = SharedItems.Stage2asm.EntryPoint.Body.Instructions.ToArray(); 146 | for (int x = 0; x < insts.Length; x++) 147 | { 148 | Instruction inst = insts[x]; 149 | 150 | if (inst.OpCode == OpCodes.Newarr) { int.Parse("0"); } 151 | 152 | if (inst.OpCode == OpCodes.Newarr && 153 | inst.Operand is TypeRef && 154 | ((TypeRef)inst.Operand).FullName == typeof(ushort).FullName && 155 | insts[x - 5].OpCode == OpCodes.Call && 156 | insts[x - 5].Operand is MethodDef && 157 | insts[x - 1].IsLdcI4()) 158 | { 159 | if (((MethodDef)insts[x - 5].Operand).Body.Instructions[3].OpCode == OpCodes.Xor && 160 | ((MethodDef)insts[x - 5].Operand).Body.Instructions[4].OpCode == OpCodes.Xor && 161 | ((MethodDef)insts[x - 5].Operand).Body.Instructions[5].OpCode == OpCodes.Ret) 162 | { 163 | //SharedItems.UnXorMethod_IntPart = (MethodDef)insts[x - 5].Operand; 164 | resNameArr = MSILArray.ParseArray(insts, x - 1); 165 | } 166 | } 167 | } 168 | 169 | string resourceName = SharedItems.XORMng.Compute(Utils.ToUInt16(resNameArr.ToArray())); 170 | 171 | int[] bufVal = GetBufferValues(); 172 | using (ResourceReader rr = new ResourceReader(SharedItems.Stage2app.GetManifestResourceStream( 173 | SharedItems.Stage2app.GetManifestResourceNames()[bufVal[0]]))) 174 | { 175 | byte[] srcApp; 176 | string resType; 177 | 178 | rr.GetResourceData(resourceName, out resType, out srcApp); 179 | 180 | if (SharedItems.isCompressed) 181 | { 182 | byte[] decompSrc = new byte[srcApp.Length]; 183 | bool offsetHeader = (srcApp[0] != 0x1F) && (srcApp[1] != 0x8B); 184 | Buffer.BlockCopy(srcApp, offsetHeader ? 4 : 0, decompSrc, 0, srcApp.Length - 4); 185 | MemoryStream tempMs = new MemoryStream(); 186 | GZipStream decomp = new GZipStream(new MemoryStream(decompSrc), CompressionMode.Decompress); 187 | decomp.CopyTo(tempMs); 188 | srcApp = tempMs.ToArray(); 189 | SharedItems.OutputRawFile = srcApp; 190 | } 191 | else 192 | { 193 | if (srcApp[0] != 0x4d && // PEunion sometimes just adds 4 junk bytes to the source file, causing the packed app to not 194 | srcApp[1] != 0x5a) // be usable, this fixes it. 195 | { 196 | SharedItems.OutputRawFile = new byte[(srcApp.Length - bufVal[1]) - 4]; 197 | Buffer.BlockCopy(srcApp, bufVal[2] + 4, SharedItems.OutputRawFile, 0, SharedItems.OutputRawFile.Length - 4); 198 | } 199 | else 200 | { 201 | SharedItems.OutputRawFile = new byte[srcApp.Length - bufVal[1]]; 202 | Buffer.BlockCopy(srcApp, bufVal[2], SharedItems.OutputRawFile, 0, SharedItems.OutputRawFile.Length); 203 | } 204 | } 205 | } 206 | } 207 | } 208 | 209 | private XUnion.v4.SharedItems SharedItems; 210 | 211 | public v4(ref XUnion.v4.SharedItems sharedItems) { SharedItems = sharedItems; } 212 | 213 | public long[] GetDecryptionValues() 214 | { 215 | long[] result = new long[6]; 216 | 217 | Instruction[] insts = SharedItems.asm.EntryPoint.Body.Instructions.ToArray(); 218 | for (int x_inst = 0; x_inst < insts.Length; x_inst++) 219 | { 220 | Instruction inst = insts[x_inst]; 221 | 222 | try 223 | { 224 | if (inst.OpCode == OpCodes.Call && 225 | inst.Operand is MethodDef && 226 | insts[x_inst + 2].OpCode == OpCodes.Call && 227 | insts[x_inst + 2].Operand is MemberRef && 228 | (MethodDef)inst.Operand == SharedItems.UnXorMethod_IntPart && 229 | ((MemberRef)insts[x_inst + 2].Operand).DeclaringType.FullName == typeof(Assembly).FullName) 230 | { 231 | result[0] = insts[x_inst - 6].GetLdcI4Value(); // Bitshift 232 | result[1] = insts[x_inst - 4].GetLdcI4Value(); // Bitshift 233 | result[4] = SharedItems.XORMng.ToInt(insts[x_inst - 2].GetLdcI4Value(), 234 | insts[x_inst - 1].GetLdcI4Value()); continue; // Offsets bitshift 235 | } 236 | 237 | if (inst.OpCode == OpCodes.Call && 238 | insts[x_inst - 4].OpCode == OpCodes.Newarr && 239 | insts[x_inst + 4].OpCode == OpCodes.Call && 240 | inst.Operand is MethodDef && 241 | insts[x_inst + 4].Operand is MethodDef && 242 | insts[x_inst - 4].Operand is TypeRef && 243 | (MethodDef)inst.Operand == SharedItems.UnXorMethod_IntPart && 244 | (MethodDef)insts[x_inst + 4].Operand == SharedItems.UnXorMethod_IntPart && 245 | ((ITypeDefOrRef)insts[x_inst - 4].Operand).FullName == typeof(byte).FullName) 246 | { 247 | result[2] = SharedItems.XORMng.ToInt(insts[x_inst - 2].GetLdcI4Value(), 248 | insts[x_inst - 1].GetLdcI4Value()); 249 | result[3] = SharedItems.XORMng.ToInt(insts[x_inst + 2].GetLdcI4Value(), 250 | insts[x_inst + 3].GetLdcI4Value()); 251 | result[5] = insts[x_inst - 7].GetLdcI4Value(); // Packed Assembly Size 252 | } 253 | } 254 | catch { } 255 | } 256 | return result; 257 | } 258 | public void GetStage2ASM() 259 | { 260 | Instruction[] insts = SharedItems.asm.EntryPoint.Body.Instructions.ToArray(); 261 | 262 | MSILTryCatch hdex = MSILTryCatch.Parse(insts, SharedItems.asm.EntryPoint.Body.ExceptionHandlers[2]); 263 | 264 | int resNamArrStartIndex = -1; 265 | 266 | for (int x = 0; x < hdex.TryInstructions.Length; x++) 267 | { 268 | Instruction inst = hdex.TryInstructions[x]; 269 | try 270 | { 271 | if (inst.IsStloc() && 272 | hdex.TryInstructions[x - 1].OpCode == OpCodes.Newarr && 273 | hdex.TryInstructions[x - 1].Operand is TypeRef && 274 | ((TypeRef)hdex.TryInstructions[x - 1].Operand).FullName == typeof(ushort).FullName && 275 | hdex.TryInstructions[x - 5].IsLdloc()) { resNamArrStartIndex = x - 2; break; } 276 | } 277 | catch { } 278 | } 279 | 280 | ushort[] nameArr = Utils.ToUInt16(MSILArray.ParseArray(hdex.TryInstructions).ToArray()), 281 | resourceNameArr = Utils.ToUInt16(MSILArray.ParseArray(hdex.TryInstructions, resNamArrStartIndex).ToArray()); 282 | 283 | string name = SharedItems.XORMng.Compute(nameArr), 284 | resourceName = SharedItems.XORMng.Compute(resourceNameArr); 285 | 286 | long[] dVal = GetDecryptionValues(); 287 | 288 | MemoryStream ms = new MemoryStream(); 289 | SharedItems.app.GetManifestResourceStream(name).CopyTo(ms); 290 | 291 | using (ResourceReader rr = new ResourceReader(SharedItems.app.GetManifestResourceStream(name))) 292 | { 293 | byte[] encryptedApp; 294 | byte[] decryptedApp = new byte[dVal[5]]; 295 | 296 | string resType; 297 | 298 | rr.GetResourceData(resourceName, out resType, out encryptedApp); 299 | 300 | for (long x = dVal[2]; x < dVal[5]; x++) 301 | { 302 | decryptedApp[x] = (byte)((uint)encryptedApp[dVal[3]++] ^ (uint)dVal[0]); 303 | if ((dVal[1] & 1U) == 1U) { dVal[3] += dVal[4]; } 304 | dVal[0] = ((uint)dVal[0] >> 5 | (uint)dVal[0] << 27) * 7U; 305 | dVal[1] = ((uint)dVal[1] >> 1 | (uint)dVal[1] << 31); 306 | } 307 | 308 | SharedItems.Stage2RawFile = decryptedApp; 309 | SharedItems.Stage2app = Assembly.Load(decryptedApp); 310 | SharedItems.Stage2asm = ModuleDefMD.Load(decryptedApp); 311 | } 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /XUnion/Updater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Security.Cryptography; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Net.NetworkInformation; 8 | using Microsoft.VisualBasic; 9 | using System.Diagnostics; 10 | using System.IO; 11 | 12 | namespace XUnion 13 | { 14 | class Updater 15 | { 16 | public Updater(byte[] updateFileStream) 17 | { 18 | string[] fileData = Encoding.Default.GetString(updateFileStream).Split("\n\n".ToArray()); 19 | bool isChangelogData = false; 20 | List ChangeLogData = new List(); 21 | foreach (string line in fileData) 22 | { 23 | if (line.StartsWith("##") || line.StartsWith("//") || line == "") { continue; } 24 | if (isChangelogData) { ChangeLogData.Add(line); continue; } 25 | if (line == "[CHANGELOG]") { isChangelogData = true; continue; } 26 | string p1 = line.Split("=".ToCharArray())[0].Replace("\t", null); 27 | string p2 = line.Split("=".ToCharArray())[1].Remove(0, 1); 28 | switch (p1) 29 | { 30 | case "AppName": AppName = p2; break; 31 | case "Version": LatestVersion = p2; break; 32 | case "DownloadLink": UpdateLink = p2; break; 33 | case "SHA256_Checksum": SHA256Checksum = FromHexString(p2.ToUpper()); break; 34 | case "UpdateSize": UpdateSize = p2; break; 35 | } 36 | } 37 | UpdateDownloadName = CurrentAppName + "-Update_v" + LatestVersion + ".zip"; 38 | ChangeLog = ChangeLogData.ToArray(); 39 | if (SHA256Checksum == null) { throw new Exception("Missing Checksum for Update!"); } // sekurity 101 40 | } 41 | 42 | private byte[] FromHexString(string hex) 43 | { 44 | return Enumerable.Range(0, hex.Length) 45 | .Where(x => x % 2 == 0) 46 | .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) 47 | .ToArray(); 48 | } 49 | 50 | public const string 51 | CurrentAppName = "XUnion", 52 | CurrentVersion = "b1.0", 53 | RepoLink = "https://github.com/miso-xyz/GruMinion", 54 | UpdateLogLink = "https://raw.githubusercontent.com/miso-xyz/GruMinion/main/latest.info"; 55 | 56 | public string AppName { get; set; } 57 | public string LatestVersion { get; set; } 58 | public string[] ChangeLog { get; set; } 59 | public string UpdateLink { get; set; } 60 | public byte[] SHA256Checksum { get; set; } 61 | public string UpdateSize { get; set; } 62 | public string UpdateDownloadName { get; set; } 63 | 64 | public static bool HasInternetConnection() { try { new Ping().Send("github.com", 750); return true; } catch { return false; } } 65 | 66 | public bool IsRunningLatest() { return CurrentVersion == LatestVersion; } 67 | public bool VerifyChecksum(byte[] data) { return BitConverter.ToString(SHA256.Create().ComputeHash(data)) == BitConverter.ToString(SHA256Checksum); } 68 | 69 | public void DownloadUpdate() 70 | { 71 | using (WebClient wc = new WebClient()) 72 | { 73 | wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadingUpdateEvent); 74 | wc.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(wc_DownloadFileCompleted); 75 | wc.DownloadFileAsync(new Uri(UpdateLink), UpdateDownloadName); 76 | } 77 | while (true) { Console.ReadKey(); } 78 | } 79 | 80 | void wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) 81 | { 82 | //Process.Start(AppDomain.CurrentDomain.BaseDirectory + "UniShieldUpdateInstaller.exe", Path.GetFullPath(UpdateDownloadName) + " --restart " + Program.path); 83 | Console.Clear(); 84 | Console.ForegroundColor = ConsoleColor.Green; 85 | Console.SetCursorPosition(1, 1); 86 | Console.Write("Comparing Checksums..."); 87 | if (!VerifyChecksum(File.ReadAllBytes(AppDomain.CurrentDomain.BaseDirectory + UpdateDownloadName))) 88 | { 89 | Console.SetCursorPosition(1, 1); 90 | Console.ForegroundColor = ConsoleColor.DarkRed; 91 | Console.WriteLine("Invalid Checksum!"); 92 | Console.ResetColor(); 93 | Console.SetCursorPosition(1, 2); 94 | Console.WriteLine("Press any key to exit!"); 95 | Console.ReadKey(); 96 | Environment.Exit(0); 97 | } 98 | Console.SetCursorPosition(1, 1); 99 | Console.ForegroundColor = ConsoleColor.Green; 100 | Console.WriteLine("Valid Checksum!"); 101 | Process p = new Process(); 102 | p.StartInfo.FileName = "cmd"; 103 | p.StartInfo.Arguments = "/K timeout /T 1 /nobreak && cd " + AppDomain.CurrentDomain.BaseDirectory + " && 7z.exe e -y " + UpdateDownloadName + " && start " + CurrentAppName + ".exe " + '"' + Program.path + '"' + " && exit"; 104 | p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; 105 | p.Start(); 106 | Environment.Exit(0); 107 | //Process.Start(Path.GetFullPath(UpdateDownloadName)); 108 | } 109 | 110 | void wc_DownloadingUpdateEvent(object sender, DownloadProgressChangedEventArgs e) 111 | { 112 | Console.ForegroundColor = ConsoleColor.Cyan; 113 | Console.Clear(); 114 | Console.SetCursorPosition(1, 1); 115 | Console.Write(e.ProgressPercentage + "% (" + e.BytesReceived + "/" + e.TotalBytesToReceive + ")"); 116 | } 117 | 118 | public static byte[] GetUpdate(string updateLink = UpdateLogLink) { ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; return new WebClient().DownloadData(updateLink); } 119 | } 120 | } -------------------------------------------------------------------------------- /XUnion/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using dnlib.DotNet; 7 | using dnlib.DotNet.Emit; 8 | 9 | namespace XUnion 10 | { 11 | class Utils 12 | { 13 | public static ushort[] ToUInt16(object[] src) 14 | { 15 | List result = new List(); 16 | for (int x = 0; x < src.Length; x++) { result.Add(ushort.Parse(src[x].ToString())); } 17 | return result.ToArray(); 18 | } 19 | 20 | public static bool CompareNamespaceMemberRef (Instruction srcInst, Type matchType, string methodName, Type[] methodArgs = null) 21 | { 22 | string instNamespace = ((MemberRef)srcInst.Operand).DeclaringType.FullName + "." + ((MemberRef)srcInst.Operand).Name, 23 | matchClassNamespace = matchType.FullName, 24 | matchMethodName = ""; 25 | if (methodArgs == null) { matchMethodName = matchType.GetMethod(methodName).Name; } 26 | else { matchMethodName = matchType.GetMethod(methodName, methodArgs).Name; } 27 | return instNamespace == matchClassNamespace + "." + matchMethodName; 28 | } 29 | public static bool CompareNamespaceMethodSpec(Instruction srcInst, Type matchType, string methodName, Type[] methodArgs = null) 30 | { 31 | string instNamespace = ((MethodSpec)srcInst.Operand).DeclaringType.FullName + "." + ((MethodSpec)srcInst.Operand).Name, 32 | matchClassNamespace = matchType.FullName, 33 | matchMethodName = ""; 34 | if (methodArgs == null) { matchMethodName = matchType.GetMethod(methodName).Name; } 35 | else { matchMethodName = matchType.GetMethod(methodName, methodArgs).Name; } 36 | return instNamespace == matchClassNamespace + "." + matchMethodName; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /XUnion/XORManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace XUnion 8 | { 9 | public class XORManager 10 | { 11 | public XORManager(int skipVal, int arrVal, int intXor) 12 | { 13 | SkipValue = skipVal; 14 | ArrayValue = arrVal; 15 | IntXorValue = intXor; 16 | } 17 | 18 | public static int ToInt(int val1, int val2, int xorValue) { return val1 ^ (val2 ^ xorValue); } 19 | public int ToInt(int val1, int val2) { return ToInt(val1, val2, IntXorValue); } 20 | 21 | public string Compute(ushort[] src) 22 | { 23 | string result = ""; 24 | foreach (ushort b in src.Skip(SkipValue)) { result += (char)(b ^ src[ArrayValue]); } 25 | return result; 26 | } 27 | 28 | public int SkipValue { get; set; } 29 | public int ArrayValue { get; set; } 30 | public int IntXorValue { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /XUnion/XUnion.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {13F9CE4F-16F9-4313-BB8E-EC737BCD3898} 8 | Exe 9 | Properties 10 | XUnion 11 | XUnion 12 | v4.5 13 | 512 14 | 15 | 16 | x86 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | true 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\..\..\dlls\dnlib.dll 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 | 70 | -------------------------------------------------------------------------------- /XUnion/dnlibUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using dnlib.DotNet.Emit; 7 | using dnlib.DotNet; 8 | using dnlib; 9 | 10 | namespace XUnion 11 | { 12 | class dnlibUtils 13 | { 14 | public static int IndexOf(Instruction[] insts, Instruction inst, bool throwIfInvalid = false) 15 | { 16 | if (insts.Contains(inst)) { return insts.ToList().IndexOf(inst); } else { if (throwIfInvalid) { new IndexOutOfRangeException(); } return -1; } 17 | } 18 | } 19 | 20 | class MSILArray 21 | { 22 | /* 0x00 = Array Length | ldc 23 | * 0x01 = Array Type | newarr 24 | * 0x02 = Set 0x00 and 0x01 to defined Array Stack Value/Local | stloc 25 | * 26 | * --- ITEM LAYOUT --- 27 | * 28 | * 0x00 = Load defined Array Stack Value/Local | ldloc 29 | * 0x01 = Index of current item | ldc 30 | * 0x02 = Data | OpCode with Operand of matching nature with Array Type 31 | * 0x03 = Replaces data at 0x01 to 0x02 | stelem */ 32 | 33 | public MSILArray() { } 34 | public MSILArray(Instruction[] insts) { ParseArray(insts); } 35 | public MSILArray(Instruction[] insts, int index = 0) { ParseArray(insts, index); } 36 | public MSILArray(MethodDef method , int index = 0) { ParseArray(method.Body.Instructions.ToArray(), index); } 37 | 38 | public static MSILArray ParseArray(Instruction[] insts, int index = 0) 39 | { 40 | int ARR_SIZE = insts[index].GetLdcI4Value(); 41 | Local ARR_VAL = (Local)insts[index + 2].Operand; 42 | ITypeDefOrRef ARR_TYPE = (ITypeDefOrRef)insts[index + 1].Operand; 43 | 44 | List arr = new List(ARR_SIZE); 45 | MSILArray result = new MSILArray(); 46 | 47 | for (int x_item = index + 5; arr.Count < ARR_SIZE; x_item += 4) 48 | { 49 | Instruction CurrentItem = insts[x_item], 50 | IndexInstruction = insts[x_item-1]; 51 | 52 | if (!IndexInstruction.IsLdcI4()) { break; } 53 | 54 | int CurrentIndex = IndexInstruction.GetLdcI4Value(); 55 | if (IndexInstruction.GetLdcI4Value() != arr.Count) 56 | { 57 | for (int x_offset = arr.Count; x_offset < CurrentIndex; x_offset++) 58 | { 59 | if (IsLdcI4Array(ARR_TYPE)) { arr.Add( Instruction.CreateLdcI4(0)); } 60 | else { arr.Add(new Instruction(OpCodes.UNKNOWN1)); } 61 | } 62 | } 63 | arr.Add(insts[x_item]); 64 | } 65 | 66 | result.Type = ARR_TYPE; 67 | result.Items = arr.ToArray(); 68 | result.LocalStack = ARR_VAL; 69 | result.RawSource = insts.ToList().GetRange(index, ARR_SIZE * 4).ToArray(); 70 | 71 | return result; 72 | } 73 | 74 | private static bool IsLdcI4Array(ITypeDefOrRef srcType) 75 | { 76 | if (srcType.FullName == typeof(int).FullName || 77 | srcType.FullName == typeof(short).FullName || 78 | srcType.FullName == typeof(ushort).FullName || 79 | srcType.FullName == typeof(byte).FullName || 80 | srcType.FullName == typeof(long).FullName || 81 | srcType.FullName == typeof(ulong).FullName || 82 | srcType.FullName == typeof(uint).FullName) 83 | { return true; } 84 | return false; 85 | } 86 | 87 | public object[] ToArray() 88 | { 89 | object[] obj = new object[Items.Length]; 90 | for (int x = 0; x < Items.Length; x++) { if (Items[x].IsLdcI4()) { obj[x] = Items[x].GetLdcI4Value(); } else { obj[x] = Items[x].Operand; } } 91 | return obj; 92 | } 93 | 94 | public int RawCount { get { return (Items.Length * 4) + 3; } } 95 | public Instruction[] RawSource { get; set; } 96 | public Instruction[] Items { get; set; } 97 | public ITypeDefOrRef Type { get; set; } 98 | public Local LocalStack { get; set; } 99 | } 100 | 101 | class MSILTryCatch 102 | { 103 | public MSILTryCatch() { } 104 | public MSILTryCatch(CilBody body , int exhdIndex ) { Parse(body.Instructions.ToArray(), body.ExceptionHandlers[exhdIndex]); } 105 | public MSILTryCatch(MethodDef method , int exhdIndex ) { Parse(method.Body.Instructions.ToArray(), method.Body.ExceptionHandlers[exhdIndex]); } 106 | public MSILTryCatch(Instruction[] insts, ExceptionHandler exhd) { Parse(insts, exhd); } 107 | 108 | public static MSILTryCatch Parse(Instruction[] insts, ExceptionHandler exhd) 109 | { 110 | List TryInstructions = new List(), 111 | HandlerInstructions = new List(); 112 | 113 | // Try Instructions 114 | for (int x_tryInst = dnlibUtils.IndexOf(insts, exhd.TryStart , true); x_tryInst < dnlibUtils.IndexOf(insts, exhd.TryEnd); x_tryInst++) { TryInstructions.Add(insts[x_tryInst]); } 115 | 116 | // Handler Instructions 117 | for (int x_hdInst = dnlibUtils.IndexOf(insts, exhd.HandlerStart, true); x_hdInst < dnlibUtils.IndexOf(insts, exhd.HandlerEnd); x_hdInst++) { HandlerInstructions.Add(insts[x_hdInst]); } 118 | 119 | MSILTryCatch result = new MSILTryCatch() 120 | { 121 | TryInstructions = TryInstructions.ToArray(), 122 | HandlerInstructions = HandlerInstructions.ToArray(), 123 | CatchType = exhd.CatchType, 124 | HandlerType = exhd.HandlerType, 125 | 126 | TryStart = dnlibUtils.IndexOf(insts, exhd.TryStart), 127 | TryEnd = dnlibUtils.IndexOf(insts, exhd.TryEnd), 128 | HandlerStart = dnlibUtils.IndexOf(insts, exhd.HandlerStart), 129 | HandlerEnd = dnlibUtils.IndexOf(insts, exhd.HandlerEnd), 130 | FilterStart = dnlibUtils.IndexOf(insts, exhd.FilterStart) 131 | }; 132 | 133 | return result; 134 | } 135 | 136 | public Instruction[] TryInstructions { get; set; } 137 | public Instruction[] HandlerInstructions { get; set; } 138 | 139 | public int TryStart, TryEnd, HandlerStart, HandlerEnd, FilterStart; 140 | 141 | public ExceptionHandlerType HandlerType; 142 | public ITypeDefOrRef CatchType; 143 | } 144 | } -------------------------------------------------------------------------------- /XUnionPE/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /XUnionPE/Decompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Gee.External.Capstone; 8 | using Gee.External.Capstone.X86; 9 | 10 | namespace XUnionPE 11 | { 12 | class Decompiler 13 | { 14 | public Decompiler(string appPath) 15 | { 16 | AddressBook = new Dictionary(); 17 | 18 | path = Path.GetFullPath(appPath); 19 | RawApp = File.ReadAllBytes(path); 20 | 21 | Headers = new PEUtils.Headers(RawApp); 22 | PEUtils.Headers.SectionHeader mainSection = Headers.GetSectionHeader(Headers.OptionalHdr.EntrypointAdr, true); 23 | 24 | byte[] RawInsts = new byte[mainSection.VirtSize + mainSection.VirtAdr]; 25 | Array.Copy(RawApp, mainSection.RawAdr, RawInsts, 0, mainSection.RawSize); 26 | 27 | disasm = CapstoneDisassembler.CreateX86Disassembler(GetBitness()); 28 | disasm.EnableInstructionDetails = true; 29 | disasm.DisassembleSyntax = DisassembleSyntax.Intel; 30 | 31 | //X86Instruction[] insts = disasm.Disassemble(RawInsts); 32 | } 33 | 34 | public enum MachineType : ushort { Native = 0, x86 = 0x014c, Itanium = 0x0200, x64 = 0x8664 } 35 | 36 | private string path; 37 | private byte[] RawApp, outputRaw; 38 | private CapstoneX86Disassembler disasm; 39 | 40 | public PEUtils.Headers Headers; 41 | public Dictionary AddressBook { get; set; } 42 | 43 | public X86DisassembleMode GetBitness() 44 | { 45 | switch (BitConverter.ToUInt16(RawApp, 0x4A)) 46 | { 47 | case 0x6486: return X86DisassembleMode.Bit64; 48 | case 0x4c01: return X86DisassembleMode.Bit32; 49 | default: throw new Exception("Unsupporteed Bitness (must be x86_64)"); 50 | } 51 | } 52 | 53 | public uint Stage2Entrypoint = uint.MaxValue, 54 | Stage1Entrypoint = uint.MaxValue, 55 | Stage2Size = uint.MaxValue, 56 | Stage2Key = uint.MaxValue, 57 | Stage2PaddingMask = uint.MaxValue, 58 | PaddingCount = uint.MaxValue; 59 | 60 | public void Unpack() 61 | { 62 | Console.WriteLine(" Checking if EXE is packed with PEUnion..."); 63 | Console.WriteLine(); 64 | if (!isPEUnionPacked()) 65 | { 66 | Console.Write(" Invalid Executable! (Not packed using PEUnion)"); 67 | Console.ReadKey(); 68 | return; 69 | } 70 | Console.WriteLine(" PEUnion detected!"); 71 | Console.WriteLine(); 72 | X86Instruction[] insts = disasm.Disassemble(Utils.GetBuffer(RawApp, AddressBook["mainFunction"])); 73 | for (int x = 8; x < insts.Length; x++) 74 | { 75 | X86Instruction inst = insts[x]; 76 | switch (inst.Id) 77 | { 78 | case X86InstructionId.X86_INS_IMUL: 79 | if (inst.Details.Operands[0].Type == X86OperandType.Register && 80 | inst.Details.Operands[0].Register.Id == X86RegisterId.X86_REG_EDX && 81 | inst.Details.Operands[1].Type == X86OperandType.Register && 82 | inst.Details.Operands[1].Register.Id == X86RegisterId.X86_REG_EDX && 83 | inst.Details.Operands[2].Type == X86OperandType.Immediate && 84 | inst.Details.Operands[2].Immediate == 7) 85 | { 86 | for (int y = x; y < insts.Length; y++) 87 | { 88 | if (PaddingCount != uint.MaxValue) { break; } 89 | X86Instruction paddingCount = insts[y]; 90 | switch (paddingCount.Id) 91 | { 92 | case X86InstructionId.X86_INS_TEST: 93 | if (paddingCount.Details.Operands[0].Type == X86OperandType.Register && 94 | paddingCount.Details.Operands[0].Register.Id == X86RegisterId.X86_REG_EBX && 95 | paddingCount.Details.Operands[1].Type == X86OperandType.Immediate) 96 | { PaddingCount = Convert.ToUInt32(paddingCount.Details.Operands[1].Immediate); } 97 | break; 98 | } 99 | } 100 | } 101 | break; 102 | case X86InstructionId.X86_INS_CALL: 103 | if (inst.Details.Operands[0].Type == X86OperandType.Memory && 104 | inst.Details.Operands[0].Memory.Base != null && 105 | inst.Details.Operands[0].Memory.Base.Id == X86RegisterId.X86_REG_EBP && 106 | inst.Operand.EndsWith("0xc]") && 107 | Utils.FromHexUInt32(insts[x - 1].Operand) == Headers.OptionalHdr.EntrypointAdr + Headers.OptionalHdr.ImageBase) 108 | { 109 | for (int y = x; y < insts.Length; y++) 110 | { 111 | if (Stage1Entrypoint != uint.MaxValue && 112 | Stage2Entrypoint != uint.MaxValue && 113 | Stage2Size != uint.MaxValue && 114 | Stage2Key != uint.MaxValue && 115 | Stage2PaddingMask != uint.MaxValue) 116 | { break; } 117 | inst = insts[y]; 118 | switch (inst.Id) 119 | { 120 | case X86InstructionId.X86_INS_MOV: 121 | if (inst.Details.Operands.Length == 2 && 122 | inst.Details.Operands[0].Type == X86OperandType.Register && 123 | inst.Details.Operands[1].Type == X86OperandType.Immediate) 124 | { 125 | switch (inst.Details.Operands[0].Register.Id) 126 | { 127 | case X86RegisterId.X86_REG_EDI: Stage1Entrypoint = Headers.GetRawOffset((uint)inst.Details.Operands[1].Immediate, Headers.OptionalHdr.ImageBase); break; 128 | case X86RegisterId.X86_REG_ESI: Stage2Entrypoint = Headers.GetRawOffset((uint)inst.Details.Operands[1].Immediate, Headers.OptionalHdr.ImageBase); break; 129 | case X86RegisterId.X86_REG_ECX: Stage2Size = Convert.ToUInt32(inst.Details.Operands[1].Immediate); break; 130 | case X86RegisterId.X86_REG_EDX: Stage2Key = Convert.ToUInt32(inst.Details.Operands[1].Immediate); /*Utils.RightBitShift((int)inst.Details.Operands[1].Immediate, 5) * 7;*/ break; 131 | case X86RegisterId.X86_REG_EBX: Stage2PaddingMask = Convert.ToUInt32(inst.Details.Operands[1].Immediate); break; 132 | } 133 | } 134 | break; 135 | } 136 | } 137 | } 138 | break; 139 | } 140 | } 141 | List stage2Raw = new List(); 142 | Console.WriteLine(" unXORing application..."); 143 | using (BinaryReader br = new BinaryReader(new MemoryStream(RawApp))) 144 | { 145 | br.BaseStream.Position = Stage2Entrypoint; 146 | int curByte = -1; 147 | while ((curByte = (int)br.ReadByte()) != -1) 148 | { 149 | if (stage2Raw.Count == Stage2Size) { break; } 150 | stage2Raw.Add((byte)(curByte ^ BitConverter.GetBytes(Stage2Key)[0])); 151 | Stage2Key = Utils.RightBitShift(Stage2Key, 5) * 7; 152 | if ((Stage2PaddingMask & 1) == 1) { for (int y = 0; y < PaddingCount; y++) { curByte = br.ReadByte(); } } 153 | Stage2PaddingMask = Utils.RightBitShift(Stage2PaddingMask, 1); 154 | } 155 | } 156 | outputRaw = new byte[stage2Raw.Count]; 157 | Array.Copy(stage2Raw.ToArray(), 0x700, outputRaw, 0, stage2Raw.Count - 0x700); // removes junk 158 | File.WriteAllBytes("output_pe32_xunion.exe", outputRaw.ToArray()); 159 | Console.WriteLine(" Extracted application saved as 'output_pe32_xunion.exe'!"); 160 | Console.ReadKey(); 161 | } 162 | 163 | public bool isPEUnionPacked() 164 | { 165 | X86Instruction[] insts = disasm.Disassemble(Utils.GetBuffer(RawApp, Headers.GetRawOffset(Headers.OptionalHdr.EntrypointAdr))); 166 | if (insts[0].Id == X86InstructionId.X86_INS_CALL && 167 | insts[1].Id == X86InstructionId.X86_INS_PUSH && 168 | insts[2].Id == X86InstructionId.X86_INS_CALL && 169 | insts[3].Id == X86InstructionId.X86_INS_RET) 170 | { 171 | PEUtils.Headers.ImportDll kernel32ExitPrc = Headers.GetImport(Headers.GetRawOffset((uint)PEUtils.InstructionUtils.GetCallAdr(insts[2], Headers.OptionalHdr.ImageBase))); 172 | if (kernel32ExitPrc != null && 173 | kernel32ExitPrc.Name == "kernel32.dll" && 174 | kernel32ExitPrc.Functions[0].Name == "ExitProcess") 175 | { 176 | AddressBook.Add("mainFunction", Headers.GetRawOffset(Utils.FromHexUInt32(insts[0].Operand))); 177 | return true; 178 | } 179 | } 180 | return false; 181 | } 182 | } 183 | } -------------------------------------------------------------------------------- /XUnionPE/PEUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Gee.External.Capstone.X86; 8 | 9 | namespace XUnionPE 10 | { 11 | class PEUtils 12 | { 13 | public class Headers 14 | { 15 | public Headers(byte[] data) 16 | { 17 | DOSHdr = new DOSHeader(); 18 | FileHdr = new FileHeader(); 19 | OptionalHdr = new OptionalHeader(); 20 | 21 | BinaryReader br = new BinaryReader(new MemoryStream(data)); 22 | 23 | #region DOS Header 24 | DOSHdr.Magic = new string(br.ReadChars(2)); 25 | DOSHdr.LastPageByteCount = br.ReadUInt16(); 26 | DOSHdr.PageCount = br.ReadUInt16(); 27 | DOSHdr.RelocationCount = br.ReadUInt16(); 28 | DOSHdr.SizeOfHeaderParagraph = br.ReadUInt16(); 29 | DOSHdr.MinExtraParagraph = br.ReadUInt16(); 30 | DOSHdr.MaxExtraParagraph = br.ReadUInt16(); 31 | DOSHdr.SSValue = br.ReadUInt16(); 32 | DOSHdr.SPValue = br.ReadUInt16(); 33 | DOSHdr.SPValue = br.ReadUInt16(); 34 | DOSHdr.Checksum = br.ReadUInt16(); 35 | DOSHdr.IPValue = br.ReadUInt16(); 36 | DOSHdr.CSValue = br.ReadUInt16(); 37 | DOSHdr.RelocationTableAdr = br.ReadUInt16(); 38 | DOSHdr.OverlayNum = br.ReadUInt16(); 39 | 40 | ushort[] reservedArr = new ushort[4]; 41 | for (int x = 0; x < 4; x++) { reservedArr[x] = br.ReadUInt16(); } 42 | DOSHdr.ReservedWords1 = reservedArr; 43 | 44 | DOSHdr.OEMID = br.ReadUInt16(); 45 | DOSHdr.OEMInfo = br.ReadUInt16(); 46 | 47 | reservedArr = new ushort[10]; 48 | for (int x = 0; x < 10; x++) { reservedArr[x] = br.ReadUInt16(); } 49 | DOSHdr.ReservedWords2 = reservedArr; 50 | 51 | DOSHdr.NewExeHeaderAdr = br.ReadUInt32(); 52 | #endregion 53 | #region File Header 54 | // 0x40 -> 0x7F is DOS Stub, 0x80 is Start of File Header 55 | br.BaseStream.Position = 0x80; 56 | 57 | br.ReadUInt32(); // Skips Magic Signature 58 | FileHdr.Machine = br.ReadUInt16(); 59 | FileHdr.SectionCount = br.ReadUInt16(); 60 | FileHdr.Timestamp = new DateTime(br.ReadUInt32()); 61 | FileHdr.SymbolTableAdr = br.ReadUInt32(); 62 | FileHdr.SymbolCount = br.ReadUInt32(); 63 | FileHdr.OptionalHeaderSize = br.ReadUInt16(); 64 | FileHdr.Characteristics = br.ReadUInt16(); 65 | #endregion 66 | #region Optional Header 67 | OptionalHdr.Magic = br.ReadUInt16(); 68 | OptionalHdr.MajorLinkerVersion = br.ReadByte(); 69 | OptionalHdr.MinorLinkerVersion = br.ReadByte(); 70 | OptionalHdr.SizeOfCode = br.ReadUInt32(); 71 | OptionalHdr.SizeOfInitializedData = br.ReadUInt32(); 72 | OptionalHdr.SizeOfUnInitializedData = br.ReadUInt32(); 73 | OptionalHdr.EntrypointAdr = br.ReadUInt32(); 74 | OptionalHdr.BaseOfCode = br.ReadUInt32(); 75 | OptionalHdr.BaseOfData = br.ReadUInt32(); 76 | OptionalHdr.ImageBase = br.ReadUInt32(); 77 | OptionalHdr.SectionAlign = br.ReadUInt32(); 78 | OptionalHdr.FileAlign = br.ReadUInt32(); 79 | OptionalHdr.MajorOSVersion = br.ReadUInt16(); 80 | OptionalHdr.MinorOSVersion = br.ReadUInt16(); 81 | OptionalHdr.MajorImageVersion = br.ReadUInt16(); 82 | OptionalHdr.MinorImageVersion = br.ReadUInt16(); 83 | OptionalHdr.MajorSubsystemVersion = br.ReadUInt16(); 84 | OptionalHdr.MinorSubsystemVersion = br.ReadUInt16(); 85 | OptionalHdr.Win32Version = br.ReadUInt32(); 86 | OptionalHdr.SizeOfImage = br.ReadUInt32(); 87 | OptionalHdr.SizeOfHeaders = br.ReadUInt32(); 88 | OptionalHdr.Checksum = br.ReadUInt32(); 89 | OptionalHdr.Subsystem = br.ReadUInt16(); 90 | OptionalHdr.DLLCharacteristics = br.ReadUInt16(); 91 | OptionalHdr.SizeOfStackReserve = br.ReadUInt32(); 92 | OptionalHdr.SizeOfStackCommit = br.ReadUInt32(); 93 | OptionalHdr.SizeOfHeapReverse = br.ReadUInt32(); 94 | OptionalHdr.SizeOfHeapCommit = br.ReadUInt32(); 95 | OptionalHdr.LoaderFlags = br.ReadUInt32(); 96 | OptionalHdr.RVAAndSizeCount = br.ReadUInt32(); 97 | #region Data Directories 98 | OptionalHdr.DataDirs.ExportDir = br.ReadUInt64(); 99 | OptionalHdr.DataDirs.ImportDir = br.ReadUInt64(); 100 | OptionalHdr.DataDirs.ResourceDir = br.ReadUInt64(); 101 | OptionalHdr.DataDirs.ExceptionDir = br.ReadUInt64(); 102 | OptionalHdr.DataDirs.SecurityDir = br.ReadUInt64(); 103 | OptionalHdr.DataDirs.BaseRelocationTable = br.ReadUInt64(); 104 | OptionalHdr.DataDirs.DebugDir = br.ReadUInt64(); 105 | OptionalHdr.DataDirs.ArchitectureSpecificData = br.ReadUInt64(); 106 | OptionalHdr.DataDirs.GlobalPtrRVA = br.ReadUInt64(); 107 | OptionalHdr.DataDirs.TLSDir = br.ReadUInt64(); 108 | OptionalHdr.DataDirs.LoadConfigDir = br.ReadUInt64(); 109 | OptionalHdr.DataDirs.HeadersImportDirBound = br.ReadUInt64(); 110 | OptionalHdr.DataDirs.ImportAdrTable = br.ReadUInt64(); 111 | OptionalHdr.DataDirs.DelayLoadImportDescriptors = br.ReadUInt64(); 112 | OptionalHdr.DataDirs.DotNetHeader = br.ReadUInt64(); 113 | #endregion 114 | #endregion 115 | #region Section Headers 116 | br.ReadUInt64(); // 8 byte gap 117 | List SectionHdrsList = new List(); 118 | for (int x = 0; x < FileHdr.SectionCount; x++) 119 | { 120 | SectionHeader sectionHdr = new SectionHeader(); 121 | 122 | sectionHdr.Name = Encoding.Default.GetString( 123 | br.ReadBytes(sizeof(ulong))); 124 | sectionHdr.VirtSize = br.ReadUInt32(); 125 | sectionHdr.VirtAdr = br.ReadUInt32(); 126 | sectionHdr.RawSize = br.ReadUInt32(); 127 | sectionHdr.RawAdr = br.ReadUInt32(); 128 | sectionHdr.PtrToReloc = br.ReadUInt32(); 129 | br.ReadUInt32(); // 4 byte gap 130 | sectionHdr.RelocCount = br.ReadUInt16(); 131 | sectionHdr.LinenumCount = br.ReadUInt16(); 132 | sectionHdr.Characteristics = br.ReadUInt32(); 133 | 134 | SectionHdrsList.Add(sectionHdr); 135 | } 136 | SectionHdrs = SectionHdrsList.ToArray(); 137 | #endregion 138 | #region Imports 139 | uint idataStart = 0xFFFFFFFF; 140 | foreach (SectionHeader sectionHeader in SectionHdrs) { if (sectionHeader.Name.StartsWith(".idata")) { idataStart = sectionHeader.RawAdr; break; } } 141 | 142 | br.BaseStream.Position = idataStart; 143 | List importsList = new List(); 144 | for (byte[] x = br.ReadBytes(20); 145 | x[0] != 0x00; 146 | x = br.ReadBytes(20)) 147 | { 148 | ImportDll impDll = new ImportDll(); 149 | impDll.OriginalFirstThunk = GetRawOffset(BitConverter.ToUInt32(x, 0)); 150 | impDll.TimedateStamp = BitConverter.ToUInt32(x, 4); 151 | impDll.Forwarder = BitConverter.ToUInt32(x, 8); 152 | impDll.NameRVA = GetRawOffset(BitConverter.ToUInt32(x, 12)); 153 | impDll.FirstThunk = GetRawOffset(BitConverter.ToUInt32(x, 16)); 154 | 155 | using (BinaryReader callViaBr = new BinaryReader(new MemoryStream(data))) 156 | { 157 | callViaBr.BaseStream.Position = impDll.FirstThunk; 158 | 159 | List impFuncList = new List(); 160 | while (true) 161 | { 162 | ImportDll.Function impFunc = new ImportDll.Function(); 163 | impFunc.CallViaAdr = (uint)callViaBr.BaseStream.Position; 164 | uint callviaAdr = callViaBr.ReadUInt32(); 165 | if (callviaAdr == 0) { break; } 166 | impFunc.OriginalThunk = impFunc.Thunk = GetRawOffset(callviaAdr); 167 | impFuncList.Add(impFunc); 168 | } 169 | impDll.Functions = impFuncList.ToArray(); 170 | } 171 | importsList.Add(impDll); 172 | } 173 | 174 | string[] dllNames = null; 175 | for (int x_dll = 0; x_dll < importsList.Count; x_dll++) 176 | { 177 | ImportDll impDll = importsList[x_dll]; 178 | string[] functionNames = null; 179 | 180 | int[] nullSkip = null; 181 | 182 | if (dllNames == null) { dllNames = GetStrings(br, importsList.Count); } 183 | if (impDll.Name == null) { impDll.Name = dllNames[x_dll]; } 184 | 185 | using (BinaryReader impFuncBr = new BinaryReader(new MemoryStream(data))) 186 | { 187 | for (int x = 0; x < impDll.Functions.Length; x++) 188 | { 189 | ImportDll.Function impFunc = impDll.Functions[x]; 190 | 191 | impFuncBr.BaseStream.Position = impFunc.CallViaAdr; 192 | uint ThunkAdr = GetRawOffset(impFuncBr.ReadUInt32()); 193 | impFuncBr.BaseStream.Position = ThunkAdr; 194 | if (functionNames == null) { functionNames = GetStrings(impFuncBr, impDll.Functions.Length, out nullSkip); impFuncBr.BaseStream.Position = ThunkAdr; } 195 | 196 | impFunc.Hint = impFuncBr.ReadUInt16(); 197 | impFunc.Name = functionNames[x]; 198 | 199 | impFuncBr.BaseStream.Position += functionNames[x].Length + nullSkip[x]; 200 | } 201 | } 202 | } 203 | 204 | Imports = importsList.ToArray(); 205 | #endregion 206 | } 207 | 208 | public class DOSHeader 209 | { 210 | public string Magic; 211 | 212 | public ushort LastPageByteCount, 213 | PageCount, 214 | RelocationCount, 215 | SizeOfHeaderParagraph, 216 | MinExtraParagraph, 217 | MaxExtraParagraph, 218 | SSValue, 219 | SPValue, 220 | Checksum, 221 | IPValue, 222 | CSValue, 223 | RelocationTableAdr, 224 | OverlayNum, 225 | OEMID, 226 | OEMInfo; 227 | 228 | public uint NewExeHeaderAdr; 229 | 230 | public ushort[] ReservedWords1 = new ushort[4], 231 | ReservedWords2 = new ushort[10]; 232 | } 233 | public class FileHeader 234 | { 235 | public ushort Machine, 236 | SectionCount, 237 | OptionalHeaderSize, 238 | Characteristics; 239 | 240 | public DateTime Timestamp; 241 | 242 | public uint SymbolTableAdr, 243 | SymbolCount; 244 | } 245 | public class OptionalHeader 246 | { 247 | public OptionalHeader() { DataDirs = new DataDirectory(); } 248 | 249 | public class DataDirectory 250 | { 251 | public ulong ExportDir, 252 | ImportDir, 253 | ResourceDir, 254 | ExceptionDir, 255 | SecurityDir, 256 | BaseRelocationTable, 257 | DebugDir, 258 | ArchitectureSpecificData, 259 | GlobalPtrRVA, 260 | TLSDir, 261 | LoadConfigDir, 262 | HeadersImportDirBound, 263 | ImportAdrTable, 264 | DelayLoadImportDescriptors, 265 | DotNetHeader; 266 | } 267 | 268 | public ushort Magic; 269 | 270 | public byte MajorLinkerVersion, 271 | MinorLinkerVersion; 272 | 273 | public ushort MajorOSVersion, 274 | MinorOSVersion, 275 | MajorImageVersion, 276 | MinorImageVersion, 277 | MajorSubsystemVersion, 278 | MinorSubsystemVersion, 279 | Subsystem, 280 | DLLCharacteristics; 281 | 282 | public uint SizeOfCode, 283 | SizeOfInitializedData, 284 | SizeOfUnInitializedData, 285 | EntrypointAdr, 286 | BaseOfCode, 287 | BaseOfData, 288 | ImageBase, 289 | SectionAlign, 290 | FileAlign, 291 | Win32Version, 292 | SizeOfImage, 293 | SizeOfHeaders, 294 | Checksum, 295 | SizeOfStackReserve, 296 | SizeOfStackCommit, 297 | SizeOfHeapReverse, 298 | SizeOfHeapCommit, 299 | LoaderFlags, 300 | RVAAndSizeCount; 301 | 302 | public DataDirectory DataDirs { get; set; } 303 | } 304 | public class SectionHeader 305 | { 306 | public string Name; 307 | 308 | public ushort RelocCount, 309 | LinenumCount; 310 | 311 | public uint VirtSize, 312 | VirtAdr, 313 | RawSize, 314 | RawAdr, 315 | PtrToReloc, 316 | Characteristics; 317 | } 318 | public class ImportDll 319 | { 320 | public class Function 321 | { 322 | public string Name; 323 | 324 | public uint OriginalThunk, Thunk, CallViaAdr; 325 | 326 | public ushort Hint; 327 | } 328 | 329 | public string Name; 330 | 331 | public uint OriginalFirstThunk, 332 | TimedateStamp, 333 | Forwarder, 334 | NameRVA, 335 | FirstThunk; 336 | 337 | public bool IsBound { get { return TimedateStamp == 0xFFFFFFFF; } } 338 | 339 | public Function[] Functions { get; set; } 340 | } 341 | 342 | public DOSHeader DOSHdr { get; set; } 343 | public FileHeader FileHdr { get; set; } 344 | public OptionalHeader OptionalHdr { get; set; } 345 | public SectionHeader[] SectionHdrs { get; set; } 346 | public ImportDll[] Imports { get; set; } 347 | 348 | private string[] GetStrings(BinaryReader br, int expectedCount) 349 | { 350 | int[] nullSkip = null; 351 | return GetStrings(br, expectedCount, out nullSkip); 352 | } 353 | private string[] GetStrings(BinaryReader br, int expectedCount, out int[] nullSkip) 354 | { 355 | string[] result = new string[expectedCount]; 356 | nullSkip = new int[expectedCount]; 357 | string tempString = ""; 358 | int arrPos = 0; 359 | for (byte x = br.ReadByte(); arrPos != expectedCount; x = br.ReadByte()) 360 | { 361 | if (arrPos == expectedCount) { break; } 362 | if (x == 0x00) 363 | { 364 | string filter = tempString.Replace("\0", null); 365 | if (filter == "") { continue; } 366 | nullSkip[arrPos] = tempString.Split("\0".ToCharArray()).Length; 367 | result[arrPos] = filter; 368 | tempString = ""; 369 | arrPos++; 370 | } 371 | tempString += (char)x; 372 | } 373 | return result.ToArray(); 374 | } 375 | 376 | public SectionHeader GetSectionHeader(uint adr, bool rawCompare = false) 377 | { 378 | foreach (SectionHeader sectionHdr in SectionHdrs) 379 | { 380 | if (rawCompare) 381 | { 382 | if (sectionHdr.RawAdr <= adr && 383 | sectionHdr.RawAdr + sectionHdr.RawSize > adr) 384 | { 385 | return sectionHdr; 386 | } 387 | } 388 | else 389 | { 390 | if (sectionHdr.VirtAdr <= adr && 391 | sectionHdr.VirtAdr + sectionHdr.VirtSize > adr) 392 | { 393 | return sectionHdr; 394 | } 395 | } 396 | } 397 | return null; 398 | } 399 | public ImportDll GetImport(uint RAWadr) 400 | { 401 | ImportDll result = new ImportDll(); 402 | foreach (ImportDll impDll in Imports) 403 | { 404 | foreach (ImportDll.Function impFunc in impDll.Functions) 405 | { 406 | if (impFunc.CallViaAdr == RAWadr) 407 | { 408 | result = impDll; 409 | List impFuncs = result.Functions.ToList(); 410 | impFuncs.Clear(); 411 | impFuncs.Add(impFunc); 412 | result.Functions = impFuncs.ToArray(); 413 | return result; 414 | } 415 | } 416 | } 417 | return null; 418 | } 419 | 420 | /*public ulong ToRVA(ulong adr) { return adr + OptionalHdr.ImageBase; } 421 | public ulong ToVA (ulong adr) { return adr - OptionalHdr.ImageBase; }*/ 422 | 423 | public uint GetRawOffset(uint adr) 424 | { 425 | SectionHeader sectionHdr = GetSectionHeader(adr); 426 | return (adr - sectionHdr.VirtAdr) + sectionHdr.RawAdr; 427 | } 428 | 429 | public uint GetRawOffset(uint adr, uint imageBase) { return GetRawOffset(adr - imageBase); } 430 | } 431 | 432 | public class InstructionUtils 433 | { 434 | public static long GetCallAdr(X86Instruction callInst, uint imageBase = 0xFFFFFFFF) 435 | { 436 | if (callInst.HasDetails) { return imageBase != 0xFFFFFFFF ? callInst.Details.Displacement - imageBase : callInst.Details.Displacement; } 437 | else 438 | { 439 | if (callInst.Operand.Contains(" ptr [")) 440 | { 441 | foreach (string str in callInst.Operand.Split('[')) 442 | { 443 | try { return imageBase != 0xFFFFFFFF ? Utils.FromHexInt64(str) - imageBase : Utils.FromHexInt64(str); } 444 | catch { } 445 | } 446 | throw new Exception("Could not process call operand!"); 447 | } 448 | else { return imageBase != 0xFFFFFFFF ? Utils.FromHexInt64(callInst.Operand) - imageBase : Utils.FromHexInt64(callInst.Operand); } 449 | } 450 | } 451 | } 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /XUnionPE/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Gee.External.Capstone; 9 | using Gee.External.Capstone.X86; 10 | 11 | namespace XUnionPE 12 | { 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | Console.SetWindowSize(50, 15); 18 | Console.CursorVisible = false; 19 | Console.BackgroundColor = ConsoleColor.DarkGray; 20 | Console.ForegroundColor = ConsoleColor.Gray; 21 | drawOnBottomCorner(' '); 22 | Console.Clear(); 23 | Console.WriteLine(); 24 | new Decompiler(Path.GetFullPath(args[0])).Unpack(); 25 | } 26 | 27 | private static void drawOnBottomCorner(char ch) 28 | { 29 | Console.SetBufferSize(Console.WindowWidth + 1, Console.WindowHeight); 30 | Console.SetCursorPosition(Console.WindowWidth - 1, Console.WindowHeight - 1); 31 | Console.Write(ch); 32 | Console.SetBufferSize(Console.WindowWidth, Console.WindowHeight); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /XUnionPE/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Globalization; 7 | 8 | namespace XUnionPE 9 | { 10 | class Utils 11 | { 12 | public static short FromHexInt16(string hexInt) { return short.Parse(hexInt.Replace("0x", null), NumberStyles.HexNumber); } 13 | public static ushort FromHexUInt16(string hexInt) { return ushort.Parse(hexInt.Replace("0x", null), NumberStyles.HexNumber); } 14 | public static int FromHexInt32(string hexInt) { return int.Parse(hexInt.Replace("0x", null), NumberStyles.HexNumber); } 15 | public static uint FromHexUInt32(string hexInt) { return uint.Parse(hexInt.Replace("0x", null), NumberStyles.HexNumber); } 16 | public static long FromHexInt64(string hexInt) { return long.Parse(hexInt.Replace("0x", null), NumberStyles.HexNumber); } 17 | public static ulong FromHexUInt64(string hexInt) { return ulong.Parse(hexInt.Replace("0x", null), NumberStyles.HexNumber); } 18 | 19 | public static uint RightBitShift(uint value, int bits) 20 | { 21 | bits &= 31; 22 | return value >> bits | value << 32 - bits; 23 | } 24 | 25 | public static uint LeftBitShift(uint value, int bits) 26 | { 27 | bits &= 31; 28 | return value << bits | value >> 32 - bits; 29 | } 30 | 31 | public static byte[] GetBuffer(byte[] srcData, long keyAdr) { return GetBuffer(srcData, keyAdr, srcData.Length - keyAdr); } 32 | public static byte[] GetBuffer(byte[] srcData, long keyAdr, long keySize) 33 | { 34 | List result = new List(); 35 | for (int x = 0; x < keySize; x++) { result.Add(srcData[Convert.ToInt32(keyAdr) + x]); } 36 | return result.ToArray(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /XUnionPE/XUnionPE.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9F2136EC-4F47-4EC7-89D6-FAA2A0F33856} 8 | Exe 9 | Properties 10 | XUnionPE 11 | XUnionPE 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | true 36 | bin\x86\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x86 40 | prompt 41 | MinimumRecommendedRules.ruleset 42 | true 43 | 44 | 45 | bin\x86\Release\ 46 | TRACE 47 | true 48 | pdbonly 49 | x86 50 | prompt 51 | MinimumRecommendedRules.ruleset 52 | true 53 | 54 | 55 | 56 | ..\..\..\dlls\Gee.External.Capstone.dll 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 84 | --------------------------------------------------------------------------------