News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

userName

Started by six_L, June 23, 2006, 02:35:10 PM

Previous topic - Next topic

six_L

Hey,all
in Taskmgr.exe, the usename of process svchost.exe,alg.exe can be read. but the following code can't read that. how do i figure out?
_GetUserName proc uses edi hProcess:HANDLE
LOCAL hToken:HANDLE
LOCAL dwBytesRead:DWORD
LOCAL dwBytesRead2:DWORD
LOCAL dwSidType:DWORD
LOCAL pData:DWORD
local szDomainBuffer[256]:BYTE

invoke RtlZeroMemory,offset plUserName,sizeof plUserName
invoke OpenProcessToken, hProcess, TOKEN_QUERY, addr hToken
invoke GetTokenInformation, hToken, TokenUser, NULL, NULL, addr dwBytesRead

invoke GlobalAlloc, GPTR, dwBytesRead
mov pData, eax

invoke GetTokenInformation, hToken,TokenUser, pData, dwBytesRead, addr dwBytesRead

mov edi, pData
invoke LookupAccountSid, NULL, [edi][TOKEN_USER.User.Sid], offset plUserName, \
addr dwBytesRead, addr szDomainBuffer,\
addr dwBytesRead2, addr dwSidType
invoke FreeSid,[edi][TOKEN_USER.User.Sid]
invoke GlobalFree, pData
ret
_GetUserName endp

regards

Ossa

First of all, there is no error handling, so it's hard to find the error... but after altering the code I posted here, I get the following output (shortened).

Current User: Robert

Process ID      User    Module Name

00000000        --- Process Handle Could Not Be Retrieved ---
Access is denied.
00000004        --- Process Token Could Not Be Retrieved ---
000002C4        SYSTEM  smss.exe
00000310        SYSTEM  csrss.exe
00000328        SYSTEM  winlogon.exe
...
00000408        SYSTEM  svchost.exe
Access is denied.
00000460        --- Process Token Could Not Be Retrieved ---
Access is denied.
0000051C        --- Process Token Could Not Be Retrieved ---
...


The alterations were just to strip the path from the module names and to add some GetLastError/FormatMessage code after the OpenProcessToken call (formatted errors appear before the line for the corresponding PID). The errors are due to the program not having the required access privilege. I tried adding debug privileges, but that didn't help. I don't know what privilege you need - I will try to find out.

Ossa
Website (very old): ossa.the-wot.co.uk

six_L

hey,Ossa
First of all, Thanks for your answer.

the above code is yours.
i uesed the debug privilege to open the svchost.exe and alg.exe.  OFFSET plUserName = NULL,
it should be LOCAL SERVICE and NETWORK SERVICE.

the comrade's code sysview has the same error.

best regards
regards

Ossa

Quote from: six_L on June 23, 2006, 04:34:41 PM
the above code is yours.

:red should have guessed that... the error checking did expand it quite a bit - but I had it in for cases like this.

Quote from: six_L on June 23, 2006, 04:34:41 PM
i uesed the debug privilege to open the svchost.exe and alg.exe.

Did it work? Sorry - I don't understand what you mean by "OFFSET plUserName = NULL, it should be LOCAL SERVICE and NETWORK SERVICE." - did you mean it still doesn't work? If so, thats what I get.

Anyway on with what I was going to say. What I decided to do was to check privileges of taskmgr.exe whilst it was running to see what it uses and I get this:

00000B2C        Robert  taskmgr.exe
Task Manager: Number of Privileges: 20
              LUID                      Access
              00000017:00000014         00000000
              00000008:00000003         00000000
              00000011:00000000         00000000
              00000012:00000000         00000000
              0000000C:00000000         00000000
              00000013:00000000         00000000
              00000018:00000000         00000000
              00000009:00000000         00000000
              00000014:00000000         00000000
              00000016:00000000         00000000
              0000000B:00000000         00000000
              0000000D:00000000         00000000
              0000000E:00000000         00000000
              0000000A:00000000         00000000
              0000000F:00000000         00000000
              00000005:00000000         00000000
              00000019:00000000         00000000
              0000001C:00000000         00000000
              0000001E:00000000         00000000
              0000001D:00000003         00000000


