Jump to content

Recommended Posts

I've been working with the Windows Credentials store to store credentials for lots of RDP connections.  I'm also using this code in other scripts to store and retrieve "legacy" credentials for my scripts that have a Save Password checkbox.

All goes well, until someone requests a button to display a list of all saved credentials.  I found the CredEnumerate call and it looks like it's working but the Target and UserName field that I want is stored inside an array of pointers and I can't figure out how to get data from inside that.  I found a post from 2009 that talks about this, but there was never a solution.

 

Below are my functions put into an example script.  the _Credentials_Enumerate() is where i'm having problems.   Anybody have some ideas?

Thanks,

Mike

 

;Credentials Manager
#include <array.au3>
#include <WinAPI.au3>   ;Needed for the _WinAPI_GetLastError()

;------------------------------------------------------------------------
;----- Add items into the Credentials Store  ----------------------------
;------------------------------------------------------------------------
;~ _Cred_Add("MyCredStored", "ItsMe", "Secret1", "", 1)                         ;Add a Local Credentials so we can test the retrieval of a password

;~ $aAddCred = _Cred_Add("MyServer", "Domain\adminAccount", "MyS3cr3+P@ssw0rd") ;Add domain Credentials that can only be used with RDP and other such items
;~ _ArrayDisplay($aAddCred, "AddCred")
;------------------------------------------------------------------------


;------------------------------------------------------------------------
;----- Retrieve Credentials from the Credentials Store  -----------------
;------------------------------------------------------------------------
;~ $aCreds = _Cred_Get("MyServer", 2)                                           ;Retrieve Domain Cred's, won't have password in it
;~ _ArrayDisplay($aCreds, "Credentials")

$aCreds = _Credentials_Enumerate()                                              ;Get a list of all credentials currently stored on the system **(DOES NOT WORK)**
;------------------------------------------------------------------------



;------------------------------------------------------------------------
;----- Delete a Credential from the Credentials Store  ------------------
;------------------------------------------------------------------------
;~ _Cred_Delete("MyServer")                                                     ;Delete the specified item from the Credential Store

;~ For $d = 1 to UBound($aCreds) - 1
;~  _Cred_Delete($aCreds[$d][0])                                                ;Loop to delete all items found.  **(DOES NOT WORK)**
;~ Next
;------------------------------------------------------------------------




;================================================================================================
;===== Add a Credential into the Credentials Store  =============================================
;================================================================================================
Func _Cred_Add($sTarget, $sUser, $sPassword, $sComm = "", $iType = 2)   ;Type: 2=Domain, 1=Local
    Local $structTarget = DllStructCreate("wchar[100]")                 ; Create a structure to hold the Target object name
    DllStructSetData($structTarget, 1, $sTarget)                        ; Insert the target name into that Structure

    Local $structUser = DllStructCreate("wchar[100]")                   ; Create a structure to hold the UserName to use
    DllStructSetData($structUser, 1, $sUser)                            ; Insert the user name into the structure

    Local $structPwd = DllStructCreate("wchar[100]")                    ; Create a structure to hole the password to use
    DllStructSetData($structPwd, 1, $sPassword)                         ; Insert the password into the structure

    Local $structComment = DllStructCreate("wchar[100]")                ; I don't see where this is used, but was in all the examples
    DllStructSetData($structComment, 1, $sComm)

    Local $structCREDENTIAL= "" & _
        "DWORD Flags;" & _
        "DWORD Type;"  & _
        "Ptr TargetName;" & _
        "Ptr Comment;" & _
        "UINT64 LastWritten;" & _
        "DWORD CredintialBlobSize;" & _
        "Ptr CredentialBlob;" & _
        "DWORD Persist;" & _
        "DWORD AttributeCount;" & _
        "ptr Attributes;" & _
        "Ptr TargetAlias;" & _
        "Ptr Username"

    Local $NewCred = DllStructCreate($structCREDENTIAL)
    If @error Then
        MsgBox(0, "NewCred", "Error in DllStructCreate " & @error);
        Exit
    EndIf

    DllStructSetData($NewCred,"Flags",0)
    DllStructSetData($NewCred,"Type",$iType)    ;2 = Domain, 1 = Generic
    DllStructSetData($NewCred,"TargetName",DllStructGetPtr($structTarget))
    DllStructSetData($NewCred,"Persist",3)
    DllStructSetData($NewCred,"AttributeCount",0)
    DllStructSetData($NewCred,"UserName",DllStructGetPtr($structUser))
    DllStructSetData($NewCred,"CredentialBlob",DllStructGetPtr($structPwd))
    DllStructSetData($NewCred,"CredintialBlobSize",StringLen($sPassword)*2)
    DllStructSetData($NewCred,"Comment",DllStructGetPtr($structComment))

    Local $hAdvapi32 = DllOpen("Advapi32.dll")
    If $hAdvapi32 = -1 Then
        Msgbox(0, "Error", "Failed to connect to the Credentials Store")
        Exit
    Endif

    $Ret = DllCall($hAdvapi32, 'bool', 'CredWriteW', 'ptr', DllStructGetPtr($NewCred), 'dword', 0)
    $NewCred = 0

    If IsArray($Ret) Then
        Return $Ret
    Else
        Return SetError(1)
    EndIf

