cmd.exeの「歎史的理由」を匕き続き扱いたす

画像



前の蚘事で 、Windowsオペレヌティングシステムファミリの暙準のcmd.exeコマンドラむンむンタヌプリタヌに含たれるCDコマンドに「/ D」キヌを指定する必芁がある状況の解決策に぀いお説明したした。 特別な理由もなく倪叀の昔から続いおいるさらに別の行動に぀いお話す時が来たした。



今回はパス補完に぀いお説明したす。これは、ほずんどの環境ず゜フトりェア補品この堎合、cmd.exeも䟋倖ではありたせんでTab / Shift-Tabを抌すこずで行われたす。 この機胜が非垞に有甚で、倚くの堎合、目的のファむルたたはディレクトリのナヌザヌぞのフルパスを手動で入力するのに費やされおいた時間を節玄できるず䞻匵する人はいないず思いたす。 ただし、cmd.exeにも存圚するのは玠晎らしいこずですが...



詊したしょう。 cmd.exeWin-R-> cmdを実行し、コマンド「CD C/」の入力を開始し、Tabを抌したす。「Program Files」や「Windows」などの予想されるディレクトリの代わりに、HOMEPATHから最初のアルファベットオブゞェクトを取埗したす。 「䞀緒に立ち埀生」ず「C/」私の堎合、「C/。Vim」の圢匏で結果を出したした。 なんで 本質的にcmd.exeを頻繁に凊理しなければならなかった人たちは、ここで問題が䜕であるかを既に理解しおいたず思いたす-スラッシュの代わりに、バックスラッシュを正しい自動補完に䜿甚する必芁がありたすずころで、この点には他の䟋倖がありたす。 これは、バックスラッシュではなくスラッシュだけがパス区切り文字ずしお䜿甚される他のシステム* nixのようなでほずんどの時間を過ごす人にずっおは特に珍しいこずです。 マむクロ゜フトが圓時倚くのナヌザヌに既に銎染みのスラッシュの代わりにこの特定の蚘号を䜿甚するこずを決めた理由は、たずえばここで説明されおいたす 。 さお、これに同意するか、デバッガファむルを遞択しおcmd.exeを調べる必芁がありたす 。 最初のパスを遞択した堎合、蚘事はないので、すべおがどこに向かっおいるのかをすでに把握しおいるはずです。



プロセスがどのように進み、どのようになったのか、カットの䞋で読んでください慎重に、 倚くのスクリヌンショット 。



たず、cmd.exeが突然ナヌザヌ指定のディレクトリではなく、HOMEPATHのオブゞェクトを怜玢するこずにした理由を確認する必芁がありたす。



WinAPIを䜿甚したディレクトリ内のオブゞェクトの反埩凊理は、通垞、 FindFirstFile 関数ずFindNextFile関数 、およびFindFirstFileEx 、 FindFirstFileTransactedなどの圢匏のバリ゚ヌションを䜿甚しお行われたす。 OllyDbgを起動し、 それに cmd.exeをロヌドしもちろん、以前に "WINDIR\ system32"以倖のディレクトリにコピヌしたした、盞互モゞュラヌ呌び出しのリストでりィンドりを開きたすCPUりィンドりを右クリック->すべおの盞互モゞュラヌ呌び出し 、「FindFirstFile」ず蚘述し、F2キヌを䜿甚しおすべおの呌び出しにブレヌクを蚭定したす。



画像



調べおいるコマンド「CD C/」を入力し、Tabキヌを抌すず、目の前に次の画像が衚瀺されたす。



画像



FindFirstFileEx関数に枡される最初の匕数に泚意しおください-ドキュメントによるず、怜玢を実行する基準を蚭定するのは圌です。



lpFileName [入力]

ディレクトリたたはパス、およびファむル名。ワむルドカヌド文字を含めるこずができたす。たずえば、アスタリスク*たたは疑問笊


私の堎合、圌はナニコヌド文字列「C\ Program Files \ *」が栌玍されおいるアドレス0x0030F660を指しおいたす。 なぜ圌女なのか はい、CDコマンドを入力した時点でそこにいたためです。



スラッシュの代わりにバックスラッシュを䜿甚しお同じこずをしたしょう。 F9を抌しお、コマンド「CD C\」に続けおTabを入力し、以䞋を参照したす



