[Functions] Processes / Threads / Handles / Modules

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

[Functions] Processes / Threads / Handles / Modules

20 Jun 2016, 08:11

GetThreadStartAddr – gets the start address of a thread
(source: GitHub)

Code: Select all

GetThreadStartAddr(ProcessID)
{
    hModule := DllCall("LoadLibrary", "str", "ntdll.dll", "uptr")

    if !(hSnapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x4, "uint", ProcessID))
        return "Error in CreateToolhelp32Snapshot"

    NumPut(VarSetCapacity(THREADENTRY32, 28, 0), THREADENTRY32, "uint")
    if !(DllCall("Thread32First", "ptr", hSnapshot, "ptr", &THREADENTRY32))
        return "Error in Thread32First", DllCall("CloseHandle", "ptr", hSnapshot)

    Addr := {}, cnt := 1
    while (DllCall("Thread32Next", "ptr", hSnapshot, "ptr", &THREADENTRY32)) {
        if (NumGet(THREADENTRY32, 12, "uint") = ProcessID) {
            hThread := DllCall("OpenThread", "uint", 0x0040, "int", 0, "uint", NumGet(THREADENTRY32, 8, "uint"), "ptr")
            if (DllCall("ntdll\NtQueryInformationThread", "ptr", hThread, "uint", 9, "ptr*", ThreadStartAddr, "uint", A_PtrSize, "uint*", 0) != 0)
                return "Error in NtQueryInformationThread", DllCall("CloseHandle", "ptr", hThread) && DllCall("FreeLibrary", "ptr", hModule)
            Addr[cnt, "StartAddr"] := Format("{:#016x}", ThreadStartAddr)
            Addr[cnt, "ThreadID"]  := NumGet(THREADENTRY32, 8, "uint")
            DllCall("CloseHandle", "ptr", hThread), cnt++
        }
    }

    return Addr, DllCall("CloseHandle", "ptr", hSnapshot) && DllCall("FreeLibrary", "ptr", hModule)
}
example & output:

Code: Select all

MsgBox % "StartAddr of first Thread:`t" GetThreadStartAddr(2280)[1].StartAddr
; Tested with PID of notepad++
; 0x000000003c5aa2

; ===============================================================================================================================

for k, v in GetThreadStartAddr(2280)                                             
    MsgBox % "ThreadID:`t`t" v.ThreadID "`nStartAddr:`t`t" v.StartAddr
; Tested with PID of notepad++
; ThreadID:     8052              |  1460              |  12116             |  8668              |  5376
; StartAddr:    0x000000003c5aa2  |  0x000000777ac6d0  |  0x0000000032c660  |  0x000000748875f0  |  0x000000777ac6d0

Ref:
- CreateToolhelp32Snapshot function (msdn)
- Thread32First function (msdn)
- Thread32Next function (msdn)
- OpenThread function (msdn)
- NtQueryInformationThread function (msdn)
- CloseHandle function (msdn)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

20 Jun 2016, 08:11

GetModuleBaseAddr – gets the base address and size of the module in the context of the owning process
(source: GitHub)

Code: Select all

GetModuleBaseAddr(ModuleName, ProcessID)
{
    if !(hSnapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x18, "uint", ProcessID))
        return "Error in CreateToolhelp32Snapshot"

    NumPut(VarSetCapacity(MODULEENTRY32, (A_PtrSize = 8 ? 568 : 548), 0), MODULEENTRY32, "uint")
    if !(DllCall("Module32First", "ptr", hSnapshot, "ptr", &MODULEENTRY32))
        return "Error in Module32First", DllCall("CloseHandle", "ptr", hSnapshot)

    ME32 := {}
    while (DllCall("Module32Next", "ptr", hSnapshot, "ptr", &MODULEENTRY32)) {
        if (ModuleName = StrGet(&MODULEENTRY32+ (A_PtrSize = 8 ? 48 : 32), 256, "cp0")) {
            ME32.Addr := Format("{:#016x}", NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 24 : 20), "uptr"))
            ME32.Size := Format("{:#016x}", NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 32 : 24), "uint"))
        }
    }

    return ME32, DllCall("CloseHandle", "ptr", hSnapshot)
}
example & output:

Code: Select all

ModuleBase := GetModuleBaseAddr("user32.dll", 2280)
MsgBox % "modBaseAddr:`t" ModuleBase.Addr "`nmodBaseSize:`t" ModuleBase.Size " bytes"
; Tested with PID of notepad++
; modBaseAddr:	0x00000074510000
; modBaseSize:	0x00000000147000 bytes

Ref:
- CreateToolhelp32Snapshot function (msdn)
- Module32First function (msdn)
- Module32Next function (msdn)
- CloseHandle function (msdn)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

20 Jun 2016, 08:12

GetProcessThreads – gets a list of threads in a process
(source: GitHub)

Code: Select all

GetProcessThreads(ProcessID)
{
    if !(hSnapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x4, "uint", ProcessID))
        return "Error in CreateToolhelp32Snapshot"

    NumPut(VarSetCapacity(THREADENTRY32, 28, 0), THREADENTRY32, "uint")
    if !(DllCall("Thread32First", "ptr", hSnapshot, "ptr", &THREADENTRY32))
        return "Error in Thread32First", DllCall("CloseHandle", "ptr", hSnapshot)

    Threads := []
    while (DllCall("Thread32Next", "ptr", hSnapshot, "ptr", &THREADENTRY32))
        if (NumGet(THREADENTRY32, 12, "uint") = ProcessID)
            Threads.Push(NumGet(THREADENTRY32, 8, "uint"))

    return Threads, DllCall("CloseHandle", "ptr", hSnapshot)
}
example & output:

Code: Select all

for k, v in GetProcessThreads(2280)                                             
    MsgBox % "ThreadID:`t`t" v
; Tested with PID of notepad++
; ThreadID:     6428    |    7296    |    7396    |    6844

Ref:
- CreateToolhelp32Snapshot function (msdn)
- Thread32First function (msdn)
- Thread32Next function (msdn)
- CloseHandle function (msdn)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

06 Jul 2016, 01:58

SuspendThread / Wow64SuspendThread & ResumeThread – suspends and resume the specified thread.
(source: GitHub)

Code: Select all

SuspendThread(ThreadID)
{
    if !(hThread := DllCall("OpenThread", "uint", 0x0002, "int", 0, "uint", ThreadID, "ptr"))
        return "Error in OpenThread"
    if (DllCall("SuspendThread", "ptr", hThread) = -1)
        return "Error in SuspendThread", DllCall("CloseHandle", "ptr", hThread)
    return true, DllCall("CloseHandle", "ptr", hThread)
}

Code: Select all

Wow64SuspendThread(ThreadID)
{
    if !(hThread := DllCall("OpenThread", "uint", 0x0002, "int", 0, "uint", ThreadID, "ptr"))
        return "Error in OpenThread"
    if (DllCall("Wow64SuspendThread", "ptr", hThread) = -1)
        return "Error in Wow64SuspendThread", DllCall("CloseHandle", "ptr", hThread)
    return true, DllCall("CloseHandle", "ptr", hThread)
}

Code: Select all

ResumeThread(ThreadID)
{
    if !(hThread := DllCall("OpenThread", "uint", 0x0002, "int", 0, "uint", ThreadID, "ptr"))
        return "Error in OpenThread"
    if (DllCall("ResumeThread", "ptr", hThread) = -1)
        return "Error in ResumeThread", DllCall("CloseHandle", "ptr", hThread)
    return true, DllCall("CloseHandle", "ptr", hThread)
}

Ref:
- OpenThread function (msdn)
- SuspendThread function (msdn)
- ResumeThread function (msdn)
- CloseHandle function (msdn)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Functions] Processes / Threads / Module

