/* Copyright (C) 2011-2015 de4dot@gmail.com This file is part of de4dot. de4dot is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. de4dot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with de4dot. If not, see . */ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.IO; using System.Text; using dnlib.IO; using dnlib.DotNet; using dnlib.DotNet.Emit; using de4dot.blocks; using CR = System.Runtime.InteropServices; using DR = dnlib.DotNet; namespace de4dot.code.deobfuscators.Babel_NET { class ImageReader { static int METHODS_SIG = 0x0000BEBA; static int METADATA_SIG = 0x0100BEBA; static int METHOD_NAMES_SIG = 0x0200BEBA; static int ASSEMBLY_NAMES_SIG = 0x0201BEBA; static int TYPEREFS_SIG = 0x0202BEBA; static int STRINGS_SIG = 0x0203BEBA; enum TypeId : byte { TypeRef = 0, GenericInstance = 1, Pointer = 2, Array = 3, ByRef = 4, } ModuleDefMD module; IBinaryReader reader; string[] strings; AssemblyRef[] assemblyNames; Dictionary methodOffsets; List typeRefs; MemberRefConverter memberRefConverter; //IDeobfuscatorContext deobfuscatorContext; public ImageReader(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, byte[] data) { //this.deobfuscatorContext = deobfuscatorContext; this.module = module; this.reader = MemoryImageStream.Create(data); this.memberRefConverter = new MemberRefConverter(module); } public bool Initialize() { if (reader.ReadInt32() != METHODS_SIG) return false; int metadataOffset = GetMetadataOffset(); if (metadataOffset < 0) return false; long pos = metadataOffset + 4; reader.Position = pos; int version = reader.ReadInt16(); // major, minor if (version == 0x0001) { InitializeV10(); return true; } reader.Position = pos; InitializeV55(); return true; } void InitializeV10() { reader.ReadInt16(); int methodNamesOffset = (int)reader.ReadInt64(); int typeRefsOffset = (int)reader.ReadInt64(); int assemblyRefsOffset = (int)reader.ReadInt64(); int stringsOffset = (int)reader.ReadInt64(); InitializeStrings(stringsOffset); InitializeAssemblyNames(assemblyRefsOffset); InitializeMethodNames(methodNamesOffset); InitializeTypeRefs(typeRefsOffset); } void InitializeV55() { int methodNamesOffset = (int)reader.ReadInt64() ^ METADATA_SIG; int typeRefsOffset = (int)reader.ReadInt64() ^ (METADATA_SIG << 1); int assemblyRefsOffset = (int)reader.ReadInt64() ^ ((METADATA_SIG << 1) + 1); int stringsOffset = (int)reader.ReadInt64() ^ (((METADATA_SIG << 1) + 1) << 1); InitializeStrings(stringsOffset); InitializeAssemblyNames(assemblyRefsOffset); InitializeMethodNames(methodNamesOffset); InitializeTypeRefs(typeRefsOffset); } public void Restore(string name, MethodDef method) { var babelMethod = GetMethod(name); var body = method.Body; body.MaxStack = babelMethod.MaxStack; body.InitLocals = babelMethod.InitLocals; body.Variables.Clear(); foreach (var local in babelMethod.Locals) body.Variables.Add(local); var toNewOperand = new Dictionary(); if (babelMethod.ThisParameter != null) toNewOperand[babelMethod.ThisParameter] = method.Parameters[0]; for (int i = 0; i < babelMethod.Parameters.Length; i++) toNewOperand[babelMethod.Parameters[i]] = method.Parameters[i + method.Parameters.MethodSigIndexBase]; body.Instructions.Clear(); foreach (var instr in babelMethod.Instructions) { object newOperand; if (instr.Operand != null && toNewOperand.TryGetValue(instr.Operand, out newOperand)) instr.Operand = newOperand; body.Instructions.Add(instr); } body.ExceptionHandlers.Clear(); foreach (var eh in babelMethod.ExceptionHandlers) body.ExceptionHandlers.Add(eh); } BabelMethodDef GetMethod(string name) { int offset = methodOffsets[name]; methodOffsets.Remove(name); reader.Position = offset; return new MethodDefReader(this, reader).Read(); } public string ReadString() { return strings[ReadVariableLengthInt32()]; } public TypeSig ReadTypeSig() { return typeRefs[ReadVariableLengthInt32()]; } public TypeSig[] ReadTypeSigs() { var refs = new TypeSig[ReadVariableLengthInt32()]; for (int i = 0; i < refs.Length; i++) refs[i] = ReadTypeSig(); return refs; } public IField ReadFieldRef() { var name = ReadString(); var declaringType = ReadTypeSig(); var fields = GetFields(Resolve(declaringType), name); if (fields == null || fields.Count != 1) { throw new ApplicationException(string.Format("Couldn't find one field named '{0}' in type {1}", name, Utils.RemoveNewlines(declaringType))); } return memberRefConverter.Convert(fields[0]); } static List GetFields(TypeDef type, string name) { if (type == null) return null; return new List(type.FindFields(name)); } public IMethod ReadMethodRef() { var babelMethodRef = new MethodRefReader(this, reader).Read(); var method = GetMethodRef(babelMethodRef); if (method == null) { throw new ApplicationException(string.Format("Could not find method '{0}' in type '{1}'", Utils.RemoveNewlines(babelMethodRef.Name), Utils.RemoveNewlines(babelMethodRef.DeclaringType))); } var git = babelMethodRef.DeclaringType.ToGenericInstSig(); if (git == null) return method; var mr = new MemberRefUser(module, method.Name, method.MethodSig.Clone(), babelMethodRef.DeclaringType.ToTypeDefOrRef()); return module.UpdateRowId(mr); } IMethod GetMethodRef(BabelMethodreference babelMethodRef) { var declaringType = Resolve(babelMethodRef.DeclaringType); if (declaringType == null) return null; var methods = GetMethods(declaringType, babelMethodRef); if (methods.Count != 1) { throw new ApplicationException(string.Format("Couldn't find one method named '{0}' in type {1}", babelMethodRef.Name, Utils.RemoveNewlines(declaringType))); } return methods[0]; } List GetMethods(TypeDef declaringType, BabelMethodreference babelMethodRef) { var methods = new List(); var gis = babelMethodRef.DeclaringType as GenericInstSig; var gim = babelMethodRef.GenericArguments; foreach (var method in declaringType.Methods) { if (CompareMethod(GenericArgsSubstitutor.Create(method, gis, gim), babelMethodRef)) { if (!babelMethodRef.IsGenericMethod) methods.Add(memberRefConverter.Convert(method)); else { var gim2 = new GenericInstMethodSig(babelMethodRef.GenericArguments); var ms = module.UpdateRowId(new MethodSpecUser(memberRefConverter.Convert(method), gim2)); methods.Add(ms); } } } return methods; } bool CompareMethod(IMethod method, BabelMethodreference babelMethodRef) { var sig = method.MethodSig; if (sig.Params.Count != babelMethodRef.Parameters.Length) return false; if (method.Name != babelMethodRef.Name) return false; if (sig.HasThis != babelMethodRef.HasThis) return false; if (sig.GenParamCount != babelMethodRef.GenericArguments.Length) return false; if (!new SigComparer().Equals(sig.RetType, babelMethodRef.ReturnType)) return false; for (int i = 0; i < babelMethodRef.Parameters.Length; i++) { if (!new SigComparer().Equals(sig.Params[i], babelMethodRef.Parameters[i].Type)) return false; } return true; } TypeDef Resolve(TypeSig type) { type = type.RemovePinnedAndModifiers(); var gis = type as GenericInstSig; if (gis != null) type = gis.GenericType; var tdrs = type as TypeDefOrRefSig; if (tdrs == null) return null; var td = tdrs.TypeDef; if (td != null) return td; var tr = tdrs.TypeRef; if (tr != null) return tr.Resolve(); return null; } public MethodSig ReadCallSite() { var returnType = ReadTypeSig(); var paramTypes = ReadTypeSigs(); var callingConvention = (CR.CallingConvention)reader.ReadInt32(); return new MethodSig(ConvertCallingConvention(callingConvention), 0, returnType, paramTypes); } static DR.CallingConvention ConvertCallingConvention(CR.CallingConvention callingConvention) { switch (callingConvention) { case CR.CallingConvention.Winapi: return DR.CallingConvention.Default; case CR.CallingConvention.Cdecl: return DR.CallingConvention.C; case CR.CallingConvention.StdCall: return DR.CallingConvention.StdCall; case CR.CallingConvention.ThisCall: return DR.CallingConvention.ThisCall; case CR.CallingConvention.FastCall: return DR.CallingConvention.FastCall; default: throw new ApplicationException(string.Format("Unknown CallingConvention {0}", callingConvention)); } } void InitializeStrings(int headerOffset) { reader.Position = headerOffset; if (reader.ReadInt32() != STRINGS_SIG) throw new ApplicationException("Invalid strings sig"); strings = new string[ReadVariableLengthInt32()]; for (int i = 0; i < strings.Length; i++) strings[i] = reader.ReadString(); } void InitializeAssemblyNames(int headerOffset) { reader.Position = headerOffset; if (reader.ReadInt32() != ASSEMBLY_NAMES_SIG) throw new ApplicationException("Invalid assembly names sig"); assemblyNames = new AssemblyRef[ReadVariableLengthInt32()]; for (int i = 0; i < assemblyNames.Length; i++) assemblyNames[i] = module.UpdateRowId(new AssemblyRefUser(new AssemblyNameInfo(ReadString()))); } void InitializeMethodNames(int headerOffset) { reader.Position = headerOffset; if (reader.ReadInt32() != METHOD_NAMES_SIG) throw new ApplicationException("Invalid methods sig"); int numMethods = ReadVariableLengthInt32(); methodOffsets = new Dictionary(numMethods, StringComparer.Ordinal); for (int i = 0; i < numMethods; i++) { var methodName = ReadString(); methodOffsets[methodName] = ReadVariableLengthInt32(); } } void InitializeTypeRefs(int headerOffset) { reader.Position = headerOffset; if (reader.ReadInt32() != TYPEREFS_SIG) throw new ApplicationException("Invalid typerefs sig"); int numTypeRefs = reader.ReadInt32(); typeRefs = new List(numTypeRefs + 1); typeRefs.Add(null); var genericArgFixes = new Dictionary>(); for (int i = 0; i < numTypeRefs; i++) { TypeId typeId = (TypeId)reader.ReadByte(); switch (typeId) { case TypeId.TypeRef: typeRefs.Add(ReadTypeRef()); break; case TypeId.GenericInstance: List genericArgs; var git = ReadGenericInstanceType(out genericArgs); typeRefs.Add(git); genericArgFixes[git] = genericArgs; break; case TypeId.Pointer: typeRefs.Add(ReadPointerType()); break; case TypeId.Array: typeRefs.Add(ReadArrayType()); break; case TypeId.ByRef: typeRefs.Add(ReadByRefType()); break; default: throw new ApplicationException(string.Format("Unknown type id {0}", (int)typeId)); } } foreach (var kv in genericArgFixes) { var git = kv.Key; foreach (var typeNum in kv.Value) git.GenericArguments.Add(typeRefs[typeNum]); } } TypeSig ReadTypeRef() { string ns, name; ParseReflectionTypeName(ReadString(), out ns, out name); var asmRef = assemblyNames[ReadVariableLengthInt32()]; var declaringType = ReadTypeSig(); var typeRef = new TypeRefUser(module, ns, name); if (declaringType != null) typeRef.ResolutionScope = GetTypeRef(declaringType); else typeRef.ResolutionScope = asmRef; return memberRefConverter.Convert(typeRef); } TypeRef GetTypeRef(TypeSig type) { var tdr = type as TypeDefOrRefSig; if (tdr == null) throw new ApplicationException("Not a type ref"); if (tdr.TypeRef != null) return tdr.TypeRef; var td = tdr.TypeDef; if (td != null) return new Importer(module).Import(td) as TypeRef; throw new ApplicationException("Not a type ref"); } static void ParseReflectionTypeName(string fullName, out string ns, out string name) { int index = GetLastChar(fullName, '.'); if (index < 0) { ns = ""; name = fullName; } else { ns = UnEscape(fullName.Substring(0, index)); name = fullName.Substring(index + 1); } index = GetLastChar(name, '+'); if (index < 0) name = UnEscape(name); else { ns = ""; name = UnEscape(name.Substring(index + 1)); } } static int GetLastChar(string name, char c) { if (string.IsNullOrEmpty(name)) return -1; int index = name.Length - 1; while (true) { index = name.LastIndexOf(c, index); if (index < 0) return -1; if (index == 0) return index; if (name[index - 1] != '\\') return index; index--; } } static string UnEscape(string s) { var sb = new StringBuilder(s.Length); for (int i = 0; i < s.Length; i++) { if (s[i] == '\\' && i + 1 < s.Length) i++; sb.Append(s[i]); } return sb.ToString(); } GenericInstSig ReadGenericInstanceType(out List genericArgs) { var git = new GenericInstSig(ReadTypeSig() as ClassOrValueTypeSig); int numArgs = ReadVariableLengthInt32(); genericArgs = new List(numArgs); for (int i = 0; i < numArgs; i++) genericArgs.Add(ReadVariableLengthInt32()); return git; } PtrSig ReadPointerType() { return new PtrSig(ReadTypeSig()); } TypeSig ReadArrayType() { var typeSig = ReadTypeSig(); int rank = ReadVariableLengthInt32(); if (rank == 1) return new SZArraySig(typeSig); return new ArraySig(typeSig, rank); } ByRefSig ReadByRefType() { return new ByRefSig(ReadTypeSig()); } public uint ReadVariableLengthUInt32() { uint val; reader.ReadCompressedUInt32(out val); return val; } public int ReadVariableLengthInt32() { uint val; reader.ReadCompressedUInt32(out val); return (int)val; } int GetMetadataOffset() { reader.Position = reader.Length - 4; for (int i = 0; i < 30; i++) { if (reader.ReadInt32() == METADATA_SIG) return (int)reader.Position - 4; reader.Position -= 8; } return -1; } } }