├── README.md ├── SkaterStrFiner.csproj └── Program.cs /README.md: -------------------------------------------------------------------------------- 1 | # Skater.NetDeobfuscator 2 | Deobfuscator for RustemSoft Skater.Net Obfuscator 3 | 4 | :round_pushpin: This tool is able to decrypt strings from Native DLL of the latest version of Skater.Net Obfuscator (9.1.34) 5 | 6 | 7 | :round_pushpin: Control Flow is also handled 8 | 9 | ```Thanks to XenocodeRCE for his help ``` :triangular_ruler: 10 | -------------------------------------------------------------------------------- /SkaterStrFiner.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {32EB2EA4-E5B3-40C1-88F7-36F428E36DC4} 8 | Exe 9 | SkaterStrFiner 10 | SkaterStrFiner 11 | v4.7.2 12 | 512 13 | true 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | False 38 | ..\..\..\Documents\MEGAsync Downloads\MEGAsync\Reverse Engineering\Deobfuscateurs\ConfuserEx\de4dot ConfuserEx\bin\de4dot.blocks.dll 39 | 40 | 41 | False 42 | ..\..\..\Documents\MEGAsync Downloads\MEGAsync\Reverse Engineering\Deobfuscateurs\ConfuserEx\de4dot ConfuserEx\bin\dnlib.dll 43 | 44 | 45 | False 46 | bin\Debug\PEReader.dll 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using AlphaOmega.Debug; 2 | using de4dot.blocks; 3 | using de4dot.blocks.cflow; 4 | using dnlib.DotNet; 5 | using dnlib.DotNet.Emit; 6 | using dnlib.DotNet.Writer; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Diagnostics; 13 | 14 | namespace SkaterStrFiner 15 | { 16 | class Program 17 | { 18 | #region "Methods to deal with bytes in section" 19 | public static string ByteArrayToString(byte[] ba) 20 | { 21 | StringBuilder hex = new StringBuilder(ba.Length * 2); 22 | foreach (byte b in ba) 23 | hex.AppendFormat("{0:x2}", b); 24 | return hex.ToString(); 25 | } 26 | 27 | public static byte[] StringToByteArray(String hex) 28 | { 29 | int NumberChars = hex.Length; 30 | byte[] bytes = new byte[NumberChars / 2]; 31 | for (int i = 0; i < NumberChars; i += 2) 32 | bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); 33 | return bytes; 34 | } 35 | #endregion 36 | 37 | public static byte[] dataSectionContent = null; 38 | 39 | static void Main(string[] args) 40 | { 41 | Console.WriteLine("Skater.net Deobfuscator \n \n"); 42 | 43 | int nombreDeStringEncodée = 0; 44 | ModuleDefMD module = ModuleDefMD.Load(args[0]); 45 | //Type which contains String Initialization 46 | TypeDef InitialType = null; 47 | //We grab native method to count all of them 48 | List ListOfNativeMeths = new List(); 49 | Stopwatch stopWatch = new Stopwatch(); 50 | stopWatch.Start(); 51 | foreach (TypeDef t in module.GetTypes()) 52 | { 53 | foreach (MethodDef m in t.Methods) 54 | { 55 | if (m.Name.Contains("OOOOlx")) 56 | { 57 | ListOfNativeMeths.Add(m); 58 | } 59 | } 60 | } 61 | nombreDeStringEncodée = ListOfNativeMeths.Count(); 62 | //We grab the type to find cctor afterwards 63 | InitialType = ListOfNativeMeths.First().DeclaringType; 64 | string DLLName = ListOfNativeMeths.First().ImplMap.Module.Name; 65 | Console.WriteLine("Native DLL Name : " + DLLName); 66 | //We stock strings in a list 67 | List deHexedstring = new List(); 68 | 69 | 70 | //Reading PE 71 | using (PEFile file = new PEFile(StreamLoader.FromFile(Path.GetDirectoryName(args[0]) + @"\" + DLLName))) 72 | { 73 | var section = file.Header.Sections; 74 | foreach (var item in section) 75 | { 76 | var realName = new string(item.Name); 77 | //Strings are stored in .data section 78 | if (realName.Contains(".data")) 79 | { 80 | //offset of the beginning of the section 81 | var start = item.PointerToRawData; 82 | //calculation of section's size 83 | var length = item.PointerToRawData + item.SizeOfRawData; 84 | 85 | //We copy all bytes into a list 86 | List b = new List(); 87 | using (FileStream fsSourceDDS = new FileStream(Path.GetDirectoryName(args[0]) + @"\" + DLLName, FileMode.Open, FileAccess.Read)) 88 | using (BinaryReader binaryReader = new BinaryReader(fsSourceDDS)) 89 | { 90 | fsSourceDDS.Seek(start, SeekOrigin.Begin); 91 | while (start < length) 92 | { 93 | byte y = binaryReader.ReadByte(); 94 | b.Add(y); 95 | start++; 96 | } 97 | } 98 | dataSectionContent = b.ToArray(); 99 | 100 | string input = ByteArrayToString(dataSectionContent); 101 | 102 | //we split the whole strings 103 | string[] datacontent = input.Split(new string[] { "00" }, StringSplitOptions.None); 104 | 105 | //We remove empty results 106 | datacontent = datacontent.Where(x => !string.IsNullOrEmpty(x)).ToArray(); 107 | 108 | 109 | //We only need values associated to strings 110 | string[] encodedstring = datacontent.Take(nombreDeStringEncodée).Select(i => i.ToString()).ToArray(); 111 | 112 | 113 | foreach (var hexedstring in encodedstring) 114 | { 115 | deHexedstring.Add(Encoding.UTF8.GetString(StringToByteArray(hexedstring))); 116 | } 117 | } 118 | } 119 | } 120 | //That's why we needed to find the type 121 | MethodDef cctor = InitialType.FindStaticConstructor(); 122 | if (cctor == null) 123 | { 124 | Console.WriteLine("Impossible to find Method which initialize strings"); 125 | return; 126 | } 127 | //We make a dictionnary to replace values of field 128 | Dictionary FieldValue = new Dictionary(); 129 | for (int i = 0; i < cctor.Body.Instructions.Count - 1; i++) 130 | { 131 | if (cctor.Body.Instructions[i].OpCode == OpCodes.Ldsfld && cctor.Body.Instructions[i + 1].OpCode == OpCodes.Call && cctor.Body.Instructions[i + 2].OpCode == OpCodes.Stsfld) 132 | { 133 | cctor.Body.Instructions[i].OpCode = OpCodes.Nop; 134 | cctor.Body.Instructions[i + 1].OpCode = OpCodes.Ldstr; 135 | string decrypted = smethod_0(deHexedstring[0]); 136 | cctor.Body.Instructions[i + 1].Operand = decrypted; 137 | FieldValue.Add((FieldDef)cctor.Body.Instructions[i + 2].Operand, decrypted); 138 | deHexedstring.RemoveAt(0); 139 | } 140 | } 141 | int DecryptedStrings = 0; 142 | //Replacing field values 143 | foreach (TypeDef type in module.Types) 144 | { 145 | foreach (MethodDef method in type.Methods) 146 | { 147 | if (method.HasBody && method.Body.HasInstructions) 148 | { 149 | Cleaner(method); 150 | for (int i = 0; i < method.Body.Instructions.Count - 1; i++) 151 | { 152 | try 153 | { 154 | if (method.Body.Instructions[i].OpCode == OpCodes.Ldsfld) 155 | { 156 | FieldDef fld = (FieldDef)method.Body.Instructions[i].Operand; 157 | if (FieldValue.Keys.Contains(fld)) 158 | { 159 | method.Body.Instructions[i].OpCode = OpCodes.Ldstr; 160 | method.Body.Instructions[i].Operand = FieldValue[fld]; 161 | DecryptedStrings++; 162 | } 163 | } 164 | } 165 | catch 166 | { 167 | 168 | } 169 | } 170 | } 171 | } 172 | } 173 | stopWatch.Stop(); 174 | Console.WriteLine("Done ! Elapsed time : " + stopWatch.Elapsed.TotalSeconds); 175 | //Saving ASM 176 | string SavingPath = module.Kind == ModuleKind.Dll ? args[0].Replace(".dll", "-Deobfuscated.dll") : args[0].Replace(".exe", "-Deobfuscated.exe"); 177 | var opts = new ModuleWriterOptions(module); 178 | opts.MetaDataOptions.Flags = MetaDataFlags.PreserveAll; 179 | opts.Logger = DummyLogger.NoThrowInstance; 180 | module.Write(SavingPath, opts); 181 | Console.WriteLine("Sucessfully decrypted {0} strings", DecryptedStrings); 182 | Console.WriteLine("Control Flow removed", DecryptedStrings); 183 | Console.ReadLine(); 184 | } 185 | 186 | //Removing Control Flow 187 | public static void Cleaner(MethodDef method) 188 | { 189 | BlocksCflowDeobfuscator blocksCflowDeobfuscator = new BlocksCflowDeobfuscator(); 190 | Blocks blocks = new Blocks(method); 191 | blocksCflowDeobfuscator.Initialize(blocks); 192 | blocksCflowDeobfuscator.Deobfuscate(); 193 | blocks.RepartitionBlocks(); 194 | IList list; 195 | IList exceptionHandlers; 196 | blocks.GetCode(out list, out exceptionHandlers); 197 | DotNetUtils.RestoreBody(method, list, exceptionHandlers); 198 | } 199 | 200 | #region "Skater static string decoding" 201 | 202 | public static string smethod_0(string string_8) 203 | { 204 | string text = string.Empty; 205 | double value = 3.1415; 206 | string[] array = string_8.Split(new char[] 207 | { 208 | ' ' 209 | }); 210 | int num = 0; 211 | int upperBound = array.GetUpperBound(0); 212 | checked 213 | { 214 | for (int i = num; i <= upperBound; i += 3) 215 | { 216 | if ((double)Convert.ToInt32(array[i + 2]) / (double)Convert.ToInt32(value) == unchecked(11.0 * (double)Convert.ToInt32(value))) 217 | { 218 | text += char.ConvertFromUtf32(Convert.ToInt32(unchecked(Convert.ToDouble(array[i]) + Convert.ToDouble(255))) * smethod_1(Convert.ToInt32(array[i + 1]))); 219 | } 220 | else 221 | { 222 | text += char.ConvertFromUtf32(Convert.ToInt32(array[i]) * smethod_1(Convert.ToInt32(array[i + 1]))); 223 | } 224 | } 225 | return text; 226 | } 227 | } 228 | 229 | public static int smethod_1(int int_0) 230 | { 231 | int result; 232 | if ((double)int_0 / Convert.ToDouble(2) == (double)Convert.ToInt32((double)int_0 / Convert.ToDouble(2))) 233 | { 234 | result = 2; 235 | } 236 | else 237 | { 238 | result = 1; 239 | } 240 | return result; 241 | } 242 | 243 | #endregion 244 | } 245 | } 246 | --------------------------------------------------------------------------------