06 Jul 2016, 02:07

IsProcessElevated – check if a running process is elevated
(source: GitHub)

Code: Select all

IsProcessElevated(ProcessID)
{
    if !(hProcess := DllCall("OpenProcess", "uint", 0x1000, "int", 0, "uint", ProcessID, "ptr"))
        throw Exception("OpenProcess failed", -1)
    if !(DllCall("advapi32\OpenProcessToken", "ptr", hProcess, "uint", 0x0008, "ptr*", hToken))
        throw Exception("OpenProcessToken failed", -1), DllCall("CloseHandle", "ptr", hProcess)
    if !(DllCall("advapi32\GetTokenInformation", "ptr", hToken, "int", 20, "uint*", IsElevated, "uint", 4, "uint*", size))
        throw Exception("GetTokenInformation failed", -1), DllCall("CloseHandle", "ptr", hToken) && DllCall("CloseHandle", "ptr", hProcess)
    return IsElevated, DllCall("CloseHandle", "ptr", hToken) && DllCall("CloseHandle", "ptr", hProcess)
}
example & output:

Code: Select all

MsgBox % IsProcessElevated(DllCall("GetCurrentProcessId"))
; 0 => Process is not elevated
; 1 => Process is elevated

Ref:
- OpenProcess function (msdn)
- OpenProcessToken function (msdn)
- GetTokenInformation function (msdn)
- CloseHandle function (msdn)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Functions] Processes / Threads / Modules

07 Jul 2016, 02:58

GetProcessHandles – gets a list of handles in a process.
(source: tba)

Code: Select all

global hADVAPI32 := DllCall("LoadLibrary", "str", "advapi32.dll", "ptr")
global hNTDLL    := DllCall("LoadLibrary", "str", "ntdll.dll", "ptr")

; ===============================================================================================================================

GetProcessHandles(ProcessID)
{
	static hCurrentProcess := DllCall("GetCurrentProcess", "ptr")
    AdjustTokenPrivileges(hToken := OpenProcessToken(OpenProcess(hCurrentProcess, 0x400)), LookupPrivilegeValue("SeDebugPrivilege"))
    CloseHandle(hToken)
    
    SHI := SystemHandleInformation(HandleCount), PH := [], index := 1
    loop % HandleCount
    {
        if (SHI[A_Index, "PID"] = ProcessID)
        {
            if !(hProcess := OpenProcess(SHI[A_Index, "PID"], 0x440))
                continue
            if !(hObject  := DuplicateObject(hProcess, hCurrentProcess, Handle := SHI[A_Index, "Handle"], 4)) {
                CloseHandle(hObject)
                continue
            }
            PH[index, "Handle"]   := Handle
            PH[index, "Type"]     := ObjectTypeInformation(hObject)
            PH[index, "Name"]     := ObjectNameInformation(hObject)
            PH[index, "FilePath"] := GetFinalPathNameByHandle(hObject)
            CloseHandle(hObject)
            CloseHandle(hProcess)
            index++
        }
    }
    return PH
}

; ===============================================================================================================================

OpenProcess(ProcessID, Access := 0x400)                         ; https://msdn.microsoft.com/en-us/library/ms684320(v=vs.85).aspx
{
    if !(hProcess := DllCall("OpenProcess", "uint", Access, "int", 0, "uint", ProcessID, "ptr"))
        return (ErrorLevel := 1) & 0
    return hProcess
}

; ===============================================================================================================================

CloseHandle(hObject)                                            ; https://msdn.microsoft.com/en-us/library/ms724211(v=vs.85).aspx
{
    if !(DllCall("CloseHandle", "ptr", hObject))
        return (ErrorLevel := 1) & 0
    return 1
}

; ===============================================================================================================================

OpenProcessToken(hProcess, Access := 0x20)                      ; https://msdn.microsoft.com/en-us/library/aa379295(v=vs.85).aspx
{
    if !(DllCall("advapi32\OpenProcessToken", "ptr", hProcess, "uint", Access, "ptr*", hToken))
        return (ErrorLevel := 1) & 0
    return hToken
}