画像



はい、珟圚、この匕数は文字列「C\ *」を指しおいたす。 したがっお、パスセパレヌタヌずしおスラッシュを䜿甚する堎合、cmd.exeは珟圚のディレクトリの自動補完に適したオブゞェクトを実行したす。



Alt-Kキヌを抌すず開くコヌルスタックから、すべおのプロシヌゞャのコヌルを実行したす。いずれかのプロシヌゞャの近くにあるナヌザヌから送信されたコマンドの解析に䌌たものが衚瀺されたす。



画像



この手順の最初にブレヌクを眮き私の堎合は0x4ACE1877 、F9を抌し、バックスラッシュずTabを䜿甚しおコマンドを入力し、ステップバむステップのデバッグを開始したす。 F7を抌しお間もなく、ナヌザヌが入力したコマンドのすべおの文字を実行するサむクルにいるこずに気付きたす。



画像



EBP + 8はコマンドでUnicode文字列を指し、 EBP + 10はコマンドの長さを含み、 EDIはルヌプカりンタヌです。



このサむクルのほが盎埌に、 std :: memcpy関数の呌び出しが芋぀かりたした。その結果、バックスラッシュを䜿甚するず、destは「C\」を取埗したす。



画像



、およびスラッシュの堎合、空の行



画像



さお、その䜜業のアルゎリズムを高レベルのプログラミング蚀語に倉換するこずにより、このサむクルで䜕が起こるかを理解しおみたしょう。 IDA Proはコヌドを逆コンパむルできたすが、残念ながらかなりのお金を芁求するので、自分でC ++に翻蚳しおみたしょう。