When I use LookupPrivilegeName on these LUIDs, i get the following error (for all of them - so my code is probably wrong somehow):

QuoteA specified privilege does not exist.

So from here I'm not sure what can be done as I can't find the error in my (very very messy) code,
Ossa

[edit] decided to attach the code in case anyone was interested anyway [/edit]

[attachment deleted by admin]
Website (very old): ossa.the-wot.co.uk

six_L

#4
hey,Ossa
Quotethe above code is yours.

should have guessed that... the error checking did expand it quite a bit - but I had it in for cases like this.
http://www.masm32.com/board/index.php?topic=4868.msg36528#msg36528

ofcourse, your code was modified a bit for my usage.

compared taskmgr.exe, the above code can't work while it reads the username of process svchost.exe,alg.exe.
;-------------------------------------
                              taskmgr.exe                _GetUserName
svchost.exe          NETWORK SERVICE
alg.exe                LOCAL SERVICE
;-------------------------------------
the plUserName is a buffer that received the username of process. 
regards

KSS

Six_L,
I don't know in what way your program get process information, but maybe this is what your need:
...
status=NtQuerySystemInformation(SystemProcessInformation,m_pvBuffer,m_cbBuffer,NULL);
...


six_L

Hey,KSS
Thanks.

Are you sure that can get process user name?
QuoteNtQuerySystemInformation

[NtQuerySystemInformation may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic.]

Retrieves the specified system information.

NTSTATUS WINAPI NtQuerySystemInformation(
  SYSTEM_INFORMATION_CLASS SystemInformationClass,
  PVOID SystemInformation,
  ULONG SystemInformationLength,
  PULONG ReturnLength
);

Parameters

SystemInformationClass
    [in] One of the values enumerated in SYSTEM_INFORMATION_CLASS, which indicate the kind of system information to be retrieved. These include the following values.

    SystemBasicInformation
        Returns the number of processors in the system in a SYSTEM_BASIC_INFORMATION structure. Use the GetSystemInfo function instead.
    SystemPerformanceInformation
        Returns an opaque SYSTEM_PERFORMANCE_INFORMATION structure that can be used to generate an unpredictable seed for a random number generator. Use the CryptGenRandom function instead.
    SystemTimeOfDayInformation
        Returns an opaque SYSTEM_TIMEOFDAY_INFORMATION structure that can be used to generate an unpredictable seed for a random number generator. Use the CryptGenRandom function instead.
    SystemProcessInformation
        Returns an array of SYSTEM_PROCESS_INFORMATION structures, one for each process running in the system.

        These structures contain information about the resource usage of each process, including the number of handles used by the process, the peak page-file usage, and the number of memory pages that the process has allocated.
    SystemProcessorPerformanceInformation
        Returns an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION structures, one for each processor installed in the system.
    SystemInterruptInformation
        Returns an opaque SYSTEM_INTERRUPT_INFORMATION structure that can be used to generate an unpredictable seed for a random number generator. Use the CryptGenRandom function instead.
    SystemExceptionInformation
        Returns an opaque SYSTEM_EXCEPTION_INFORMATION structure that can be used to generate an unpredictable seed for a random number generator. Use the CryptGenRandom function instead.
    SystemRegistryQuotaInformation
        Returns a SYSTEM_REGISTRY_QUOTA_INFORMATION structure.
    SystemLookasideInformation
        Returns an opaque SYSTEM_LOOKASIDE_INFORMATION structure that can be used to generate an unpredictable seed for a random number generator. Use the CryptGenRandom function instead.

