Giter Site home page Giter Site logo

msdn-whiteknight / ciltools Goto Github PK

View Code? Open in Web Editor NEW
22.0 5.0 3.0 7.52 MB

A set of tools to work with CIL in .NET applications

License: BSD 3-Clause "New" or "Revised" License

C# 98.82% Batchfile 0.09% PowerShell 0.33% Visual Basic .NET 0.04% Shell 0.01% HTML 0.72%
csharp library dotnet cil msil parser bytecode csharp-library analysis

ciltools's People

Contributors

dependabot[bot] avatar msdn-whiteknight avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ciltools's Issues

Escape identifiers with forbidden characters

From ECMA 335 II.5 General syntax:

ID is a contiguous string of characters which starts with either an alphabetic character (A–Z, a–z) or
one of “_”, “$”, “@”, “`” (grave accent), or “?”, and is followed by any number of alphanumeric
characters (A–Z, a–z, 0–9) or the characters “_”, “$”, “@”, “`” (grave accent), and “?”.

...

Identifiers are used to name entities. Simple identifiers are equivalent to an ID. However, the ILAsm
syntax allows the use of any identifier that can be formed using the Unicode character set (see
Partition I). To achieve this, an identifier shall be placed within single quotation marks.

Example C++/CLI program:

using namespace System;

public class A {
    int _x;
    public: A(int x) { this->_x = x; }
};


int main(array<System::String ^> ^args)
{
    A a(1);
	Console::ReadKey();
    return 0;
}

Expected disassembler output:

.method assembly static valuetype A* modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall)
        'A.{ctor}'(valuetype A* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst) A_0,
                   int32 x) cil managed

Current output:

.method  assembly static valuetype [CliTest]A* modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) A.{ctor}(
    valuetype [CliTest]A* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst) par0,
    int32 x
) cil managed

Support .pack and .size directives

ECMA 335 II.10.2 - Body of a type definition

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TestStruct
{
    public int dummy;
}

Expected disassembly:

.class public sequential ansi sealed beforefieldinit CilTools.Tests.Common.TestStruct
       extends [mscorlib]System.ValueType
{
  .pack 1
  .size 0

  //...
}

Support generic constraints

using System;
using System.Collections.Generic;
using System.Text;
using CilTools.BytecodeAnalysis;

namespace CilToolsTest
{
    class Program
    {
        public static T Sum<T>(T x,T y) where T:struct
        {
            if (typeof(T) == typeof(int))
            {
                int res = (Convert.ToInt32(x) + Convert.ToInt32(y));
                return (T)Convert.ChangeType(res, typeof(int));
            }
            else if (typeof(T) == typeof(float))
            {
                float res = (Convert.ToSingle(x) + Convert.ToSingle(y));
                return (T)Convert.ChangeType(res, typeof(int));
            }
            else throw new NotSupportedException();
        }

        static void Main(string[] args)
        {
            Console.WriteLine(CilAnalysis.MethodToText(typeof(Program).GetMethod("Sum")));
            Console.ReadKey();
        }
    }
}

Current output:

.method   public hidebysig static !!T Sum<T>(
    !!T x,
    !!T y
) cil managed
// ...

Expected output:

.method public hidebysig static !!T  Sum<valuetype .ctor ([mscorlib]System.ValueType) T>(
     !!T x,
     !!T y
) cil managed
// ...

Improvements for "Show source"

  • Support optional parameters in signature decompiler
  • Support source link (browser navigation)
  • Support source link (download)
  • Correctly handle the last sequence point for C++/CLI symbols
  • Don't reload all method's sequence points each time user navigates to previous/next point
  • Support decompiling signatures for internalcall or P/Invoke methods
  • Syntax highlighting for CIL instructions (in left column)
  • Syntax highlighting for displayed source code
  • Correctly handle release builds in "Show source (method)" (add {} when they are not included in sequence points)
  • Produce proper tokens for generic types, references and pointers in signature decompiler
  • Support function pointers in signature decompiler
  • MethodSourceViewWindow: button to show whole file with method highlighted
  • Support generic constraints
  • Support embedded sources in Portable PDB
  • Support state machine methods (connect with MoveNext method via StateMachineMethod Table)

Support .vtfixup directives in assembly manifest

C++/CLI assemblies contain VTable fixups declared like this:

