private void InitEntryType() { var c = peFile.ClrHeader.EntryPointToken; var entryPoint = c & 0xFF; if (entryPoint == 0) { //No entry point entryType = null; entryMethod = null; return; } foreach (var item in Types) { foreach (var m2 in item.Methods) { if (m2.BackendTabel == peFile.Tabels.MethodTabel[(int)entryPoint - 1]) { entryType = m2.Parent; entryMethod = m2; break; } } } }
public IlDecompiler(DotNetMethod method) { if (method == null) { throw new ArgumentException("method"); } m = method; mainFile = m.File; code = m.GetBody(); AddReference(m.Parent.File); }
/// <summary> /// Calls the entrypoint in the file. /// </summary> public void Start() { DotNetMethod m = null; foreach (var item in file.Types) { foreach (var m2 in item.Methods) { if (m2.IsStatic && m2.Name == "Main") { m = m2; break; } } } if (m == null) { throw new System.Exception("Invaild .NET EXE: Entry Point not found!"); } ProcessMethod(m, "Arg 1", "Arg 2"); }
public ILInstruction GetInstructionAtOffset(int Offset, int relPostion) { byte opCodeb = code[Offset]; var opCode = OpCodes.SingleOpCodes[opCodeb]; int size = 0; if (opCodeb == 0xFE) { opCodeb = code[Offset + 1]; opCode = OpCodes.MultiOpCodes[opCodeb]; Offset++; size++; } if (opCode == null) { Console.WriteLine("ILDecompiler failed decompilation."); opCode = OpCodes.SingleOpCodes[0]; } ILInstruction ret = new ILInstruction() { OpCode = opCode.Value, OpCodeName = opCode.Name, OperandType = opCode.OpCodeOperandType, Position = Offset, RelPosition = relPostion, Size = size }; if (relPostion == -1) { var arr = Decompile(); foreach (var item in arr) { if (item.Position == Offset) { return(item); } } throw new Exception("Target instruction not found!"); } //TODO: Implment the rest of these switch (opCode.OpCodeOperandType) { case OpCodeOperandType.InlineNone: { return(ret); } case OpCodeOperandType.InlinePhi: //Never should be used throw new InvalidOperationException(); case OpCodeOperandType.InlineTok: { byte fi = code[Offset + 1]; byte s2 = code[Offset + 2]; byte t = code[Offset + 3]; byte Tabel = code[Offset + 4]; byte[] num2 = new byte[] { fi, s2, t, 0 }; var numb2 = BitConverter.ToInt32(num2, 0); var tabel2 = BitConverter.ToInt32(code, Offset + 1); var tabel = tabel2 >> 24; var info = new FieldInfo(); info.IndexInTabel = numb2; //TODO: fix this as the below if statement maybe incorrect if (Tabel == 1) { // type ref var typeRef = mainFile.Backend.Tabels.TypeRefTabel[numb2 - 1]; info.IsInFieldTabel = false; info.Name = mainFile.Backend.ClrStringsStream.GetByOffset(typeRef.TypeName); info.Namespace = mainFile.Backend.ClrStringsStream.GetByOffset(typeRef.TypeNamespace); } else if (Tabel == 2) { //type def var typeRef = mainFile.Backend.Tabels.TypeDefTabel[numb2 - 1]; info.IsInFieldTabel = true; info.Name = mainFile.Backend.ClrStringsStream.GetByOffset(typeRef.Name); info.Namespace = mainFile.Backend.ClrStringsStream.GetByOffset(typeRef.Namespace); } else if (Tabel == 0x1B) { throw new NotImplementedException(); } else { throw new NotImplementedException(); } ret.Size += 4; ret.Operand = info; return(ret); } //8 bit int operand case OpCodeOperandType.ShortInlineVar: { byte fi = code[Offset + 1]; ret.Size += 1; ret.Operand = fi; return(ret); } case OpCodeOperandType.ShortInlineBrTarget: { sbyte fi = (sbyte)code[Offset + 1]; ret.Size += 1; ret.Operand = fi + 1; return(ret); } case OpCodeOperandType.ShortInlineI: { byte fi = code[Offset + 1]; ret.Size += 1; ret.Operand = fi; return(ret); } // 16 bit int case OpCodeOperandType.InlineVar: throw new NotImplementedException(); // 32 bit int case OpCodeOperandType.InlineI: { var numb2 = BitConverter.ToInt32(code, Offset + 1); ret.Size += 4; ret.Operand = numb2; return(ret); } case OpCodeOperandType.InlineBrTarget: { var numb2 = BitConverter.ToInt32(code, Offset + 1); ret.Size += 4; ret.Operand = numb2 + 4; //add the size return(ret); } case OpCodeOperandType.InlineField: { byte f = code[Offset + 4]; var numb2 = BitConverter.ToUInt16(code, Offset + 1); ret.Size += 4; if (f == 4) { //Inside of field table var c = mainFile.Backend.Tabels.FieldTabel[numb2 - 1]; var ret2 = new FieldInfo() { Name = mainFile.Backend.ClrStringsStream.GetByOffset(c.Name), IsInFieldTabel = true, IndexInTabel = numb2 }; ret.Operand = ret2; } else if (f == 10) { //Inside of MemberRef table var c = mainFile.Backend.Tabels.MemberRefTabelRow[numb2 - 1]; var ret2 = new FieldInfo() { Name = mainFile.Backend.ClrStringsStream.GetByOffset(c.Name), IsInFieldTabel = true, IndexInTabel = numb2 }; DecodeMemberRefParent(c.Class, out MemberRefParentType type, out uint row2); if (type == MemberRefParentType.TypeSpec) { //Method spec var tt = mainFile.Backend.Tabels.TypeSpecTabel[(int)(row2 - 1)]; //See https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/corelementtype-enumeration for more info var b = mainFile.Backend.BlobStream[tt.Signature]; var bi = new BinaryReader(new MemoryStream(mainFile.Backend.BlobStream)); bi.BaseStream.Position = tt.Signature; var a = bi.ReadByte(); //Normally 0x5 var type3 = bi.ReadByte(); //Normally 0x15 (ELEMENT_TYPE_GENERICINST) var n = ParseNumber(bi); // var n2 = ParseNumber(bi); var bb = bi.ReadByte(); //PTR_END var aa = bi.ReadByte(); //depends on the generic type. uint type2; uint index; DecodeTypeDefOrRef((uint)n2, out type2, out index); uint Namespace = 0; uint classs = 0; if (type2 == 0) { //Type def var Realtabel = mainFile.Backend.Tabels.TypeDefTabel[(int)(index - 1)]; Namespace = Realtabel.Namespace; classs = Realtabel.Name; } else if (type2 == 1) { //Type ref var Realtabel = mainFile.Backend.Tabels.TypeRefTabel[(int)(index - 1)]; Namespace = Realtabel.TypeNamespace; classs = Realtabel.TypeName; } else if (type2 == 2) { //Type spec???? What throw new NotImplementedException(); } else { throw new NotImplementedException("Invaild type"); } ret2.Namespace = mainFile.Backend.ClrStringsStream.GetByOffset(Namespace); ret2.Class = mainFile.Backend.ClrStringsStream.GetByOffset(classs); ret.Operand = ret2; } else if (type == MemberRefParentType.TypeRef) { var tt = mainFile.Backend.Tabels.TypeDefTabel[(int)(row2 - 1)]; ret2.Name = mainFile.Backend.ClrStringsStream.GetByOffset(tt.Name); ret2.Namespace = mainFile.Backend.ClrStringsStream.GetByOffset(tt.Namespace); } else { throw new NotImplementedException(); } } else { throw new NotImplementedException(); } return(ret); } case OpCodeOperandType.InlineMethod: { byte fi = code[Offset + 1]; byte s2 = code[Offset + 2]; byte t = code[Offset + 3]; byte f = code[Offset + 4]; //Method type. 6=Method,10=MemberRef byte[] num2 = new byte[] { fi, s2, t, 0 }; var numb2 = BitConverter.ToInt32(num2, 0); //Method Token if (f == 10) //MemberRef { //Get the method that we are calling var c = mainFile.Backend.Tabels.MemberRefTabelRow[numb2 - 1]; #region Decode //Decode the class bytes DecodeMemberRefParent(c.Class, out MemberRefParentType tabel, out uint row); var funcName = mainFile.Backend.ClrStringsStream.GetByOffset(c.Name); uint classs = 0; uint Namespace = 0; string genericArgType = ""; //TYPE def if (tabel == MemberRefParentType.TypeDef) { var tt = mainFile.Backend.Tabels.TypeDefTabel[(int)row - 1]; classs = tt.Name; Namespace = tt.Namespace; } //Type REF else if (tabel == MemberRefParentType.TypeRef) { var tt = mainFile.Backend.Tabels.TypeRefTabel[(int)row - 1]; classs = tt.TypeName; Namespace = tt.TypeNamespace; } //Module Ref else if (tabel == MemberRefParentType.ModuleRef) { var tt = mainFile.Backend.Tabels.ModuleRefTabel[(int)row - 1]; classs = tt.Name; Namespace = tt.Name; } //Type Spec else if (tabel == MemberRefParentType.TypeSpec) { //See https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/corelementtype-enumeration for more info var tt = mainFile.Backend.Tabels.TypeSpecTabel[(int)row - 1]; var b = mainFile.Backend.BlobStream[tt.Signature]; var bi = new BinaryReader(new MemoryStream(mainFile.Backend.BlobStream)); bi.BaseStream.Position = tt.Signature; var a = bi.ReadByte(); //Normally 0x5 var type = bi.ReadByte(); //Normally 0x15 (ELEMENT_TYPE_GENERICINST) var n = ParseNumber(bi); // var n2 = ParseNumber(bi); var bb = bi.ReadByte(); //PTR_END var aa = bi.ReadByte(); //depends on the generic type. uint type2; uint index; DecodeTypeDefOrRef((uint)n2, out type2, out index); if (type2 == 0) { //Type def var Realtabel = mainFile.Backend.Tabels.TypeDefTabel[(int)(index - 1)]; Namespace = Realtabel.Namespace; classs = Realtabel.Name; } else if (type2 == 1) { //Type ref var Realtabel = mainFile.Backend.Tabels.TypeRefTabel[(int)(index - 1)]; Namespace = Realtabel.TypeNamespace; classs = Realtabel.TypeName; } else if (type2 == 2) { //Type spec???? What throw new NotImplementedException(); } else { throw new NotImplementedException("Invaild type"); } if (aa == 0xE) { //String genericArgType = "string"; } else if (aa == 0x13) { genericArgType = "var"; } else { //TODO: implement the rest of these genericArgType = "Todo"; //throw new NotImplementedException(); } } //Unknown else { classs = 0; Namespace = 0; throw new NotImplementedException(); } #endregion var anamespace = mainFile.Backend.ClrStringsStream.GetByOffset(Namespace); var typeName = mainFile.Backend.ClrStringsStream.GetByOffset(classs); //Now, resolve this method //TODO: Resolve the method properly by first //1) resolve the type of the method //2) search for the method in the type //3) get method RVA //For now, resolve it by name var sig = DotNetMethod.ParseMethodSignature(c.Signature, mainFile, funcName).Signature; DotNetMethod m = null; foreach (var type in ContextTypes) { foreach (var item in type.Types) { foreach (var meth in item.Methods) { if (meth.Name == funcName && meth.Parent.Name == typeName && meth.Parent.NameSpace == anamespace && meth.Signature == sig) { m = meth; } } } } uint rva = 0; if (m != null) { rva = m.RVA; } else { //Ignore if it is system object constructor if (anamespace == "System" && typeName == "Object" && funcName == ".ctor") { } else { // Console.WriteLine($"[ILDecompiler: WARN] Cannot resolve method RVA. Are you missing a call to AddReference()??. Method data: {anamespace}.{typeName}.{funcName}"); } } ret.Size += 4; ret.Operand = new InlineMethodOperandData() { NameSpace = anamespace, ClassName = typeName, FunctionName = funcName, RVA = rva, GenericArg = genericArgType, Signature = sig }; return(ret); } else if (f == 6) //method { //Get the method that we are calling var c = mainFile.Backend.Tabels.MethodTabel[numb2 - 1]; string name = mainFile.Backend.ClrStringsStream.GetByOffset(c.Name); //Now, resolve this method DotNetMethod m = null; foreach (var item in mainFile.Types) { foreach (var meth in item.Methods) { if (meth.RVA == c.RVA && meth.Name == name && meth.ParamListIndex == c.ParamList) { m = meth; } } } string className = "CannotFind"; string Namespace = "CannotFind"; if (m != null) { className = m.Parent.Name; Namespace = m.Parent.NameSpace; } ret.Size += 4; ret.Operand = new InlineMethodOperandData() { NameSpace = Namespace, ClassName = className, FunctionName = name, RVA = c.RVA, Signature = m.Signature, ParamListIndex = c.ParamList }; return(ret); } else if (f == 0x2B) { //Method spec var idx = mainFile.Backend.Tabels.MethodSpecTable[numb2 - 1]; uint fa; uint row; DecodeMethodDefOrRef(idx.Method, out fa, out row); if (fa == 0) { //Method var c = mainFile.Backend.Tabels.MethodTabel[(int)(row - 1)]; string name = mainFile.Backend.ClrStringsStream.GetByOffset(c.Name); //Now, resolve this method DotNetMethod m = null; foreach (var item in mainFile.Types) { foreach (var meth in item.Methods) { if (meth.RVA == c.RVA && meth.Name == name) { m = meth; } } } string className = "CannotFind"; string Namespace = "CannotFind"; if (m != null) { className = m.Parent.Name; Namespace = m.Parent.NameSpace; } ret.Size += 4; ret.Operand = new InlineMethodOperandData() { NameSpace = Namespace, ClassName = className, FunctionName = name, RVA = c.RVA, Signature = m.Signature, }; return(ret); } else if (fa == 1) { //MemberRef var c = mainFile.Backend.Tabels.MemberRefTabelRow[(int)(row - 1)]; #region Decode //Decode the class bytes DecodeMemberRefParent(c.Class, out MemberRefParentType tabel, out uint row2); var funcName = mainFile.Backend.ClrStringsStream.GetByOffset(c.Name); uint classs = 0; uint Namespace = 0; string genericArgType = ""; //TYPE def if (tabel == MemberRefParentType.TypeDef) { var tt = mainFile.Backend.Tabels.TypeDefTabel[(int)row2 - 1]; classs = tt.Name; Namespace = tt.Namespace; } //Type REF else if (tabel == MemberRefParentType.TypeRef) { var tt = mainFile.Backend.Tabels.TypeRefTabel[(int)row2 - 1]; classs = tt.TypeName; Namespace = tt.TypeNamespace; } //Module Ref else if (tabel == MemberRefParentType.ModuleRef) { var tt = mainFile.Backend.Tabels.ModuleRefTabel[(int)row2 - 1]; classs = tt.Name; Namespace = tt.Name; } //Type Spec else if (tabel == MemberRefParentType.TypeSpec) { //See https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/corelementtype-enumeration for more info var tt = mainFile.Backend.Tabels.TypeSpecTabel[(int)row2 - 1]; var b = mainFile.Backend.BlobStream[tt.Signature]; var bi = new BinaryReader(new MemoryStream(mainFile.Backend.BlobStream)); bi.BaseStream.Position = tt.Signature; var a = bi.ReadByte(); //Normally 0x5 var type = bi.ReadByte(); //Normally 0x15 (ELEMENT_TYPE_GENERICINST) var n = ParseNumber(bi); // var n2 = ParseNumber(bi); var bb = bi.ReadByte(); //PTR_END var aa = bi.ReadByte(); //depends on the generic type. uint type2; uint index; DecodeTypeDefOrRef((uint)n2, out type2, out index); if (type2 == 0) { //Type def var Realtabel = mainFile.Backend.Tabels.TypeDefTabel[(int)(index - 1)]; Namespace = Realtabel.Namespace; classs = Realtabel.Name; } else if (type2 == 1) { //Type ref var Realtabel = mainFile.Backend.Tabels.TypeRefTabel[(int)(index - 1)]; Namespace = Realtabel.TypeNamespace; classs = Realtabel.TypeName; } else if (type2 == 2) { //Type spec???? What throw new NotImplementedException(); } else { throw new NotImplementedException("Invaild type"); } if (aa == 0xE) { //String genericArgType = "string"; } else if (aa == 0x13) { genericArgType = "var"; } else { //TODO: implement the rest of these genericArgType = "Todo"; //throw new NotImplementedException(); } } //Unknown else { classs = 0; Namespace = 0; throw new NotImplementedException(); } #endregion var anamespace = mainFile.Backend.ClrStringsStream.GetByOffset(Namespace); var typeName = mainFile.Backend.ClrStringsStream.GetByOffset(classs); //Now, resolve this method //TODO: Resolve the method properly by first //1) resolve the type of the method //2) search for the method in the type //3) get method RVA //For now, resolve it by name DotNetMethod m = null; foreach (var type in ContextTypes) { foreach (var item in type.Types) { foreach (var meth in item.Methods) { if (meth.Name == funcName && meth.Parent.Name == typeName && meth.Parent.NameSpace == anamespace) { m = meth; } } } } uint rva = 0; if (m != null) { rva = m.RVA; } else { //Ignore if it is system object constructor if (anamespace == "System" && typeName == "Object" && funcName == ".ctor") { } else { Console.WriteLine($"[ILDecompiler: WARN] Cannot resolve method RVA. Are you missing a call to AddReference()??. Method data: {anamespace}.{typeName}.{funcName}"); } } ret.Size += 4; ret.Operand = new InlineMethodOperandData() { NameSpace = anamespace, ClassName = typeName, FunctionName = funcName, RVA = rva, GenericArg = genericArgType, Signature = DotNetMethod.ParseMethodSignature(c.Signature, mainFile, funcName).Signature }; return(ret); } else { throw new NotImplementedException(); } } else { throw new NotImplementedException(); } } case OpCodeOperandType.InlineSig: { var numb2 = BitConverter.ToInt32(code, Offset + 1); ret.Size += 4; ret.Operand = numb2; return(ret); } case OpCodeOperandType.InlineString: { byte first = code[Offset + 1]; //1st index byte sec = code[Offset + 2]; //2nd byte third = code[Offset + 3]; //3rd byte forth = code[Offset + 4]; //string type byte[] num = new byte[] { first, sec, third, 0 }; var numb = BitConverter.ToInt32(num, 0); //Get the string string s; if (forth != 112) { //Will this ever be in the String Stream? s = mainFile.Backend.ClrStringsStream.GetByOffset((uint)numb); } else { //US stream s = mainFile.Backend.ClrUsStream.GetByOffset((uint)numb); } ret.Size += 4; ret.Operand = s; return(ret); } case OpCodeOperandType.InlineSwitch: throw new NotImplementedException(); case OpCodeOperandType.ShortInlineR: { var numb2 = BitConverter.ToSingle(code, Offset + 1); ret.Size += 4; ret.Operand = numb2; return(ret); } case OpCodeOperandType.InlineType: { byte fi = code[Offset + 1]; ret.Size += 4; ret.Operand = fi; return(ret); } // 64 bit int case OpCodeOperandType.InlineI8: { var numb2 = BitConverter.ToInt64(code, Offset + 1); ret.Size += 8; ret.Operand = numb2; return(ret); } case OpCodeOperandType.InlineR: { var numb2 = BitConverter.ToDouble(code, Offset + 1); ret.Size += 8; ret.Operand = numb2; return(ret); } default: break; } return(null); }
/// <summary> /// Makes things much easier for the DoStartMethod() function /// </summary> /// <param name="m"></param> /// <param name="args"></param> // All of the Opcodes that have additional parameters have to be processed here. private void ProcessMethod(DotNetMethod m, params string[] args) { byte[] code = m.GetBody(); List <ILInstruction> inr = new List <ILInstruction>(); for (int i = 0; i < code.Length; i++) { byte opCode = code[i]; if (opCode == (byte)OpCodes.Ldstr) { //Decode the number byte first = code[i + 1]; //1st index byte sec = code[i + 2]; //2nd byte third = code[i + 3]; byte forth = code[i + 4]; byte[] num = new byte[] { first, sec, third, 0 }; var numb = BitConverter.ToInt32(num, 0); //Get the string string s; //Microsoft does really good documentation on front-end things. For example: Windows Apis, and the dot net framework. //But, They don't do backend documentation, for example: Decoding this string token. //I have to go through 100,000+ lines of code in the .NET Clr to figure out how these string tokens work and still didnt figure it out. if (forth != 112) { //Will this ever be in the String Stream? s = file.Backend.ClrStringsStream.GetByOffset((uint)numb); } else { //US stream //This is only a temp. hack s = file.Backend.ClrUsStream.GetByOffset((uint)numb); } int rid = numb & 0x00ffffff; //Not sure weather this is needed, but I found it in the CLR i += 4; //skip past the string inr.Add(new ILInstruction() { OpCode = OpCodes.Ldstr, Operand = s }); } else if (opCode == OpCodes.Call) { try { byte fi = code[i + 1]; byte s = code[i + 2]; byte t = code[i + 3]; byte f = code[i + 4]; byte[] num = new byte[] { fi, s, t, f }; short numb = BitConverter.ToInt16(num, 0); //Method Token //Get the method that we are calling var c = file.Backend.tabels.MemberRefTabelRow[numb - 1]; //is the -1 needed? i += 4; //skip past the string #region Decode //Decode the class bytes uint tabel; uint row; DecodeMemberRefParent(c.Class, out tabel, out row); var funcName = file.Backend.ClrStringsStream.GetByOffset(c.Name); string classs; string Namespace; //TYPE def if (tabel == 02) { var tt = file.Backend.tabels.TypeDefTabel[(int)row - 1]; classs = file.Backend.ClrStringsStream.GetByOffset(tt.Name); Namespace = file.Backend.ClrStringsStream.GetByOffset(tt.Namespace); } //Type REF else if (tabel == 01) { var tt = file.Backend.tabels.TypeRefTabel[(int)row - 1]; classs = file.Backend.ClrStringsStream.GetByOffset(tt.TypeName); Namespace = file.Backend.ClrStringsStream.GetByOffset(tt.TypeNamespace); } //Module Ref else if (tabel == 26) { //var tt = file.Backend.MetaDataStreamTablesHeader.Tables.ModuleRef[(int)row - 1]; //classs = file.Backend.ClrStringsStream.GetByOffset(tt.Name); //Namespace = file.Backend.ClrStringsStream.GetByOffset(tt.Namespace); Console.WriteLine("Module Ref not supported!"); classs = "<Module Ref>"; Namespace = "<Module Ref>"; } //Unknown else { classs = "<unknown>"; Namespace = "<unknown>"; } #endregion var inst = new ILInstruction() { OpCode = OpCodes.Call, DecompilerExtraData = numb }; inst.Operand = new CallMethodDataHolder() { ClassName = classs, NameSpace = Namespace, FunctionName = funcName }; inr.Add(inst); } catch { } } else { inr.Add(new ILInstruction() { OpCode = opCode }); } } //After decoding, start the method DoStartMethod(inr, args); }