
The field of software development is one of those areas of human activity where the term “historical causes” is used most often. It is understandable - many “long-running” projects like kernels of various operating systems, browsers and other things have acquired a non-generous arsenal of things during their existence, not everyone’s behavior will change, even if the perfectionist inside the developer says the opposite. Most likely, most of the code was written by programmers who have not worked for a long time in the company, and even those who still associate their lives with this corporation doubt that the other components of the software complex will normally respond to these or other changes. "No, I'd rather leave it as it is."
As an example, one of these things is
cmd.exe . Yes, yes, this is the same command-line interpreter included in the delivery of all modern (and not so) operating systems of the Windows family. There are a lot of historical reasons for him - just recall how it is necessary to paste and copy into this interpreter (for the sake of fairness, it should be said that in Windows 10 this situation was finally corrected, and applications like
ConEmu are great in this). But today we will talk about a different behavior, which makes it necessary for us to think about the person who first encountered cmd.exe, where it would not be necessary at all.
')
As you know, one of the commands that cmd.exe perceives is the “CD”. The official help for this command reports the following:
C: \ Users \ Nikita.Trophimov> CD /?
Displays the current directory.
[...]
It would seem, everything is simple. You call a CD without an argument - the path to the current directory is output to stdout, you pass another directory as an argument - it changes the current directory to the specified one. Pitfalls here begin in the event that the user decides to change the directory simultaneously with the disk. For example, if you are in the directory "C: \ Windows \ system32", then the command "CD D: \ books" will not do anything. In my opinion, there is absolutely nothing obvious for new users, so Google or the official documentation saves them, which, by the way, says:
Switch the switch on in addition to changing current
directory for a drive.
Of course, this question, as well as the reasons for the emergence of such behavior, has been repeatedly discussed on the Internet (for example,
here ), so we will not dwell on such things. Instead, we will try to debug cmd.exe to eliminate the need to explicitly specify the "/ D" key.
How was the process, and what came of it, read under the cut.
An increasing part of the Windows OS is 64-bit, which is no exception in my case. All standard utilities (calc.exe, taskmgr.exe, our cmd.exe, etc.) also got 64-bit counterparts, which are shipped by default along with the operating system. For the reverse, this means that, in this case, we cannot, unfortunately, use the familiar to us from previous articles (which can be found, for example,
here )
OllyDbg (by the way, work on x64 support is still
underway ).
What options do we have? With x64,
IDA Pro and a relatively new
x64_dbg are able to work at least. Unfortunately, only paid versions of IDA Pro have x64 support, so I suggest to stay at the second option.
Make a copy of cmd.exe,
download the snapshot of the latest version of x64_dbg, launch it and load the executable file we are exploring into it:

Press F9 until the program stops breaking breakpoints (it's nice that so many hot keys from OllyDbg work here), right-click on the contents of the CPU window -> Search for -> String references and look for the "/ D" :

We put a bryak on each of them with F2, enter the “CD / DD: \ books” command into the window of the running process cmd.exe (assuming that we, of course, are on another disk) and stop at the breakpoint at
0x7F6D01F972A :

Next to the breakpoint is the function call
_wcsnicmp , used to compare the specified number of bytes in the lines passed to it:

It is important to understand that, unlike x86, the x64 uses a completely different
calling convention :
Microsoft x64 UEFI (for long mode on x86-64). It uses the registers RCX, RDX, R8, R9, XMM0, XMM1, XMM2, XMM3 are used for floating points. Additional arguments are pushed onto the stack (right to left). Integer return values (similar to x86) are returned in RAX if 64 bits or less. Floating point return values are returned in XMM0. Parameters less than 64 bits long are not zero extended; the high bits are not zeroed
In this case, the _wcsnicmp function passes "/ D" and "/ DD: \ books" as string arguments, and the R8 register stores information about how many bytes need to be compared (in this case, 2). Of course, in this case, as a result of calling the _wcsnicmp function in the EAX register, it will be zero, which will force the program to go to the address
0x7F6D01F97F2 .
The first thing that comes to mind is to make this transition unconditional (change the
JE instruction to
JMP ), thus making the program think that the argument "/ D" has always been passed to it. Let's do it. Press F9, move to the previous directory for the uniformity of the source data, enter the command "CD D: \ books" (note the lack of the "/ D" key),
select the line with the instruction
je cmd.7F6D01F97F2 located at
0x7F6D01F9747 , press space and change
JE to
JMP , not forgetting to put a tick next to "Fill with NOP's":

Press F9 again and see that the team still incorrectly completed its work, but at least it did not remain silent, as it was the last time:

We set bryak on
JMP and we are engaged in tracing. Immediately after the jump, the "trimmed" version of the string is stored in the
RCX register, which is stored at the address specified in the
RBX register. To be more precise, the first two characters are “deleted” from it (two, because the strings are Unicode, which could be understood by the signature of the
_wcsnicmp function and the “L” character before the string literals, and therefore each requires two bytes, and the command "cuts" the line with
RBX + 4 ):

It is easy to guess that this is done just to remove the path "/ D" from the line containing the program of interest to the directory, which consists of two characters. Of course, we don’t have to do this anymore, because Now such actions will “cut off” part of the path to the directory specified by the user. Well, let's replace this instruction with
lea rcx, qword ptr ds: [rbx] (you can't zapopit it, because the value must be in the
RCX register):

Again, enter the command without specifying the key "/ D", and ... We see that the transition to the desired directory is actually carried out.
In order to save the changes we have done, open the “Patches” menu with Ctrl-P, check that all necessary changes are highlighted, click on the “Patch File” button and select the name for the patched version of cmd.exe.
Unfortunately, even if we manage to replace the original cmd.exe from the "% WINDIR% \ system32" directory with the patched one, Windows will still
restore the old executable version of the file from the cache, so make a separate shortcut for the patched binary and use it.
Afterword
Sometimes even the little things can make our life easier and more pleasant, or, conversely, only aggravate the situation. If you have already stumbled several times on the pitfall in the form of the missing "/ D" flag, then why not pick up a debugger and not fix this situation? Do not forget that bugs and "historical reasons" are very often encountered, and the developers do not always intend to edit them.
In fairness it should be noted that in
PowerShell, the need to specify the "/ D" key for the CD team was removed.
Thank you for your attention, and again I hope that the article was useful to someone.