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