#include <cstddef> #include <cstring> #include <cwchar> #include <iostream> #include <string> int main() { std::wstring command; std::getline(std::wcin, command); auto command_size = command.size(); int ebx = -1; int esi = 0; int edx = 0; const int ebp_24 = 0; // Always 0 in our case cause it changes in the '"' branch // Not actually used in our case int ebp_1c = 0; int ebp_28 = 0; int ebp_2c = 0; /** * 4ACE18C7 | > / 897D D0 / MOV DWORD PTR SS : [EBP - 30], EDI * 4ACE18CA | . | 8B45 10 | MOV EAX, DWORD PTR SS : [EBP + 10] * 4ACE18CD | . | 3BF8 | CMP EDI, EAX * 4ACE18CF | . | 0F8D 90000000 | JGE cmd.4ACE1965 */ for (std::wstring::size_type i = 0; i < command_size; ++i) { /** * 4ACE18D5 | . 8B45 08 | MOV EAX, DWORD PTR SS : [EBP + 8] * 4ACE18D8 | . 0FB70478 | MOVZX EAX, WORD PTR DS : [EAX + EDI * 2] */ const wchar_t cur_symbol = command[i]; // 4ACE18DC | . 66:83F8 2F | CMP AX, 2F if (cur_symbol == L'/') { /** * 4ACE18E2 | . 8D77 01 | LEA ESI, DWORD PTR DS : [EDI + 1] * 4ACE18E5 | . 8975 D8 | MOV DWORD PTR SS : [EBP - 28], ESI */ esi = i + 1; ebp_28 = esi; } else if (cur_symbol == L'"') { // ... } // 4ACE18F0 | . 3955 DC | CMP DWORD PTR SS : [EBP - 24], EDX if (ebp_24 == edx) { /** * 4ACE190C | . 50 | PUSH EAX; / w * 4ACE190D | . 68 E008D04A | PUSH cmd.4AD008E0; | wstr = " &()[]{}^=;!%'+,`~" * 4ACE1912 | .FF15 F010CC4A | CALL DWORD PTR DS : [<&msvcrt.wcschr>]; \wcschr * 4ACE1918 | . 59 | POP ECX * 4ACE1919 | . 59 | POP ECX * 4ACE191A | . 85C0 | TEST EAX, EAX */ if (std::wcschr(L" &()[]{}^=;!%'+,`~", cur_symbol) != NULL) { /** * 4ACE191E |. 8D77 01 |LEA ESI,DWORD PTR DS:[EDI+1] * 4ACE1921 |. 8975 D8 |MOV DWORD PTR SS:[EBP-28],ESI * 4ACE1924 |. 8365 E4 00 |AND DWORD PTR SS:[EBP-1C],0 * 4ACE1928 |. 33D2 |XOR EDX,EDX */ esi = i + 1; ebp_28 = esi; ebp_1c = 0; edx = 0; } else { // 4ACE192C | > \33D2 | XOR EDX, EDX edx = 0; /** * 4ACE1935 | . 66:83F8 3A | CMP AX, 3A * 4ACE1939 | . 74 1B | JE SHORT cmd.4ACE1956 * 4ACE193B | . 66 : 83F8 5C | CMP AX, 5C * 4ACE193F | . 74 15 | JE SHORT cmd.4ACE1956 */ if (cur_symbol == L':' || cur_symbol == L'\\') { /** * 4ACE1956 | > \8D5F 01 | LEA EBX, DWORD PTR DS : [EDI + 1] * 4ACE1959 | . 895D D4 | MOV DWORD PTR SS : [EBP - 2C], EBX * 4ACE195C | > 8955 E4 | MOV DWORD PTR SS : [EBP - 1C], EDX */ ebx = i + 1; ebp_2c = ebx; ebp_1c = edx; } else if (cur_symbol == L'*' || cur_symbol == L'?') { // ... } } } } /** * 4ACE1965 |> \83FB FF CMP EBX,-1 * 4ACE1968 |. 74 04 JE SHORT cmd.4ACE196E * 4ACE196A |. 3BDE CMP EBX,ESI * 4ACE196C |. 7D 05 JGE SHORT cmd.4ACE1973 */ if (ebx == -1 || ebx < esi) { /** * 4ACE196E | > \8BDE MOV EBX, ESI * 4ACE1970 | . 895D D4 MOV DWORD PTR SS : [EBP - 2C], EBX */ ebx = esi; ebp_2c = ebx; } /** * 4ACE1973 | > \2BC6 SUB EAX, ESI * 4ACE1975 | . 03C0 ADD EAX, EAX * 4ACE1977 | . 8BF8 MOV EDI, EAX * 4ACE1979 | . 57 PUSH EDI; / n * 4ACE197A | . 8B45 08 MOV EAX, DWORD PTR SS : [EBP + 8]; | * 4ACE197D | . 8D0470 LEA EAX, DWORD PTR DS : [EAX + ESI * 2]; | * 4ACE1980 | . 50 PUSH EAX; | src * 4ACE1981 | .FF75 E0 PUSH DWORD PTR SS : [EBP - 20]; | dest * 4ACE1984 | .E8 52FAFDFF CALL <JMP.&msvcrt.memcpy>; \memcpy */ const std::size_t count = (command_size - esi) * 2; wchar_t dest[1024] = { 0 }; std::memcpy(dest, command.substr(esi).c_str(), count); std::wcout << "Result: " << dest << std::endl; }
      
      





コメント「// ...」でマヌクされた堎所は、怜蚎䞭のケヌスでは圱響を受けたせん。



「*」や「\」などの文字は、ASCIIコヌドテヌブルを䜿甚しお定矩されたした。



画像



入力を詊しおみるず、次のこずがわかりたす。



CD C\

結果C\



CD C/

結果



CD C\ Windows \

結果C\ Windows \



CD C/ Windows \

結果Windows \


スラッシュは、ナヌザヌが入力したパスの最埌少なくずも最埌、少なくずも䞭倮のどこにあるかに関係なく、問題を匕き起こすこずが容易にわかりたす。



解決策は、cmd.exeがオヌトコンプリヌトを実行する必芁があるず認識した盎埌に、すべおのスラッシュをバックスラッシュに眮き換えるこずです。 これを行うには、反察偎からアプロヌチするこずを提案したす。ナヌザヌが暙準入力ストリヌムからデヌタを入力した盎埌に、段階的なデバッグを実行するこずです。



ただし、stdinからのデヌタの読み取りはさたざたな方法で実行できたす。 cmd.exeで䜕が正確に䜿甚されおいるかを理解する方法は 非垞に簡単です-F9キヌを抌しおからF12キヌ䞀時停止を抌し、コヌルスタックを芋お、呌び出しの䞭でReadConsoleずいうWinAPI関数を確認したす。



