C# NTLM Hash Calculator

2019-05-26 22:47发布

问题:

I recently began learning C#. I tried to generate an NTLM hash in this language but I could't find a function to do this for me. In python 3.x I would import hashlib and calculate it with hashlib.new("md4", "Hello, World!".encode("utf-16le")).

I searched through the Object Browser in C# but didn't find anything, the closest was a windows NTLM authentication class. I also searched Microsoft's docs and found hash calculators, but only for sha1 and md5.

Is there a way to calculate an NTLM hash in C#? Can you please show me an example of how to do it, and I would prefer a short method to keep it simple.

Thanks.

回答1:

You can create an extension to the existing cryptography providers using Reflection to call CNG for MD4 (something .Net should probably either do, or make much easier):

namespace System.Security.Cryptography {
    [System.Runtime.InteropServices.ComVisible(true)]
    public abstract class MD4 : HashAlgorithm {
        static MD4() {
            CryptoConfig.AddAlgorithm(typeof(MD4CryptoServiceProvider), "System.Security.Cryptography.MD4");
        }

        protected MD4() {
            HashSizeValue = 128;
        }

        new static public MD4 Create() {
            return Create("System.Security.Cryptography.MD4");
        }

        new static public MD4 Create(string algName) {
            return (MD4)CryptoConfig.CreateFromName(algName);
        }
    }

    [System.Runtime.InteropServices.ComVisible(true)]
    public sealed class MD4CryptoServiceProvider : MD4 {
        internal static class Utils {
            internal static Type UtilsType = Type.GetType("System.Security.Cryptography.Utils");

            public static T InvokeInternalMethodOfType<T>(object o, object pType, string methodName, params object[] args) {
                var internalType = (pType is string internalTypeName) ? Type.GetType(internalTypeName) : (Type)pType;
                var internalMethods = internalType.GetMethods(BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | (o == null ? BindingFlags.Static : 0));
                var internalMethod = internalMethods.Where(m => m.Name == methodName && m.GetParameters().Length == args.Length).Single();
                return (T)internalMethod?.Invoke(o, args);
            }

            public static T GetInternalPropertyValueOfInternalType<T>(object o, object pType, string propertyName) {
                var internalType = (pType is string internalTypeName) ? Type.GetType(internalTypeName) : (Type)pType;
                var internalProperty = internalType.GetProperty(propertyName, BindingFlags.NonPublic | (o == null ? BindingFlags.Static : 0));
                return (T)internalProperty.GetValue(o);
            }

            internal static SafeHandle CreateHash(int algid) {
                return InvokeInternalMethodOfType<SafeHandle>(null, UtilsType, "CreateHash", GetInternalPropertyValueOfInternalType<object>(null, UtilsType, "StaticProvHandle"), algid);
            }

            internal static void HashData(SafeHandle h, byte[] data, int ibStart, int cbSize) {
                InvokeInternalMethodOfType<object>(null, UtilsType, "HashData", h, data, ibStart, cbSize);
            }

            internal static byte[] EndHash(SafeHandle h) {
                return InvokeInternalMethodOfType<byte[]>(null, UtilsType, "EndHash", h);
            }
        }

        internal const int ALG_CLASS_HASH = (4 << 13);
        internal const int ALG_TYPE_ANY = (0);
        internal const int ALG_SID_MD4 = 2;
        internal const int CALG_MD4 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD4);

        [System.Security.SecurityCritical]
        private SafeHandle _safeHashHandle = null;

        [System.Security.SecuritySafeCritical]
        public MD4CryptoServiceProvider() {
            if (CryptoConfig.AllowOnlyFipsAlgorithms)
                throw new InvalidOperationException("Cryptography_NonCompliantFIPSAlgorithm");
            Contract.EndContractBlock();
            // cheat with Reflection
            _safeHashHandle = Utils.CreateHash(CALG_MD4);
        }

        protected override void Dispose(bool disposing) {
            if (_safeHashHandle != null && !_safeHashHandle.IsClosed)
                _safeHashHandle.Dispose();
            base.Dispose(disposing);
        }

        public override void Initialize() {
            if (_safeHashHandle != null && !_safeHashHandle.IsClosed)
                _safeHashHandle.Dispose();

            _safeHashHandle = Utils.CreateHash(CALG_MD4);
        }

        protected override void HashCore(byte[] rgb, int ibStart, int cbSize) {
            Utils.HashData(_safeHashHandle, rgb, ibStart, cbSize);
        }

        protected override byte[] HashFinal() {
            return Utils.EndHash(_safeHashHandle);
        }
    }
}

Once you've done that, a couple of helper extensions will let you use it easily (I modified this to create a singleton so it doesn't have to do the work of reflecting/creating every time you use it):

static class Ext {
    public static HashAlgorithm MD4Singleton;

    static Ext() { 
        MD4Singleton = System.Security.Cryptography.MD4.Create();   
    }