EndFunc ;_Cred_Add


;================================================================================================
;===== Retrieve the Credentials for the specified item  =========================================
;================================================================================================
Func _Cred_Get($sTarget, $iType = 1)  ;Type: 2=Domain, 1=Local.  CAN'T RETURN DOMAIN PASSWORDS!!!
    Local $FuncRet[3]

    Local $structTarget = DllStructCreate("wchar[100]")
    DllStructSetData($structTarget,1,$sTarget)

    Local $hAdvapi32 = DllOpen("Advapi32.dll")
    If $hAdvapi32 = -1 Then
        Msgbox(0, "Error", "Failed to connect to the Credentials Store")
        Exit
    Endif

    Local $Ret = DllCall($hAdvapi32, 'bool', 'CredReadW', 'ptr', DllStructGetPtr($structTarget), 'dword', $iType, 'dword', 0, 'ptr*', 0)

    if $ret[0]=0 then Return SetError(1,0,$FuncRet)

    Local $structCREDENTIAL= "" & _
        "DWORD Flags;" & _
        "DWORD Type;"  & _
        "Ptr TargetName;" & _
        "Ptr Comment;" & _
        "UINT64 LastWritten;" & _
        "DWORD CredintialBlobSize;" & _
        "Ptr CredentialBlob;" & _
        "DWORD Persist;" & _
        "DWORD AttributeCount;" & _
        "Ptr Attributes;" & _
        "Ptr TargetAlias;" & _
        "Ptr Username"

    Local $tdata=DllStructCreate($structCREDENTIAL, $Ret[4])

    Local $userName = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'Username'))
    Local $User = DllStructGetData($userName, 1)

    Local $CredentialBlobSize = DllStructGetData($tdata, 'CredintialBlobSize')
    Local $credentialBlob = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'CredentialBlob'))
    Local $Password = StringLeft(DllStructGetData($credentialBlob, 1), $CredentialBlobSize/2)

    Local $Comment = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'Comment'))
    Local $Comm = DllStructGetData($Comment, 1)

    Dim $FuncRet[] = [$User, $Password, $Comm]
    Return $FuncRet

EndFunc ;_Cred_Get