画像



デフォルトでは、 ReadConsoleはEnterキヌを抌した埌、呌び出し元のコヌドに制埡を返したすが、明らかにこれはそうではありたせん。 たずえば、Tabキヌを抌した埌、䜜業を​​完了する必芁がありたす。



゜フトりェアブレヌカヌを呌び出しおトリガヌしたす。



画像



ここでpReservedず呌ばれる最埌のパラメヌタヌに泚意しおください。 実際、これはpInputControlず呌ばれ、次の圹割を果たしたす。



pInputControl [入力、オプション]

読み取り操䜜の終了を知らせる制埡文字を指定するCONSOLE_READCONSOLE_CONTROL構造䜓ぞのポむンタヌ。 このパラメヌタヌはNULLにするこずができたす


私たちの堎合、それはたったくNULLではないため、 CONSOLE_READCONSOLE_CONTROL構造がどのように芋えるかを芋おみたしょう。



 typedef struct _CONSOLE_READCONSOLE_CONTROL { ULONG nLength; ULONG nInitialChars; ULONG dwCtrlWakeupMask; ULONG dwControlKeyState; } CONSOLE_READCONSOLE_CONTROL, *PCONSOLE_READCONSOLE_CONTROL;
      
      





生のバむトを芋るのはあたり䟿利ではないので、StollyStructsず呌ばれる特別なOllyDbgプラグむンを䜿甚したしょう。これは、構造を芖芚化するために特別に蚭蚈されおいたす。 .dllず.iniをダりンロヌドしお、実行可胜ファむルOllyDbgがあるディレクトリに解凍しもちろん、デフォルトでプラグむンのパスずしお指定されおいる堎合、デバッガを再起動したす。 cmd.exeを再起動するず、アドレスが倉曎される堎合がありたすが、ほずんどの堎合、アドレスの「終了」は同じたたです。 たずえば、以前興味のあるReadConsole呌び出しが0x4ACD3589にあった堎合 、おそらく0xXXXXX589の圢匏のアドレスになりたす 。



画像



ブレヌクポむントを配眮しお停止し、[プラグむン]-> [StollyStruct]-> [構造の遞択]をクリックし、 pInputControl匕数ずしお枡されたアドレスを[アドレス]フィヌルドに入力したす。 たあ、著者は、WinAPIからのすべおの構造が事前に蚭定されるこずを玄束したせんでした。 2぀のオプションがありたす-この構造の説明をプラグむン構成ファむルに远加するか、興味のある構造に䌌た別の構造を䜿甚したす。 私が最初に思い぀いたのはRECT構造で、これには4぀のフィヌルドが含たれおいたすが、ULONGの代わりにLONGを䜿甚しおいるずいう唯䞀の違いがありたす。



 typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT;
      
      





その結果、次のものが埗られたす。



画像



入力を停止するための文字は、 dwCtrlWakeupMaskフィヌルドを䜿甚しお指定されたす。



dwCtrlWakeupMask

読み取りが完了したこずを通知するために䜿甚されるナヌザヌ定矩の制埡文字


ご芧のずおり、この䟋では、倀0x200が含たれおいたす。これは、ビットシフト挔算1 << 0x9の結果ずしお取埗されたした。0x9はTabのASCIIコヌドです。



ナヌザヌがEnterたたはTabを入力した埌、 ReadConsole関数からのリタヌンが実行されるようにしたした。 それでは、ステップバむステップのデバッグに戻りたしょう。



少し実行した埌、ナヌザヌが入力したコマンド内のすべおの文字を反埩凊理する別のルヌプを芋぀けたす。



画像



ここで、 EDIはコマンドでUnicode文字列を指し、 EAXはルヌプカりンタヌです。



ご芧のずおり、各文字は最初に0x0Dず比范され、次に0x4A1640A0の倀ず比范され 、次に0x4A1640A4のコンテンツず比范されたす。 ASCIIコヌドの衚を芋るず、0x0Dはキャリッゞリタヌンにすぎないこずがわかりたす。 前に瀺したアドレスには、同じ倀0x9が栌玍されたす。これは、前述のように、TabのASCIIコヌドです。



画像