; ===============================================================================================================================

LookupPrivilegeValue(Name := "SeDebugPrivilege")                ; https://msdn.microsoft.com/en-us/library/aa379180(v=vs.85).aspx
{
    if !(DllCall("advapi32\LookupPrivilegeValue", "ptr", 0, "str", Name, "int64*", LUID))
        return (ErrorLevel := 1) & 0
    return LUID
}

; ===============================================================================================================================

AdjustTokenPrivileges(hToken, LUID)                             ; https://msdn.microsoft.com/en-us/library/aa375202(v=vs.85).aspx
{
    VarSetCapacity(TP, 16, 0) && NumPut(1, TP, 0, "uint") && NumPut(LUID, TP, 4, "int64") && NumPut(2, TP, 12, "uint")
    if !(DllCall("advapi32\AdjustTokenPrivileges", "ptr", hToken, "int", 0, "ptr", &TP, "uint", 0, "ptr", 0, "ptr", 0))
        return (ErrorLevel := 1) & 0
    return 1
}

; ===============================================================================================================================

QueryFullProcessImageName(hProcess)                             ; https://msdn.microsoft.com/en-us/library/ms684919(v=vs.85).aspx
{
    VarSetCapacity(ProcName, 520, 0)
    DllCall("QueryFullProcessImageName", "ptr", hProcess, "uint", 0, "str", ProcName, "uint*", size)
    return size
}

; ===============================================================================================================================

GetFinalPathNameByHandle(hFile)                                 ; https://msdn.microsoft.com/en-us/library/aa364962(v=vs.85).aspx
{
    size := DllCall("GetFinalPathNameByHandle", "ptr", hFile, "ptr", 0, "uint", 0, "uint", 0, "uint")
    VarSetCapacity(FilePath, size << 1, 0)
    if !(DllCall("GetFinalPathNameByHandle", "ptr", hFile, "str", FilePath, "uint", size, "uint", 0, "uint"))
        return (ErrorLevel := 1) & 0
    return SubStr(FilePath, 1, 4) = "\\?\" ? SubStr(FilePath, 5) : FilePath
}

; ===============================================================================================================================

DuplicateObject(hProcess, hCurrentProcess, Handle, Options)     ; https://msdn.microsoft.com/en-us/library/ff566445(v=vs.85).aspx
{
    if (DllCall("ntdll\ZwDuplicateObject", "ptr", hProcess, "ptr", Handle, "ptr", hCurrentProcess, "ptr*", hObject, "uint", 0, "uint", 0, "uint", Options) != 0)
        return (ErrorLevel := 1) & 0
    return hObject
}

; ===============================================================================================================================

