dnSpy – Random IT Utensils https://blog.adamfurmanek.pl IT, operating systems, maths, and more. Mon, 18 Nov 2019 00:31:38 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 .NET Inside Out Part 13 — Bypassing license checks https://blog.adamfurmanek.pl/2019/11/16/net-inside-out-part-13-bypassing-license-checks/ https://blog.adamfurmanek.pl/2019/11/16/net-inside-out-part-13-bypassing-license-checks/#comments Sat, 16 Nov 2019 09:00:50 +0000 https://blog.adamfurmanek.pl/?p=3162 Continue reading .NET Inside Out Part 13 — Bypassing license checks]]>

This is the thirteenth part of the .NET Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 – Virtual and non-virtual calls in C#

Last time we saw how to modify library on a binary level. Today we will have a case study of some hypothetical library which uses packer and validates license using System.ComponentModel.LicenseManager.

The library comes with a license file which gives us a short trial period. After that time the library throws exception during initialization. First, let’s check what the library does to verify the license. Most likely it is reading registry or accessing some files from user profile. Run Procmon from Sysinternals, dump whole activity and see what’s there. It can bee something like:

HKCU\Software\Classes\.some_class
c:\Users\%USERNAME%\.someFile
c:\Users\%USERNAME%\appdata\local\temp\someOtherFile
c:\Users\%USERNAME%\appdata\local\temp\yetAnotherFile

If you have a new license which doesn’t work (because your previous one already finished the trial period), try removing those files and registry keys, and then restart the application. Your mileage may vary.

Let’s now try decompiling the library. We open it with ILSpy and we get this:

So we can see that ILSpy failed to decompile the classes. If we try showing IL, we get

System.InvalidOperationException: Operation is not valid due to the current state of the object.
   at Mono.Cecil.Cil.CodeReader.ReadMethodBody()
   at Mono.Cecil.Cil.CodeReader.ReadMethodBody(MethodDefinition method)
   at Mono.Cecil.MethodDefinition.<get_Body>b__2(MethodDefinition method, MetadataReader reader)
   at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TRet& variable, TItem item, Func`3 read)
   at Mono.Cecil.MethodDefinition.get_Body()
   at ICSharpCode.Decompiler.MemberMapping..ctor(MethodDefinition method)
   at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleMethodInternal(MethodDefinition method)
   at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleType(TypeDefinition type)
   at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleNamespace(String nameSpace, IEnumerable`1 types)
   at ICSharpCode.ILSpy.TreeNodes.NamespaceTreeNode.Decompile(Language language, ITextOutput output, DecompilationOptions options)
   at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput)
   at ICSharpCode.ILSpy.TextView.DecompilerTextView.<>c__DisplayClass17.<DecompileAsync>b__16()

Okay, so the library uses some kind of a packer. But let’s actually see what happens if we run it with wrong license. We get this exception:

Yada yada about license
   at some garbage
   at System.ComponentModel.LicenseManager.ValidateInternalRecursive(LicenseContext context, Type type, Object instance, Boolean allowExceptions, License& license, String& licenseKey)
   at System.ComponentModel.LicenseManager.Validate(Type type, Object instance)
   at &#x200d;‮‮‫‮‫‎‮‏&#x200d;‏​‏​‭‮‬‎‪more garbage

So we can see it uses .NET LicenseManager to validate the license.

Let’s now run the application with correct license and use dnSpy to see what we get:

So we can see that it creates a license with some content. Let’s try replaying attack.

We now want to override Validate method to apply our own logic. Let’s try this code:

using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Library
{
	class FakeLicense : License
	{
		public override string LicenseKey
		{
			get
			{
				return "CorrectKeyExtractedFromDnSpy";
			}
		}

		public override void Dispose()
		{
		}

		public static License Validate(Type type, object instance)
		{
			return new FakeLicense();
		}

		public static void HackLicense()
		{
			HijackMethod(typeof(LicenseManager).GetMethod(nameof(LicenseManager.Validate), new[] { typeof(Type), typeof(object) }), 
                typeof(FakeLicense).GetMethod(nameof(FakeLicense.Validate), BindingFlags.Static | BindingFlags.Public));
		}
        
        public enum Protection
        {
            PAGE_NOACCESS = 0x01,
            PAGE_READONLY = 0x02,
            PAGE_READWRITE = 0x04,
            PAGE_WRITECOPY = 0x08,
            PAGE_EXECUTE = 0x10,
            PAGE_EXECUTE_READ = 0x20,
            PAGE_EXECUTE_READWRITE = 0x40,
            PAGE_EXECUTE_WRITECOPY = 0x80,
            PAGE_GUARD = 0x100,
            PAGE_NOCACHE = 0x200,
            PAGE_WRITECOMBINE = 0x400
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);

        private static void UnlockPage(int address)
        {
            uint old;
            VirtualProtect((IntPtr)address, 4096, (uint)Protection.PAGE_EXECUTE_READWRITE, out old);
        }

        public static void HijackMethod(MethodInfo source, MethodInfo target)
        {
            RuntimeHelpers.PrepareMethod(source.MethodHandle);
			RuntimeHelpers.PrepareMethod(target.MethodHandle);

			var sourceAddress = source.MethodHandle.GetFunctionPointer();
			var targetAddress = (long)target.MethodHandle.GetFunctionPointer();

            UnlockPage((int)sourceAddress);
            UnlockPage((int)targetAddress);


            int offset = (int)(targetAddress - (long)sourceAddress - 4 - 1); // four bytes for relative address and one byte for opcode

			byte[] instruction = {
				0xE9, // Long jump relative instruction
				(byte)(offset & 0xFF),
				(byte)((offset >> 8) & 0xFF),
				(byte)((offset >> 16) & 0xFF),
				(byte)((offset >> 24) & 0xFF)
			};

			Marshal.Copy(instruction, 0, sourceAddress, instruction.Length);
		}
	}
}

Let’s run the hijacking code before loading the library and see if it works.

Yes, we can see that our code is called correctly. Now the question is if library figures out if there was a different license applied but let’s assume it doesn’t.

]]>
https://blog.adamfurmanek.pl/2019/11/16/net-inside-out-part-13-bypassing-license-checks/feed/ 1