Files
got-mod-customserver-server/cmd/extract/main.go

216 lines
5.0 KiB
Go
Raw Normal View History

2026-01-14 21:33:21 +01:00
package main
import (
"bufio"
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
)
type LogEntry struct {
Timestamp string `json:"timestamp"`
Protocol string `json:"protocol"`
Type string `json:"type"`
Summary string `json:"summary"`
Data interface{} `json:"data"`
}
func main() {
// Hardcode for reliability
logFile := "session_20260107_174406.jsonl"
if logFile == "" {
panic("No session log found")
}
fmt.Printf("Reading %s...\n", logFile)
f, err := os.Open(logFile)
if err != nil {
panic(err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
// Larger buffer for huge lines
buf := make([]byte, 1024*1024)
scanner.Buffer(buf, 1024*1024)
for scanner.Scan() {
var entry LogEntry
if err := json.Unmarshal(scanner.Bytes(), &entry); err != nil {
continue
}
// Looking for the huge packet
// Summary: "Packet 1 | Req 256 | Len 24182" (or similar length)
if entry.Protocol == "TCP" && strings.Contains(entry.Summary, "Len") {
// Check if Data has hex
dataMap, ok := entry.Data.(map[string]interface{})
if !ok {
continue
}
hexData, ok := dataMap["hex"].(string)
if !ok || len(hexData) < 20000 { // Look for huge payload
continue
}
fmt.Println("Found candidate packet:", entry.Summary)
packetBytes, err := hex.DecodeString(hexData)
if err != nil {
panic(err)
}
// Parse wrapper
// We assume the payload of the wrapper is the Message
// Wrapper: [Tag1:PacketID] [Tag3:MessageBytes]
// We want MessageBytes.
// Simple parser: skip Tag1/Val1. Find Tag3 (0x1a).
// 08 02 (Packet ID 2) -> 2 bytes.
// 1a (Tag 3) -> 1 byte.
// Length (Varint).
// Heuristic search for 1a and length
// Or simple protobuf reader
r := bytes.NewReader(packetBytes)
// Skip Field 1 (08 xx)
b, _ := r.ReadByte()
if b == 0x08 {
// Read varint
for {
b, _ := r.ReadByte()
if b < 0x80 {
break
}
}
} else {
// Might be diff packet ID? Log said Packet 1. 08 01.
fmt.Println("Warning: Expected 0x08 tag, got", b)
// Reset and try to find 0x1a anyway
r.Seek(0, 0)
}
// Read Tag
for {
b, err := r.ReadByte()
if err != nil {
break
}
if b == 0x1a { // Field 3
// This is likely the payload
fmt.Println("Found Field 3 tag (Message Payload)")
// Decode length
lenVal := uint64(0)
shift := uint(0)
for {
b, _ := r.ReadByte()
lenVal |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
shift += 7
}
fmt.Printf("Payload Length: %d\n", lenVal)
payload := make([]byte, lenVal)
n, _ := r.Read(payload)
if uint64(n) != lenVal {
fmt.Println("Warning: Short read")
}
// Now we have the Message (RequestNumber + GameStatusReportRequest).
// RequestNumber is Field 1 (08 80 04 -> 512).
// GameStatusReportRequest is Field 512 (Tag 82 20 -> 1002).
// We need the value of Field 512.
r2 := bytes.NewReader(payload)
for {
tag, err := readVarint(r2)
if err != nil {
break
}
fieldNum := tag >> 3
wireType := tag & 7
if fieldNum == 512 {
fmt.Println("Found Field 512 (GameStatusReportRequest)")
// Read length
msgLen, _ := readVarint(r2)
msgBytes := make([]byte, msgLen)
r2.Read(msgBytes)
// msgBytes is GameStatusReportRequest.
// It contains status_report (Field 1).
// We want to save *msgBytes* so we can inject it into a new Message struct?
// Or extract StatusReport payload?
// The user wants to set GameStatusReportRequest.StatusReport.
// So check inside msgBytes for Field 1.
r3 := bytes.NewReader(msgBytes)
tag3, _ := readVarint(r3)
if (tag3 >> 3) == 1 {
fmt.Println("Found Field 1 (StatusReport)")
srLen, _ := readVarint(r3)
srBytes := make([]byte, srLen)
r3.Read(srBytes)
// Wait, is StatusReport just one field?
// The log showed StatusReport had GameID, Status, Players...
// Ah, GameStatusReportRequest message only has ONE field: `status_report`.
// So `msgBytes` is the wire bytes for `GameStatusReportRequest`.
// If we assume `main.go` has `GameStatusReportRequest` struct, we can unmarshal `msgBytes` into it.
// YES.
err = ioutil.WriteFile("mock_gamestatus.bin", msgBytes, 0644)
if err != nil {
panic(err)
}
fmt.Println("Saved mock_gamestatus.bin")
return
}
} else {
// Skip
skip(r2, wireType)
}
}
}
}
}
}
}
func readVarint(r *bytes.Reader) (uint64, error) {
x := uint64(0)
s := uint(0)
for {
b, err := r.ReadByte()
if err != nil {
return 0, err
}
x |= uint64(b&0x7F) << s
if b < 0x80 {
return x, nil
}
s += 7
}
}
func skip(r *bytes.Reader, wireType uint64) {
switch wireType {
case 0:
readVarint(r)
case 1:
r.Seek(8, 1)
case 2:
l, _ := readVarint(r)
r.Seek(int64(l), 1)
case 5:
r.Seek(4, 1)
}
}