How to get the session ID of Windows using ctypes in Python?

93 Views Asked by At

I am attempting to retrieve the session ID of the current user session on Windows using ctypes in Python. My goal is to accomplish this without relying on external libraries.

Here is the code I have so far:

import ctypes
from ctypes import wintypes

def get_current_session_id():
    WTS_CURRENT_SERVER_HANDLE = ctypes.wintypes.HANDLE(-1)
    WTS_CURRENT_SESSION = 0xFFFFFFFF
    session_id = ctypes.create_string_buffer(4)  # Assuming a DWORD is 4 bytes

    server_handle = ctypes.windll.wtsapi32.WTSOpenServerW(None)

    if not server_handle:
        raise OSError("Failed to open server handle")

    try:
        result = ctypes.windll.wtsapi32.WTSQuerySessionInformationW(
            server_handle,
            WTS_CURRENT_SESSION,
            0,
            ctypes.byref(session_id),
            ctypes.byref(ctypes.c_ulong())
        )

        if not result:
            raise OSError("Failed to query session information")
    finally:
        ctypes.windll.wtsapi32.WTSCloseServer(server_handle)

    session_id_value = struct.unpack("I", session_id.raw)[0]
    return session_id_value

try:
    session_id = get_current_session_id()
    print("Current session ID:", session_id)
except OSError as e:
    print(f"Error: {e}")

However, I am encountering an access violation error (exception: access violation reading). I suspect there might be an issue with memory access or the usage of ctypes.

Can someone help me identify and correct the issue in the code? Additionally, if there are alternative approaches to achieve the same result using ctypes, I would appreciate any guidance.

I would appreciate any insights into what might be causing the access violation error and any suggestions for correcting the code.

What I Tried: I attempted to retrieve the session ID of the current user session on Windows using the provided Python script that utilizes ctypes.

What I Expected: I expected the script to successfully retrieve the session ID without encountering any errors, providing the correct session ID as output.

Actual Result: However, during execution, an access violation error (exception: access violation reading) occurred.

2

There are 2 best solutions below

1
zen2818 On

PROBLEM SOLVED:

import ctypes
from ctypes import wintypes

def get_current_session_id():
    ProcessIdToSessionId = ctypes.windll.kernel32.ProcessIdToSessionId
    ProcessIdToSessionId.argtypes = [wintypes.DWORD, wintypes.PDWORD]
    ProcessIdToSessionId.restype = wintypes.BOOL

    process_id = wintypes.DWORD(ctypes.windll.kernel32.GetCurrentProcessId())
    session_id = wintypes.DWORD()

    if ProcessIdToSessionId(process_id, ctypes.byref(session_id)):
        return session_id.value
    else:
        raise ctypes.WinError()

current_session_id = get_current_session_id()
print(f"Current session ID: {current_session_id}")
0
Mark Tolonen On

As you found in your answer, ProcessIdToSessionId is the correct function to call. The error handling is incorrect, however, and it is best practice to fully specify .argtypes and .restype for each function used:

import ctypes as ct
import ctypes.wintypes as w

# Function for .errcheck attribute
def boolcheck(result, func, args):
    if not result:
        raise ct.WinError(ct.get_last_error())

# Correctly capture Win32 GetLastError.
kernel32 = ct.WinDLL('kernel32', use_last_error=True)

# Fully specify .argtypes and .restype for each Win32 function
# so ctypes can typecheck parameters.
#
# .errcheck allows implicit error checking of return value
# for cleaner code and keeps errors from passing silently.

# BOOL ProcessIdToSessionId(
#   [in]  DWORD dwProcessId,
#   [out] DWORD *pSessionId
# );
ProcessIdToSessionId = kernel32.ProcessIdToSessionId
ProcessIdToSessionId.argtypes = w.DWORD, w.LPDWORD
ProcessIdToSessionId.restype = w.BOOL
ProcessIdToSessionId.errcheck = boolcheck

# DWORD GetCurrentProcessId();
GetCurrentProcessId = kernel32.GetCurrentProcessId
GetCurrentProcessId.argtypes = ()
GetCurrentProcessId.restype = w.DWORD

def get_current_session_id():
    pid = GetCurrentProcessId()
    sid = w.DWORD()
    ProcessIdToSessionId(pid, ct.byref(sid))
    return sid.value

print(f'{get_current_session_id()=}')

# Pass bad PID to test errcheck
sid = w.DWORD()
ProcessIdToSessionId(123, ct.byref(sid))

Output:

get_current_session_id()=1
Traceback (most recent call last):
  File "C:\test.py", line 37, in <module>
    ProcessIdToSessionId(123, ct.byref(sid))
  File "C:\test.py", line 7, in boolcheck
    raise ct.WinError(ct.get_last_error())
OSError: [WinError 87] The parameter is incorrect.