A new feature coming to WLS is the ability to monitor commands executed at the command prompt. Using methods similar to Volatility’s cmdhistory.py and Extracting Windows Cmd Line Details from Physical Memory (pdf), commands and their associated cmd.exe PID can now be logged in near real-time.
Update: Here’s a video showing the results:
On WIndows XP, each cmd.exe process has it’s history stored in csrss.exe, which you can see using Process Explorer or Process Hacker:
On Windows Vista+, each cmd.exe process has it’s own conhost.exe:
The only correlation (that I could find) between the cmd.exe process and the process that holds it’s command history, is that csrss.exe / conhost.exe has an open handle to the associated cmd.exe process.
Finding the handle
NtQuerySystemInformation is used to obtain a list of handle entries which contain: OwnerPID, ObjectType, HandleFlags, ObjectPointer, and AccessMask. Each entry is checked for an ObjectType of Process, an OwnerPID belonging to csrss.exe / conhost.exe and a HandleValue that references a cmd.exe process. The HandleValue will be used to correlate this entry with a command history structure.
Finding the history structure
Scanning for the command history structure in memory involves searching for a known value, typically the maximum command history, then checking if the data surrounding it matches the structure definition. Invariably there will be false positives which can mostly be weeded out by value checking the first four values in the structure, and that the pointer in the structure points to it’s start address. This structure also contains the process handle value found in the handle entry above.
Address | Description | |
x86 | x64 | |
0x00 | 0x00 | Command Count (0 – Max) |
0x02 | 0x02 | Last Added (0 – Max) |
0x04 | 0x04 | Last Displayed (0 – Max) |
0x06 | 0x06 | First Command (0 – Max) |
0x08 | 0x08 | Max History |
0x0C | 0x10 | Virtual Address / Handle |
0x10 | 0x18 | Pointer to this structure @ 0x00 |
0x14 | 0x20 | Pointer to this structure @ 0x00 |
Now there is a 1:1 relationship between a cmd.exe instance and the command history in memory. Aside from the obvious benefit of knowing the ProcessId, scanning memory for new structures is no longer needed once each relationship is known, eliminating the most resource intensive part of the monitoring process.
Example!
cmd.exe opened with a ProcessId of 8884 (0x22B4), conhost.exe 14868 (0x3A14) has it’s command history.
A handle entry scan returns an entry with an OwnerPID matching that of conhost.exe and an ObjectType of 0x07, which is a Process on Vista+. The cmd.exe PID is obtained by duplicating the handle in HandleValue and calling GetProcessId. Since OwnerPID equals 0x3A14 and the value returned from GetProcessId is 0x22B4, we have a match.
AccessMask: 0x1FFFFF |
HandleFlags: 0x00 |
HandleValue: 0x94 |
ObjectPointer:&HFFFFFA8010AEB170 |
ObjectType: 0x07 |
OwnerPID: 0x3A14 |
A memory scan for the max history value (typically 0x32) finds the following structure, indicating 1 executed command, a virtual address of 0x94 which matches the HandleValue above, and two pointers that correctly identify the location in memory of the structure.
Address | Value | Description |
0x00 | 0x01 | CommandCount |
0x01 | 0x00 | CommandCount |
0x02 | 0x00 | LastAdded |
0x03 | 0x00 | LastAdded |
0x04 | 0x00 | LastDisplayed |
0x05 | 0x00 | LastDisplayed |
0x06 | 0x00 | FirstCommand |
0x07 | 0x00 | FirstCommand |
0x08 | 0x32 | MaxHistory |
0x09 | 0x00 | MaxHistory |
0x0A | 0xEF | |
0x0B | 0xFF | |
0x0C | 0xFC | |
0x0D | 0xFC | |
0x0E | 0xFC | |
0x0F | 0xFF | |
0x10 | 0x94 | VirtualAddress |
0x11 | 0x00 | VirtualAddress |
0x12 | 0x00 | VirtualAddress |
0x13 | 0x00 | VirtualAddress |
0x14 | 0x00 | VirtualAddress |
0x15 | 0x00 | VirtualAddress |
0x16 | 0x00 | VirtualAddress |
0x17 | 0x00 | VirtualAddress |
0x18 | 0x78 | StructurePointer1 |
0x19 | 0xE8 | StructurePointer1 |
0x1A | 0x16 | StructurePointer1 |
0x1B | 0x00 | StructurePointer1 |
0x1C | 0x00 | StructurePointer1 |
0x1D | 0x00 | StructurePointer1 |
0x1E | 0x00 | StructurePointer1 |
0x1F | 0x00 | StructurePointer1 |
0x20 | 0x78 | StructurePointer2 |
0x21 | 0xE8 | StructurePointer2 |
0x22 | 0x16 | StructurePointer2 |
0x23 | 0x00 | StructurePointer2 |
0x24 | 0x00 | StructurePointer2 |
0x25 | 0x00 | StructurePointer2 |
0x26 | 0x00 | StructurePointer2 |
0x27 | 0x00 | StructurePointer2 |
The next section of the command history structure contains a list of pointers to simple structures containing the length of the command and the command itself in unicode.
0x28 | &H70 | Command1 |
0x29 | &HDA | Command1 |
0x2A | &H16 | Command1 |
0x2B | &H0 | Command1 |
0x2C | &H0 | Command1 |
0x2D | &H0 | Command1 |
0x2E | &H0 | Command1 |
0x2F | &H0 | Command1 |
The structure found by following the pointer above correctly indicates a length of 14 and the unicode encoded command “echo hi”, which was the command executed.
Address | Value | |
0x00 | 0x0E | 14 |
0x01 | 0X00 | |
0x02 | &H65 | e |
0x03 | &H00 | |
0x04 | &H63 | c |
0x05 | &H00 | |
0x06 | &H68 | h |
0x07 | &H00 | |
0x08 | &H6F | o |
0x09 | &H00 | |
0x0A | &H20 | |
0x0B | &H00 | |
0x0C | &H68 | h |
0x0D | &H00 | |
0x0E | &H69 | i |
0x0F | &H00 |
The resulting log from WLS looks like this:
2013-10-27T12:28:43-05:00 [pc] WLS_CommandMonitor: LogType=”WLS”, Command=”echo hi”, ProcessId=”0x22b4″, Type=”Executed”
WLS tracks the LastAdded and LastDisplayed values, and logs them when a change occurs, noting the Type in the log entry. Here I entered some more commands, arrowed up through history, then appended a character to a previous entry and executed it.
2013-10-27T13:28:30-05:00 [pc] WLS_CommandMonitor: LogType=”WLS”, Command=”echo hi2″, ProcessId=”0x22b4″, Type=”Executed”
2013-10-27T13:28:32-05:00 [pc] WLS_CommandMonitor: LogType=”WLS”, Command=”echo hi3″, ProcessId=”0x22b4″, Type=”Executed”
2013-10-27T13:28:33-05:00 [pc] WLS_CommandMonitor: LogType=”WLS”, Command=”echo hi2″, ProcessId=”0x22b4″, Type=”Displayed”
2013-10-27T13:28:37-05:00 [pc] WLS_CommandMonitor: LogType=”WLS”, Command=”echo hi24″, ProcessId=”0x22b4″, Type=”Executed”
For more information on WLS, click “WLS Information” at the top, or here: WLS Information
If you’d like additional information about WLS, send me a note via the contact form. WLS is currently available to US entities, but does require a signed license agreement.