SystemInformation
    [in, out] A pointer to a buffer that receives the requested information. The size and structure of this information varies depending on the value of the SystemInformationClass parameter:

    SYSTEM_BASIC_INFORMATION
        When the SystemInformationClass parameter is SystemBasicInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold a single SYSTEM_BASIC_INFORMATION structure having the following layout:

        typedef struct _SYSTEM_BASIC_INFORMATION {
            BYTE Reserved1[24];
            PVOID Reserved2[4];
            CCHAR NumberOfProcessors;
        } SYSTEM_BASIC_INFORMATION;

        The NumberOfProcessors member contains the number of processors present in the system. Use GetSystemInfo instead to retrieve this information.

        The other members of the structure are reserved for internal use by the operating system.
    SYSTEM_PERFORMANCE_INFORMATION
        When the SystemInformationClass parameter is SystemPerformanceInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold an opaque SYSTEM_PERFORMANCE_INFORMATION structure for use in generating an unpredictable seed for a random number generator. For this purpose, the structure has the following layout:

        typedef struct _SYSTEM_PERFORMANCE_INFORMATION {
            BYTE Reserved1[312];
        } SYSTEM_PERFORMANCE_INFORMATION;

        Individual members of the structure are reserved for internal use by the operating system.

        Use the CryptGenRandom function instead to generate cryptographically random data.
    SYSTEM_TIMEOFDAY_INFORMATION
        When the SystemInformationClass parameter is SystemTimeOfDayInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold an opaque SYSTEM_TIMEOFDAY_INFORMATION structure for use in generating an unpredictable seed for a random number generator. For this purpose, the structure has the following layout:

        typedef struct _SYSTEM_TIMEOFDAY_INFORMATION {
            BYTE Reserved1[48];
        } SYSTEM_TIMEOFDAY_INFORMATION;

        Individual members of the structure are reserved for internal use by the operating system.

        Use the CryptGenRandom function instead to generate cryptographically random data.
    SYSTEM_PROCESS_INFORMATION
        When the SystemInformationClass parameter is SystemProcessInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold an array that contains as many SYSTEM_PROCESS_INFORMATION structures as there are processes running in the system. Each structure has the following layout:

        typedef struct _SYSTEM_PROCESS_INFORMATION {
            ULONG NextEntryOffset;
            ULONG NumberOfThreads;
            BYTE Reserved1[48];
            PVOID Reserved2[3];
            HANDLE UniqueProcessId;
            PVOID Reserved3;
            ULONG HandleCount;
            BYTE Reserved4[4];
            PVOID Reserved5[11];
            SIZE_T PeakPagefileUsage;
            SIZE_T PrivatePageCount;
            LARGE_INTEGER Reserved6[6];
        } SYSTEM_PROCESS_INFORMATION;

        The NumberOfThreads member contains the total number of currently running threads in the process.

        The HandleCount member contains the total number of handles being used by the process in question; use GetProcessHandleCount to retrieve this information instead.

        The PeakPagefileUsage member contains the maximum number of bytes of page-file storage used by the process, and the PrivatePageCount member contains the number of memory pages allocated for the use of this process.

        You can also retrieve this information using either the GetProcessMemoryInfo function or the Win32_Process class.

        The other members of the structure are reserved for internal use by the operating system.
    SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
        When the SystemInformationClass parameter is SystemProcessorPerformanceInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold an array that contains as many SYSTEM_PROCESS_INFORMATION structures as there are processors (CPUs) installed in the system. Each structure has the following layout:

        typedef struct
        _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
            LARGE_INTEGER IdleTime;
            LARGE_INTEGER KernelTime;
            LARGE_INTEGER UserTime;
            LARGE_INTEGER Reserved1[2];
            ULONG Reserved2;
        } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;

        The IdleTime member contains the amount of time that the system has been idle, in 100-nanosecond intervals.

        The KernelTime member contains the amount of time that the system has spent executing in Kernel mode (including all threads in all processes, on all processors), in 100-nanosecond intervals.

        The UserTime member contains the amount of time that the system has spent executing in User mode (including all threads in all processes, on all processors), in 100-nanosecond intervals.

        Use GetSystemTimes instead to retrieve this information.
    SYSTEM_INTERRUPT_INFORMATION
        When the SystemInformationClass parameter is SystemInterruptInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold an array that contains as many opaque SYSTEM_INTERRUPT_INFORMATION structures as there are processors (CPUs) installed on the system. Each structure, or the array as a whole, can be used to generate an unpredictable seed for a random number generator. For this purpose, the structure has the following layout:

        typedef struct _SYSTEM_INTERRUPT_INFORMATION {
            BYTE Reserved1[24];
        } SYSTEM_INTERRUPT_INFORMATION;

        Individual members of the structure are reserved for internal use by the operating system.

        Use the CryptGenRandom function instead to generate cryptographically random data.
    SYSTEM_EXCEPTION_INFORMATION
        When the SystemInformationClass parameter is SystemExceptionInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold an opaque SYSTEM_EXCEPTION_INFORMATION structure for use in generating an unpredictable seed for a random number generator. For this purpose, the structure has the following layout:

        typedef struct _SYSTEM_EXCEPTION_INFORMATION {
            BYTE Reserved1[16];
        } SYSTEM_EXCEPTION_INFORMATION;

        Individual members of the structure are reserved for internal use by the operating system.

        Use the CryptGenRandom function instead to generate cryptographically random data.
    SYSTEM_REGISTRY_QUOTA_INFORMATION
        When the SystemInformationClass parameter is SystemRegistryQuotaInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold a single SYSTEM_REGISTRY_QUOTA_INFORMATION structure having the following layout:

        typedef struct _SYSTEM_REGISTRY_QUOTA_INFORMATION {
            ULONG RegistryQuotaAllowed;
            ULONG RegistryQuotaUsed;
            PVOID Reserved1;
        } SYSTEM_REGISTRY_QUOTA_INFORMATION;

        The RegistryQuotaAllowed member contains the maximum size, in bytes, that the Registry can attain on this system.

        The RegistryQuotaUsed member contains the current size of the Registry, in bytes.

        Use GetSystemRegistryQuota instead to retrieve this information.

        The other member of the structure is reserved for internal use by the operating system.
    SYSTEM_LOOKASIDE_INFORMATION
        When the SystemInformationClass parameter is SystemLookasideInformation, the buffer pointed to by the SystemInformation parameter should be large enough to hold an opaque SYSTEM_LOOKASIDE_INFORMATION structure for use in generating an unpredictable seed for a random number generator. For this purpose, the structure has the following layout:

        typedef struct _SYSTEM_LOOKASIDE_INFORMATION {
            BYTE Reserved1[32];
        } SYSTEM_LOOKASIDE_INFORMATION;

        Individual members of the structure are reserved for internal use by the operating system.

        Use the CryptGenRandom function instead to generate cryptographically random data.