    public static byte[] MD4(this string s) { 
        return MD4Singleton.ComputeHash(System.Text.Encoding.Unicode.GetBytes(s));
    }

    public static string AsHexString(this byte[] bytes) { 
        return String.Join("", bytes.Select(h => h.ToString("X2")));
    }
}

Now you just call the extension methods on some sample data:

void Main() {
    var input = "testing";

    var hash = input.MD4();
    var hashStr = hash.AsHexString();
    Console.WriteLine(hashStr);
}


回答2:

I think you need to use the BouncyCastle to calculate the HASH, there is a .net porting that works quite well.

and here is all the step to calculate the NTLM hash: https://asecuritysite.com/encryption/lmhash



回答3:

Code can be found at the end of the post here. It uses BC for the MD4, as most MD4 implementations have a way to avid weak keys. NTLM does not account weak keys, so you must be able to use them if they arise.

https://markgamache.blogspot.com/2013/01/ntlm-challenge-response-is-100-broken.html



回答4:

Here's a general solution to call CNG for any valid ALG_ID, based on the accepted answer above. Thank you NetMage!

public class HashByID : HashAlgorithm {
    static readonly Dictionary<int, int> hashSizes = new Dictionary<int,int>() { {0x8001,128},{0x8002,128},{0x8003,128},{0x8004,160},{0x8006,128},{0x8007,160},{0x800c,256},{0x800d,384},{0x800e,512}};
    static readonly Type hUtils;
    static readonly SafeHandle hStaticProv;
    static readonly Func<SafeHandle, int, SafeHandle> fCreate;
    static readonly Action<SafeHandle, byte[], int, int> fHash;
    static readonly Func<SafeHandle, byte[]> fHashEnd;
    public static bool inited;
    public readonly int algID;
    SafeHandle hh = null;
    static HashByID() {
        try {
            hUtils = Type.GetType("System.Security.Cryptography.Utils");
            hStaticProv = (SafeHandle)hUtils.GetProperty("StaticProvHandle", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null, null);
            fCreate = (Func<SafeHandle, int, SafeHandle>)hUtils.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(x => x.Name == "CreateHash" && x.GetParameters().Length == 2).Single().CreateDelegate(null, typeof(SafeHandle), typeof(int), typeof(SafeHandle));
            fHash = (Action<SafeHandle, byte[], int, int>)hUtils.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(x => x.Name == "HashData" && x.GetParameters().Length == 4).Single().CreateDelegate(null, typeof(SafeHandle), typeof(byte[]), typeof(int), typeof(int));
            fHashEnd = (Func<SafeHandle, byte[]>)hUtils.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(x => x.Name == "EndHash" && x.GetParameters().Length == 1).Single().CreateDelegate(null, typeof(SafeHandle), typeof(byte[]));
            inited = true;
        } catch { }
    }
    public HashByID(int algID) {
        if (algID == 0x8009) algID = 0x8004;    //map CALG_HMAC -> CALG_SHA1
        this.algID = algID;
        hashSizes.TryGetValue(algID, out HashSizeValue);
        Initialize();
    }
    protected override void Dispose(bool disposing) {
        if (hh != null && !hh.IsClosed) hh.Dispose();
        base.Dispose(disposing);
    }
    public override void Initialize() {
        if (hh != null && !hh.IsClosed) hh.Dispose();
        hh = fCreate(hStaticProv, algID);
    }
    protected override void HashCore(byte[] data, int ofs, int len) {
        fHash(hh, data, ofs, len);
    }
    protected override byte[] HashFinal() {
        return fHashEnd(hh);
    }
}

//Delegate creation helper
public static Delegate CreateDelegate(this MethodInfo methodInfo, object target, params Type[] custTypes) {
    Func<Type[], Type> getType;
    bool isAction = methodInfo.ReturnType.Equals((typeof(void))), cust = custTypes.Length > 0;
    Type[] types = cust ? custTypes : methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
    if (isAction) getType = Expression.GetActionType;
    else {
        getType = Expression.GetFuncType;
        if (!cust) types = types.Concat(new[] { methodInfo.ReturnType }).ToArray();
    }
    if (cust) {
        int i, nargs = types.Length - (isAction ? 0 : 1);
        var dm = new DynamicMethod(methodInfo.Name, isAction ? typeof(void) : types.Last(), types.Take(nargs).ToArray(), typeof(object), true);
        var il = dm.GetILGenerator();
        for (i = 0; i < nargs; i++)
            il.Emit(OpCodes.Ldarg_S, i);
        il.Emit(OpCodes.Call, methodInfo);
        il.Emit(OpCodes.Ret);
        if (methodInfo.IsStatic) return dm.CreateDelegate(getType(types));
        return dm.CreateDelegate(getType(types), target);
    }
    if (methodInfo.IsStatic) return Delegate.CreateDelegate(getType(types), methodInfo);
    return Delegate.CreateDelegate(getType(types), target, methodInfo.Name);
}

Create an instance with algID = 0x8002 for MD4.



标签: c# hash ntlm