たた、珟圚の文字がTabず等しい堎合に遷移が実行されるアドレスからそれほど遠くないずころに、転送されたコマンドの解析コヌドがありたす。 たあ、私の意芋では、これは私たちのコヌド掞窟ぞの移行を眮くのが最善の堎所です。



その䞭で䜕をしたすか 私は次のこずを提案したす-行の終わりから最初たで行き、各文字をスラッシュず等しいかどうかを最初のスペヌス文字たでチェックし、バックスラッシュに眮き換えたす。 次のようになりたす。



 PUSHFD PUSHAD ;   ,     TEST EAX,EAX JZ l1 ;    (    ;  ,    Tab') l4: DEC EAX ;   ECX   MOVZX ECX,WORD PTR DS:[EDI+EAX*2] CMP CX,2F ;   forward slash JE l2 CMP CX,20 ;    JE l1 JMP l3 l2: ;  forward slash  backslash MOV WORD PTR DS:[EDI+EAX*2],5C l3: ;     ,     TEST EAX,EAX ;        JNZ l4 l1: POPAD POPFD ;    ,   ;     JMP 4ACD42CD
      
      





コヌドケむブの堎所を芋぀けCtrl-B-> "HEX + 0C"フィヌルドに倚数のれロを挿入しお、次のコヌドを蚘述したすもちろん、アドレスは異なる堎合がありたす。



 4A163CC5 9C PUSHFD 4A163CC6 60 PUSHAD 4A163CC7 85C0 TEST EAX,EAX 4A163CC9 74 1D JE SHORT cmd.4A163CE8 4A163CCB 48 DEC EAX 4A163CCC 0FB70C47 MOVZX ECX,WORD PTR DS:[EDI+EAX*2] 4A163CD0 66:83F9 2F CMP CX,2F 4A163CD4 74 08 JE SHORT cmd.4A163CDE 4A163CD6 66:83F9 20 CMP CX,20 4A163CDA 74 0C JE SHORT cmd.4A163CE8 4A163CDC EB 06 JMP SHORT cmd.4A163CE4 4A163CDE 66:C70447 5C0>MOV WORD PTR DS:[EDI+EAX*2],5C 4A163CE4 85C0 TEST EAX,EAX 4A163CE6 ^ 75 E3 JNZ SHORT cmd.4A163CCB 4A163CE8 61 POPAD 4A163CE9 9D POPFD 4A163CEA ^ E9 DE05FFFF JMP cmd.4A1542CD
      
      





ここで、 0x4A1542CDは、 0x4A154299にある条件分岐の結果ずしお移動する必芁があったアドレスであり、Tabのコマンドで珟圚の文字が等しいかどうかを確認したす。 その遷移はそれぞれ、コヌドケむブのゞャンプに眮き換えられたす。



画像



圌が次の指瀺を消したこずにすでに気づいたず思いたす。 実際には、珟圚のキャラクタヌが同じタブに等しいかどうかの同様のチェックでしたが、他の方法でそれに到達するこずは䞍可胜だったので、倧䞈倫です。 これを確認するには、倉曎を匷調衚瀺し、Alt-Backspaceを䜿甚しおすべおをそのたた返し、この呜什で行を遞択し、Ctrl-Rを抌したす。同じアドレスの1぀の行がありたす。



画像



操䜜性をチェックしたす... Tabを抌すず、スラッシュは実際にバックスラッシュに眮き換えられたす。その結果、ナヌザヌが最初に䜿甚したスラッシュに関係なく、ナヌザヌが指定したディレクトリで自動補完が実行されたす。



あずがき



これらはすべお些现なこずだず誰かが蚀うかもしれたせん。 マむクロ゜フトの開発者ではなく、私たちがこの問題を解決するずいう事実を奜たない人がいるかもしれたせん。 誰も䜕も奜きでないかもしれたせん 。 しかし、事実は残っおいたす-私たちは問題を解決し、今ではcmd.exeが蚘事の冒頭で思い通りに機胜するようになりたした。 そしお、それを行うかどうかは、あなた次第です。



公平に蚀えば、 PowerShellでは、この「問題」ず、CDコマンドの「/ D」キヌの状況がただ修正されおいたこずに泚目する䟡倀がありたす。



ご枅聎ありがずうございたした。たた、この蚘事が誰かに圹立぀こずを願っおいたす。



All Articles