「Linux なら expect というツールがあるけど、Windows の場合、どうしたらいいんだろう」などと思って、API を調べていたら、WriteConsoleInput
などというコンソールイベントを「捏造」できる API があるのを発見。
ちょっと、ツールを作ってみた。
package main import ( "os" "syscall" "unsafe" ) var kernel32 = syscall.NewLazyDLL("kernel32") var writeConsoleInput = kernel32.NewProc("WriteConsoleInputW") type inputRecordT struct { eventType uint16 _ uint16 bKeyDown int32 wRepeartCount uint16 wVirtualKeyCode uint16 wVirtualScanCode uint16 unicodeChar uint16 dwControlKeyState uint32 } type Handle syscall.Handle func NewHandle() (Handle, error) { handle, err := syscall.Open("CONIN$", syscall.O_RDWR, 0) return Handle(handle), err } const ( KEY_EVENT = 1 ) func (handle Handle) WriteRune(c rune) uint32 { records := []inputRecordT{ inputRecordT{ eventType: KEY_EVENT, bKeyDown: 1, unicodeChar: uint16(c), dwControlKeyState: 0, }, inputRecordT{ eventType: KEY_EVENT, bKeyDown: 0, unicodeChar: uint16(c), dwControlKeyState: 0, }, } var count uint32 writeConsoleInput.Call(uintptr(handle), uintptr(unsafe.Pointer(&records[0])), 2, uintptr(unsafe.Pointer(&count))) return count } func (handle Handle) WriteString(s string) { for _, c := range s { handle.WriteRune(c) } } func main() { console, err := NewHandle() if err != nil { println(err.Error()) return } for _, s := range os.Args[1:] { console.WriteString(s) console.WriteRune('\r') } }
これをビルドして typekeyas.exe というコマンドを作って「typekeyas "dir /w"
」などと打つと、あたかも直後にコマンドプロンプトに対して自分が「dir /w」と打ったかのようにディレクトリが表示される(いや、引数の中で打ってはいるんだけど)
これを使ってパスワードを打たせてみよう(以下、一部伏字)
$ typekeyas (PASSWORD) & "c:\Program Files\Git\usr\bin\ssh.exe" XXXXX@XXXXX.org XXXXX@XXXXX.org's password: Last login: Wed Jun 7 22:09:15 2017 from XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX FreeBSD 9.1-RELEASE-p24 (SAKURA17) #0: Thu Feb 5 10:03:29 JST 2015 Welcome to FreeBSD!
nyagos の場合はコマンド区切り文字の「&
」を「;
」にすればよいが。。。
これパスワードがヒストリに残ってしまうからすごく危険なんだな!
で、「これ画面の出力も拾えれば、完全な expect が出来るのでは?」とか思ったが、最初の XXXXX@XXXXX.org's password
という行だけに限って標準出力・標準エラー出力ともに出ていない(他は出てる)。コンソール(CONOUT$
)に直接出しているんでしょうかね…