Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1449

[VB6, twinBASIC] Code snippet: List all protected files on Vista+ w/ x64 compat

$
0
0
So on Windows XP and earlier, you had documented functions SfcGetFiles and SfcGetNextProtectedFile to get a list of all system-protected files under Windows Resource Protection. But these were either entirely removed or set to return an error in Vista+. Turns out this was just arbitrary and capricious behavior on Microsoft's part; they didn't remove the ability to generate such a list, merely locked it behind undocumented APIs GetNextFileMapContent/BeginFileMapEnumeration/CloseFileMapEnumeration. I found an existing implementation of how to use those APIs in Alex Dragokas' excellent HiJackThis program, but making that snippet simplified to use normal declares and make it x64 compatible turned out to be more challenging than expected-- I scoured the internet, checking GitHub code search, Google, Bing, and some LLMs; *nowhere* besides that module, which he credits to SSTREGG, gives any information about the arguments for those APIs, making it complete guesswork what needed to become LongPtr. (And another issue; the code as written in the module had string encoding issues).

It was still crashing after trying the most obvious combinations, until I finally correctly deduced that the size arguments in GetNextFileMapContent could possibly be size_t, which is 8 bytes under x64 instead of 4, and I finally found the right combo for the reserved arguments after making that change. Since this was tons of guesswork and non-obvious to anyone who isn't familiar with the myriad Windows data types, thought I'd post my version too:

On a Form with CommandButton Command1, with no other dependencies:

Code:

Option Explicit

    Private Type PPROTECTED_FILE_INFO
        Length As Long
        FileName(259) As Integer
    End Type
    #If VBA7 Then
    Private Declare PtrSafe Function BeginFileMapEnumeration Lib "sfc_os.dll" (ByVal Reserved0 As Long, ByVal Reserved1 As LongPtr, Handle As LongPtr) As Long
    Private Declare PtrSafe Function CloseFileMapEnumeration Lib "sfc_os.dll" (ByVal Handle As LongPtr) As Long
    Private Declare PtrSafe Function GetNextFileMapContent Lib "sfc_os.dll" (ByVal Reserved As Long, ByVal SfcHandle As LongPtr, ByVal Size As LongPtr, ProtectedInfo As PPROTECTED_FILE_INFO, dwNeeded As LongPtr) As Long
    #Else
    Private Enum LongPtr
        [_]
    End Enum
    Private Declare Function BeginFileMapEnumeration Lib "sfc_os.dll" (ByVal Reserved0 As Long, ByVal Reserved1 As LongPtr, Handle As LongPtr) As Long
    Private Declare Function CloseFileMapEnumeration Lib "sfc_os.dll" (ByVal Handle As LongPtr) As Long
    Private Declare Function GetNextFileMapContent Lib "sfc_os.dll" (ByVal Reserved As Long, ByVal SfcHandle As LongPtr, ByVal Size As LongPtr, ProtectedInfo As PPROTECTED_FILE_INFO, dwNeeded As LongPtr) As Long
    #End If
    Private Const ERROR_NO_MORE_FILES As Long = 18
    Private Const ERROR_INSUFFICIENT_BUFFER As Long = 122
 
    Private Sub Command1_Click() 'Handles Command1.Click
        Dim sf() As String
        sf = SFCList_Vista()
 
        Dim i As Long
        For i = 0 To UBound(sf)
            Debug.Print sf(i)
        Next
   
    End Sub
    Public Function SFCList_Vista() As String()
        On Error GoTo ErrorHandler:

        Dim dwNeeded        As LongPtr
        Dim dwBufferSize    As Long
        Dim pData            As PPROTECTED_FILE_INFO
        Dim hSFC            As LongPtr
        Dim ret              As Long
        Dim SFCList()        As String
        Dim i                As Long
 
        ret = BeginFileMapEnumeration(0&, 0&, hSFC)
        If hSFC = 0 Then
            Debug.Print "Error! Cannot get handle of first element of BeginFileMapEnumeration."
            Exit Function
        Else
            Debug.Print "Init ok"
        End If
   
        dwBufferSize = LenB(pData)
 
        ReDim SFCList(300)
   
        Do
            ret = GetNextFileMapContent(0&, hSFC, dwBufferSize, pData, dwNeeded)
   
            Select Case Err.LastDllError ' <--- Does not working here !!!
       
                Case 0
                    If UBound(SFCList) < i Then ReDim Preserve SFCList(i + 100)
                    SFCList(i) = WCHARtoSTR(pData.FileName)
                    i = i + 1
       
                Case ERROR_NO_MORE_FILES Or (pData.Length = 0)
                    Exit Do
       
                Case ERROR_INSUFFICIENT_BUFFER Or (dwNeeded > dwBufferSize)
                    Debug.Print "ERROR_INSUFFICIENT_BUFFER"
   
            End Select

            If pData.Length = 0 Then Exit Do

        Loop
 
        CloseFileMapEnumeration hSFC
   
        If i = 0 Then
            ReDim SFCList(0)
        Else
            ReDim Preserve SFCList(i - 1)
        End If
        SFCList_Vista = SFCList
   
        Exit Function
ErrorHandler:
        Debug.Print "SFCList_Vista errorhandler::" & Err.Number & "->" & Err.Description
    End Function
 
    Private Function WCHARtoSTR(aCh() As Integer) As String
    Dim i As Long
    Dim sz As String
    For i = LBound(aCh) To UBound(aCh)
        If aCh(i) <> 0 Then
            sz = sz & ChrW$(CLng(aCh(i)))
        End If
    Next
    WCHARtoSTR = sz
    End Function

Thanks again to Dragokas and SSTREGG.

PS- The C prototypes would then be (as typedefs since you'd have to call them by pointer from GetProcAddress):

Code:

typedef struct PPROTECTED_FILE_INFO {
    DWORD length;
    WCHAR FileName[MAX_PATH];
} PPROTECTED_FILE_INFO, *PPPROTECTED_FILE_INFO;
typedef BOOL (WINAPI * BeginFileMapEnumeration)(_In_ DWORD Reserved0, _Reserved_ PVOID Reserved1, _Out_ PHANDLE Handle);
typedef BOOL (WINAPI * CloseFileMapEnumeration)(_In_ HANDLE SfcHandle);
typedef BOOL (WINAPI * GetNextFileMapContent)(_In_ DWORD Reserved, _In_ HANDLE SfcHandle, _In_ SIZE_T Size, _Out_ PPROTECTED_FILE_INFO *ProtectedInfo As , _Out_ SIZE_T *dwNeeded);

I'm guessing on the BOOL return type for the first two; only fairly sure that GetNextFileMapContent is because of the GetLastError usage (Err.LastDllError), the others could plausibly be HRESULT but since they're both 4-byte types it at least won't crash. The others are guesses with e.g. DWORD vs UINT, PVOID vs void*, etc, but are interchangeable.

Viewing all articles
Browse latest Browse all 1449

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>