;================================================================================================
;===== Delete a specified item from the Credentials Store  ======================================
;================================================================================================
Func _Cred_Delete($sTarget, $iType = 2)                 ;Type: 2=Domain, 1=Local
    Local $structTarget = DllStructCreate("wchar[100]") ;Create a structure to hold the object name we want to delete
    DllStructSetData($structTarget, 1, $sTarget)        ;Insert the Object Name into the Structure

    Local $hAdvapi32 = DllOpen("Advapi32.dll")
    If $hAdvapi32 = -1 Then
        Msgbox(0, "Error", "Failed to connect to the Credentials Store")
        Exit
    Endif

    ;Now send all the info into the DLL to delete the item
    $Ret = DllCall($hAdvapi32, 'bool', 'CredDeleteW', 'ptr', DllStructGetPtr($structTarget), 'dword', $iType, 'dword', 0) ;$iType 2 = Domain, 1 = Local

EndFunc ;_Cred_Delete



;================================================================================================
;===== Return a 2D array with the Target, UserName, Password for every item  ====================
;===== in the Credentials Store                                              ====================
;================================================================================================
Func _Credentials_Enumerate()
    ;https://msdn.microsoft.com/en-us/library/windows/desktop/aa374794(v=vs.85).aspx
    ;https://www.autoitscript.com/forum/topic/99705-credenumerate-function-call/?do=findComment&comment=715159

    Local $aResult
    Local $structCREDENTIAL = "DWORD Flags;" & _
                                "DWORD Type;"  & _
                                "Ptr TargetName;" & _
                                "Ptr Comment;" & _
                                "UINT64 LastWritten;" & _
                                "DWORD CredintialBlobSize;" & _
                                "Ptr CredentialBlob;" & _
                                "DWORD Persist;" & _
                                "DWORD AttributeCount;" & _
                                "Ptr Attributes;" & _
                                "Ptr TargetAlias;" & _
                                "Ptr Username"


    $aResult = DllCall('advapi32.dll', 'int', 'CredEnumerateW', _   ;Call the Unicode version of CredEnumerate
                        'wstr', Null, _                             ;Don't use any filter since I want everything returned
                        'uint', 1, _                                ;1 = CRED_ENUMERATE_ALL_CREDENTIALS
                        'uint*', '', _                              ;Return the Count of all stored credentials
                        'ptr*', '')                                 ;Returns a pointer to an Array of pointers?

    If @error Or ($aResult[0] = 0) Then
        ConsoleWrite('Error: ' & @error & @TAB & 'Extended: ' & @extended & @CRLF)
        ConsoleWrite(_WinAPI_GetLastError() & @CRLF)                ;1168 = Nothing matches the filter, 1312 = no credential set for this user, 1004 = Flag/Filter options are wrong
        Return SetError(1)
    EndIf


    ConsoleWrite("DllCall Returned = " & $aResult[0] & @CRLF & "Credential Count = " & $aResult[3] & @CRLF & "Pointer to Creds Array = " & $aResult[4] & @CRLF)

    For $c = 2 to $aResult[3] ;Create enough struct for each item in each credential found
        $structCREDENTIAL &= "DWORD Flags;" & _
                                "DWORD Type;"  & _
                                "Ptr TargetName;" & _
                                "Ptr Comment;" & _
                                "UINT64 LastWritten;" & _
                                "DWORD CredintialBlobSize;" & _
                                "Ptr CredentialBlob;" & _
                                "DWORD Persist;" & _
                                "DWORD AttributeCount;" & _
                                "Ptr Attributes;" & _
                                "Ptr TargetAlias;" & _
                                "Ptr Username"
    Next


    Local $tdata = DllStructCreate($structCREDENTIAL, $aResult[4])                                      ;Insert all the data from the array of pointers into this struct
    Local $FullTarget = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'TargetName'))           ;Create and Get the array storing TargetName
    Local $userName = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'Username'))               ;Create and Get the array storing Username
    Local $CredentialBlobSize = DllStructGetData($tdata, 'CredintialBlobSize')                          ;Get the password blob
    Local $credentialBlob = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'CredentialBlob'))   ;Create and get the password text
    Local $Comment = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'Comment'))                 ;Don't see a need for comments


    ;Retrieve the data
    For $c = 1 to $aResult[3]
        Local $Target = DllStructGetData($FullTarget, $c)                                               ;Retrieve the Target Name from the item #
        Local $User = DllStructGetData($userName, $c)                                                   ;Retrieve the User Name from the item #
        Local $Password = StringLeft(DllStructGetData($credentialBlob, $c), $CredentialBlobSize/2)      ;Retrieve the password, Only works for 1, legacy.  domain creds will not return passwords
        Local $Comm = DllStructGetData($Comment, $c)                                                    ;Don't need comments but getting it since it's in all the examples

        ConsoleWrite("Loop = " & $c & ":  Target = " & $Target & ":  UserName = " & $User & ":  Comment = " & $Comm & @CRLF)

    Next

    If $aResult[3] > 0 Then
        $aCreds = DllCall('advapi32.dll', 'none', 'CredFree', 'ptr', $aResult[4])   ;This is just used to release the pointer.  Call when done
    EndIf