SystemInformationLength
    [in] The size of the buffer pointed to by the SystemInformation parameter, in bytes.
ReturnLength
    [out, optional] An optional pointer to a location where the function writes the actual size of the information requested. If that size is less than or equal to the SystemInformationLength parameter, the function copies the information into the SystemInformation buffer; otherwise, it returns an NTSTATUS error code and returns in ReturnLength the size of buffer required to receive the requested information.

Return Values
Returns an NTSTATUS success or error code.

The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the Windows Device Driver Kit (DDK), and are described in the DDK documentation.
Remarks

The NtQuerySystemInformation function and the structures that it returns are internal to the operating system and subject to change from one release of Windows to another. To maintain the compatibility of your application, it is better to use the alternate functions previously mentioned instead.

If you do use NtQuerySystemInformation, access the function through run-time dynamic linking. This gives your code an opportunity to respond gracefully if the function has been changed or removed from the operating system. Signature changes, however, may not be detectable.

This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll.
i want to know how the taskmgr.exe get it.
regards

KSS

Six_L, :red
1. NtQuerySystemInformation()
2. WinStationGetProcessSid()
3. IsValidSid()
4. CachedGetUserFromSid()

six_L

Hey,KSS
Thanks.
;======================================================
.text:0100AAC7                 mov     [ebp-230h], ebx
.text:0100AACD                 mov     [ebp-22Ch], ebx
.text:0100AAD3                 lea     eax, [ebp-22Ch]
.text:0100AAD9                 push    eax
.text:0100AADA                 push    ebx
.text:0100AADB                 push    dword ptr [edi+4]
.text:0100AADE                 push    dword ptr [edi]
.text:0100AAE0                 mov     ecx, esi
.text:0100AAE2                 call    sub_100A171
.text:0100AAE7                 push    eax
.text:0100AAE8                 push    ebx
.text:0100AAE9                 call    WinStationGetProcessSid
.text:0100AAEE                 test    al, al
.text:0100AAF0                 jnz     loc_100ABBA
.text:0100AAF6                 push    dword ptr [ebp-22Ch]
.text:0100AAFC                 call    sub_1002665
.text:0100AB01                 pop     ecx
.text:0100AB02                 mov     ebx, eax
.text:0100AB04                 mov     [ebp-240h], ebx
.text:0100AB0A                 mov     [ebp-230h], ebx
.text:0100AB10                 test    ebx, ebx
.text:0100AB12                 jz      short loc_100AB92
.text:0100AB14                 lea     eax, [ebp-22Ch]
.text:0100AB1A                 push    eax
.text:0100AB1B                 push    ebx
.text:0100AB1C                 push    dword ptr [edi+4]
.text:0100AB1F                 push    dword ptr [edi]
.text:0100AB21                 mov     ecx, esi
.text:0100AB23                 call    sub_100A171
.text:0100AB28                 push    eax
.text:0100AB29                 push    0
.text:0100AB2B                 call    WinStationGetProcessSid
.text:0100AB30                 test    al, al
.text:0100AB32                 jz      short loc_100AB83
.text:0100AB34                 push    ebx             ; pSid
.text:0100AB35                 call    ds:IsValidSid
.text:0100AB3B                 test    eax, eax
.text:0100AB3D                 jz      short loc_100AB83
.text:0100AB3F                 mov     dword ptr [ebp-238h], 104h
.text:0100AB49                 lea     eax, [ebp-238h]
.text:0100AB4F                 push    eax
.text:0100AB50                 lea     eax, [ebp-224h]
.text:0100AB56                 push    eax
.text:0100AB57                 push    ebx
.text:0100AB58                 call    CachedGetUserFromSid
.text:0100AB5D                 push    412h
.text:0100AB62                 call    sub_1002665
.text:0100AB67                 pop     ecx
.text:0100AB68                 mov     [ebp-244h], eax
.text:0100AB6E                 mov     [esi+0Ch], eax
.text:0100AB71                 test    eax, eax
.text:0100AB73                 jz      short loc_100AB83
.text:0100AB75                 lea     ecx, [ebp-224h]
.text:0100AB7B                 push    ecx             ; lpString2
.text:0100AB7C                 push    eax             ; lpString1
.text:0100AB7D                 call    ds:lstrcpyW
.text:0100AB83
;======================================================
Quote from: KSS on June 25, 2006, 11:37:02 PM
2. WinStationGetProcessSid()
3. IsValidSid()
4. CachedGetUserFromSid()
API help?