.vtfixup [1] int32 retainappdomain at D_0000E000 // 06000001
.vtfixup [1] int32 retainappdomain at D_0000E00C // 06000029
.vtfixup [1] int32 retainappdomain at D_0000E010 // 06000004
.vtfixup [1] int32 retainappdomain at D_0000E014 // 06000005
.vtfixup [1] int32 retainappdomain at D_0000E018 // 06000006

ECMA-335 II.15.5.1 - Method transition thunks

Related to #69

Infinite recursion in MethodRef.LoadImpl

MethodRef.LoadImpl goes into infinite recursion when signatures being compared involve generic parameters

 	mscorlib.dll!System.Text.StringBuilder.StringBuilder(string value, int startIndex, int length, int capacity)	
 	mscorlib.dll!System.Text.StringBuilder.StringBuilder(int capacity)	
>	CilTools.Metadata.dll!CilTools.Metadata.TypeRef.FullName.get()Строка 97	C#
 	CilTools.Metadata.dll!CilTools.Metadata.AssemblyReader.LoadType(System.Type t)Строка 256	C#
 	CilTools.Metadata.dll!CilTools.Metadata.MethodRef.LoadImpl()Строка 102	C#
 	CilTools.Metadata.dll!CilTools.Metadata.MethodRef.IsGenericMethod.get()Строка 434	C#
 	CilTools.BytecodeAnalysis.dll!CilTools.Reflection.GenericContext.TryGetGenericArguments(System.Reflection.MemberInfo member)Строка 102	C#
 	CilTools.BytecodeAnalysis.dll!CilTools.Reflection.GenericParamType.TryLoadName()Строка 47	C#
 	CilTools.BytecodeAnalysis.dll!CilTools.Reflection.GenericParamType.Name.get()Строка 319	C#
 	CilTools.BytecodeAnalysis.dll!CilTools.Reflection.ComplexType.Name.get()Строка 377	C#
 	CilTools.BytecodeAnalysis.dll!CilTools.BytecodeAnalysis.TypeSpec.Name.get()Строка 517	C#
 	CilTools.Metadata.dll!CilTools.Metadata.MethodRef.LoadImpl()Строка 160	C#

GetTypeFromHandleUnsafe method not found on .NET Core 3.1+ Linux

Test_CilGraph_DynamicMethod crashes on .NET Core 3.1+ Linux

Error:

System.MissingMethodException: GetTypeFromHandleUnsafe is not found
at CilTools.Reflection.ModuleWrapperDynamic.ResolveMember(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) in /home/runner/work/CilTools/CilTools/CilTools.BytecodeAnalysis/Reflection/ModuleWrapperDynamic.cs:line 278
at CilTools.BytecodeAnalysis.CilInstruction.ResolveMemberToken(Int32 token) in /home/runner/work/CilTools/CilTools/CilTools.BytecodeAnalysis/CilInstruction.cs:line 248
at CilTools.BytecodeAnalysis.CilTokenInstruction.get_ReferencedMember() in /home/runner/work/CilTools/CilTools/CilTools.BytecodeAnalysis/CilTokenInstruction.cs:line 39

Improve target framework detection

Followup to 13f1256

  • For .NET Framework versions different from current, use the target version path instead of current
  • For new .NET Standard versions that does not support .NET Framework, use .NET Core instead

Support .override directive

ECMA-335 II.10.3.2

using System;
using System.Reflection;
using CilTools.BytecodeAnalysis;

class Program
{
    static void Main(string[] args)
    {        
        CilGraph graph = CilGraph.Create(typeof(Derived).GetMethod(
            "IBase.PrintName", 
            BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance
            ));
        Console.WriteLine(graph.ToText());
        Console.ReadKey();
    }
}

public interface IBase
{
    void PrintName();
}

public class Derived:IBase
{
    void IBase.PrintName()
    {
        Console.WriteLine("Derived");
    }
}

Current output:

.method   private hidebysig newslot virtual final instance void IBase.PrintName() cil managed
{
 .maxstack   8

          nop
          ldstr       "Derived"
          call        void [mscorlib]System.Console::WriteLine(string)
          nop
          ret
}

Expected output:

