How do I get the command line of another process?


Win32 doesn't expose a process's command line to other processes. From Win32's point of view, the command line is just a conveniently initialized parameter to the process's startup code, some data copied from the launching process to the new process and forgotten. We'll get back to the Win32 point of view a little later.

If you look around in WMI, you'll find a Win32_Process object, and lo and behold, it has a CommandLine property. Let's check it out, using the standard WMI application:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_Process")
For Each objItem in colItems
     Wscript.Echo objItem.Name
     Wscript.Echo objItem.CommandLine
Next

I fully anticipate that half of my readers will stop right there. "Thanks for the script. Bye!" And they won't bother reading the analysis. "Because analysis is boring, and it'll just tell me stuff I don't want to hear. The analysis is going to tell me why this won't work, or why it's a bad idea, and that just cramps my style."

Remember that from Win32's point of view, the command line is just a string that is copied into the address space of the new process. How the launching process and the new process interpret this string is governed not by rules but by convention.

What's more, since the string is merely a "preinitialized variable", a process could in principle (and many do in practice, although usually inadvertently) write to the memory that holds the command line, in which case, if you go snooping around for it, you'll see the modified command line. There is no secret hiding place where the kernel keeps the "real original command line," any more than there is a secret hiding place where the C compiler keeps the "real original parameters to a function."

This is just another manifestation of the principle of not keeping track of information you don't need.

What does this mean for people who disregard this principle and go after the command line of another process? You have to understand what you are getting is non-authoritative information. In fact, it's worse. It's information the application itself may have changed in order to try to fool you, so don't use it to make important decisions.

Comments (28)
  1. someone else says:

    Aw come on, Raymond. You should know that most readers come here for the analysis!

  2. Alexandre says:

    @someone else: I don’t know. Quite a large number of people seem to come here just for the nitpicking and vista-bashing.

    Back to the topic at hand, it makes sense. Once a process is launched and has used its own command line, it and a lot else has little use for the original command line. I was surprised it’s even kept somewhere.

  3. Boris says:

    Well, no, it would be useful for programs like Process Explorer (already mentioned) to give the user a better idea of what’s running on their system than "Host Process for Windows Services", either to determine whether it’s malicious or to track down what’s taking up so much RAM/disk space. I could even think of cases where taking action automatically on such information is useful. Think antiviruses removing known threats based on command line arguments.

  4. JohnJS says:

    @Alexandre: Well, you come for the analysis but you stay for the vista-bashing :-).

  5. Frymaster says:

    "….Process Explorer (already mentioned) to give the user a better idea of what’s running on their system than "Host Process for Windows Services","

    not only does process explorer tell you that, you can also use "tasklist /svc" from the command line

  6. Mark says:

    From a contractual perspective, it’s kept around because it’s a parameter to main(), and by C convention main doesn’t return, putting its parameters out of scope. However, you can of course do whatever you want, including terminating the thread that main is in.

  7. Dave says:

    You should know that most readers come here for the analysis!

    I come for the analysis, but I stay for the insults!

  8. On a related note, if I remember correctly, some of djb’s daemontools (used with qmail on Unix) deliberately overwrite their command lines, so that they can report status and errors to someone using ‘top’ or ‘ps’.

  9. Timothy Byrd says:

    Don’t even need to bother with Process Explorer – on Vista, the Task Manager can show the (possibly over-written) command line.

    And what will the poor Vista-bashers do now that Windows 7 is out?

  10. porter says:

    > and by C convention main doesn’t return

    Yes it does, when the program finishes! Hence why it returns an int which the stub which called main then passes to exit().

  11. James says:

    I suppose this qualifies as an "application trying to fool you", but a specific use case is for applications that accepts passwords via command-line arguments.

  12. Boris says:

    @Timothy, here? I’m running Windows 7 and don’t see the command line.

  13. Boris says:

    Sorry, by "here" I meant "where"

  14. porter says:

    Similarly, can you see the environment variables for a process?

  15. manyirons says:

    @Alexandre: You should have used a capitol ‘V’ in "Vista", not that it deserves it.

  16. manyirons says:

    Pre-emptive nit-pick:  I know I spelled capital incorrectly.

    Raymond, being this precise is hard.  You do a great job!

  17. Keeron Modi says:

    Vista / Win7 Task manager:

    • "View" menu

    • "Select Columns"

    • Scroll down to "Command line"

  18. Dean Harding says:

    @Boris: You can add a column for command-line in the "Processes" list.

  19. IanB says:

    Don’t even need to bother with Process Explorer – on Vista, the Task Manager can show the (possibly over-written) command line.

    Argh, why didn’t I know that! Thanks.

    Random aside: Windows 7 (Pro x64) now allows you again to drag and drop directories from Explorer to the command prompt to save typing the full path. This useful feature was removed from Vista.

  20. Yep, just tested the following dummy program:

    #include <windows.h>

    int main(int argc, char **argv)

    {

     wchar_t *x = GetCommandLineW();

     while(*x) *x++ = L’x’;

     Sleep(10000000);

     return 0;

    }

    When I opened it up with Process Explorer, it reported the command line as "xxxxxxxxxx" instead of what it actually was.

  21. Jon says:

    "not keeping track of information you don’t need" is wrong. You do need it, for diagnostics.

  22. Miral says:

    While deliberately overwriting the command line for purposes of reporting status (as mentioned above) could be useful — how do you know how much room you have?

    I guess you can be reasonably safe in assuming that if someone gave you 20 characters in actual parameters then the buffer is at least that big, but I don’t think there are any particular guarantees anywhere, especially when a program is being run with no parameters.

    So it might end up just being another way to buffer-overrun yourself…

  23. Miral says:

    Also: is there a Win32 way of getting the (possibly overwritten) command line of a process, or is it only available through WMI?  (I don’t regard WMI as part of Win32.)

  24. Yuhong Bao says:

    "Windows 7 (Pro x64) now allows you again to drag and drop directories from Explorer to the command prompt to save typing the full path. This useful feature was removed from Vista."

    As mentioned by Raymond before, that was a side effect of the introduction of UIPI to Vista. Restoring this feature to Windows 7 required moving console windows into a separate conhost.exe.

  25. porter says:

    > Also: is there a Win32 way of getting the (possibly overwritten) command line of a process,

    You mean like inject a DLL using CreateRemoteThread into another process? And that DLL then calls GetCommandLineW and sends you the result back?

  26. Neil says:

    And don’t forget that the command line doesn’t necessarily include the correct, or indeed any, executable. (WinDbg 6.6.0007.5 gets this wrong.)

  27. Miral says:

    > You mean like inject a DLL using CreateRemoteThread into another process? And that DLL then calls GetCommandLineW and sends you the result back?

    That’s the only way I could think of, yes.  I was wondering if there were anything more elegant.

    Although then I noticed that Raymond <a href="http://blogs.msdn.com/oldnewthing/archive/2009/02/23/9440784.aspx">had covered this before</a>.

  28. anonymous says:

    @Miral

    I do not know if you consider this more elegant

    but i guess ReadProcessMemory instead of injecting a thread has less risk of destroying something.

    It still has the sampe problems with reading commandline from privileged processes and is surely equally unsupported…

    http://www.codeproject.com/KB/threads/GetNtProcessInfo.aspx

    http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/

    AFAIK this is the way most tools implement reading commandline…

Comments are closed.