ObjectNameInformation(handle)                                   ; https://msdn.microsoft.com/en-us/library/ff567062(v=vs.85).aspx
{
    static OBJECT_INFORMATION_CLASS := 1 ; ObjectNameInformation

    DllCall("ntdll\ZwQueryObject", "ptr", handle, "uint", OBJECT_INFORMATION_CLASS, "ptr", 0, "uint", 0, "uint*", size, "uint")
    VarSetCapacity(buf, size << 1, 0)
    NT_STATUS := DllCall("ntdll\ZwQueryObject", "ptr", handle, "uint", OBJECT_INFORMATION_CLASS, "ptr", &buf, "uint", size, "uint*", size, "uint")

    if (NT_STATUS != 0)
        return (ErrorLevel := 1) & 0

    return StrGet(NumGet(buf, A_PtrSize, "uptr"), NumGet(buf, 0, "ushort") // 2, "UTF-16")
}

; ===============================================================================================================================

ObjectTypeInformation(handle)                                   ; https://msdn.microsoft.com/en-us/library/ff567062(v=vs.85).aspx
{
    static OBJECT_INFORMATION_CLASS := 2 ; ObjectTypeInformation

    DllCall("ntdll\ZwQueryObject", "ptr", handle, "uint", OBJECT_INFORMATION_CLASS, "ptr", 0, "uint", 0, "uint*", size, "uint")
    VarSetCapacity(buf, size << 1, 0)
    NT_STATUS := DllCall("ntdll\ZwQueryObject", "ptr", handle, "uint", OBJECT_INFORMATION_CLASS, "ptr", &buf, "uint", size, "uint*", size, "uint")

    if (NT_STATUS != 0)
        return (ErrorLevel := 1) & 0

    return StrGet(NumGet(buf, A_PtrSize, "uptr"), NumGet(buf, 0, "ushort") // 2, "UTF-16")
}
; ===============================================================================================================================

SystemHandleInformation(ByRef NumberOfHandles)                  ; https://msdn.microsoft.com/en-us/library/ms725506(v=vs.85).aspx
{
    static SYSTEM_INFORMATION_CLASS := 0x10 ; SYSTEM_HANDLE_INFORMATION

    size := VarSetCapacity(buf, A_PtrSize * 4096)
    NT_STATUS := DllCall("ntdll\ZwQuerySystemInformation", "int", SYSTEM_INFORMATION_CLASS, "ptr", &buf, "uint", size, "uint*", size, "uint")
    while (NT_STATUS = 0xC0000004) {
        VarSetCapacity(buf, size)
        NT_STATUS := DllCall("ntdll\ZwQuerySystemInformation", "int", SYSTEM_INFORMATION_CLASS, "ptr", &buf, "uint", size, "uint*", size, "uint")
    }

    if (NT_STATUS != 0)
        return (ErrorLevel := 1) & 0

    NumberOfHandles := NumGet(buf, "uint")

    addr := &buf + A_PtrSize, SYSTEM_HANDLE_INFORMATION := {}
    loop % NumberOfHandles
    {
        SYSTEM_HANDLE_INFORMATION.Push({ PID:    NumGet(addr + 0, "uint")
                                       , Type:   NumGet(addr + 4, "uchar")
									   , Flags:  NumGet(addr + 5, "uchar")
                                       , Handle: NumGet(addr + 6, "ushort")
									   , Addr:   NumGet(addr + 8, "uptr")
									   , Access: NumGet(addr + 8, A_PtrSize, "uint") })
        addr += 8 + (A_PtrSize * 2)
    }
    return SYSTEM_HANDLE_INFORMATION
}

; ===============================================================================================================================

SystemHandleInformationEx(ByRef NumberOfHandles)                ; https://msdn.microsoft.com/en-us/library/ms725506(v=vs.85).aspx
{
    static SYSTEM_INFORMATION_CLASS := 0x40 ; SYSTEM_HANDLE_INFORMATION_EX

    size := VarSetCapacity(buf, A_PtrSize * 4096)
    NT_STATUS := DllCall("ntdll\ZwQuerySystemInformation", "int", SYSTEM_INFORMATION_CLASS, "ptr", &buf, "uint", size, "uint*", size, "uint")
    while (NT_STATUS = 0xC0000004) {
        VarSetCapacity(buf, size)
        NT_STATUS := DllCall("ntdll\ZwQuerySystemInformation", "int", SYSTEM_INFORMATION_CLASS, "ptr", &buf, "uint", size, "uint*", size, "uint")
    }

    if (NT_STATUS != 0)
        return (ErrorLevel := 1) & 0

    NumberOfHandles := NumGet(buf, "uint")

    addr := &buf + (A_PtrSize * 2), SYSTEM_HANDLE_INFORMATION_EX := {}
    loop % NumberOfHandles
    {
        SYSTEM_HANDLE_INFORMATION_EX.Push({ PID:    NumGet(addr + A_PtrSize,         "uptr")
                                          , Type:   NumGet(addr + A_PtrSize * 2,     "uptr")
                                          , Handle: NumGet(addr + A_PtrSize * 3 + 6, "ushort") })
        addr += 16 + (A_PtrSize * 3)
    }
    return SYSTEM_HANDLE_INFORMATION_EX
}

; ===============================================================================================================================
example:

Code: Select all

; GLOBAL SETTINGS ===============================================================================================================

#NoEnv
#SingleInstance Force
SetBatchLines -1

GPH := GetProcessHandles(your_pid_here)

; GUI ===========================================================================================================================

Gui, Margin, 5, 5
Gui, Add, ListView, xm ym w1200 h600, % "Handle|Type|Name|Path"
for i, v in GPH
    LV_Add("", v.Handle, v.Type, v.Name, v.FilePath)
LV_ModifyCol()
Gui, Show, AutoSize
return

; INCLUDES ======================================================================================================================

#Include GetProcessHandles.ahk

; EXIT ==========================================================================================================================

GuiEscape:
GuiClose:
ExitApp

; ===============================================================================================================================

Ref:
- OpenProcess function (msdn)
- CloseHandle function (msdn)
- OpenProcessToken function (msdn)
- LookupPrivilegeValue function (msdn)
- AdjustTokenPrivileges function (msdn)
- GetFinalPathNameByHandle function (msdn)
- ZwDuplicateObject function (msdn)
- ZwQueryObject (msdn)
- ZwQuerySystemInformation function (msdn)

Todo:
- Cleanup code
- More informations
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Functions] Processes / Threads / Modules

30 Mar 2017, 01:48

EnumProcesses – gets a list of all running processes.
(source: tba)

Code: Select all

WTSEnumerateProcessesEx()
{
    static hWTSAPI := DllCall("LoadLibrary", "str", "wtsapi32.dll", "ptr")

    if !(DllCall("wtsapi32\WTSEnumerateProcessesEx", "ptr", 0, "uint*", 0, "uint", -2, "ptr*", buf, "uint*", TTL))
        throw Exception("WTSEnumerateProcessesEx failed", -1)
    addr := buf, WTS_PROCESS_INFO := []
    loop % TTL
    {
        WTS_PROCESS_INFO[A_Index, "SessionID"]   := NumGet(addr+0, "uint")
        WTS_PROCESS_INFO[A_Index, "ProsessID"]   := NumGet(addr+4, "uint")
        WTS_PROCESS_INFO[A_Index, "ProcessName"] := StrGet(NumGet(addr+8, "ptr"))
        WTS_PROCESS_INFO[A_Index, "UserSID"]     := NumGet(addr+8+A_PtrSize, "ptr")
        addr += 8 + (A_PtrSize * 2)
    }
    if !(DllCall("wtsapi32\WTSFreeMemoryEx", "int", 0, "ptr", buf, "uint", TTL))
        throw Exception("WTSFreeMemoryEx failed", -1)
    return WTS_PROCESS_INFO
}
example:

Code: Select all

for i, v in WTSEnumerateProcessesEx()
	MsgBox % "SessionID:`t`t" v.SessionID   "`n"
	       . "ProsessID:`t`t" v.ProsessID   "`n"
		   . "ProcessName:`t" v.ProcessName "`n"
		   . "UserSID:`t`t"   v.UserSID
Ref:
WTSEnumerateProcessesEx function
WTS_PROCESS_INFO structure

Code: Select all

todo
Ref:
WTSEnumerateProcessesEx function
WTS_PROCESS_INFO_EX structure
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Functions] Processes / Threads / Modules

30 Mar 2017, 01:53

Questions & Answers

-> [Q & A] Processes / Threads / Handles / Modules

Updates

Code: Select all

Added EnumProcesses
Changed GetProcessHandles
  -> new returns
     -> handle type
	 -> handle name
	 -> filepath if handle type is file
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Functions] Processes / Threads / Handles / Modules

24 Oct 2017, 05:25

All functions will be implemented here (Process Explorer)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 100 guests