標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

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