Tags: misc
Rating: 3.0
You're given a picture of a Lego Mindstorms EV3 display block with the flag partially displayed on it. You're also given a macOS PacketLogger file.
If you open the PacketLogger file, you'll find a capture of packets as they were being sent back and forth to the device. You can export that to a format that's easier to write a parser for via File -> Export -> Payload as Text...
Once you do that, you can use the EV3 developer kit documentation to find the specifications for the protocol used. To get the flag, you only need to parse outgoing packets, and you only really need to pay attention to one thing: the TEXT
command of the opUI_DRAW
instruction.
We can write a crude Go parser like so:
package main
import (
"bufio"
"encoding/binary"
"encoding/hex"
"fmt"
"os"
"sort"
"strings"
)
func parseParameter(b *[]byte) interface{} {
switch (*b)[0] & 0xc0 {
case 0:
// short constant
var ret int
if (*b)[0]&0x20 == 0 {
// positive
ret = int((*b)[0] & 0x1f)
} else {
// negative
ret = -int((*b)[0] & 0x1f)
}
*b = (*b)[1:]
return ret
case 0x80:
// long constant
if (*b)[0]&0xf0 == 0x80 {
// value
switch (*b)[0] & 0x07 {
case 0, 4:
// null-terminated
s := ""
*b = (*b)[1:]
for (*b)[0] != 0 {
s += string(rune((*b)[0]))
*b = (*b)[1:]
}
*b = (*b)[1:]
return s
case 1:
// 1 byte
ret := (*b)[1]
*b = (*b)[2:]
return int(ret)
case 2:
// 2 bytes
ret := binary.LittleEndian.Uint16((*b)[1:])
*b = (*b)[3:]
return int(ret)
case 3:
// 4 bytes
ret := binary.LittleEndian.Uint32((*b)[1:])
*b = (*b)[5:]
return int(ret)
}
}
}
return nil
}
func main() {
f, err := os.Open("Extracted Data/RFCOMM-Oct 18 16_06_54.995.txt")
if err != nil {
panic(err)
}
defer f.Close()
type Text struct {
X int
Y int
S string
}
var texts []Text
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if !strings.HasPrefix(line, "0B EF") {
continue
}
line = strings.Replace(line[9:], " ", "", -1)
b, err := hex.DecodeString(line)
if err != nil {
panic(err)
}
byteCodes := b[7:]
byteCodes = byteCodes[:len(byteCodes)-2]
for len(byteCodes) > 0 {
switch byteCodes[0] {
case 0x80:
// FLUSH
byteCodes = byteCodes[1:]
case 0x84:
// DRAW
switch byteCodes[1] {
case 0x00:
// UPDATE
byteCodes = byteCodes[2:]
case 0x05:
// TEXT
byteCodes = byteCodes[2:]
parseParameter(&byteCodes)
x := parseParameter(&byteCodes).(int)
y := parseParameter(&byteCodes).(int)
s := parseParameter(&byteCodes).(string)
texts = append(texts, Text{x, y, s})
case 0x12:
// TOPLINE
byteCodes = byteCodes[3:]
case 0x13:
// FILLWINDOW
byteCodes = byteCodes[4:]
default:
panic(fmt.Errorf("unexpected byte code: %x", byteCodes[1]))
}
default:
panic(fmt.Errorf("unexpected byte code: %x", byteCodes[0]))
}
}
}
if err := scanner.Err(); err != nil {
panic(err)
}
sort.Slice(texts, func(i, j int) bool {
if texts[i].Y < texts[j].Y {
return true
}
if texts[i].Y == texts[j].Y {
return texts[i].X < texts[j].X
}
return false
})
y := 0
for _, t := range texts {
if t.Y != y {
fmt.Printf("\n")
y = t.Y
}
fmt.Printf("%s", t.S)
}
fmt.Printf("\n")
}
The result:
hitcon{m1nd5t0rm
_communication_a
nd_firmware_deve
loper_kit}