.method private hidebysig newslot virtual final 
        instance void  IBase.PrintName() cil managed
{
  .override IBase::PrintName
  .maxstack   8

          nop
          ldstr       "Derived"
          call        void [mscorlib]System.Console::WriteLine(string)
          nop
          ret
}

NullReferenceException on constructor in C++ assembly

System.NullReferenceException: Ссылка на объект не указывает на экземпляр объекта.
в System.Reflection.Metadata.Parameter.GetCustomAttributes()
в CilTools.Metadata.ParameterSpec.GetCustomAttributes(Boolean inherit)
в CilTools.Syntax.SyntaxNode.GetDefaultsSyntax(MethodBase m, Int32 startIndent)
в CilTools.BytecodeAnalysis.CilGraph.ToSyntaxTreeImpl(DisassemblerParams pars, Int32 startIndent)
в CilTools.BytecodeAnalysis.CilGraph.ToSyntaxTree(DisassemblerParams pars)
в CilView.UI.Controls.CilBrowserPage..ctor(MethodBase m, Int32 start, Int32 end, RoutedEventHandler navigation) в D:\VADIM\repos\CilTools\CilView\UI.Controls\CilBrowserPage.xaml.cs:строка 31
в CilView.UI.Controls.CilBrowser.NavigateToMethod(MethodBase mb, Int32 start, Int32 end) в D:\VADIM\repos\CilTools\CilView\UI.Controls\CilBrowser.xaml.cs:строка 124
в CilView.UI.Controls.CilBrowser.Navigated(Object sender, RoutedEventArgs e) в D:\VADIM\repos\CilTools\CilView\UI.Controls\CilBrowser.xaml.cs:строка 109
Module: System.Reflection.Metadata
Method: System.Reflection.Metadata.CustomAttributeHandleCollection GetCustomAttributes()

(Introduced after v2.4)

Escape IL assembler keywords when used as identifiers

CLI specification defines a set of keywords that must be escaped (enclosed into single or double quotes) when used as identifiers; for example: field, assembly. Methods that convert instructions/method signatures to text should be updated to escape them.

Example code:

using System;
using System.Reflection;
using CilBytecodeParser;

class Program
{
    public static void field()
    {
        Console.WriteLine("In the method 'field'...");
    }

    public static void Main(string[] args)
    {
        Console.WriteLine(CilAnalysis.MethodToText(typeof(Program).GetMethod("field")));        
        Console.ReadKey();
    }    
}

Current output:

 .method public hidebysig static void field() cil managed {
 .maxstack 8

          nop
          ldstr      "In the method 'field'..."
          call       void [mscorlib]System.Console::WriteLine(string)
          nop
          ret
}

Expected output:

 .method public hidebysig static void 'field'() cil managed {
 .maxstack 8

          nop
          ldstr      "In the method 'field'..."
          call       void [mscorlib]System.Console::WriteLine(string)
          nop
          ret
}

Ths list of keywords is defined in ECMA-335 Specification, section VI.C.1.

Don't escape math symbols in string literals

https://github.com/MSDN-WhiteKnight/CilTools/blob/master/CilTools.BytecodeAnalysis/CilAnalysis.cs#L333

using System;
using System.Collections.Generic;
using System.Text;
using CilTools.BytecodeAnalysis;
using CilTools.BytecodeAnalysis.Extensions;
using CilTools.Syntax;

namespace NetCoreTest
{
    class Program
    {
        public static void Foo(int x, int y)
        {
            if (x > y) Console.WriteLine("x>y");
            else if (x == y) Console.WriteLine("x=y");
            else Console.WriteLine("x<y");
        }

        public static void Main(string[] args)
        {
            CilGraph graph = typeof(Program).GetMethod("Foo").GetCilGraph();
            MethodDefSyntax syntax = graph.ToSyntaxTree();
            Console.WriteLine(syntax.ToString());
            Console.ReadKey();
        }
    }
}

Current output:

ldstr        "x\076y"

Expected:

ldstr      "x>y"

Mark entry point with .entrypoint directive

When outputting signature of entry point method as CIL assembler code, it should be marked with .entrypoint directive.

Example code:

using System;
using System.Reflection;
using CilBytecodeParser;

class Program
{    
    public static void Main(string[] args)
    {
        Console.WriteLine(CilAnalysis.MethodToText(MethodBase.GetCurrentMethod()));
        Console.ReadKey();
    }    
}

Current output:

.method public hidebysig static void Main(
    string[] args
) cil managed {
 .maxstack 8

          nop
          call       class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetCurrentMethod()
          call       string [CilBytecodeParser]CilBytecodeParser.CilAnalysis::MethodToText(class [mscorlib]System.Reflection.MethodBase)
          call       void [mscorlib]System.Console::WriteLine(string)
          nop
          call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
          pop
          ret
}

Expected output:

.method public hidebysig static void Main(
    string[] args
) cil managed {
 .entrypoint
 .maxstack 8

          nop
          call       class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetCurrentMethod()
          call       string [CilBytecodeParser]CilBytecodeParser.CilAnalysis::MethodToText(class [mscorlib]System.Reflection.MethodBase)
          call       void [mscorlib]System.Console::WriteLine(string)
          nop
          call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
          pop
          ret
}

The Assembly.EntryPoint property can be used to determine what method is an entry point in the given assembly.

Relevant ECMA-335 Specification section: II.15.4.1.2 The .entrypoint directive.

Support RVA fields in type disassembler

ECMA-335 II.16.3.1 - Data declaration

Current:

.field static assembly initonly int64 '8F1A52EA427069AA8B6FC1D0A7BC426B2160A963E2DBFA59CE3E254462981A0B'

Expected:

.field static assembly initonly int64 '8F1A52EA427069AA8B6FC1D0A7BC426B2160A963E2DBFA59CE3E254462981A0B' at I_0000DC30
// ...
.data cil I_0000DC30 = bytearray (
             20 00 09 00 0D 00 0A 00)

FieldDefinition.GetRelativeVirtualAddress

CIL View features

  • Open memory dumps (.dmp)
  • Open file (assembly/project) with dependencies
  • Open nuget package

Reference to generic method parameter is handled incorrectly

mscorlib / System.Threading.Interlocked / CompareExchange

Expected:

.method public hidebysig static !!T  CompareExchange<class T>(!!T& location1,
                                                          !!T 'value',
                                                          !!T comparand) cil managed

Actual

.method  public hidebysig static !!0 CompareExchange<T>(
    !0 location1, 
    !!0 'value', 
    !!0 comparand
) cil managed

New tests plan

  • Syntax API: verify that all nodes have parent node
  • CilTools.Runtime tests (.NET Framework)
  • CilTools.Runtime tests (.NET core)
  • CilTools.Runtime tests: add integration tests that read memory images and inspect them via CilTools.Metadata
  • Create test framework to avoid test projects duplication between standard reflection and CilTools.Metadata
  • Long-running tests that verify CilTools.BytecodeAnalysis handles methods without crashes on large number of inputs
  • Long-running tests that verify CilTools.Metadata returns results matching standard reflection on large number of inputs
  • Test for .entrypoint directive using standard reflection as bytecode source
  • Tests for TypeRef (GetMethods and others)
  • TypeDef.GetInterfaceMap - tests where interface type is not RuntimeType
  • Print execution time in CilTools.Runtime tests

SyntaxGenerator.GetPropertyMethod causes external assemblies resolution

SyntaxGenerator.GetPropertyMethod runs into TypeLoadException when property is on a type derived from external assembly which cannot be resolved.

