|
| 1 | +/* |
| 2 | + Copyright (C) 2021 CodeStrikers.org |
| 3 | + This file is part of NETReactorSlayer. |
| 4 | + NETReactorSlayer is free software: you can redistribute it and/or modify |
| 5 | + it under the terms of the GNU General Public License as published by |
| 6 | + the Free Software Foundation, either version 3 of the License, or |
| 7 | + (at your option) any later version. |
| 8 | + NETReactorSlayer is distributed in the hope that it will be useful, |
| 9 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | + GNU General Public License for more details. |
| 12 | + You should have received a copy of the GNU General Public License |
| 13 | + along with NETReactorSlayer. If not, see <http://www.gnu.org/licenses/>. |
| 14 | +*/ |
| 15 | +using de4dot.blocks; |
| 16 | +using de4dot.blocks.cflow; |
| 17 | +using dnlib.DotNet; |
| 18 | +using dnlib.DotNet.Emit; |
| 19 | +using NETReactorSlayer.Core.Helper.De4dot; |
| 20 | +using System; |
| 21 | +using System.Collections.Generic; |
| 22 | +using System.Linq; |
| 23 | +using static NETReactorSlayer.Core.Deobfuscators.ResourceDecryptor; |
| 24 | + |
| 25 | +namespace NETReactorSlayer.Core.Deobfuscators |
| 26 | +{ |
| 27 | + class BooleanDecryptor : IDeobfuscator |
| 28 | + { |
| 29 | + private MethodDef decryptorMethod; |
| 30 | + private EmbeddedResource encryptedResource; |
| 31 | + public void Execute() |
| 32 | + { |
| 33 | + long count = 0L; |
| 34 | + Find(); |
| 35 | + if (decryptorMethod == null || encryptedResource == null) |
| 36 | + { |
| 37 | + Logger.Warn("Couldn't find any encrypted boolean."); |
| 38 | + return; |
| 39 | + } |
| 40 | + Cleaner.ResourceToRemove.Add(encryptedResource); |
| 41 | + Cleaner.MethodsToPatch.Add(decryptorMethod); |
| 42 | + Deobfuscate(decryptorMethod); |
| 43 | + DnrDecrypterType decrypterType = GetDecrypterType(decryptorMethod, new string[0]); |
| 44 | + byte[] key = GetBytes(decryptorMethod, 32); |
| 45 | + if (decrypterType == DnrDecrypterType.V3) |
| 46 | + { |
| 47 | + V3 V3 = new V3(decryptorMethod); |
| 48 | + decryptedBytes = V3.Decrypt(encryptedResource); |
| 49 | + goto Continue; |
| 50 | + } |
| 51 | + byte[] iv = GetBytes(decryptorMethod, 16); |
| 52 | + if (IsNeedReverse(decryptorMethod)) |
| 53 | + Array.Reverse(iv); |
| 54 | + if (UsesPublicKeyToken(decryptorMethod)) |
| 55 | + { |
| 56 | + PublicKeyToken publicKeyToken = DeobfuscatorContext.Module.Assembly.PublicKeyToken; |
| 57 | + if (publicKeyToken != null && publicKeyToken.Data.Length != 0) |
| 58 | + { |
| 59 | + for (int z = 0; z < 8; z++) |
| 60 | + { |
| 61 | + iv[z * 2 + 1] = publicKeyToken.Data[z]; |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + if (decrypterType == DnrDecrypterType.V1) |
| 66 | + { |
| 67 | + V1 V1 = new V1(iv, key); |
| 68 | + decryptedBytes = V1.Decrypt(encryptedResource); |
| 69 | + goto Continue; |
| 70 | + } |
| 71 | + else if (decrypterType == DnrDecrypterType.V2) |
| 72 | + { |
| 73 | + V2 V2 = new V2(iv, key, decryptorMethod); |
| 74 | + decryptedBytes = V2.Decrypt(encryptedResource); |
| 75 | + goto Continue; |
| 76 | + } |
| 77 | + Logger.Warn("Couldn't find any encrypted boolean."); |
| 78 | + return; |
| 79 | + Continue: |
| 80 | + if (decryptedBytes == null) |
| 81 | + { |
| 82 | + Logger.Error("Failed to decrypt booleans."); |
| 83 | + return; |
| 84 | + } |
| 85 | + foreach (TypeDef type in DeobfuscatorContext.Module.GetTypes().Where(x => x.HasMethods)) |
| 86 | + { |
| 87 | + foreach (MethodDef method in type.Methods.Where(x => x.HasBody && x.Body.HasInstructions)) |
| 88 | + { |
| 89 | + for (int i = 0; i < method.Body.Instructions.Count; i++) |
| 90 | + { |
| 91 | + try |
| 92 | + { |
| 93 | + if (!method.Body.Instructions[i].OpCode.Equals(OpCodes.Call) || !method.Body.Instructions[i - 1].IsLdcI4() || !method.Body.Instructions[i + 1].IsConditionalBranch()) |
| 94 | + continue; |
| 95 | + if (method.Body.Instructions[i].Operand is IMethod iMethod && iMethod.MDToken.ToInt32() == decryptorMethod.MDToken.ToInt32()) |
| 96 | + { |
| 97 | + int offset = method.Body.Instructions[i - 1].GetLdcI4Value(); |
| 98 | + bool value = Decrypt(offset); |
| 99 | + if (value) |
| 100 | + method.Body.Instructions[i] = Instruction.CreateLdcI4(1); |
| 101 | + else |
| 102 | + method.Body.Instructions[i] = Instruction.CreateLdcI4(0); |
| 103 | + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; |
| 104 | + count += 1L; |
| 105 | + } |
| 106 | + } |
| 107 | + catch { } |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | + if (count > 0L) Logger.Done((int)count + " Booleans decrypted."); |
| 112 | + else Logger.Warn("Couldn't find any encrypted boolean."); |
| 113 | + } |
| 114 | + public bool Decrypt(int offset) => DeobfuscatorContext.ModuleBytes[BitConverter.ToUInt32(decryptedBytes, offset)] == 0x80; |
| 115 | + |
| 116 | + private void Find() |
| 117 | + { |
| 118 | + foreach (var type in DeobfuscatorContext.Module.Types.Where(x => x.BaseType != null && x.BaseType.FullName == "System.Object")) |
| 119 | + { |
| 120 | + var methodDef = DotNetUtils.GetMethod(type, "System.Boolean", "(System.Int32)"); |
| 121 | + if (methodDef != null && GetEncryptedResource(methodDef) != null && GetDecrypterType(methodDef, new string[0]) != DnrDecrypterType.Unknown) |
| 122 | + { |
| 123 | + decryptorMethod = methodDef; |
| 124 | + encryptedResource = GetEncryptedResource(decryptorMethod); |
| 125 | + break; |
| 126 | + } |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + EmbeddedResource GetEncryptedResource(MethodDef method) |
| 131 | + { |
| 132 | + if (method == null || !method.HasBody || !method.Body.HasInstructions) return null; |
| 133 | + foreach (string s in DotNetUtils.GetCodeStrings(method)) |
| 134 | + { |
| 135 | + if (DotNetUtils.GetResource(DeobfuscatorContext.Module, s) is EmbeddedResource resource) return resource; |
| 136 | + } |
| 137 | + return null; |
| 138 | + } |
| 139 | + |
| 140 | + private DnrDecrypterType GetDecrypterType(MethodDef method, IList<string> additionalTypes) |
| 141 | + { |
| 142 | + if (method == null || !method.IsStatic || method.Body == null) |
| 143 | + { |
| 144 | + return DnrDecrypterType.Unknown; |
| 145 | + } |
| 146 | + if (additionalTypes == null) |
| 147 | + { |
| 148 | + additionalTypes = new string[0]; |
| 149 | + } |
| 150 | + LocalTypes localTypes = new LocalTypes(method); |
| 151 | + if (V1.CouldBeResourceDecrypter(method, localTypes, additionalTypes)) |
| 152 | + { |
| 153 | + return DnrDecrypterType.V1; |
| 154 | + } |
| 155 | + if (V2.CouldBeResourceDecrypter(localTypes, additionalTypes)) |
| 156 | + { |
| 157 | + return DnrDecrypterType.V2; |
| 158 | + } |
| 159 | + if (V3.CouldBeResourceDecrypter(localTypes, additionalTypes)) |
| 160 | + { |
| 161 | + return DnrDecrypterType.V3; |
| 162 | + } |
| 163 | + return DnrDecrypterType.Unknown; |
| 164 | + } |
| 165 | + |
| 166 | + public void Deobfuscate(MethodDef method) |
| 167 | + { |
| 168 | + List<IBlocksDeobfuscator> list = new List<IBlocksDeobfuscator> { new MethodCallInliner(false) }; |
| 169 | + SimpleDeobfuscatorFlags flags = 0; |
| 170 | + if (!(method == null || (!((flags & SimpleDeobfuscatorFlags.Force) > 0U) && Check(method, SimpleDeobFlags.HasDeobfuscated)))) |
| 171 | + { |
| 172 | + Deobfuscate(method, delegate (Blocks blocks) |
| 173 | + { |
| 174 | + bool disableNewCFCode = (flags & SimpleDeobfuscatorFlags.DisableConstantsFolderExtraInstrs) > (SimpleDeobfuscatorFlags)0U; |
| 175 | + BlocksCflowDeobfuscator cflowDeobfuscator = new BlocksCflowDeobfuscator(list, disableNewCFCode); |
| 176 | + cflowDeobfuscator.Initialize(blocks); |
| 177 | + cflowDeobfuscator.Deobfuscate(); |
| 178 | + }); |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + void Deobfuscate(MethodDef method, Action<Blocks> handler) |
| 183 | + { |
| 184 | + if (method == null || !method.HasBody || !method.Body.HasInstructions) return; |
| 185 | + try |
| 186 | + { |
| 187 | + if (method.Body.Instructions.Any((Instruction instr) => instr.OpCode.Equals(OpCodes.Switch))) |
| 188 | + DeobfuscateEquations(method); |
| 189 | + Blocks blocks = new Blocks(method); |
| 190 | + handler(blocks); |
| 191 | + blocks.GetCode(out IList<Instruction> allInstructions, out IList<ExceptionHandler> allExceptionHandlers); |
| 192 | + DotNetUtils.RestoreBody(method, allInstructions, allExceptionHandlers); |
| 193 | + } |
| 194 | + catch |
| 195 | + { |
| 196 | + Logger.Warn("Couldn't deobfuscate " + method.FullName); |
| 197 | + } |
| 198 | + } |
| 199 | + |
| 200 | + void DeobfuscateEquations(MethodDef method) |
| 201 | + { |
| 202 | + for (int i = 0; i < method.Body.Instructions.Count; i++) |
| 203 | + { |
| 204 | + if (method.Body.Instructions[i].IsBrtrue() && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Pop) && method.Body.Instructions[i - 1].OpCode.Equals(OpCodes.Call)) |
| 205 | + { |
| 206 | + if (method.Body.Instructions[i - 1].Operand is MethodDef methodDef) |
| 207 | + { |
| 208 | + IList<Instruction> methodDefInstr = methodDef.Body.Instructions; |
| 209 | + if (methodDef.ReturnType.FullName == "System.Boolean") |
| 210 | + { |
| 211 | + if (methodDefInstr[methodDefInstr.Count - 2].OpCode.Equals(OpCodes.Ldc_I4_0)) |
| 212 | + { |
| 213 | + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; |
| 214 | + method.Body.Instructions[i].OpCode = OpCodes.Nop; |
| 215 | + } |
| 216 | + else |
| 217 | + { |
| 218 | + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; |
| 219 | + method.Body.Instructions[i].OpCode = OpCodes.Br_S; |
| 220 | + } |
| 221 | + } |
| 222 | + else |
| 223 | + { |
| 224 | + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; |
| 225 | + method.Body.Instructions[i].OpCode = OpCodes.Nop; |
| 226 | + } |
| 227 | + } |
| 228 | + } |
| 229 | + else |
| 230 | + { |
| 231 | + if (method.Body.Instructions[i].IsBrfalse() && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Pop) && method.Body.Instructions[i - 1].OpCode.Equals(OpCodes.Call)) |
| 232 | + { |
| 233 | + if (method.Body.Instructions[i - 1].Operand is MethodDef methodDef2) |
| 234 | + { |
| 235 | + IList<Instruction> methodDefInstr2 = methodDef2.Body.Instructions; |
| 236 | + if (methodDef2.ReturnType.FullName == "System.Boolean") |
| 237 | + { |
| 238 | + if (methodDefInstr2[methodDefInstr2.Count - 2].OpCode.Equals(OpCodes.Ldc_I4_0)) |
| 239 | + { |
| 240 | + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; |
| 241 | + method.Body.Instructions[i].OpCode = OpCodes.Br_S; |
| 242 | + } |
| 243 | + else |
| 244 | + { |
| 245 | + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; |
| 246 | + method.Body.Instructions[i].OpCode = OpCodes.Nop; |
| 247 | + } |
| 248 | + } |
| 249 | + else |
| 250 | + { |
| 251 | + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; |
| 252 | + method.Body.Instructions[i].OpCode = OpCodes.Br_S; |
| 253 | + } |
| 254 | + } |
| 255 | + } |
| 256 | + } |
| 257 | + } |
| 258 | + } |
| 259 | + |
| 260 | + bool Check(MethodDef method, SimpleDeobFlags flags) |
| 261 | + { |
| 262 | + if (method == null) return false; |
| 263 | + simpleDeobfuscatorFlags.TryGetValue(method, out SimpleDeobFlags oldFlags); |
| 264 | + simpleDeobfuscatorFlags[method] = (oldFlags | flags); |
| 265 | + return ((oldFlags & flags) == flags); |
| 266 | + } |
| 267 | + |
| 268 | + [Flags] public enum SimpleDeobfuscatorFlags : uint { Force = 1U, DisableConstantsFolderExtraInstrs = 2U } |
| 269 | + |
| 270 | + [Flags] enum SimpleDeobFlags { HasDeobfuscated = 1 } |
| 271 | + |
| 272 | + readonly Dictionary<MethodDef, SimpleDeobFlags> simpleDeobfuscatorFlags = new Dictionary<MethodDef, SimpleDeobFlags>(); |
| 273 | + |
| 274 | + byte[] decryptedBytes = null; |
| 275 | + } |
| 276 | +} |
0 commit comments