EndFunc ;_Credentials_Enumerate

 

Link to post
Share on other sites

I have a workaround for now which uses the cmdkey.exe with stdoutread.  Not as graceful as using the DLL calls, but it gets me what I want.

Func _Credentials_Enumerate()
    Local $sCredList
    Local $iCred = -1
    Local $aCredList[10000][2]  ;set max number of Creds initally to 10,000, will be reDimed down before returning the array

    $iPID = Run(@SystemDir & "\cmdkey.exe /list", @SystemDir, @SW_HIDE, $STDOUT_CHILD)
    ProcessWaitClose($iPID)
    $sOutput = StdoutRead($iPID)

    If StringInStr($sOutput, "Currently stored credentials:") Then

        $aSplit = StringSplit($sOutput, @CRLF)

        For $c = 1 to $aSplit[0]
            If StringInStr($aSplit[$c], "Target:") Then

                $sCredList = StringStripWS(StringRight($aSplit[$c], StringLen($aSplit[$c]) - StringInStr($aSplit[$c], "=")), 3) ;Get the Target Server name
                $iCred += 1                                                 ;Increase the Credential counter by 1
                $aCredList[$iCred][0] = $sCredList                          ;Store the credential target in the 2D array

                For $d = $c to $aSplit[0]                                   ;Continue searching the output for this credentials UserName
                    If StringInStr($aSplit[$d], "User:") Then

                        $sCredList = StringStripWS(StringRight($aSplit[$d], StringLen($aSplit[$d]) - StringInStr($aSplit[$d], ":")), 3) ;Get the UserName
                        $aCredList[$iCred][1] = $sCredList                  ;Store the credential UserName in the 2D array

                        ExitLoop                                            ;We found the UserName, so exit out of this loop to look for the next Credential Target line
                    EndIf
                Next

                $c = $d                                                     ;Set the C loop to where we found the UserName line to speed it up a little

            EndIf
        Next

    Else

        MsgBox(0, "Stored Creds", "No stored credentials were found")

    EndIf

    ReDim $aCredList[$iCred][2]

    Return  $aCredList

EndFunc ;_Credentials_Enumerate

 

If anyone does know how to get the DLL call to work, I'd love to see it.

Thanks,