>	CilTools.Metadata.dll!CilTools.Metadata.AssemblyReader.LoadType(System.Type t)Строка 277	C#
 	CilTools.Metadata.dll!CilTools.Metadata.TypeRef.LoadImpl()Строка 42	C#
 	CilTools.Metadata.dll!CilTools.Metadata.TypeRef.GetMembers(System.Reflection.BindingFlags bindingAttr)Строка 193	C#
 	CilTools.Metadata.dll!CilTools.Metadata.TypeDef.GetMembers(System.Reflection.BindingFlags bindingAttr)Строка 482	C#
 	CilTools.Metadata.dll!CilTools.Metadata.TypeDef.GetMember(string name, System.Reflection.BindingFlags bindingAttr)Строка 406	C#
 	CilTools.BytecodeAnalysis.dll!CilTools.Syntax.Generation.SyntaxGenerator.GetPropertyMethod(System.Reflection.PropertyInfo p, string accName)Строка 458	C#
 	CilTools.BytecodeAnalysis.dll!CilTools.Syntax.Generation.SyntaxGenerator.GetTypeDefSyntaxImpl(System.Type t, bool full, CilTools.Syntax.DisassemblerParams disassemblerParams, int startIndent)Строка 1061	C#
 	System.Linq.dll!System.Collections.Generic.LargeArrayBuilder<CilTools.Syntax.SyntaxNode>.AddRange(System.Collections.Generic.IEnumerable<CilTools.Syntax.SyntaxNode> items)	Нет данных
 	System.Linq.dll!System.Collections.Generic.EnumerableHelpers.ToArray<CilTools.Syntax.SyntaxNode>(System.Collections.Generic.IEnumerable<CilTools.Syntax.SyntaxNode> source)	Нет данных
 	System.Linq.dll!System.Linq.Enumerable.ToArray<CilTools.Syntax.SyntaxNode>(System.Collections.Generic.IEnumerable<CilTools.Syntax.SyntaxNode> source)	Нет данных
 	CilBrowser.Core.dll!CilBrowser.Core.HtmlGenerator.VisualizeType(System.Type t, System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<System.Type>> typeMap)Строка 382	C#
 	CilBrowser.Core.dll!CilBrowser.Core.WebsiteGenerator.GenerateFromAssembly(System.Reflection.Assembly ass, string nsFilter, string outputPath, string customFooter)Строка 105	C#
 	CilBrowser.dll!CilBrowser.Program.GenerateDemo()Строка 33	C#
 	CilBrowser.dll!CilBrowser.Program.Main(string[] args)Строка 98	C#

MethodDefTests.Test_GetBaseDefinition_FromObject does not work on .NET Core

