XenocodeRCE/Noisette-Obfuscator

Anti-tampering solution

Closed this issue · 10 comments

Here's the start of an idea of how to implement anti-tampering for all individual methods in the app. I have included a sample project below. Use this, or this idea if you want to.

I've written down all the possible ways to get around this protection that I could think of. The list is in the comments of Program.cs.

Program.cs

using System;

namespace sample
{
	public class Program
	{
		public static void Main()
		{
			/*
			 * Possible ways to bypass checksums
			 * 
			 * 1. Remove protection method in IntegrityValidator
			 * 2. Remove whole content of .ctor of IntegrityValidator
			 * 3. Remove [Checksum] attribute on methods before altering
			 * 4. Actually calculating the new checksums and replacing them (which I doubt anyone will bother doing)
			 * 5. Remove call to IntegrityValidator in Program
			 * 6. Change the call of IntegrityValidator in program to typeof(SomethingElse)
			 * 
			 * And fixes:
			 * 
			 * 3. Enforce all methods to have a Checksum attribute, that way if they're missing
			 *    the program will fail
			 */

			/*
			 * Possible ways to make the checksums fail
			 * 
			 * 1. Tamper with a methods body (if it has checksum)
			 * 2. Tamper with the class signature containing a method with a checksum
			 */
			new IntegrityValidator(typeof(Program)).ValidateIntegrity();
			Console.WriteLine("Untampered program! yay! Don't obfuscate or crack me, or I will be crashing");
			Console.WriteLine(DateTime.Now);
			Console.ReadLine();
		}

		[Checksum("8163cb50e7332f915355e94cfe324fb5")] //hash of method body bytes
		public void DontTamper()
		{
			Console.WriteLine("hi");
		}

		[Checksum("8163cb50e7332f915355e94cfe324fb5")]//hash of method body bytes
		public void DontTamper2()
		{
			Console.WriteLine("hi");
		}
	}
}

IntegrityValidator.cs

using System;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

namespace sample
{
	public class IntegrityValidator
	{
		public IntegrityValidator(Type type)// do validation checks on all methods with our attribute
		{
			int j = 0;
			foreach (var method in type.GetMethods().Where(item => item.GetCustomAttribute(typeof(ChecksumAttribute)) != null))
			{
				j++;
				if (!Validate(method)) { //if one method is tampered
					Environment.FailFast(null);
				}
			}
			if (j == 0) {
				Environment.FailFast(null);
			}
		}

		public string CalculateChecksum(MethodInfo o)
		{
			StringBuilder sb = new StringBuilder();
			var data = o.GetMethodBody().GetILAsByteArray();
			using (var md5 = MD5.Create())
			{
				var hash = md5.ComputeHash(data);
				for (int i = 0; i < hash.Length; i++)
				{
					sb.Append(hash[i].ToString("x2"));
				}
			}
			return sb.ToString();
		}

		public bool Validate(MethodInfo o)
		{
			foreach (var attr in o.GetCustomAttributes(typeof(ChecksumAttribute), false))
			{
				if (attr.GetType() == typeof(ChecksumAttribute)) { 
					var attrib = attr as ChecksumAttribute;
					//Console.WriteLine(attrib.Hash);
					//Console.WriteLine(CalculateChecksum(o));
					return attrib.Hash == CalculateChecksum(o);
				}
			}
			return false; //doesn't have checksum
		}

		public bool ValidateIntegrity() => true;
	}
}

ChecksumAttribute.cs

using System;
namespace sample
{
	[AttributeUsage(AttributeTargets.Method)]
	public class ChecksumAttribute : Attribute
	{
		public string Hash { get; set; }
		public ChecksumAttribute(string hash)
		{
			this.Hash = hash;
		}
	}
}

It calculates the checksum based on il body as byte array... the il body doesn't change unless you change the.... well body

Compile the code as a project and play with it

Also, on another note, LePanthere once said changing the heap size to greater than 500 crashes CFF Explorer. That could be something you'd want to look into.

And erasing the DOS header except leaving e_lfanew and e_magic throws off signature detctors on virustotal and malwr.com

I just thought of another anti tamper method. Have 1 global integer that keeps increasing chronologically in all calls/methods and verify after every 3rd or 5th method by XORing some other integer critical to the program by the global integer

That way if you remove or change 1 call the integer won't match up and future calls will be xored wrong = crash or worse

This is an exploit that will crash OllyDbg
Debugger.Log(0, null, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s");

dnSpy will detect Debugger.IsAttached, and in .NET 2.0 can not successfully perform interop calls Kernel32+IsDebuggerPresent in debug mode. But if you can somehow read the IsDebugged value from PEB runtime in a managed way, you may have a way around that. https://www.symantec.com/connect/articles/windows-anti-debug-reference

I know it's possible to do in ASM, but c# not sure

Here's my email rottsploit@national.shitposting.agency (yes, it's real)