kenarsuleyman
kenarsuleyman

Reputation: 1020

WinAPI CredEnumerate Marshal.PtrToStructure Exception

I have problems with enumerating Windows Credentials from VB.Net application with WinAPI functions. My code is below.

<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Private Shared Function CredEnumerate(filter As String, flag As Integer, ByRef count As Integer, ByRef pCredentials As IntPtr) As Boolean
End Function

Public Enum CRED_PERSIST As UInteger
    SESSION = 1
    LOCAL_MACHINE = 2
    ENTERPRISE = 3
End Enum

Public Enum CRED_TYPE As UInteger
    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
End Enum

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Public Structure CREDENTIAL_ATTRIBUTE
    Private Keyword As String
    Private Flags As UInteger
    Private ValueSize As UInteger
    Private Value As IntPtr
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Private Class Credential
    Public Flags As UInt32
    Public Type As CRED_TYPE
    Public TargetName As String
    Public Comment As String
    Public LastWritten As ComTypes.FILETIME
    Public CredentialBlobSize As UInt32
    Public CredentialBlob As IntPtr
    Public Persist As CRED_PERSIST
    Public AttributeCount As UInt32
    Public Attributes As IntPtr
    Public TargetAlias As String
    Public UserName As String
End Class


Private Function GetCredentials() As Credential()
    Dim count As Integer = 0
    Dim pCredentials As IntPtr = IntPtr.Zero
    Dim credentials As List(Of Credential) = New List(Of Credential)
    Dim ret As Boolean = CredEnumerate(Nothing, 0, count, pCredentials)
    If ret <> False Then
        Dim p As IntPtr = pCredentials
        For n As Integer = 0 To count - 1
            If Marshal.SizeOf(p) = 4 Then
                p = New IntPtr(p.ToInt32() + n)
            Else
                p = New IntPtr(p.ToInt64() + n)
            End If
            credentials.Add(Marshal.PtrToStructure(Marshal.ReadIntPtr(p), GetType(Credential)))
        Next
    End If
    Return credentials.ToArray
End Function

Marshal.PtrToStructure function throws System.ExecetionEngineException without any useful information. I suspected with wrong credential structure but it seems correct to me. If you have any idea about what is wrong, I'm waiting your answers. Thanks

Edit: Thanks to @Zaggler here is my corrected function now it adds credentials to array but whole structure is empty. Here is new function.

Private Function GetCredentials() As Credential()
    Dim count As Integer = 0
    Dim pCredentials As IntPtr = IntPtr.Zero
    Dim credentials As List(Of Credential) = New List(Of Credential)

    Dim ret As Boolean = CredEnumerate(Nothing, 0, count, pCredentials)
    If ret <> False Then
        Dim p As IntPtr = pCredentials
        For n As Integer = 0 To count - 1
            Dim cred As Credential = New Credential
            Dim pnt As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cred))
            Try

                If Marshal.SizeOf(p) = 4 Then
                    p = New IntPtr(p.ToInt32() + n)
                Else
                    p = New IntPtr(p.ToInt64() + n)
                End If
                Marshal.StructureToPtr(cred, pnt, False)
                credentials.Add(Marshal.PtrToStructure(pnt, GetType(Credential)))

            Finally
                Marshal.FreeHGlobal(pnt)
            End Try
        Next
    End If
    Return credentials.ToArray
End Function

Upvotes: 0

Views: 167

Answers (1)

user7994388
user7994388

Reputation: 473

Your first attempt was better. Don't use AllocHGlobal and FreeHGlobal. Winapi is allocating memory. Lookup CredFree to release allocated memory (after marshalling structs).

There is a mistake in your pointer arithmetic. You have to increment with pointer size so try:

...
Dim p As IntPtr = pCredentials
For n As Integer = 0 To count - 1
  credentials.Add(Marshal.PtrToStructure(Marshal.ReadIntPtr(p), GetType(Credential)))
  p = p + IntPtr.Size
Next
...

UInt32 and UInteger are the same, so be consistent and choose one.

You could try to use Charset.Auto for everything, if that doesn't work, try Charset.Unicode and use CredEnumerateW function.

Upvotes: 1

Related Questions