Friday, April 24, 2009

Episode #27: You'll Be History

Ed starts with:

In Episode #14, I touched briefly on how a cmd.exe user could hit the F7 key to view shell history, and we received a lot of positive feedback on that from folks. But, there's some more aspects of cmd.exe history that I use all the time that I think are worth bringing up here.

In particular, if you want to get access to your shell history at the command line itself so you can dump it to a file or manipulate it (rather than just displaying it on the screen so you can scroll through it with F7), you could run the following command:

C:\> doskey /history

Dude! The 80's just called, and they want their doskey command back. :)

Seriously, though, it remains a very useful command. Running doskey this way will show all of the commands typed into the current cmd.exe session. Unfortunately, you won't see history for other cmd.exe's on the screen or those invoked in the past. Still, when handling an incident, running this command with the output redirected to a file can be very useful evidence. If the bad guy was kind enough to leave a cmd.exe on the console screen of the machine he controlled the GUI on, you can quickly dump the history to an evidence file. Alternatively, if you, the incident handler or forensics analyst, are typing commands at a cmd.exe and want a record of what you've typed, invoking:

C:\> doskey /history > \\evidence_server\share\mycommands.txt

...can be very helpful to shoot your results to an evidence server somewhere.

Oh, and note that cmd.exe "telescopes" commands down, so that if you run the same command multiple times in a row, the history will only show the command once. For example:
C:\> dir > nul

C:\> ipconfig > nul

C:\> ipconfig > nul

C:\> ipconfig > nul

C:\> hostname > nul

C:\> doskey /history
dir
ipconfig
hostname
doskey /history
As we can see, the ipconfig command only appears in history once, even though we ran it three times in a row, due to this telescoping down effect.

By default, the command history stores 50 commands, but is configurable on a user-by-user basis. To change it at the command line for the current user, you could run:
C:\> reg add hkcu\console\^%systemroot^%_system32_cmd.exe /v HistoryBufferSize
/t reg_dword /d 0x400
That last number there is a hexadecimal value for the number of commands you want it to remember. 0x400 will make it remember the last 1024 commands. This new history depth will only take effect when you launch new cmd.exe's going forward. But, you don't have to logoff or reboot for it to take effect. Just launch another cmd.exe, and you should have your new history depth (which can be viewed in the GUI by right clicking the title bar of a cmd.exe window, selecting "Properties", and going to the Options tab to view Command History Buffer Size).

Oh, and another thing about cmd.exe history... I often see bashketeers get a little freaked out by how cmd.exe handles shell history when you scroll through it with the up-arrow and down-arrow keys. As we all know, you can hit the up-arrow key in cmd.exe to scroll back to a previous command in modern Windows machines. When you find the command you want, you can hit Enter to re-run that command. No problem there. The issue that puzzles some people is what happens after you've run that command from your history, and you then hit the up arrow key again. Let me illustrate it with this screen shot.

Here, I've just hit 1 followed by Enter, to seed my history with some bogus commands so we can track things easily. Because there is no program called 1.exe, my shell tells me so. I do this for 2, 3, 4, and 5 as well.

Then, I hit the up arrow key to scroll back to my 3 command. I hit Enter on that. If I hit the up arrow again, of course I get the 3 again.

Now for the interesting part. What happens when I hit the up arrow again? If you were a bash guy, you might think that the system would display the number 5, because before you did 3, you did 5. And, you'd be wrong in a cmd.exe shell, which would display the number 2. What? Why 2?

Well, cmd.exe maintains a shell history pointer, and when you scroll back into your history and then run a command from it, this pointer jumps back to your earlier command. All up or down arrow movements from that point are relative to that older command, not your most recent commands.

This used to flummox me, with my ancient Unix background. But, you know what? I'll share a dirty little secret with you if you don't tell anyone. I actually have grown to like (and... gulp... prefer) this behavior. You see, there is a certain locality of command execution from history, and, if I execute a command that was 20 commands ago, there is a reasonably good probability that I'll want to run another command from 19 commands ago after it.

So, cmd.exe history scrolling isn't broken... it's just different. And, it's different for a reason. You might not agree with the reason, but at least there is one. So many things in Windows are different for no reason whatsoever, you know... but don't get me started about tracert. ;)

Hal Responds:

I was really happy when Ed brought us back around to command-line history again, because there was a bunch of stuff that I wanted to put into Episode #14, but decided not to in the interests of space. But there's no stopping me now...

While "doskey /history" is an obvious and intuitive way to access your command history, those wacky Unix folks have decided to force you to use the obscure "history" command instead (that was sarcasm for those of you who missed it because you were trying to remember the silly cmd.exe syntax):

# history
[...]
1001 /etc/init.d/httpd restart
1002 vi /etc/httpd/conf/httpd.conf
1003 vi /etc/httpd/conf.d/ssl.conf
1004 /etc/init.d/httpd restart
1005 ps -ef | grep http
1006 ps -ef | grep http
1007 history

Oh, and notice that the bash history saves all of the commands you've typed, even the repeats. I think that's more useful-- especially from a forensic perspective-- than removing repeated commands, but I'm sure lord high doskey knows better than me.

See the numbers next to the commands in the history output above? You can use "!<n>" to execute the previous command with number <n>. For example, "!1001" would execute "/etc/init.d.http restart" again (as would "!1004"). I often use this when I'm debugging a problem with a server process like Apache-- try to start the server and fail, enter a bunch of commands to fix the problem, "!<whatever>" to try and start the server, lather, rinse, repeat. In other words, "!<n>" is useful when you are constantly re-running the same command with an arbitrary number of other commands in between attempts.

By default, the command shell keeps your last 500 commands, but you can change this by setting the HISTSIZE variable in your .bashrc or .profile:

export HISTSIZE=1024

Wow, you can even set it in decimal! Crazy! What will those wacky Unix folks think of next?