Test method CilTools.Metadata.Tests.MethodDefTests.Test_GetBaseDefinition_FromObject threw exception: 
System.NotImplementedException: The method or operation is not implemented.

 TypeRef.GetMethodImpl(String name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) строка 188
 Type.GetMethod(String name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
 MdMethodInfoBase.GetBaseDefinition() строка 107
 MethodDefTests.Test_GetBaseDefinition_FromObject() строка 284

Support parameter and return value custom attributes

using System;
using System.Collections.Generic;
using System.Text;
using CilTools.BytecodeAnalysis;

namespace CilToolsTest
{
    class MyParamAttribute : Attribute { public MyParamAttribute(int n) { } }    
    class MyReturnAttribute : Attribute { }

    class Program
    { 
        [return: MyReturn]
        public static int Sum([MyParam(1)]int x, [MyParam(2)] int y) 
        {
            return x + y;
        }

        static void Main(string[] args)
        {
            Console.WriteLine(CilAnalysis.MethodToText(typeof(Program).GetMethod("Sum")));
            Console.ReadKey();
        }
    }
}

Current output:

.method   public hidebysig static int32 Sum(
    int32 x,
    int32 y
) cil managed
{
 //...
}

Expected output:

.method public hidebysig static int32  Sum(int32 x,
                                           int32 y) cil managed
{
  .param [0]
  .custom instance void CilToolsTest.MyReturnAttribute::.ctor() = ( 01 00 00 00 ) 
  .param [1]
  .custom instance void CilToolsTest.MyParamAttribute::.ctor(int32) = ( 01 00 01 00 00 00 00 00 ) 
  .param [2]
  .custom instance void CilToolsTest.MyParamAttribute::.ctor(int32) = ( 01 00 02 00 00 00 00 00 ) 
  
  //...
} 

TypeLoadException when disassembling property on generic type

System.TypeLoadException: Failed to resolve type System.Runtime.ConstrainedExecution.CriticalFinalizerObject, System.Runtime, Version=4.2.2.0, PublicKeyToken=b03f5f7f11d50a3a
   в CilTools.Metadata.AssemblyReader.LoadType(Type t)
   в CilTools.Metadata.TypeRef.LoadImpl()
   в CilTools.Metadata.TypeRef.get_BaseType()
   в System.Type.IsSubclassOf(Type c)
   в System.Type.IsValueTypeImpl()
   в System.Type.get_IsValueType()
   в CilTools.BytecodeAnalysis.CilAnalysis.<GetTypeSyntax>d__4.MoveNext()
   в CilTools.BytecodeAnalysis.CilAnalysis.GetMethodRefSyntax(MethodBase m, Boolean inlineTok)
   в CilTools.Syntax.SyntaxNode.<GetTypeDefSyntaxImpl>d__24.MoveNext()
   в CilView.Core.Syntax.SyntaxWriter.<WriteSyntaxAsync>d__1.MoveNext()
--- Конец трассировка стека из предыдущего расположения, где возникло исключение ---
   в System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   в System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   в CilView.Core.Syntax.SyntaxWriter.<DisassembleAsync>d__5.MoveNext()
--- Конец трассировка стека из предыдущего расположения, где возникло исключение ---
   в System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   в System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   в CilView.MainWindow.<miExportAssembly_Click>d__23.MoveNext() в D:\VADIM\repos\CilTools\CilView\MainWindow.xaml.cs:строка 595
Module: CilTools.Metadata
Method: System.Type LoadType(System.Type)

Speed up ClrAssemblyReader.ReadMemoryIgnoreErrors

ReadMemoryIgnoreErrors can be speeded up by scanning valid memory regions via VirtualQueryEx and reading them as whole, instead of by pages. Example:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;


namespace ConsoleApp1
{
    class Program
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
        static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);

        [Flags]
        enum LoadLibraryFlags : uint
        {
            None = 0,
            DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
            LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
            LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
            LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
            LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
            LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
            LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
            LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,
            LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
            LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,
            LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

        [StructLayout(LayoutKind.Sequential)]
        public struct MODULEINFO
        {
            public IntPtr lpBaseOfDll;
            public uint SizeOfImage;
            public IntPtr EntryPoint;
        }

        [DllImport("psapi.dll", SetLastError = true)]
        static extern bool GetModuleInformation(IntPtr hProcess, IntPtr hModule, out MODULEINFO lpmodinfo, uint cb);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool ReadProcessMemory(
    IntPtr hProcess,
    IntPtr lpBaseAddress,
    [Out] byte[] lpBuffer,
    int dwSize,
    out IntPtr lpNumberOfBytesRead);

        [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "ReadProcessMemory")]
        static extern bool ReadProcessMemory_Byte(
            IntPtr hProcess, IntPtr lpBaseAddress, out byte lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead
            );

        [StructLayout(LayoutKind.Sequential)]
        public struct MEMORY_BASIC_INFORMATION
        {
            public IntPtr BaseAddress;
            public IntPtr AllocationBase;
            public uint AllocationProtect;
            public IntPtr RegionSize;
            public uint State;
            public uint Protect;
            public uint Type;
        }

        public enum AllocationProtect : uint
        {
            PAGE_EXECUTE = 0x00000010,
            PAGE_EXECUTE_READ = 0x00000020,
            PAGE_EXECUTE_READWRITE = 0x00000040,
            PAGE_EXECUTE_WRITECOPY = 0x00000080,
            PAGE_NOACCESS = 0x00000001,
            PAGE_READONLY = 0x00000002,
            PAGE_READWRITE = 0x00000004,
            PAGE_WRITECOPY = 0x00000008,
            PAGE_GUARD = 0x00000100,
            PAGE_NOCACHE = 0x00000200,
            PAGE_WRITECOMBINE = 0x00000400
        }

        const uint MEM_COMMIT = 0x1000;
        const uint MEM_FREE = 0x10000;
        const uint MEM_RESERVE = 0x2000;

        [DllImport("kernel32.dll")]
        static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength);

        public struct MemoryRegion
        {
            public long Address { get; set; }
            public long Size { get; set; }
            public bool IsReadable { get; set; }
        }

        static bool IsReadAccess(uint protectionFlags)
        {
            AllocationProtect ap = (AllocationProtect)protectionFlags;

            return ap == AllocationProtect.PAGE_EXECUTE_READ ||
                   ap == AllocationProtect.PAGE_EXECUTE_READWRITE ||
                   ap == AllocationProtect.PAGE_EXECUTE_WRITECOPY ||
                   ap == AllocationProtect.PAGE_READONLY ||
                   ap == AllocationProtect.PAGE_READWRITE ||
                   ap == AllocationProtect.PAGE_WRITECOPY;
        }

        public static MemoryRegion[] GetMemoryRegions(IntPtr hProcess, IntPtr address, int size)
        {
            List<MemoryRegion> ret = new List<MemoryRegion>(50);
            long MaxAddress = (long)address + size;

            while(true)
            {
                MEMORY_BASIC_INFORMATION m=new MEMORY_BASIC_INFORMATION();
                int result = VirtualQueryEx(
                    hProcess, address, out m, (uint)Marshal.SizeOf(m)
                    );

                if (result==0||m.RegionSize == IntPtr.Zero) break;

                MemoryRegion reg = new MemoryRegion();
                reg.Address = (long)m.BaseAddress;
                reg.Size = (long)m.RegionSize;

                if (m.State == MEM_COMMIT && IsReadAccess(m.AllocationProtect))
                {
                    reg.IsReadable = true;
                }

                ret.Add(reg);
                address = (IntPtr)((long)m.BaseAddress + (long)m.RegionSize);
                if ((long)address > MaxAddress) break;
            }

            return ret.ToArray();
        }

        static void EnumMemoryRegions(IntPtr hProcess,IntPtr address, int size)
        {
            MemoryRegion[] regions = GetMemoryRegions(hProcess, address, size);

            for (int i = 0; i < regions.Length; i++)
            {
                Console.WriteLine("0x{0} : {1} bytes, Readable: {2}",
                    regions[i].Address.ToString("X"), regions[i].Size, regions[i].IsReadable
                    );
            }
        }

        static void Main(string[] args)
        {
            string path = "C:\\Test\\Lib.dll";
            //string path = typeof(object).Assembly.Location;
            IntPtr hModule = LoadLibraryEx(path, IntPtr.Zero, LoadLibraryFlags.None);
            if (hModule == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error());

            IntPtr hProcess = Process.GetCurrentProcess().Handle;

            MODULEINFO mi = new MODULEINFO();
            bool res = GetModuleInformation(hProcess, hModule, out mi, (uint)Marshal.SizeOf(mi));
            if (res == false) throw new Win32Exception(Marshal.GetLastWin32Error());

            EnumMemoryRegions(hProcess, mi.lpBaseOfDll, (int)mi.SizeOfImage);            
            Console.ReadKey();
        }
    }
}

