6

我正在尝试使用无权访问该服务器的域帐户访问服务器的 C$。我需要以本地登录的形式保存该服务器的凭据,以使程序正常工作。

如何使用 CredWrite 保存这些凭据

我发现的凭据管理器类:(编辑:以下是功能代码。)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace Test_Manager
{
    public class Win32CredMan
    {
        [DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr);

        [DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool CredWrite([In] ref NativeCredential userCredential, [In] UInt32 flags);

        [DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
        static extern bool CredFree([In] IntPtr cred);

        public enum CRED_TYPE : uint
        {
            GENERIC = 1,
            DOMAIN_PASSWORD = 2,
            DOMAIN_CERTIFICATE = 3,
            DOMAIN_VISIBLE_PASSWORD = 4,
            GENERIC_CERTIFICATE = 5,
            DOMAIN_EXTENDED = 6,
            MAXIMUM = 7,      // Maximum supported cred type
            MAXIMUM_EX = (MAXIMUM + 1000),  // Allow new applications to run on old OSes
        }
        public enum CRED_PERSIST : uint
        {
            SESSION = 1,
            LOCAL_MACHINE = 2,
            ENTERPRISE = 3,
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct NativeCredential
        {
            public UInt32 Flags;
            public CRED_TYPE Type;
            public IntPtr TargetName;
            public IntPtr Comment;
            public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
            public UInt32 CredentialBlobSize;
            public IntPtr CredentialBlob;
            public UInt32 Persist;
            public UInt32 AttributeCount;
            public IntPtr Attributes;
            public IntPtr TargetAlias;
            public IntPtr UserName;

            /// <summary>
            /// This method derives a NativeCredential instance from a given Credential instance.
            /// </summary>
            /// <param name="cred">The managed Credential counterpart containing data to be stored.</param>
            /// <returns>A NativeCredential instance that is derived from the given Credential
            /// instance.</returns>
            internal static NativeCredential GetNativeCredential(Credential cred)
            {
                NativeCredential ncred = new NativeCredential();
                ncred.AttributeCount = 0;
                ncred.Attributes = IntPtr.Zero;
                ncred.Comment = IntPtr.Zero;
                ncred.TargetAlias = IntPtr.Zero;
                ncred.Type = (CRED_TYPE)cred.Type;
                ncred.Persist = (UInt32)cred.Persist;
                ncred.CredentialBlobSize = (UInt32)cred.CredentialBlobSize;
                ncred.TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName);
                ncred.CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob);
                ncred.UserName = Marshal.StringToCoTaskMemUni(cred.UserName);
                return ncred;
            }
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct Credential
        {
            public UInt32 Flags;
            public CRED_TYPE Type;
            public string TargetName;
            public string Comment;
            public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
            public UInt32 CredentialBlobSize;
            public string CredentialBlob;
            public CRED_PERSIST Persist;
            public UInt32 AttributeCount;
            public IntPtr Attributes;
            public string TargetAlias;
            public string UserName;
        }

        #region Critical Handle Type definition
        sealed class CriticalCredentialHandle : CriticalHandleZeroOrMinusOneIsInvalid
        {
            // Set the handle.
            internal CriticalCredentialHandle(IntPtr preexistingHandle)
            {
                SetHandle(preexistingHandle);
            }

            internal Credential GetCredential()
            {
                if (!IsInvalid)
                {
                    // Get the Credential from the mem location
                    NativeCredential ncred = (NativeCredential)Marshal.PtrToStructure(handle,
                          typeof(NativeCredential));

                    // Create a managed Credential type and fill it with data from the native counterpart.
                    Credential cred = new Credential();
                    cred.CredentialBlobSize = ncred.CredentialBlobSize;
                    cred.CredentialBlob = Marshal.PtrToStringUni(ncred.CredentialBlob,
                          (int)ncred.CredentialBlobSize / 2);
                    cred.UserName = Marshal.PtrToStringUni(ncred.UserName);
                    cred.TargetName = Marshal.PtrToStringUni(ncred.TargetName);
                    cred.TargetAlias = Marshal.PtrToStringUni(ncred.TargetAlias);
                    cred.Type = ncred.Type;
                    cred.Flags = ncred.Flags;
                    cred.Persist = (CRED_PERSIST)ncred.Persist;
                    return cred;
                }
                else
                {
                    throw new InvalidOperationException("Invalid CriticalHandle!");
                }
            }

            // Perform any specific actions to release the handle in the ReleaseHandle method.
            // Often, you need to use Pinvoke to make a call into the Win32 API to release the 
            // handle. In this case, however, we can use the Marshal class to release the unmanaged memory.

            override protected bool ReleaseHandle()
            {
                // If the handle was set, free it. Return success.
                if (!IsInvalid)
                {
                    // NOTE: We should also ZERO out the memory allocated to the handle, before free'ing it
                    // so there are no traces of the sensitive data left in memory.
                    CredFree(handle);
                    // Mark the handle as invalid for future users.
                    SetHandleAsInvalid();
                    return true;
                }
                // Return false. 
                return false;
            }
        }
        #endregion

        public int WriteCred(string key, string user, string secret)
        {
            // Validations.

            byte[] byteArray = Encoding.Unicode.GetBytes(secret);
            if (byteArray.Length > 512)
                throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes.");

            // Go ahead with what we have are stuff it into the CredMan structures.
            Credential cred = new Credential();
            cred.TargetName = key;
            cred.UserName = user;
            cred.CredentialBlob = secret;
            cred.CredentialBlobSize = (UInt32)Encoding.Unicode.GetBytes(secret).Length;
            cred.AttributeCount = 0;
            cred.Attributes = IntPtr.Zero;
            cred.Comment = null;
            cred.TargetAlias = null;
            cred.Type = CRED_TYPE.DOMAIN_PASSWORD;
            cred.Persist = CRED_PERSIST.ENTERPRISE;
            NativeCredential ncred = NativeCredential.GetNativeCredential(cred);
            // Write the info into the CredMan storage.
            bool written = CredWrite(ref ncred, 0);
            int lastError = Marshal.GetLastWin32Error();
            if (written)
            {
                return 0;
            }
            else
            {
                string message = string.Format("CredWrite failed with the error code {0}.", lastError);
                throw new Exception(message);
            }
        }

        public static string ReadCred(string key)
        {
            // Validations.

            IntPtr nCredPtr;
            string readPasswordText = null;

            // Make the API call using the P/Invoke signature
            bool read = CredRead(key, CRED_TYPE.GENERIC, 0, out nCredPtr);
            int lastError = Marshal.GetLastWin32Error();

            // If the API was successful then...
            if (read)
            {
                using (CriticalCredentialHandle critCred = new CriticalCredentialHandle(nCredPtr))
                {
                    Credential cred = critCred.GetCredential();
                    readPasswordText = cred.CredentialBlob;
                }
            }
            else
            {
                readPasswordText = string.Empty;

                //1168 is "element not found" -- ignore that one and return empty string:
                if (lastError != 1168)
                {
                    string message = string.Format("ReadCred failed with the error code {0}.", lastError);
                    throw new Exception(message);
                }
            }
            return readPasswordText;
        }
    }
}

长话短说,这是我尝试使用的上述代码中的方法:

public int WriteCred(string key, string user, string secret)
{
    // Validations.

    byte[] byteArray = Encoding.Unicode.GetBytes(secret);
    if (byteArray.Length > 512)
        throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes.");

    // Go ahead with what we have are stuff it into the CredMan structures.
    Credential cred = new Credential();
    cred.TargetName = key;
    cred.UserName = user;
    cred.CredentialBlob = secret;
    cred.CredentialBlobSize = (UInt32)Encoding.Unicode.GetBytes(secret).Length;
    cred.AttributeCount = 0;
    cred.Attributes = IntPtr.Zero;
    cred.Comment = null;
    cred.TargetAlias = null;
    cred.Type = CRED_TYPE.DOMAIN_PASSWORD;
    cred.Persist = CRED_PERSIST.ENTERPRISE;
    NativeCredential ncred = NativeCredential.GetNativeCredential(cred);
    // Write the info into the CredMan storage.
    bool written = CredWrite(ref ncred, 0);
    int lastError = Marshal.GetLastWin32Error();
    if (written)
    {
        return 0;
    }
    else
    {
        string message = string.Format("CredWrite failed with the error code {0}.", lastError);
        throw new Exception(message);
    }
}

这就是我在程序主体中所做的:

Win32CredMan cm = new Win32CredMan();
cm.WriteCred("TheServer-18", @"TheServer-18\Administrator", "P4SSw0rD!");

我假设我无法访问,因为没有正确添加凭据。

更新:

我列出的过程是将通用凭据添加到 Windows 凭据管理器。但是,如果未使用 WriteCred 方法中指定的用户名。我不明白为什么。

4

1 回答 1

2

问题解决了,上面的代码现在可以正常工作了。该问题与 GetNativeCredential 有关,它不是使用在 WriteCred 方法中分配给 cred 的值,而是使用已设置的一些静态值。

另外,我将信息输入到 WriteCred 方法的方式不正确。上面的代码已被修复,因此它可以正常运行。

于 2013-10-17T12:00:25.350 回答