aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/kd
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-09-28 16:51:53 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-09-28 16:53:58 +0200
commit8136bdad2fe7e71d4c395ef362adcbc6b0c02251 (patch)
tree6900b569c3a60375339307a3d5c6d6be0242e6ea /pkg/kd
parenta13111c7f3d400d2a9d0b84d0d777710b3696d29 (diff)
pkg/kd: add KD protocol decoder
Very primitive decoder that only decodes amd64 exceptions. Use it in vm/gce. Now crashes contain something more or less reasonable which is caught by manager as crash: BUG: first chance exception 0x80000003 &kd.stateChange64{state:0x3030, processorLevel:0x6, processor:0x0, numProcessors:0x2, thread:0xffff9c0bd015e080, pc:0xfffff8017615c380, exception:kd.exception64{code:0x80000003, flags:0x0, record:0x0, address:0xfffff8017615c380, numParams:0x1, unused:0x0, params:[15]uint64{ 0x0, 0x40, 0xfffff801768699e0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, firstChance:0x1}, report:kd.controlReport{ dr6:0xffff0ff0, dr7:0x400, eflags:0x86, numInstr:0x10, reportFlags:0x3, instr:[16]uint8{0xcc, 0xc3, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xf, 0x1f, 0x84, 0x0, 0x0, 0x0, 0x0, 0x0}, cs:0x10, ds:0x2b, es:0x2b, fs:0x53}}
Diffstat (limited to 'pkg/kd')
-rw-r--r--pkg/kd/kd.go100
-rw-r--r--pkg/kd/kd_test.go41
2 files changed, 141 insertions, 0 deletions
diff --git a/pkg/kd/kd.go b/pkg/kd/kd.go
new file mode 100644
index 000000000..e700d9762
--- /dev/null
+++ b/pkg/kd/kd.go
@@ -0,0 +1,100 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+// Minimal KD protocol decoder.
+// KD protocol is used by windows to talk to debuggers. Here are some links:
+// https://github.com/radare/radare2/issues/1246#issuecomment-135565901
+// http://articles.sysprogs.org/kdvmware/kdcom/
+// https://doxygen.reactos.org/df/de3/windbgkd_8h_source.html
+package kd
+
+import (
+ "bytes"
+ "fmt"
+ "unsafe"
+)
+
+var (
+ dataHeader = []byte{0x30, 0x30, 0x30, 0x30}
+)
+
+const (
+ typStateChange64 = 7
+)
+
+type packet struct {
+ header uint32
+ typ uint16
+ size uint16
+ id uint32
+ csum uint32
+}
+
+func Decode(data []byte) (start, size int, decoded []byte) {
+ if len(data) < len(dataHeader) {
+ return
+ }
+ start = bytes.Index(data, dataHeader)
+ if start == -1 {
+ start = len(data) - len(dataHeader) - 1
+ return
+ }
+ packetSize := int(unsafe.Sizeof(packet{}))
+ if len(data)-start < packetSize {
+ return // incomplete header
+ }
+ // Note: assuming little-endian machine.
+ pkt := (*packet)(unsafe.Pointer(&data[start]))
+ if len(data)-start < packetSize+int(pkt.size) {
+ return // incomplete data
+ }
+ size = packetSize + int(pkt.size) // skip whole packet
+ if pkt.typ == typStateChange64 {
+ if int(pkt.size) < int(unsafe.Sizeof(stateChange64{})) {
+ return
+ }
+ payload := (*stateChange64)(unsafe.Pointer(&data[start+packetSize]))
+ chance := "second"
+ if payload.exception.firstChance != 0 {
+ chance = "first"
+ }
+ decoded = []byte(fmt.Sprintf("\n\nBUG: %v chance exception 0x%x\n\n%#v\n\n",
+ chance, payload.exception.code, payload))
+ }
+ return
+}
+
+type stateChange64 struct {
+ state uint32
+ processorLevel uint16
+ processor uint16
+ numProcessors uint32
+ thread uint64
+ pc uint64
+ exception exception64
+ report controlReport
+}
+
+type exception64 struct {
+ code uint32
+ flags uint32
+ record uint64
+ address uint64
+ numParams uint32
+ unused uint32
+ params [15]uint64
+ firstChance uint32
+}
+
+type controlReport struct {
+ dr6 uint64
+ dr7 uint64
+ eflags uint32
+ numInstr uint16
+ reportFlags uint16
+ instr [16]byte
+ cs uint16
+ ds uint16
+ es uint16
+ fs uint16
+}
diff --git a/pkg/kd/kd_test.go b/pkg/kd/kd_test.go
new file mode 100644
index 000000000..e35a47272
--- /dev/null
+++ b/pkg/kd/kd_test.go
@@ -0,0 +1,41 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package kd
+
+import (
+ "testing"
+)
+
+func TestCanned(t *testing.T) {
+ start, size, decoded := Decode(exceptionPacket)
+ if start != 0 || size != len(exceptionPacket) {
+ t.Fatalf("bad start/size %v/%v, want %v/%v", start, size, 0, len(exceptionPacket))
+ }
+ t.Logf("%s", decoded)
+}
+
+var exceptionPacket = []byte{
+ 0x30, 0x30, 0x30, 0x30, 0x07, 0x00, 0xF0, 0x00, 0x00, 0x08, 0x80, 0x80,
+ 0xE6, 0x1F, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x22, 0xBC,
+ 0x85, 0x8C, 0xFF, 0xFF, 0x80, 0x33, 0x5E, 0xC5, 0x02, 0xF8, 0xFF, 0xFF,
+ 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x33, 0x5E, 0xC5, 0x02, 0xF8, 0xFF, 0xFF,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xE0, 0x59, 0x46, 0xC5, 0x02, 0xF8, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x03, 0x00, 0xCC, 0xC3, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+ 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x2B, 0x00,
+ 0x2B, 0x00, 0x53, 0x00,
+}