Results 1 to 31 of 31

Thread: [VB6, twinBASIC] Getting the full path of all processes when not elevated

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Name:  enumproc.jpg
Views: 1864
Size:  24.3 KB

    If you've used programs like ProcessHacker or ProcessExplorer you might have noticed that you don't need to run them elevated to see the full paths of all the running processes. If you've ever tried this yourself with the standard methods of either reading the PEB or using the QueryFullProcessImageName API, you might have noticed this will fail for processes running as SYSTEM, because we're denied even the very limited PROCESS_QUERY_LIMITED_INFORMATION access right. So how can we find the full path anyway, when those are denied and the normal enum function CreateToolhelp32Snapshot only returns the file name? One way is to ask Windows itself, since it maintains a list of all process paths internally.

    This is done with the NtQuerySystemInformation and the undocumented info class SystemProcessIdInformation. This allows us to specify a SYSTEM_PROCESS_ID_INFORMATION type with the ProcessId filled in, and it will fill a buffer with the full path to the image.


    Code:
        Private Type SYSTEM_PROCESS_ID_INFORMATION
            ProcessId As LongPtr
            ImageName As UNICODE_STRING
        End Type
    
        Private Type UNICODE_STRING
            uLength As Integer
            uMaximumLength As Integer
            pBuffer As LongPtr
        End Type
    
        Private Function GetProcessFullPathEx(pid As Long, pPath As String) As Long
            'Regular method can't get path of SYSTEM process
            'Note: API Returns NT path
            Dim Status As Long 'NTSTATUS
            Dim lpBuffer As LongPtr
            Dim spii As SYSTEM_PROCESS_ID_INFORMATION
            Dim sTemp As String
            Dim cbMax As Long: cbMax = MAX_PATH * 2 'LenB(Of Integer) ' * sizeof(WCHAR)
            Dim cbRet As Long
            lpBuffer = LocalAlloc(LMEM_FIXED, cbMax)
            spii.ProcessId = pid
            spii.ImageName.uMaximumLength = cbMax
            spii.ImageName.pBuffer = lpBuffer
            
            Status = NtQuerySystemInformation(SystemProcessIdInformation, spii, LenB(spii), cbRet)
            If NT_SUCCESS(Status) Then
                sTemp = LPWSTRtoStr(lpBuffer, False)
                If bSetVM = False Then
                    MapVolumes
                End If
                pPath = ConvertNtPathToDosPath(sTemp)
            Else
                Debug.Print "GetProcessFullPathEx error, 0x" & Hex$(Status)
            End If
            
            LocalFree lpBuffer
            GetProcessFullPathEx = Status
        End Function
    The final piece of the puzzle is the path post-processing: ConvertNtPathToDosPath. This is needed because the paths we receive here are in the format e.g. \Device\HarddiskVolume1\Windows\System32\crss.exe rather than the drive letters we're accustomed to. The way we translate these is by first creating a map with the MapVolumes function of every drive letter to the device path:

    Code:
        Private Sub MapVolumes()
        'Map out \Device\Harddiskblahblah
        Dim sDrive As String
        Dim i As Long, j As Long
        Dim sBuffer As String
        ReDim VolMap(0)
        Dim tmpMap() As VolData
        Dim nMap As Long, nfMap As Long
        Dim lIdx As Long
        Dim lnMax As Long
        Dim cb As Long
        For lIdx = 0 To 25
            sDrive = Chr$(65 + lIdx) & ":"
            sBuffer = String$(1000, vbNullChar)
            cb = QueryDosDeviceW(StrPtr(sDrive), StrPtr(sBuffer), Len(sBuffer))
            If cb Then
                ReDim Preserve tmpMap(nMap)
                tmpMap(nMap).sLetter = sDrive
                tmpMap(nMap).sName = TrimNullW(sBuffer)
                nMap = nMap + 1
            End If
        Next
        'Next we need to sort the array so e.g. 10 will always come before 1
        'We'll find the longest ones, add any of that length, then add any
        'of 1 char shorter, until we've added all items
        For i = 0 To (nMap - 1)
            If Len(tmpMap(i).sName) > lnMax Then lnMax = Len(tmpMap(i).sName)
        Next i
        ReDim VolMap(nMap - 1)
        For i = lnMax To 1 Step -1
            For j = 0 To UBound(tmpMap)
                If Len(tmpMap(j).sName) = i Then
                    VolMap(nfMap).sName = tmpMap(j).sName
                    VolMap(nfMap).sLetter = tmpMap(j).sLetter
                    nfMap = nfMap + 1
                End If
            Next j
            If nfMap = nMap Then Exit For
        Next i
        bSetVM = True
        End Sub
    Then we just run each path through a find/replace of each map name to the corresponding letter.

    Code:
        Private Function ConvertNtPathToDosPath(sPath As String) As String
        If sPath = "" Then Exit Function
        
        Dim i As Long
        ConvertNtPathToDosPath = sPath
        For i = 0 To UBound(VolMap)
            ConvertNtPathToDosPath = Replace$(ConvertNtPathToDosPath, VolMap(i).sName, VolMap(i).sLetter, 1, 1)
        Next
        End Function
    All in all, this is more complicated than the traditional way, but there's plenty of situations where you're not able to run elevated but still want to display a list of processes and their paths--- or just their name, but need their paths to look up their icon.


    -----

    This code is compatible with both VB6 and twinBASIC, including 64bit compilation in the latter. The .twinproj is included, but you can reimport yourself to see the only change made is to use the smoother built in Anchor resizing rather than Form_Resize, as noted in the code.

    There are no dependencies and this should work on all Windows versions XP and later.

    UPDATE (2023 Nov 19) - Fix for garbage in system modules with no path.
    Attached Files Attached Files
    Last edited by fafalone; Nov 19th, 2023 at 01:49 PM.

  2. #2
    Fanatic Member
    Join Date
    Jun 2016
    Location
    España
    Posts
    630

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    It works fine, except that the first two appear strange and random values, both elevated and not
    It's not important
    Code:
    ProcId 0: [System idle process]
    ProcId 4: [System]
    ProcId 56 (Secure System): ?`?`????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÁ?????????????????????????????????????????????????????????????????????????????????????????????????????
    ProcId 116 (Registry): Registry??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÁ???????????????????????????????????????????????????????????????????????????????????????????????????
    
    
    
    ProcId 0: [System idle process]
    ProcId 4: [System]
    ProcId 56 (Secure System): ?I?I????????	?
    ?
    ?????????????????"?$?%?&?'?(?)?*?+?,?-?.?/?0?1?2?3?4?5?6?7?8?9?:?;?<?=?B?D?E?F?G?H?I?J?K?L?M?N?O?P?Q?R?S?T?U?V?W?X?Y?Z?[?\?]?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t?u?v?w?x?y?z?{?|?}?~??????????????????????? ?¡?©?ª?«?*?®????
    ProcId 116 (Registry): Registry??????	?
    ?
    ?????????????????"?$?%?&?'?(?)?*?+?,?-?.?/?0?1?2?3?4?5?6?7?8?9?:?;?<?=?B?D?E?F?G?H?I?J?K?L?M?N?O?P?Q?R?S?T?U?V?W?X?Y?Z?[?\?]?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t?u?v?w?x?y?z?{?|?}?~??????????????????????? ?¡?©?ª?«?*????
    
    
    ProcId 0: [System idle process]
    ProcId 4: [System]
    ProcId 56 (Secure System): ??
    ProcId 116 (Registry): Registry??????????????????????????????????????????????????????????????????????? !13? 1? !13;? 13? 1? 13? 1?"#$%? 1??-./0245?&?<????
    Windows 10 Home 22H2

  3. #3
    Fanatic Member
    Join Date
    Jan 2015
    Posts
    641

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Very good, and also very nice to have the same code/project running in VB6 and in Twin.

    If it should be always like that, this will force on mid term the use of TwinBasic (not ready on my part, I need to have my main big projects running 100% in Twin)

  4. #4

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    I noticed that with registry too... I didn't think uninitialized memory would be a concern, but I guess so, I'll update the post.

    This is the fix applied:
    Code:
    'For VBA7 block
        Private Declare PtrSafe Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (Destination As Any, ByVal Length As LongPtr)
    'For VB6 block
        Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (Destination As Any, ByVal Length As LongPtr)
    
    'For GetProcessFullPathEx, add the ZeroMemory call after the line below:
            lpBuffer = LocalAlloc(LMEM_FIXED, cbMax)
            ZeroMemory ByVal lpBuffer, cbMax
    You can make those changes or just re-download, it's been updated. Let me know if any further issues persist or arise.
    Last edited by fafalone; Nov 19th, 2023 at 01:51 PM.

  5. #5
    Fanatic Member
    Join Date
    Jun 2016
    Location
    España
    Posts
    630

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Work fine faf

  6. #6
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    I've known about this wonderful function NtQuerySystemInformation for a long time, it's my favorite function. But did you know that it doesn't always return the truth in Windows 7? If you start the process, then close the program, then rename this folder, and then run the program again, then the path to the process will already be old and incorrect. I described this topic in detail here: https://www.cyberforum.ru/visual-bas...ad3099779.html

    But this bug has most likely already been fixed in Windows 10. However, I strongly recommend that you heed my advice and use other functions to correctly get the full path to the executable file. For example PSAPI will show the correct result or reading the PEB structure

  7. #7

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    While I agree those are better first line options, the thread exists because those are not possible, on Windows 10/11 at least, on a SYSTEM process when your app isn't elevated. For both of those, you need to open a handle to the process, and even PROCESS_QUERY_LIMITED_INFORMATION errors with 'access denied' for SYSTEM processes when you're not elevated.

  8. #8
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    6,167

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Quote Originally Posted by fafalone View Post
    While I agree those are better first line options, the thread exists because those are not possible, on Windows 10/11 at least, on a SYSTEM process when your app isn't elevated. For both of those, you need to open a handle to the process, and even PROCESS_QUERY_LIMITED_INFORMATION errors with 'access denied' for SYSTEM processes when you're not elevated.
    I was wondering if using SetPrivilege SE_DEBUG_NAME, True call as implemented in this post might grant enough priviledges on your SYSTEM owned process.

    I was hoisting some of the linked trick's implementation for my embeded console project when reimplementing tab-completion I needed cmd.exe current directory and it turned out pretty short procedure:

    Code:
    Private Function pvGetCurrentDir(ByVal hProcess As Long) As String
        Const ProcessBasicInformation           As Long = 0
        Const sizeof_PBI                        As Long = 6 * 4
        Const offsetof_ProcessParameters        As Long = &H10
        Const offsetof_CurrentDirectory         As Long = &H24
        Const sizeof_UNICODESTRING              As Long = 2 * 4
        Dim lPtr            As Long
        Dim aTemp(0 To 5)   As Long
        Dim sBuffer         As String
        
        If NtQueryInformationProcess(hProcess, ProcessBasicInformation, aTemp(0), sizeof_PBI, 0) < 0 Then
            GoTo QH
        End If
        If ReadProcessMemory(hProcess, aTemp(1) + offsetof_ProcessParameters, lPtr, 4, 0) = 0 Then
            GoTo QH
        End If
        If ReadProcessMemory(hProcess, lPtr + offsetof_CurrentDirectory, aTemp(0), sizeof_UNICODESTRING, 0) = 0 Then
            GoTo QH
        End If
        sBuffer = String$((aTemp(0) And &HFFFF&) \ 2, 0)
        If ReadProcessMemory(hProcess, aTemp(1), ByVal StrPtr(sBuffer), LenB(sBuffer), 0) = 0 Then
            GoTo QH
        End If
        pvGetCurrentDir = sBuffer
    QH:
    End Function
    Thankfully in my case child cmd.exe process is always spawned w/ same parent VB6 process bitness so no x64 checks needed here.

    cheers,
    </wqw>

  9. #9

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Indeed it would. But SeDebugPrivilege is essentially the keys to the kingdom. It's how my RunAsTrustedInstaller project works-- if you have that privilege, you can immediately escalate to SYSTEM and have full run of the computer. So you need to be running elevated to get it. Or, possibly, a member of the Administrators group with UAC disabled, though I believe that's been blocked for a while now.
    Last edited by fafalone; Nov 20th, 2023 at 03:47 AM.

  10. #10

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Quote Originally Posted by wqweto View Post
    I was wondering if using SetPrivilege SE_DEBUG_NAME, True call as implemented in this post might grant enough priviledges on your SYSTEM owned process.

    I was hoisting some of the linked trick's implementation for my embeded console project when reimplementing tab-completion I needed cmd.exe current directory and it turned out pretty short procedure:

    Code:
    Private Function pvGetCurrentDir(ByVal hProcess As Long) As String
        Const ProcessBasicInformation           As Long = 0
        Const sizeof_PBI                        As Long = 6 * 4
        Const offsetof_ProcessParameters        As Long = &H10
        Const offsetof_CurrentDirectory         As Long = &H24
        Const sizeof_UNICODESTRING              As Long = 2 * 4
        Dim lPtr            As Long
        Dim aTemp(0 To 5)   As Long
        Dim sBuffer         As String
        
        If NtQueryInformationProcess(hProcess, ProcessBasicInformation, aTemp(0), sizeof_PBI, 0) < 0 Then
            GoTo QH
        End If
        If ReadProcessMemory(hProcess, aTemp(1) + offsetof_ProcessParameters, lPtr, 4, 0) = 0 Then
            GoTo QH
        End If
        If ReadProcessMemory(hProcess, lPtr + offsetof_CurrentDirectory, aTemp(0), sizeof_UNICODESTRING, 0) = 0 Then
            GoTo QH
        End If
        sBuffer = String$((aTemp(0) And &HFFFF&) \ 2, 0)
        If ReadProcessMemory(hProcess, aTemp(1), ByVal StrPtr(sBuffer), LenB(sBuffer), 0) = 0 Then
            GoTo QH
        End If
        pvGetCurrentDir = sBuffer
    QH:
    End Function
    Thankfully in my case child cmd.exe process is always spawned w/ same parent VB6 process bitness so no x64 checks needed here.

    cheers,
    </wqw>
    If you all were curious about the full structures so you can access members normally instead of via offset... they grow with each version of Windows, but here's the base XP versions you can use on that and higher:

    Code:
    Public Type QLARGE_INTEGER
        #If (TWINBASIC = 1) Or (Win64 = 1) Then
        QuadPart As LongLong
        #Else
       lowpart As Long
       highpart As Long
       #End If
    End Type
    
    Public Enum ImageSubsystemType
        IMAGE_SUBSYSTEM_UNKNOWN  = 0  ' Unknown subsystem.
        IMAGE_SUBSYSTEM_NATIVE  = 1  ' Image doesn't require a subsystem.
        IMAGE_SUBSYSTEM_WINDOWS_GUI  = 2  ' Image runs in the Windows GUI subsystem.
        IMAGE_SUBSYSTEM_WINDOWS_CUI  = 3  ' Image runs in the Windows character subsystem.
        IMAGE_SUBSYSTEM_OS2_CUI  = 5  ' image runs in the OS/2 character subsystem.
        IMAGE_SUBSYSTEM_POSIX_CUI  = 7  ' image runs in the Posix character subsystem.
        IMAGE_SUBSYSTEM_NATIVE_WINDOWS  = 8  ' image is a native Win9x driver.
        IMAGE_SUBSYSTEM_WINDOWS_CE_GUI  = 9  ' Image runs in the Windows CE subsystem.
        IMAGE_SUBSYSTEM_EFI_APPLICATION  = 10  '
        IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  = 11  '
        IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER  = 12  '
        IMAGE_SUBSYSTEM_EFI_ROM  = 13
        IMAGE_SUBSYSTEM_XBOX  = 14
        IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION  = 16
        IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG  = 17
    End Enum
    
    Public Enum NTGLB_Flags
        FLG_STOP_ON_EXCEPTION           = &H00000001
        FLG_SHOW_LDR_SNAPS              = &H00000002
        FLG_DEBUG_INITIAL_COMMAND       = &H00000004
        FLG_STOP_ON_HUNG_GUI            = &H00000008
        FLG_HEAP_ENABLE_TAIL_CHECK      = &H00000010
        FLG_HEAP_ENABLE_FREE_CHECK      = &H00000020
        FLG_HEAP_VALIDATE_PARAMETERS    = &H00000040
        FLG_HEAP_VALIDATE_ALL           = &H00000080
        FLG_POOL_ENABLE_TAIL_CHECK      = &H00000100 '3.51 to 5.0
        FLG_APPLICATION_VERIFIER        = &H00000100 '5.1+
        FLG_MONITOR_SILENT_PROCESS_EXIT = &H00000200 '6.1+ only
        FLG_POOL_ENABLE_TAGGING         = &H00000400
        FLG_HEAP_ENABLE_TAGGING         = &H00000800
        FLG_USER_STACK_TRACE_DB         = &H00001000
        FLG_KERNEL_STACK_TRACE_DB       = &H00002000
        FLG_MAINTAIN_OBJECT_TYPELIST    = &H00004000
        FLG_HEAP_ENABLE_TAG_BY_DLL      = &H00008000&
        FLG_IGNORE_DEBUG_PRIV           = &H00010000 '3.51 to 4.0
        FLG_DISABLE_STACK_EXTENSION     = &H00010000 '5.1+(5.0 is undef)
        FLG_ENABLE_CSRDEBUG             = &H00020000
        FLG_ENABLE_KDEBUG_SYMBOL_LOAD   = &H00040000
        FLG_DISABLE_PAGE_KERNEL_STACKS  = &H00080000
        FLG_HEAP_ENABLE_CALL_TRACING    = &H00100000 '3.51 to 4.0
        FLG_ENABLE_SYSTEM_CRIT_BREAKS   = &H00100000 '5.1+ (5.0 is undef)
        FLG_HEAP_DISABLE_COALESCING     = &H00200000
        FLG_ENABLE_CLOSE_EXCEPTIONS     = &H00400000 '4.0+
        FLG_ENABLE_EXCEPTION_LOGGING    = &H00800000 '4.0+
        FLG_ENABLE_HANDLE_TYPE_TAGGING  = &H01000000 '4.0+
        FLG_HEAP_PAGE_ALLOCS            = &H02000000 '4.0+
        FLG_DEBUG_INITIAL_COMMAND_EX    = &H04000000 '4.0+
        FLG_DISABLE_DBGPRINT            = &H08000000 '5.0+
        FLG_CRITSEC_EVENT_CREATION      = &H10000000 '5.0+
        FLG_LDR_TOP_DOWN                = &H20000000 '5.1-6.2
        FLG_STOP_ON_UNHANDLED_EXCEPTION = &H20000000 '6.3+
        FLG_ENABLE_HANDLE_EXCEPTIONS    = &H40000000 '5.1+
        FLG_DISABLE_PROTDLLS            = &H80000000& '5.0+
    End Enum
    
    Public Enum APP_COMPAT_FLAGS
        KACF_OLDGETSHORTPATHNAME  = &H00000001
        KACF_VERSIONLIE_NOT_USED  = &H00000002
        KACF_GETDISKFREESPACE  = &H00000008
        KACF_FTMFROMCURRENTAPT  = &H00000020
        KACF_DISALLOWORBINDINGCHANGES  = &H00000040
        KACF_OLE32VALIDATEPTRS  = &H00000080
        KACF_DISABLECICERO  = &H00000100
        KACF_OLE32ENABLEASYNCDOCFILE  = &H00000200
        KACF_OLE32ENABLELEGACYEXCEPTIONHANDLING  = &H00000400
        KACF_RPCDISABLENDRCLIENTHARDENING  = &H00000800
        KACF_RPCDISABLENDRMAYBENULL_SIZEIS  = &H00001000
        KACF_DISABLEALLDDEHACK_NOT_USED  = &H00002000
        KACF_RPCDISABLENDR61_RANGE  = &H00004000
        KACF_RPC32ENABLELEGACYEXCEPTIONHANDLING = &H00008000&
        KACF_OLE32DOCFILEUSELEGACYNTFSFLAGS  = &H00010000
        KACF_RPCDISABLENDRCONSTIIDCHECK  = &H00020000
        KACF_USERDISABLEFORWARDERPATCH  = &H00040000
        KACF_OLE32DISABLENEW_WMPAINT_DISPATCH  = &H00100000
        KACF_ADDRESTRICTEDSIDINCOINITIALIZESECURITY  = &H00200000
        KACF_ALLOCDEBUGINFOFORCRITSECTIONS  = &H00400000
        KACF_OLEAUT32ENABLEUNSAFELOADTYPELIBRELATIVE  = &H00800000
        KACF_ALLOWMAXIMIZEDWINDOWGAMMA  = &H01000000
        KACF_DONOTADDTOCACHE  = &H80000000
    End Enum
    
    'Generally, Win XP - 8
    'https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/bitfield.htm
    Public Enum PEB_BITFIELD_OLD
        PebImageUsedLargePages = &H01
        PebIsProtectedProcess = &H02 'V+
        PebIsLegacyProcess = &H04 'V-8
        PebIsImageDynamicallyRelocated = &H08 'V+
        PebSkipPatchingUser32Forwarders = &H10
        PebIsPackagedProcess = &H20
        PebIsAppContainer = &H40
        PebIsProtectedProcessLight = &H80
    End Enum
    'Generally, Windows 10-11
    Public Enum PEB_BITFIELD_NEW
        PebNImageUsedLargePages = &H01
        PebNIsProtectedProcess = &H02 'V+
        PebNIsImageDynamicallyRelocated = &H04 'V+
        PebNSkipPatchingUser32Forwarders = &H08
        PebNIsPackagedProcess = &H10
        PebNIsAppContainer = &H20
        PebNIsProtectedProcessLight = &H40
        PebNIsLongPathAwareProcess = &H80
    End Enum
    
    '[ Description("This is the base compatibility PEB, usuable from Windows XP through 11+. For additional members, see additional PEBs, e.g. PEB_VISTA.")]
    Public Type PEB
        InheritedAddressSpace As Byte
        ReadImageFileExecOptions As Byte
        BeingDebugged As Byte
        BitField As Byte 'PEB_BITFIELD_OLD on XP, https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/bitfield.htm
        Mutant As LongPtr
        ImageBaseAddress As LongPtr
        Ldr As LongPtr
        ProcessParameters As LongPtr 'RTL_USER_PROCESS_PARAMETERS
        SubSystemData As LongPtr
        ProcessHeap As LongPtr
        FastPebLock As LongPtr
        AtlThunkSListPtr As LongPtr
        SparePtr2 As LongPtr
        EnvironmentUpdateCount As Long
        KernelCallbackTable As LongPtr
        SystemReserved(0) As Long
        SpareUlong As Long
        FreeList As LongPtr
        TlsExpansionCounter As Long
        TlsBitmap As LongPtr
        TlsBitmapBits(1) As Long
        ReadOnlySharedMemoryBase As LongPtr
        ReadOnlySharedMemoryHeap As LongPtr
        ReadOnlyStaticServerData As LongPtr
        AnsiCodePageData As LongPtr
        OemCodePageData As LongPtr
        UnicodeCaseTableData As LongPtr
        NumberOfProcessors As Long
        NtGlobalFlag As NTGLB_Flags
        #If (TWINBASIC = 0) And (Win64 = 0) Then
        pad(3) As Byte
        #End If
        CriticalSectionTimeout As QLARGE_INTEGER
        HeapSegmentReserve As LongPtr
        HeapSegmentCommit As LongPtr
        HeapDeCommitTotalFreeThreshold As LongPtr
        HeapDeCommitFreeBlockThreshold As LongPtr
        NumberOfHeaps As Long
        MaximumNumberOfHeaps As Long
        ProcessHeaps As LongPtr
        GdiSharedHandleTable As LongPtr
        ProcessStarterHelper As LongPtr
        GdiDCAttributeList As Long
        LoaderLock As LongPtr
        OSMajorVersion As Long
        OSMinorVersion As Long
        OSBuildNumber As Integer
        OSCSDVersion As Integer
        OSPlatformId As Long
        ImageSubsystem As ImageSubsystemType
        ImageSubsystemMajorVersion As Long
        ImageSubsystemMinorVersion As Long
        ImageProcessAffinityMask As LongPtr
        #If Win64 Then
        GdiHandleBuffer(59) As Long
        #Else
        GdiHandleBuffer(33) As Long    
        #End If
        PostProcessInitRoutine As LongPtr
        TlsExpansionBitmap As LongPtr
        TlsExpansionBitmapBits(31) As Long
        SessionId As Long
        AppCompatFlagsHi As Long
        AppCompatFlags As APP_COMPAT_FLAGS 'ULARGE_INTEGER
        AppCompatFlagUser As QLARGE_INTEGER
        pShimData As LongPtr
        AppCompatInfo As LongPtr
        CSDVersion As UNICODE_STRING
        ActivationContextData As LongPtr
        ProcessAssemblyStorageMap As LongPtr
        SystemDefaultActivationContextData As LongPtr
        SystemAssemblyStorageMap As LongPtr
        MinimumStackCommit As LongPtr
        #If (TWINBASIC = 0) And (Win64 = 0) Then
        pad2(3) As Byte
        #End If
    End Type
    There's a few other useful members in there. The OS version info is the *real* version, no version lies from compatibility shims.

    Here's the base XP version of RTL_USER_PROCESS_PARAMETERS:

    Code:
    Public Enum UPP_CONSOLE_FLAGS
        CONSOLE_IGNORE_CTRL_C = &H1 'Only defined flag in XP; later ones unknown
    End Enum
    
    Public Enum RTL_UPP_FLAGS
        RTL_USER_PROCESS_PARAMETERS_NORMALIZED  = &H01
        RTL_USER_PROCESS_PARAMETERS_PROFILE_USER  = &H02
        RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL  = &H04
        RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER  = &H08
        RTL_USER_PROCESS_PARAMETERS_UNKNOWN  = &H10
        RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB  = &H20
        RTL_USER_PROCESS_PARAMETERS_RESERVE_16MB  = &H40
        RTL_USER_PROCESS_PARAMETERS_CASE_SENSITIVE  = &H80
        RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS  = &H100
        RTL_USER_PROCESS_PARAMETERS_PROCESS_OR_1  = &H200
        RTL_USER_PROCESS_PARAMETERS_PROCESS_OR_2  = &H400
        RTL_USER_PROCESS_PARAMETERS_PRIVATE_DLL_PATH  = &H1000
        RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH  = &H2000
        RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING  = &H4000
        RTL_USER_PROCESS_PARAMETERS_NX  = &H20000
    End Enum
    
    Public Type RTL_DRIVE_LETTER_CURDIR
        Flags As Integer
        Length As Integer
        TimeStamp As Long
        DosPath As UNICODE_STRING
    End Type
    
    Public Type RTL_USER_PROCESS_PARAMETERS
        MaximumLength As Long
        Length As Long
        Flags As RTL_UPP_FLAGS
        DebugFlags As Long 'This is essentially a boolean that if non-zero triggers DbgBreakPoint() at certain points
        ConsoleHandle As LongPtr
        ConsoleFlags As UPP_CONSOLE_FLAGS
        StandardInput As LongPtr
        StandardOutput As LongPtr
        StandardError As LongPtr
        CurrentDirectory As CURDIR
        DllPath As UNICODE_STRING
        ImagePathName As UNICODE_STRING
        CommandLine As UNICODE_STRING
        Environment As LongPtr
        StartingX As Long
        StartingY As Long
        CountX As Long
        CountY As Long
        CountCharsX As Long
        CountCharsY As Long
        FillAttribute As CONSOLE_CHAR_ATTRIB
        WindowFlags As STARTUP_FLAGS
        ShowWindowFlags As SHOWWINDOW
        WindowTitle As UNICODE_STRING
        DesktopInfo As UNICODE_STRING
        ShellInfo As UNICODE_STRING
        RuntimeData As UNICODE_STRING
        CurrentDirectores(31) As RTL_DRIVE_LETTER_CURDIR 'Unused in all versions
    End Type
    The undefined enums are publicly documented common ones; SW_* for SHOWWINDOW, FORE/BACKGROUND_*/ and COMMON_LVB_* for FillAttrib, STARTF_* for startup flags.

    For all variations for Vista-11, see my tbShellLib API library in twinBASIC.
    Last edited by fafalone; Nov 26th, 2023 at 04:51 PM.

  11. #11
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine on fire (country of slaves)
    Posts
    750

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Thanks.
    Very useful info as well as your universal PEB. I saw a lot of mistakes all over when ppl try to declare it.
    Malware analyst, VirusNet developer, HiJackThis+ author || my CodeBank works

  12. #12

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Mistakes? What mistakes?

    (I accidentally left a stray inline comment in one, obviously only twinBASIC supports /* this */)

    Other than that, all offsets and total sizes have been confirmed with

    https://www.geoffchappell.com/studie.../peb/index.htm

    and https://www.vergiliusproject.com/kernels

  13. #13
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Dragokas meant that you have no errors. Let's put this misunderstanding down to the language barrier.

  14. #14

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    I did have an error though. I updated the post after Dragokas' comment made me look at it again

    Before he commented, it had: /* [ TypeHint(PEB_BITFIELD_OLD) ] */ BitField As Byte

    VB6 and VBA do not support inline comments with /* */ syntax like twinBASIC does, so if you pasted my definition in those, a syntax error would pop up.

    So the first line was a joke about it. *Now* there's no mistakes... now.

    Last edited by fafalone; Nov 26th, 2023 at 03:56 PM.

  15. #15
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine on fire (country of slaves)
    Posts
    750

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    fafalone, heh, the case when we both won from language barrier:

    I meant other people's deсlarations, mostly in C++, all over in the articles in Internet with random errors.
    However, I just realized, that I accidentally used outdated PEB you posted on my OSInfo topic, so you helped to fix me

    Please, also include "ImageSubsystemType" declaration, missed from code above.
    Malware analyst, VirusNet developer, HiJackThis+ author || my CodeBank works

  16. #16

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    I know you meant that, I was making a joke because my post had an error when you thought it didn't, and then I edited it to remove the error before posting my reply.

    Added ImageSubsystemType:

    Code:
    Public Enum ImageSubsystemType
        IMAGE_SUBSYSTEM_UNKNOWN  = 0  ' Unknown subsystem.
        IMAGE_SUBSYSTEM_NATIVE  = 1  ' Image doesn't require a subsystem (e.g. kernel mode drivers).
        IMAGE_SUBSYSTEM_WINDOWS_GUI  = 2  ' Image runs in the Windows GUI subsystem.
        IMAGE_SUBSYSTEM_WINDOWS_CUI  = 3  ' Image runs in the Windows character subsystem.
        IMAGE_SUBSYSTEM_OS2_CUI  = 5  ' image runs in the OS/2 character subsystem.
        IMAGE_SUBSYSTEM_POSIX_CUI  = 7  ' image runs in the Posix character subsystem.
        IMAGE_SUBSYSTEM_NATIVE_WINDOWS  = 8  ' image is a native Win9x driver.
        IMAGE_SUBSYSTEM_WINDOWS_CE_GUI  = 9  ' Image runs in the Windows CE subsystem.
        IMAGE_SUBSYSTEM_EFI_APPLICATION  = 10  '
        IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  = 11  '
        IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER  = 12  '
        IMAGE_SUBSYSTEM_EFI_ROM  = 13
        IMAGE_SUBSYSTEM_XBOX  = 14
        IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION  = 16
        IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG  = 17
    End Enum

  17. #17
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine on fire (country of slaves)
    Posts
    750

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    It's curious observation that *for some* processes:
    - SystemProcessIdInformation returns incorrect letter case for folder & correct for file names.
    - GetModuleFileNameEx vice versa returns incorrect letter case for file names & correct for folders.

    * "correct" in meaning that we're comparing with path how it looks in file system in explorer.
    Malware analyst, VirusNet developer, HiJackThis+ author || my CodeBank works

  18. #18
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    I found a bug in your project. As soon as I minimize the window, the runtime error 380 immediately pops up. Invalid property value.
    In the Form_Resize event, I added the line On Error Resume Next at the very beginning and this bug disappeared.
    Attached Images Attached Images  

  19. #19
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    I made exactly the same project, only I have half as many lines of code! There are 271 lines of code in the code from fafalone, there are only 119 lines of code in my code. Exactly the same project!!! I just don't know why fafalone wrote so much extra! I'm sorry, of course, that I didn't add 64-bit compatibility, but I don't think it will take up many more lines of code... I think to add 64-bit compatibility, you only need to add 10 lines somewhere, no more...

    Code:
    Option Explicit
    Private Declare Function NtQuerySystemInformation Lib "ntdll.dll" (ByVal infoClass As Long, Buffer As Any, ByVal BufferSize As Long, ret As Long) As Long
    Private Declare Function GetMem4 Lib "msvbvm60" (src As Any, dst As Any) As Long
    Private Declare Function GetMem8 Lib "msvbvm60" (src As Any, dst As Any) As Long
    Private Declare Function GetLogicalDriveStrings Lib "kernel32" Alias "GetLogicalDriveStringsW" (ByVal nBufferLength As Long, ByVal lpBuffer As Long) As Long
    Private Declare Function QueryDosDevice Lib "kernel32" Alias "QueryDosDeviceA" (ByVal lpDeviceName As String, ByVal lpTargetPath As String, ByVal ucchMax As Long) As Long
    Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenW" (ByVal lpString As Long) As Long
    Private Declare Sub memcpy Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    
    Private Const SystemProcessInformation As Long = &H5&
    Private Const SystemProcessIdInformation = 88
    Private Const STATUS_INFO_LENGTH_MISMATCH As Long = &HC0000004
    Private Const STATUS_SUCCESS As Long = 0&
    Private Const MAX_PATH = 260
    
    Private Type UNICODE_STRING
        Length As Integer
        MaxLength As Integer
        lpBuffer As Long
    End Type
    
    Private Type SYSTEM_PROCESS_ID_INFORMATION
        ProcessId As Long
        ImageName As UNICODE_STRING
    End Type
    
    Private Function GetProcessFullPathEx(ByVal pid As Long) As String
        Dim spii As SYSTEM_PROCESS_ID_INFORMATION
        Dim ProcName As String
        Dim cbRet As Long
        Dim cbMax As Long
        Dim sDrives As String
        Dim strBuff As String * MAX_PATH
        Dim DosDeviceName As String
        Dim cnt As Long
        Dim aDrive() As String
        Dim i As Long
        
        cbMax = MAX_PATH * 2
        ProcName = Space$(cbMax)
        
        spii.ProcessId = pid
        spii.ImageName.MaxLength = cbMax
        spii.ImageName.lpBuffer = StrPtr(ProcName)
        
        If NtQuerySystemInformation(SystemProcessIdInformation, spii, LenB(spii), cbRet) >= 0 Then
            ProcName = Left$(ProcName, spii.ImageName.Length / 2)
            
            cnt = GetLogicalDriveStrings(0&, StrPtr(sDrives))
            sDrives = Space$(cnt * 2)
            cnt = GetLogicalDriveStrings(Len(sDrives), StrPtr(sDrives))
            
            If Err.LastDllError = 0 Then
                aDrive = Split(Left$(sDrives, cnt - 1), vbNullChar)
                
                For i = 0 To UBound(aDrive)
                    If QueryDosDevice(Left$(aDrive(i), 2), strBuff, MAX_PATH) Then
                        DosDeviceName = Left$(strBuff, lstrlen(StrPtr(strBuff)))
                        
                        If InStr(1, ProcName, DosDeviceName, vbTextCompare) > 0 Then
                            GetProcessFullPathEx = Replace(ProcName, DosDeviceName, Left$(aDrive(i), 2), , 1, vbTextCompare)
                            Exit Function
                        End If
                    End If
                Next
            End If
        End If
    End Function
    
    Private Sub Command1_Click()
        Dim ret As Long
        Dim buf() As Byte
        Dim Offset As Long
        Dim deltaOffset As Long
        Dim pid As Long
        Dim ImgName As UNICODE_STRING
        Dim ProcName As String
        Dim nProc As Long
        
        Text1.Text = vbNullString
        
        If NtQuerySystemInformation(SystemProcessInformation, ByVal 0&, 0&, ret) = STATUS_INFO_LENGTH_MISMATCH Then
            ReDim buf(ret - 1)
            
            If NtQuerySystemInformation(SystemProcessInformation, buf(0), ret, ret) = STATUS_SUCCESS Then
                Do
                    nProc = nProc + 1
                    
                    GetMem4 buf(Offset + &H44), pid
                    GetMem8 buf(Offset + &H38), ImgName
                    ProcName = Space$(ImgName.Length \ 2)
                    memcpy ByVal StrPtr(ProcName), ByVal ImgName.lpBuffer, ImgName.Length
                    
                    If pid = 0 Then
                        PostLog "ProcId 0: [System idle process]"
                    ElseIf pid = 4 Then
                        PostLog "ProcId 4: [System]"
                    Else
                        PostLog "ProcId " & pid & " (" & ProcName & "): " & GetProcessFullPathEx(pid)
                    End If
                    
                    GetMem4 buf(Offset), deltaOffset
                    Offset = Offset + deltaOffset
                Loop While deltaOffset
                
                Text1.Text = Text1.Text & "Done. Enumerated " & nProc & " processes."
            End If
        End If
    End Sub
    
    Private Sub PostLog(sMsg As String)
        Text1.Text = Text1.Text & sMsg & vbCrLf
    End Sub
    
    Private Sub Form_Resize()
        On Error Resume Next
        Text1.Width = Me.Width - 380
        Text1.Height = Me.Height - 1270
    End Sub
    Attached Files Attached Files

  20. #20

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    You'd actually need more than 10 lines for x64 because your magic numbers trick to play the lines of code game hardcodes offsets that differ in x64, and the size of UniqueProcessId differs so you'd need to set up GetMemPtr conditionally calling GetMem8.

    Also part of the difference is creating a cache to avoid the performance penalty of remapping drives every time; I reused code from an app where that matters.

    I don't know why the attitude, you want me to go next and write an even smaller version that removes more features , performance, and readability?

  21. #21
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Just don't be mad at me, please. Well, yes, you're right, there will be more than 10 additional lines of code... And the offset addresses for 64-bit will also need to be redone, that's right. To be honest, I was very surprised why you didn't use the GetLogicalDriveStrings function in your code, it just shocked me.

    And in terms of execution speed, I don't think I'll be much slower, although I haven't checked. There may be a slight difference.

  22. #22

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Shocked? Lol. Don't worry I'm not mad.

    Just for a one off list it doesn't matter performance wise; but I reused code from my ETW app where performance very much matters; if your code takes too long the system will start dropping events, and it needs to convert dozens or even hundreds of paths per second. Wasn't trying to make it as compact as possible no matter the sacrifices.

  23. #23
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Quote Originally Posted by fafalone View Post
    There's a few other useful members in there. The OS version info is the *real* version, no version lies from compatibility shims.
    I would like to know more about this. Do you have a really-example in order to find out the Windows version using the NtQuerySystemInformation function?

  24. #24
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine on fire (country of slaves)
    Posts
    750

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    fafalone, don't take HackerVlad seriously. He always running for very questionable solutions causing saving few bytes of code sacrificing features, performance, and readability.
    Now, I don't even understand which offset of PE he read without deeping into reverse-engineering.
    Malware analyst, VirusNet developer, HiJackThis+ author || my CodeBank works

  25. #25

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Quote Originally Posted by Dragokas View Post
    fafalone, don't take HackerVlad seriously. He always running for very questionable solutions causing saving few bytes of code sacrificing features, performance, and readability.
    Now, I don't even understand which offset of PE he read without deeping into reverse-engineering.
    https://www.geoffchappell.com/studie...fo/process.htm

    I have the full def in vb syntax in WDL.

    Quote Originally Posted by HackerVlad View Post
    I would like to know more about this. Do you have a really-example in order to find out the Windows version using the NtQuerySystemInformation function?
    You're already reading the PEB in your code... its these:

    OSMajorVersion As Long
    OSMinorVersion As Long
    OSBuildNumber As Integer
    OSCSDVersion As Integer

    If you used full UDT defs instead of reading magic number offsets you'd be able to easily expand your code.

    But if you really want the most fun way to get the real Windows version...

    Code:
            Dim dwMajor As Long, dwMinor As Long, dwBuild As Long
            CopyMemory dwMajor, ByVal &H7FFE026C, 4
            CopyMemory dwMinor, ByVal &H7FFE0270, 4
            CopyMemory dwBuild, ByVal &H7FFE0260, 4
            Debug.Print dwMajor & "." & dwMinor & "." & dwBuild
    That's it. Works in both 32bit and 64bit. Details: https://www.vbforums.com/showthread....ER_SHARED_DATA

  26. #26
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Quote Originally Posted by fafalone View Post
    Also part of the difference is creating a cache to avoid the performance penalty of remapping drives every time; I reused code from an app where that matters.
    However, I don't think caching disks is the right strategy. Since a process in memory may suddenly appear unexpectedly with a new drive letter, a new process may be started from removable media. I have already tested this and thus your algorithm gets a path detection error.

    I took it, launched your program, clicked for the first time to get a list of processes. Then I connected a removable USB drive to the computer, after that a new drive letter appeared in the system, started a new process from this removable drive, then I clicked on the button to get a list of processes in your program again and of course it did not determine. It turned out to be a bug.

  27. #27

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Could add a remap if path not found condition without needlessly doing it every time.

  28. #28
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    fafalone, Thank you for your efforts! I'm really grateful to you. I am currently working on my project to read the full process paths, but I took your developments as a basis, in particular the NtQuerySystemInformation call with the SystemProcessIdInformation class. Today I decided to check this code in Windows XP and found that it doesn't work there at all. Please tell me, is there a way to make SystemProcessIdInformation work in Windows XP? To make your project work the same way in XP.

  29. #29

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Happy you've found my codeuseful Looks like it's Vista+ only. Would have to use a different method. You really need to support XP still?

  30. #30
    Fanatic Member HackerVlad's Avatar
    Join Date
    Nov 2023
    Posts
    681

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    I have the code for XP. But in order to read system processes, you need to increase user to admin-rights. It seems that in Windows XP, without elevation, it is impossible to read system processes...

  31. #31

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6, twinBASIC] Getting the full path of all processes when not elevated

    Cant on any version without elevation. Thats the point of the original post here; it's an alternate to more standard methods which require elevation

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width