Easiest and laziest way to view a process’s command line


QUESTION - What is the easiest way to view an arbitrary process's command line?

SHORT ANSWER - Fire up the debugger (ntsd/windbg), attach to the process, fix symbols and dump out the process's environment Block (PEB).

LONG ANSWER - For 32-bit processes running on x86 systems, the PEB is "generally" located at 0x7ffd6000. At an offset of 0x10 bytes from the start of the PEB, you will find the pointer to the RTL_USER_PROCESS_PARAMETERS structure. The pointer to the unicode string containing the command line is located at an offset of 0x040 from the start of the RTL_USER_PROCESS_PARAMETERS structure.

Suppose you start solitaire from the cmd shell with a command line option "/xyz". [The "/xyz" option really doesn't do anything; I am using this just as an example]

C:\> sol /xyz

Now attach a debugger to this running process and view the command line via the PEB.

0:001> !peb
PEB at 7ffd6000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            Yes
    ImageBaseAddress:         01000000
    Ldr                       001a1e90
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 001a1f28 . 001a2768
    Ldr.InLoadOrderModuleList:           001a1ec0 . 001a2bc0
    Ldr.InMemoryOrderModuleList:         001a1ec8 . 001a2bc8
            Base TimeStamp                     Module
         1000000 3b7d8480 Aug 17 13:54:24 2001 C:\WINDOWS\system32\sol.exe
        7c900000 411096b4 Aug 04 00:56:36 2004 C:\WINDOWS\system32\ntdll.dll
        7c800000 411096b4 Aug 04 00:56:36 2004 C:\WINDOWS\system32\kernel32.dll
        77c10000 41109752 Aug 04 00:59:14 2004 C:\WINDOWS\system32\msvcrt.dll
        77dd0000 411096a7 Aug 04 00:56:23 2004 C:\WINDOWS\system32\ADVAPI32.dll
        77e70000 43740e6d Nov 10 19:22:21 2005 C:\WINDOWS\system32\RPCRT4.dll
        77f10000 43b34feb Dec 28 18:54:35 2005 C:\WINDOWS\system32\GDI32.dll
        77d40000 42260159 Mar 02 10:09:29 2005 C:\WINDOWS\system32\USER32.dll
        6fc10000 3b7dfe43 Aug 17 22:33:55 2001 C:\WINDOWS\system32\CARDS.dll
        7c9c0000 433370f6 Sep 22 20:05:26 2005 C:\WINDOWS\system32\SHELL32.dll
        77f60000 43c2a517 Jan 09 10:01:59 2006 C:\WINDOWS\system32\SHLWAPI.dll
        5cb70000 411096ba Aug 04 00:56:42 2004 C:\WINDOWS\system32\ShimEng.dll
        76b40000 411096d6 Aug 04 00:57:10 2004 C:\WINDOWS\system32\WINMM.dll
        774e0000 42e5be93 Jul 25 21:39:47 2005 C:\WINDOWS\system32\ole32.dll
        77120000 411096f3 Aug 04 00:57:39 2004 C:\WINDOWS\system32\OLEAUT32.dll
        77be0000 411096cf Aug 04 00:57:03 2004 C:\WINDOWS\system32\MSACM32.dll
        77c00000 411096b7 Aug 04 00:56:39 2004 C:\WINDOWS\system32\VERSION.dll
        769c0000 411096b9 Aug 04 00:56:41 2004 C:\WINDOWS\system32\USERENV.dll
        5ad70000 411096bb Aug 04 00:56:43 2004 C:\WINDOWS\system32\UxTheme.dll
    SubSystemData:     00000000
    ProcessHeap:       000a0000
    ProcessParameters: 00020000
    WindowTitle:  'sol /xyz'
    ImageFile:    'C:\WINDOWS\system32\sol.exe'
    CommandLine:  'sol /xyz'
    DllPath:      'C:\WINDOWS\system32;'
    Environment:  00010000
        ALLUSERSPROFILE=C:\Documents and Settings\All Users
        CommonProgramFiles=C:\Program Files\Common Files
        PROCESSOR_IDENTIFIER=x86 Family 6 Model 13 Stepping 8, GenuineIntel
        ProgramFiles=C:\Program Files

[The output has been edited for brevity]


You can view the internals of the PEB and RTL_USER_PROCESS_PARAMETERS blocks for any process using the following ntsd commands -

0:001> dt nt!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 SpareBool        : UChar
   +0x004 Mutant           : Ptr32 Void
   +0x008 ImageBaseAddress : Ptr32 Void
   +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
   +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
   +0x014 SubSystemData    : Ptr32 Void
   +0x018 ProcessHeap      : Ptr32 Void
   +0x01c FastPebLock      : Ptr32 _RTL_CRITICAL_SECTION
   +0x020 FastPebLockRoutine : Ptr32 Void
   +0x024 FastPebUnlockRoutine : Ptr32 Void
   +0x028 EnvironmentUpdateCount : Uint4B
   +0x02c KernelCallbackTable : Ptr32 Void
   +0x030 SystemReserved   : [1] Uint4B
   +0x034 AtlThunkSListPtr32 : Uint4B
   +0x038 FreeList         : Ptr32 _PEB_FREE_BLOCK
   +0x03c TlsExpansionCounter : Uint4B
   +0x040 TlsBitmap        : Ptr32 Void
   +0x044 TlsBitmapBits    : [2] Uint4B
   +0x04c ReadOnlySharedMemoryBase : Ptr32 Void
   +0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
   +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
   +0x058 AnsiCodePageData : Ptr32 Void
   +0x05c OemCodePageData  : Ptr32 Void
   +0x060 UnicodeCaseTableData : Ptr32 Void
   +0x064 NumberOfProcessors : Uint4B
   +0x068 NtGlobalFlag     : Uint4B
   +0x070 CriticalSectionTimeout : _LARGE_INTEGER
   +0x078 HeapSegmentReserve : Uint4B
   +0x07c HeapSegmentCommit : Uint4B
   +0x080 HeapDeCommitTotalFreeThreshold : Uint4B
   +0x084 HeapDeCommitFreeBlockThreshold : Uint4B
   +0x088 NumberOfHeaps    : Uint4B
   +0x08c MaximumNumberOfHeaps : Uint4B
   +0x090 ProcessHeaps     : Ptr32 Ptr32 Void
   +0x094 GdiSharedHandleTable : Ptr32 Void
   +0x098 ProcessStarterHelper : Ptr32 Void
   +0x09c GdiDCAttributeList : Uint4B
   +0x0a0 LoaderLock       : Ptr32 Void
   +0x0a4 OSMajorVersion   : Uint4B
   +0x0a8 OSMinorVersion   : Uint4B
   +0x0ac OSBuildNumber    : Uint2B
   +0x0ae OSCSDVersion     : Uint2B
   +0x0b0 OSPlatformId     : Uint4B
   +0x0b4 ImageSubsystem   : Uint4B
   +0x0b8 ImageSubsystemMajorVersion : Uint4B
   +0x0bc ImageSubsystemMinorVersion : Uint4B
   +0x0c0 ImageProcessAffinityMask : Uint4B
   +0x0c4 GdiHandleBuffer  : [34] Uint4B
   +0x14c PostProcessInitRoutine : Ptr32
   +0x150 TlsExpansionBitmap : Ptr32 Void
   +0x154 TlsExpansionBitmapBits : [32] Uint4B
   +0x1d4 SessionId        : Uint4B
   +0x1d8 AppCompatFlags   : _ULARGE_INTEGER
   +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
   +0x1e8 pShimData        : Ptr32 Void
   +0x1ec AppCompatInfo    : Ptr32 Void
   +0x1f0 CSDVersion       : _UNICODE_STRING
   +0x1f8 ActivationContextData : Ptr32 Void
   +0x1fc ProcessAssemblyStorageMap : Ptr32 Void
   +0x200 SystemDefaultActivationContextData : Ptr32 Void
   +0x204 SystemAssemblyStorageMap : Ptr32 Void
   +0x208 MinimumStackCommit : Uint4B

   +0x000 MaximumLength    : Uint4B
   +0x004 Length           : Uint4B
   +0x008 Flags            : Uint4B
   +0x00c DebugFlags       : Uint4B
   +0x010 ConsoleHandle    : Ptr32 Void
   +0x014 ConsoleFlags     : Uint4B
   +0x018 StandardInput    : Ptr32 Void
   +0x01c StandardOutput   : Ptr32 Void
   +0x020 StandardError    : Ptr32 Void
   +0x024 CurrentDirectory : _CURDIR
   +0x030 DllPath          : _UNICODE_STRING
   +0x038 ImagePathName    : _UNICODE_STRING
   +0x040 CommandLine      : _UNICODE_STRING
   +0x048 Environment      : Ptr32 Void
   +0x04c StartingX        : Uint4B
   +0x050 StartingY        : Uint4B
   +0x054 CountX           : Uint4B
   +0x058 CountY           : Uint4B
   +0x05c CountCharsX      : Uint4B
   +0x060 CountCharsY      : Uint4B
   +0x064 FillAttribute    : Uint4B
   +0x068 WindowFlags      : Uint4B
   +0x06c ShowWindowFlags  : Uint4B
   +0x070 WindowTitle      : _UNICODE_STRING
   +0x078 DesktopInfo      : _UNICODE_STRING
   +0x080 ShellInfo        : _UNICODE_STRING
   +0x088 RuntimeData      : _UNICODE_STRING
   +0x090 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR


But to do all of the above, you would need the symbols for ntdll.dll. Export symbols will not help. Public symbols will do. The public symbols can be downloaded from Microsoft's Public Symbol Server - http://msdl.microsoft.com/download/symbols. This public server can only be accessed via a debugger and is not browsable. You would just need to fix the SYMPATH as follows -

.sympath SRV*<DownstreamStore>*http://msdl.microsoft.com/download/symbols

Substitute your downstream symbol folder location for <DownstreamStore>. If you want to store the symbols in "C:\Temp" then set the SYMPATH as follows -

.sympath SRV*C:\Temp*http://msdl.microsoft.com/download/symbols



Comments (5)

  1. Pavel Lebedinsky says:

    PEB is often located at a different address. You should probably use the @$peb pseudo-register instead of depending on it being at 0x7ffd6000.

    Actually, if all you need is the command line !peb is usually the easiest way.

    Dumping _RTL_USER_PROCESS_PARAMETERS is still useful if you want to see other parameters such as desktop, window title or current directory.

  2. mithuns says:


    Isn’t it true that the PEB and TEB are always guaranteed to be at FS:[0x30] and FS:[0x18] respectively?

  3. Pavel Lebedinsky says:

    > Isn’t it true that the PEB and TEB are always guaranteed to be at FS:[0x30] and FS:[0x18] respectively?

    I don’t know. In any case, this is not a documented fact so you can’t depend on it. It’s also specific to the x86 architecture.

    @$peb is documented (in the context of debuggers) and is supported on all Windows architectures.

  4. Dan says:

    On an x86 platform, the address of the TEB for the current thread is stored at FS:[18]. The location of the PEB is also stored in each TEB in the ProcessEnvironmentBlock field.

    If you’re using an x86 platform and the TEB, PEB and RTL_USER_PROCESS_PARAMETERS structures haven’t changed, the following command will display the command line if you don’t have access to symbols:

    du poi(poi(poi(poi(fs:18)+30)+10)+40+4)

    As Pavel suggested, it’s best to stick with using debugger interfaces as they will be updated if and when the operating system or architecture changes.

  5. keydet89 says:

    I don’t see anything specifying otherwise, so I’ll mention this…beware of the OS you’re using.  The default PEB for Windows 2000 is 0x7ffdf000, and is different for various OS’s.  

    I’m working on a Perl script to parse a dump of RAM using dd.exe (from George Garner’s site)…this stuff is very interesting to me.



Skip to main content