git for Windows 付属の ssh でのログインを自動化したい

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$)に直接出しているんでしょうかね…