[attachment deleted by admin]
regards

KSS

Platform SDK: Authorization
IsValidSid

The IsValidSid function validates a security identifier (SID) by verifying that the revision number is within a known range, and that the number of subauthorities is less than the maximum.

BOOL IsValidSid(
  PSID pSid
);

Parameters
pSid
[in] Pointer to the SID structure to validate. This parameter cannot be NULL.
Return Values
If the SID structure is valid, the return value is nonzero.

If the SID structure is not valid, the return value is zero. There is no extended error information for this function; do not call GetLastError.

Remarks
If pSid is NULL, the application will fail with an access violation.

OC

I know this is an old thread, but it taught be a lot and I feel ike giving back. This is not assembly code, but it is a complete example that illustrates the concepts discussed in this thread. If one of you wants to reimplement or expand on this example, using assembly instead of C, here's some place to start.

If any of this code looks like it came from the "Process Monitor" program by Michel van Kerkhof
http://home.wxs.nl/~wijk0550/ , then you caught me (I did make a few changes). For a more complete application, please refer to Michel's application.

=====================
#include <windows.h>
#include <stdio.h>

#define SystemProcessInformation      5

typedef long NTSTATUS;
typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(int SystemInformationClass,
                  PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
typedef BOOL (WINAPI *WINSTATIONGETPROCESSSID)(HANDLE hServer, DWORD ProcessId,
              FILETIME ProcessStartTime, PBYTE pProcessUserSid, PDWORD dwSidSize);
typedef void (WINAPI *CACHEDGETUSERFROMSID)(PSID pSid , PWCHAR pUserName, PULONG cbUserName);

typedef struct _UNICODE_STRING {
  USHORT Length;       
  USHORT MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

//SystemProcessInformation
typedef struct {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER SpareLi1;
    LARGE_INTEGER SpareLi2;
    LARGE_INTEGER SpareLi3;
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;
    UNICODE_STRING ImageName;
    LONG BasePriority;
    HANDLE UniqueProcessId;
    HANDLE InheritedFromUniqueProcessId;
    ULONG HandleCount;
    ULONG SessionId;
    ULONG SpareUl3;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG PageFaultCount;
    ULONG PeakWorkingSetSize;
    ULONG WorkingSetSize;
    SIZE_T QuotaPeakPagedPoolUsage;
    SIZE_T QuotaPagedPoolUsage;
    SIZE_T QuotaPeakNonPagedPoolUsage;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER ReadOperationCount;
    LARGE_INTEGER WriteOperationCount;
    LARGE_INTEGER OtherOperationCount;
    LARGE_INTEGER ReadTransferCount;
    LARGE_INTEGER WriteTransferCount;
    LARGE_INTEGER OtherTransferCount;
} SYSTEM_PROCESS_INFORMATION;




int main(void)
{
    int     pidCount = 0;
    int     i;
    int     nameSize;
    wchar_t OwnerName[MAX_PATH];
    ULONG   reqSize = 0;
    ULONG   sidSize;
    PSID    pSid;
    HMODULE winsta = NULL;
    HMODULE utildll = NULL;
    HMODULE ntdll = NULL;
    SYSTEM_PROCESS_INFORMATION *procInfo;
    WINSTATIONGETPROCESSSID WinStationGetProcessSid;
    CACHEDGETUSERFROMSID CachedGetUserFromSid;
    NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;

    ntdll = LoadLibrary("ntdll.dll");
    if (!ntdll)
    {
        fprintf(stderr, "Could not load ntdll.dll, err = %d\n", GetLastError());
        return 1;
    }
    NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(ntdll, "NtQuerySystemInformation");

    winsta = LoadLibrary("winsta.dll");
    if (!winsta)
    {
        fprintf(stderr, "Could not load winsta.dll, err = %d\n", GetLastError());
        return 1;
    }
    WinStationGetProcessSid = (WINSTATIONGETPROCESSSID)GetProcAddress(winsta, "WinStationGetProcessSid");

    utildll = LoadLibrary("utildll.dll");
    if (!winsta)
    {
        fprintf(stderr, "Could not load utildll.dll, err = %d\n", GetLastError());
        return 1;
    }

    CachedGetUserFromSid = (CACHEDGETUSERFROMSID)GetProcAddress(utildll, "CachedGetUserFromSid");
    NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &reqSize);

    if ((procInfo = malloc(reqSize)) != NULL)
    {
   SYSTEM_PROCESS_INFORMATION *p = procInfo;

   NtQuerySystemInformation(SystemProcessInformation, procInfo, reqSize, &reqSize);

   while (p)
   {
            if ((DWORD)p->UniqueProcessId == 0)
      printf("0   System Idle Process   SYSTEM\n");
       else
       {
      sidSize = 0;
      nameSize = MAX_PATH;
                WinStationGetProcessSid(NULL, (DWORD)p->UniqueProcessId, *((FILETIME *)&p->CreateTime),
                   (PBYTE)pSid, &sidSize);

      if ((pSid = malloc(sidSize)) == NULL)
          break;

                WinStationGetProcessSid(NULL, (DWORD)p->UniqueProcessId, *((FILETIME *)&p->CreateTime),
                   (PBYTE)pSid, &sidSize);
                CachedGetUserFromSid(pSid, OwnerName, &nameSize);

           printf("%d   %S   %S\n", (DWORD)p->UniqueProcessId, p->ImageName.Buffer, OwnerName);
      free(pSid);
       }

       if (p->NextEntryOffset == 0)
      break;

      (char *)p += p->NextEntryOffset;
   }

   free(procInfo);
    }

    FreeLibrary(utildll);
    FreeLibrary(winsta);
    FreeLibrary(ntdll);

    return 0;
}

====== And here's how to build (with VC6) =======

cl /MD /Zi /Fd pinfo.c /link /debug /incremental:no advapi32.lib kernel32.lib


Have fun,
OC