Mike

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By IndianSage
      Hi,
      My AutoIt script is as folllows:
      ;use for calling function add2NosA in dll ;Local $vNo1 = 33 ;Local $vNo2 = 11 ;use for calling function sortNos in dll Local $vNo1 = [11,7,9] Local $vNo2 = [1,3,2] ; _ArrayDisplay($vNo1, "vNo1 display") ; _ArrayDisplay($vNo2, "vNo2 display") ;Local $hWnd = DllOpen("E:\CV-Dell-1\autoit3\myComObj1.dll") Local $hWnd = ObjCreate("myComObj1.clsMath") if (@error) Then MsgBox (0, "Error", "Error1 = " & @error) Exit EndIf ;function call method - DllCall with function name ;Local $aRes = DllCall($hWnd, "int", "addNosA", "int", $vNo1, "int", $vNo2) ;Local $aRes = DllCall($hWnd, "Ptr", "sortNos", "Array", $vNo1, "Array", $vNo2) ;function call method - $hWnd.<function name> ;Local $aRes = $hWnd.add2NosA($vNo1, $vNo2) ; this works fine with ObjCreate Local $aRes = $hWnd.sortNos($vNo1, $vNo2) if (@error) Then MsgBox (0, "Error", "Error2 = " & @error) DllClose($hWnd) Exit EndIf ;use appropriate msgbox ;MsgBox(0,"Result", "Result = " & $vNo1[0]) MsgBox(0,"Result", "Result = " & $aRes) _ArrayDisplay($vNo1, "vNo1 display") _ArrayDisplay($aRes , "aRes display") DllClose($hWnd) My VB.Net - ClassLibrary - Dll - COM obj is as follows - has 2 functions - add2NosA and sortNos:
      <ComClass(clsMath.ClassId, clsMath.InterfaceId, clsMath.EventsId)> Public Class clsMath Public Const ClassId As String = "3A42F85E-24C8-4BAA-91B5-AE56C4683C13" Public Const InterfaceId As String = "D99D7C79-2BA7-4A33-B7BC-9B7F19FDF828" Public Const EventsId As String = "CA128AC4-580C-4112-9EAD-8D1599E3F37A" Public Sub New() MyBase.New() End Sub Public Function add2NosA(ByVal no1 As Integer, ByVal no2 As Integer) As Integer Return (no1 + no2) End Function Public Sub sortNos(ByRef no1 As Array, ByRef no2 As Array) Array.Sort(no1) no2 = no1 End Sub End Class  Over all I tried various 8 options mentioned in the attached Excel file - with only 1 combination working.
      Overall could not make Array returned capture in AutoIt script. 
      Can someone help please?
      Thanks in  advance.
      Options-Tried-Matrix-Results.xlsx
    • By IndianSage
      I have create a function in FreeBasic like below:
      Extern "Windows-MS" Type tA f1 As Integer f2 As Integer End Type Public Function _switchOrder(ByVal no1 As Integer, ByVal no2 As Integer) As tA Export Dim result As Integer Dim taa As tA taa.f1 = no2 taa.f2 = no1 Return taa End Function End Extern Caller AutoIt code is:
      #include <MsgBoxConstants.au3> Global Const $sTag_ftdi_version_info="struct; int no1a; int no2a; endstruct" Local $aRet=DllCall("Math1.dll","Ptr","_switchOrder", "Int", 10, "Int", 30) ;MsgBox (0,"",@error & "-" & $aRet[0] & "-" & $aRet[1]& "-" & $aRet[2]) Local $t_ftdi_version_info=DllStructCreate($sTag_ftdi_version_info,$aRet[0]) MsgBox (0,"msg1=",@error & "---" & $aRet[0] & "-" & $aRet[1]& "-" & $aRet[2]) ;Local $retData1 = DllStructGetData($t_ftdi_version_info,"",1) Local $retData1 = DllStructGetData($t_ftdi_version_info,"no1a") MsgBox (0,"msg2=",@error & "--" & $retData1) ;Local $retData1 = DllStructGetData($t_ftdi_version_info,"",2) Local $retData1 = DllStructGetData($t_ftdi_version_info,"no2a") MsgBox (0,"msg2=",@error & "--" & $retData1) ;ConsoleWrite(DllStructGetData($t_ftdi_version_info,"",2) & "--" & @error) ;ConsoleWrite(DllStructGetData($t_ftdi_version_info,"no2a") & @CRLF) ;ConsoleWrite(DllStructGetData($tversion_str,1) & @CRLF) Getting error 2 for DllStructGetData or it give Close Application AutoIt popup message. 
      Certainly DllCall is not returning pointer to the Structure in $aRet[0] hence issue.
      Can someone help me fix this please?
      Thanks in advance.
       
       
    • By xYuri
      This simple dllcall gives me error 5, access denied,
      Func _WinAPI_VkKeyScan($__key) _WinAPI_SetLastError(0) $res = DllCall('User32.dll', 'SHORT', 'VkKeyScan', 'CHAR', $__key) _xConsole('res: '&$res) $_LastErr = _WinAPI_GetLastError() If $_LastErr <> 0 Then _xConsole('Err: {' & $_LastErr & '}> ' & _WinAPI_GetLastErrorMessage()) Return $res EndFunc Am i doing something wrong?
      Also tried VkKeyScanA and W
      Edit:
      I want to send `:` via PostMessage() WM_KEYDOWN
    • By Skysnake
      This is relevant
      From here https://stackoverflow.com/questions/3454315/is-it-possible-to-pin-a-dll-in-memory-to-prevent-unloading
      I use several UDFs on the Forum to do various things.  Those UDFs work very well.
      Effectively the UDFs are DLL wrappers, that make it possible to access DLL functions easily without the long hard slog of DLLCall() every time.
      However, I have now run into the issue that multiple UDF DLLCalls are slow. Not mind numbingly slow, but slow enough to become noticeable with a large of repeated function calls.
      So I was wondering, is it possible to "load a DLL into memory" and leave it there for the duration of my script's lifetime, avoid repeated DLL on-disk reads with a persistent in memory DLL?
      From Microsoft
      https://docs.microsoft.com/en-us/windows/desktop/dlls/about-dynamic-link-libraries
      Looks like what I want to do is: load-time dynamic linking,
      So next question, (a) how do I do this with AutoIt (b) How would this impact on standard AutoIt type DLL calls?
       
      The point is speed.  Is there a different approach?
      Or am I barking up the wrong tree?
      Skysnake
    • By supersonic
      Hi -
      Currently I'm playing around with Windows Credential Manager. I'm trying to access it with DllCall("advapi32.dll", ...) using the functions 'CredWriteW', 'CredReadW' and 'CredDeleteW'. All well. Another function I have to deal with is 'CredEnumerateW': https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credenumeratew/ .
      That's the test code I have so far:
      #include <Array.au3> #include <String.au3> Local $tCredentialsCount = DllStructCreate("DWORD;") Local $tPointerToArrayOfPointers = DllStructCreate("PTR;") ; Local $tPointerToArrayOfPointers = DllStructCreate(_StringRepeat("PTR;", 200)) ; ??? Local $aResult = DllCall("advapi32.dll", "BOOL", "CredEnumerateW", _ "WSTR", Null, "DWORD", 1, "DWORD", DllStructGetPtr($tCredentialsCount), "PTR", DllStructGetPtr($tPointerToArrayOfPointers)) If (Not @error) Then Local $iCredentialsCount = DllStructGetData($tCredentialsCount, 1) _ArrayDisplay($aResult, $iCredentialsCount) Local $hPointerToArrayOfPointers = DllStructGetData($tPointerToArrayOfPointers, 1) MsgBox(0, "$hPointerToArrayOfPointers", $hPointerToArrayOfPointers) ; Fails... For $i = 1 To 10 ; $iCredentialsCount MsgBox(0, $i & "___" & (($i * 2) - 1), DllStructGetData($tPointerToArrayOfPointers, ($i * 2) - 1)) Next $tCredentialsCount = 0 $tPointerToArrayOfPointers = 0 DllCall("advapi32.dll", "NONE", "CredFreeW", "PTR", $hPointerToArrayOfPointers) EndIf The DllCall seems to function properly - I get a valid count of credentials (on my computer ~ 133) and a pointer "to array of pointers".
      What is meant by "array of pointers"?
      Microsoft says: Pointer to an array of pointers to credentials. The returned credential is a single allocated block. Any pointers contained within the buffer are pointers to locations within this single allocated block.
      How to access these pointers... Contained within the buffer???
      Any information you can provide me would be greatly appreciated.
×
×
  • Create New...