Getting the Command line for any process on NT

On this article I will briefly discuss how to get the command line with which a process was started. The technique I’ll describe will allow you to actually find out the command line for any process, not just the one your code is running in. It’s a fairly common question, so here’s the answer:

As it turns out, some process specific data is stored by NT at address 0×00020498 for any given process. The format of this data goes something like this:

1. The directory from which the application was loaded.
2. The current directory (represented by a single dot ".").
3. %SystemRoot%\System32. (usually C:\WinNT\System32")
4. %SystemRoot%\System. (usually C:\WinNT\System)
5. Just %SystemRoot%. (usually C:\WinNT)
6. The contents of the PATH environment variable, with one minor adjustment: before it goes the startup dir of the parent process, but only when that directory is not included in the PATH already.

Here I have to say that all this was found by experimentation and fooling around with the debugger. I also thank Slava  M. Usov, who was really the one who figured out that the data format until here was very similar to what the CreateProcess() documentation state is the directory search sequence.

Just after this goes the command line path, followed by the full command line, which is the part that actually interests us. Finally, the executable name appears one more time (might, or might not have the path included), followed by the Window Station and Desktop the process is running on. In some cases, the window station and desktop might not appear, if the process is not attached to one.

Worth noting: The full data block is in Unicode (which is logical), so the sample app is written completely in Unicode. Also, the technique I used works on NT4 and up. To try the sample app, just compile it, and run it like this: cmdline.exe <pid> where pid is the process id of the process you wan to get the command line for.

Get the code here, and look it over, there are some important notes along with the code.

Finally, some advice: As you’ve probably noticed, this technique relies on undocumented layouts of the process environment block, which is subject to change at any time. If you want a really bullet-proof method (at least for Win32 processes), you can do the folllowing:

  • Use CreateRemoteThread() to inject some code into the process.

  • Call GetCommandLine() from within the injected code.

  • IPC the results back to your app.

The obvious problem with this technique is that it seems awfully complicated just to do such a simple thing. The other reason I don’t like it is that is essentially invasive. Felix Kasza has a sample on doing it this way on his website.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>