Type disassembler improvements

  • Support field custom attributes
  • Expose constant values as LiteralSyntax instead of GenericSyntax
  • Support events
  • Remove SyntaxNode.GetPropertyMethod helper as CiLTools.Metadata now implements GetGetMethod/GetSetMethod
  • Support custom (.other) accessors in properties and events
  • Verify RVA fields behaviour (dotnet/runtime#70876)
  • Only query declared members when disassembling type. Implement querying declaring/inherited members in CilTools.Metadata.
  • Support field offsets for explicit struct layout (fixed on GitFlic)

Base type not shown for assemblies with different target framework

System.Console.dll (.NET Core 2.1) / System.IO.ConsoleStream

Expected:

.class private abstract auto ansi beforefieldinit System.IO.ConsoleStream
extends [System.Runtime]System.IO.Stream

Actual:

.class private abstract auto ansi beforefieldinit System.IO.ConsoleStream
extends UnknownType

Handle non-standard keywords in parser and disassembler

  • Tokens that seem to be used as keywords in implementation: aggressiveinlining, uint8, uint16, uint32, type (Fixed on GitFlic: ea8e39)
  • Windows ilasm & ildasm use winapi instead of platformapi

CilTools.Tests.Common.il(4103) : error : syntax error at token 'platformapi' in: .method public hidebysig static pinvok
eimpl("user32.dll" lasterr platformapi ) bool ShowWindow(

***** FAILURE *****

CilTools.Tests.Common.il(8814) : error : syntax error at token 'type' in: class [mscorlib]System.Type type,

***** FAILURE *****

CilTools.Metadata reflection updates

  • .GetCustomAttributes(bool inherit) overloads should respect inherit parameter
  • Implement .GetCustomAttributes(Type attributeType, bool inherit) overloads
  • Implement ParameterSpec.HasDefaultValue
  • Add API to convert between CilTools.Metadata and runtime reflection
  • Implement Type.GetInterfaces, Type.GetInterfaceMap (on subclasses other than TypeDef)
  • Implement Type.GetEnumValues and other enum APIs
  • Implement modifier-related APIs, such as FieldInfo.GetOptionalCustomModifiers
  • Ensure resolving members by token fails with expected exceptions when token is out of range
  • Implement MetadataToken for properties and events
  • Implement proper inheritance rules for properties and events (when queried w/out DeclaredOnly)
  • Override Type.IsEnum to correctly handle defferent corelibs

Add support for constrained. instruction prefix

There's a constrined. prefix which is emitted by compilers on virtual method calls when the type comes from generic argument which can be a struct. CilInstruction class should be updated to properly parse its operand. Example:

using System;
using System.Reflection;
using CilBytecodeParser;

class Program
{
    public static void Test<T>(T x)
    {        
        Console.WriteLine(x.ToString());
    }

    public static void Main(string[] args)
    {
        Console.WriteLine(CilAnalysis.MethodToText(typeof(Program).GetMethod("Test")));
        Console.ReadKey();
    }
}

Current output:

.method public hidebysig static void Test<T>(
    !!T x
) cil managed {
 .maxstack 8

          nop
          ldarga.s   x
          constrained. 452984833
          callvirt   instance string [mscorlib]System.Object::ToString()
          call       void [mscorlib]System.Console::WriteLine(string)
          nop
          ret
}

Expected output:

.method public hidebysig static void Test<T>(
    !!T x
) cil managed {
 .maxstack 8

          nop
          ldarga.s   x
          constrained. !!T
          callvirt   instance string [mscorlib]System.Object::ToString()
          call       void [mscorlib]System.Console::WriteLine(string)
          nop
          ret
}

CIL View: 64-bit process support

  • Step 1: Fetch 64-bit process modules using WMI, open modules as files
  • Step 2: Create separate 64-bit process using ClrMD to fetch modules
  • Step 3: Make separate process fetch all information (dynamic methods, threads)

Fetching modules using WMI

string[] GetProcessModules(int id)
{
        List<string> res = new List<string>();
        
        string query = "references of {win32_process.Handle="+id.ToString()+
            "} WHERE ResultClass = CIM_ProcessExecutable";
        
        using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
        using (ManagementObjectCollection results = searcher.Get())
        {
            foreach (ManagementObject x in results)
            {
                var antecedent = new ManagementObject((string)x["Antecedent"]);
                if (antecedent["Name"] == null) continue;
                string name = antecedent["Name"].ToString();
                res.Add(name);
            }
        }            

        return res.ToArray();
}

In .NET 3.5, every managed module has corresponding .ni.dll native module loaded via LoadLibrary. In .NET 4.0 and newer, some modules might be memory-mapped rather then loaded from native image file.

Fetching modules using ClrMD

Version 1.1.142101+

StringBuilder sb = new StringBuilder(1000);
using (DataTarget dt = DataTarget.AttachToProcess(8036, false))
{
            ClrInfo ci = dt.ClrVersions[0];
            ClrRuntime clr = ci.CreateRuntime();

            foreach (ClrModule module in clr.EnumerateModules())
            {
                if (module.IsPEFile) sb.AppendLine(module.Name);
            }
 }

Support .vtentry directive

Example C++/CLI program:

using namespace System;

public class A {
    int _x;
    public: A(int x) { this->_x = x; }
};

int main(array<System::String ^> ^args)
{
    A a(1);
	Console::ReadKey();
    return 0;
}

Expected disassembler output:

.method assembly static valuetype A* modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall)
        'A.{ctor}'(valuetype A* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst) A_0,
                   int32 x) cil managed
{
  .vtentry 1 : 1
  //...
}

The VTable data is stored in the location pointed int the CLI Header (ECMA 335 II.25.3.3). Code to get vtable data:

PEReader pr;
//...
int vt_rva = pr.PEHeaders.CorHeader.VtableFixupsDirectory.RelativeVirtualAddress;
var vt_data=pr.GetSectionData(vt_rva).GetContent();

Update string literal escaping to match CLI specification

When generating CIL assembler code, current string literal escaping uses rules similar to those of C#.

The actual escaping rules in CIL assembler are different. From ECMA-335 II.5.2 - Basic syntax categories:

QSTRING is a string surrounded by double quote (″) marks. Within the quoted string the character “\”
can be used as an escape character, with “\t” representing a tab character, “\n” representing a newline
character, and “\” followed by three octal digits representing a byte with that value

[Example: The following result in strings that are equivalent to "Hello World from CIL!":
ldstr "Hello " + "World " +
"from CIL!"
and
ldstr "Hello World\
\040from CIL!"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.