init commit

This commit is contained in:
Jose Luis Montañes Ojados
2026-01-14 21:33:21 +01:00
commit dbab788e6b
27 changed files with 4001 additions and 0 deletions

215
cmd/extract/main.go Normal file
View File

@@ -0,0 +1,215 @@
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)
}
}

441
cmd/proxy/main.go Normal file
View File

@@ -0,0 +1,441 @@
package main
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/binary"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/big"
"net"
"net/http"
"os"
"strings"
"sync"
"time"
)
// Config
var (
TargetHTTPHost = "https://api.asmodee.net"
TargetTCPHost = "got.games.asmodee.net"
TargetTCPPort = "2445"
ProxyHTTPPort = ":8080"
ProxyTCPPort = ":3000"
LogFileName = ""
LogFile *os.File
LogMutex sync.Mutex
)
// Log Entry Structure
type LogEntry struct {
Timestamp string `json:"timestamp"`
Protocol string `json:"protocol"` // HTTP or TCP
Type string `json:"type"` // REQUEST or RESPONSE
Summary string `json:"summary"` // e.g. "GET /v1/foo" or "Packet 123"
Data interface{} `json:"data"` // Structured data or raw info
}
// Write to JSONL
func logJSON(entry LogEntry) {
entry.Timestamp = time.Now().Format(time.RFC3339Nano)
LogMutex.Lock()
defer LogMutex.Unlock()
bytes, err := json.Marshal(entry)
if err == nil {
if LogFile != nil {
LogFile.Write(bytes)
LogFile.WriteString("\n")
} else {
fmt.Println(string(bytes))
}
}
}
func main() {
flag.StringVar(&TargetHTTPHost, "target-http", "https://api.asmodee.net", "Target HTTP Host")
flag.StringVar(&TargetTCPHost, "target-tcp-host", "got.games.asmodee.net", "Target TCP Host")
flag.StringVar(&TargetTCPPort, "target-tcp-port", "2445", "Target TCP Port")
flag.Parse()
LogFileName = fmt.Sprintf("session_%s.jsonl", time.Now().Format("20060102_150405"))
f, err := os.Create(LogFileName)
if err != nil {
log.Fatal(err)
}
LogFile = f
defer LogFile.Close()
fmt.Printf("Starting Proxy...\nLog: %s\nHTTP Target: %s\nTCP Target Default: %s:%s\n", LogFileName, TargetHTTPHost, TargetTCPHost, TargetTCPPort)
// Start HTTP Proxy
go startHTTPProxy()
// Start TCP Proxy
startTCPProxy()
}
// --- HTTP Proxy ---
func startHTTPProxy() {
http.HandleFunc("/", handleHTTPRequest)
fmt.Println("[HTTP] Listening on " + ProxyHTTPPort)
log.Fatal(http.ListenAndServe(ProxyHTTPPort, nil))
}
func handleHTTPRequest(w http.ResponseWriter, r *http.Request) {
// Log Request
bodyBytes, _ := ioutil.ReadAll(r.Body)
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) // Restore for forwarding
logJSON(LogEntry{
Protocol: "HTTP",
Type: "REQUEST",
Summary: fmt.Sprintf("%s %s", r.Method, r.URL.Path),
Data: map[string]interface{}{
"headers": r.Header,
"body": string(bodyBytes),
},
})
// Forward Request
targetURL := TargetHTTPHost + r.URL.Path
if r.URL.RawQuery != "" {
targetURL += "?" + r.URL.RawQuery
}
proxyReq, err := http.NewRequest(r.Method, targetURL, bytes.NewBuffer(bodyBytes))
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
// Copy headers
for name, values := range r.Header {
for _, value := range values {
proxyReq.Header.Add(name, value)
}
}
// Remove Hop-by-hop headers
proxyReq.Header.Del("Host") // Let http client set it
// Might need to set Host header manually if target expects it
// proxyReq.Host = ...
client := &http.Client{}
resp, err := client.Do(proxyReq)
if err != nil {
// Log Error
logJSON(LogEntry{
Protocol: "HTTP",
Type: "ERROR",
Summary: "Forwarding Failed",
Data: err.Error(),
})
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
defer resp.Body.Close()
respBody, _ := ioutil.ReadAll(resp.Body)
// Log Response
logJSON(LogEntry{
Protocol: "HTTP",
Type: "RESPONSE",
Summary: fmt.Sprintf("%s %d", r.URL.Path, resp.StatusCode),
Data: map[string]interface{}{
"status": resp.StatusCode,
"headers": resp.Header,
"body": string(respBody),
},
})
// Sniff for TCP Info
// Look for "server": { "host": ... } or "HostName": ...
sniffTCPInfo(respBody)
// Write Response
for name, values := range resp.Header {
for _, value := range values {
w.Header().Add(name, value)
}
}
w.WriteHeader(resp.StatusCode)
w.Write(respBody)
}
func sniffTCPInfo(body []byte) {
// Search for "host":"..." and "port":... pattern
s := string(body)
if strings.Contains(s, "\"host\"") && strings.Contains(s, "\"port\"") {
idxHost := strings.Index(s, "\"host\"")
if idxHost != -1 {
// Find value
startQuote := strings.Index(s[idxHost:], ":") + idxHost + 1
startQuote = strings.Index(s[startQuote:], "\"") + startQuote
endQuote := strings.Index(s[startQuote+1:], "\"") + startQuote + 1
if startQuote != -1 && endQuote != -1 {
extractedHost := s[startQuote+1 : endQuote]
if extractedHost != "" {
fmt.Printf("[PROXY] Sniffed TCP Host: %s\n", extractedHost)
TargetTCPHost = extractedHost
}
}
}
idxPort := strings.Index(s, "\"port\"")
if idxPort != -1 {
// Find value (number)
colon := strings.Index(s[idxPort:], ":") + idxPort
// Find first digit after colon
startNum := -1
endNum := -1
for i := colon + 1; i < len(s); i++ {
c := s[i]
if c >= '0' && c <= '9' {
if startNum == -1 {
startNum = i
}
} else if startNum != -1 {
// End of number
endNum = i
break
}
}
if startNum != -1 {
if endNum == -1 {
endNum = len(s)
}
extractedPort := s[startNum:endNum]
fmt.Printf("[PROXY] Sniffed TCP Port: %s\n", extractedPort)
TargetTCPPort = extractedPort
}
}
}
}
func findHostPortRecursive(obj interface{}) {
switch v := obj.(type) {
case map[string]interface{}:
if h, ok := v["host"].(string); ok {
TargetTCPHost = h
fmt.Printf("[PROXY] Sniffed TCP Host: %s\n", h)
}
if p, ok := v["port"].(float64); ok { // JSON numbers are float64 in generic interface
TargetTCPPort = fmt.Sprintf("%d", int(p))
fmt.Printf("[PROXY] Sniffed TCP Port: %s\n", TargetTCPPort)
}
for _, val := range v {
findHostPortRecursive(val)
}
case []interface{}:
for _, val := range v {
findHostPortRecursive(val)
}
}
}
// --- TCP Proxy ---
func startTCPProxy() {
cert, err := generateSelfSignedCert()
if err != nil {
log.Fatal(err)
}
listener, err := tls.Listen("tcp", ProxyTCPPort, cert)
if err != nil {
log.Fatalf("TCP Listen failed: %v", err)
}
fmt.Println("[TCP] Listening on " + ProxyTCPPort)
for {
conn, err := listener.Accept()
if err != nil {
log.Println("TCP Accept error:", err)
continue
}
go handleTCPConn(conn)
}
}
func handleTCPConn(clientConn net.Conn) {
defer clientConn.Close()
// Connect to Real Server
targetAddr := fmt.Sprintf("%s:%s", TargetTCPHost, TargetTCPPort)
fmt.Printf("[TCP] New Connection. Forwarding to %s\n", targetAddr)
// Use TLS to connect to Real Server
serverConn, err := tls.Dial("tcp", targetAddr, &tls.Config{
InsecureSkipVerify: true, // We assume official server cert is fine but we skip verify to avoid setup
})
if err != nil {
log.Printf("Failed to dial target %s: %v\n", targetAddr, err)
return
}
defer serverConn.Close()
// Pipe
var wg sync.WaitGroup
wg.Add(2)
// Client -> Server
go func() {
defer wg.Done()
interceptAndLogTCP(clientConn, serverConn, "CLIENT->SERVER")
}()
// Server -> Client
go func() {
defer wg.Done()
interceptAndLogTCP(serverConn, clientConn, "SERVER->CLIENT")
}()
wg.Wait()
}
func interceptAndLogTCP(src, dst net.Conn, direction string) {
// We need to parse frames to log them effectively?
// The protocol is: 4 bytes length + Payload.
// Payload: Varint PacketID, Varint RequestID, Payload.
// We will try to read in a buffer loop, parse length prefix, log, then forward.
// But simply copying stream `io.Copy` is safer for latency.
// However, we want to LOG. So we MUST read packet by packet.
header := make([]byte, 4)
for {
// Read Head
_, err := io.ReadFull(src, header)
if err != nil {
break
}
length := binary.BigEndian.Uint32(header)
// Read Body
body := make([]byte, length)
_, err = io.ReadFull(src, body)
if err != nil {
break
}
// Write to Dst
// Write header + body
dst.Write(header)
dst.Write(body)
// Log
parseAndLogPacket(direction, body)
}
}
func parseAndLogPacket(direction string, data []byte) {
// Basic Decode of Packet Wrapper
// Packet ID (Field 1, Varint)
// Payload (Field 3, Bytes) -> Message
r := bytes.NewReader(data)
packetID := int64(-1)
var payloadBytes []byte
for {
tag, err := binary.ReadUvarint(r)
if err != nil {
break
}
fieldNum := tag >> 3
wireType := tag & 7
if fieldNum == 1 && wireType == 0 {
packetID, _ = binary.ReadVarint(r) // ReadVarint for int64
} else if fieldNum == 3 && wireType == 2 {
l, _ := binary.ReadUvarint(r)
payloadBytes = make([]byte, l)
r.Read(payloadBytes)
} else {
// Skip
skip(r, wireType)
}
}
// If we found payload, parse Message
requestID := int64(-1)
if payloadBytes != nil {
pr := bytes.NewReader(payloadBytes)
for {
tag, err := binary.ReadUvarint(pr)
if err != nil {
break
}
fieldNum := tag >> 3
wireType := tag & 7
if fieldNum == 1 && wireType == 0 { // Message.request_number
requestID, _ = binary.ReadVarint(pr)
} else {
skip(pr, wireType)
}
}
}
logJSON(LogEntry{
Protocol: "TCP",
Type: direction,
Summary: fmt.Sprintf("Packet %d | Req %d | Len %d", packetID, requestID, len(data)),
Data: map[string]interface{}{
"packet_id": packetID,
"request_id": requestID,
"hex": hex.EncodeToString(data),
},
})
}
func skip(r *bytes.Reader, wireType uint64) {
switch wireType {
case 0:
binary.ReadUvarint(r)
case 1:
r.Seek(8, io.SeekCurrent)
case 2:
l, _ := binary.ReadUvarint(r)
r.Seek(int64(l), io.SeekCurrent)
case 5:
r.Seek(4, io.SeekCurrent)
}
}
// --- Cert Gen (Copied) ---
func generateSelfSignedCert() (*tls.Config, error) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{Organization: []string{"Proxy"}},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
DNSNames: []string{"localhost"},
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return nil, err
}
return &tls.Config{Certificates: []tls.Certificate{{Certificate: [][]byte{derBytes}, PrivateKey: priv}}}, nil
}

24
cmd/server/main.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"customServer/internal/network"
"fmt"
"log"
)
func main() {
// The state manager initializes itself via init() in internal/state
// 1. HTTP Server for REST API (Port 8080)
go func() {
if err := network.StartHTTPServer(":8080"); err != nil {
log.Fatalf("HTTP server failed: %v", err)
}
}()
// 2. TCP Server for Scalable Server (Port 3000) with SSL
fmt.Println("[Main] Starting TCP Server...")
if err := network.StartTCPServer(":3000"); err != nil {
log.Fatalf("TCP server failed: %v", err)
}
}

1022
cmd/server/main.go.bak Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
CICggOjD6dXeNxAKGAMiVnsibmFtZSI6InN3dCIsInZhbHVlIjp7InNsbiI6eyJlaWQiOjE5MiwidHJzIjpbeyJlbHQiOls2NTNdLCJuYW1lIjoiZWx0ciJ9XX0sImN0ciI6MX19IlZ7Im5hbWUiOiJzd3QiLCJ2YWx1ZSI6eyJzbG4iOnsiZWlkIjo2NTMsInRycyI6W3siZWx0IjpbNTUxXSwibmFtZSI6ImVsdHIifV19LCJjdHIiOjF9fSJWeyJuYW1lIjoic3d0IiwidmFsdWUiOnsic2xuIjp7ImVpZCI6MjAyLCJ0cnMiOlt7ImVsdCI6WzYzOV0sIm5hbWUiOiJlbHRyIn1dfSwiY3RyIjoxfX0iVnsibmFtZSI6InN3dCIsInZhbHVlIjp7InNsbiI6eyJlaWQiOjYzOSwidHJzIjpbeyJlbHQiOls1NTFdLCJuYW1lIjoiZWx0ciJ9XX0sImN0ciI6MX19IlZ7Im5hbWUiOiJzd3QiLCJ2YWx1ZSI6eyJzbG4iOnsiZWlkIjoyMDIsInRycyI6W3siZWx0IjpbNjM5XSwibmFtZSI6ImVsdHIifV19LCJjdHIiOjF9fSJWeyJuYW1lIjoic3d0IiwidmFsdWUiOnsic2xuIjp7ImVpZCI6MTkyLCJ0cnMiOlt7ImVsdCI6WzY2MV0sIm5hbWUiOiJlbHRyIn1dfSwiY3RyIjoxfX0iVnsibmFtZSI6InN3dCIsInZhbHVlIjp7InNsbiI6eyJlaWQiOjE5MCwidHJzIjpbeyJlbHQiOls2NjNdLCJuYW1lIjoiZWx0ciJ9XX0sImN0ciI6MX19IlZ7Im5hbWUiOiJzd3QiLCJ2YWx1ZSI6eyJzbG4iOnsiZWlkIjo2NjMsInRycyI6W3siZWx0IjpbNTUxXSwibmFtZSI6ImVsdHIifV19LCJjdHIiOjF9fSJWeyJuYW1lIjoic3d0IiwidmFsdWUiOnsic2xuIjp7ImVpZCI6MTkwLCJ0cnMiOlt7ImVsdCI6WzY0NV0sIm5hbWUiOiJlbHRyIn1dfSwiY3RyIjoxfX0iIHsibmFtZSI6InN3dCIsInZhbHVlIjp7InNsbiI6eyJlaWQiOjE5MiwidHJzIjpbeyJlbHQiOltdLCJuYW1lIjoiZWx0ciJ9XX0sImN0ciI6NTd9fSJXeyJuYW1lIjoic3d0IiwidmFsdWUiOnsic2xuIjp7ImVpZCI6MTc1LCJ0cnMiOlt7ImVsdCI6WzE2N10sIm5hbWUiOiJlbHRyIn1dfSwiY3RyIjo2MX19IlR7Im5hbWUiOiJzd3QiLCJ2YWx1ZSI6eyJzbG4iOnsiZWlkIjoxNjksInRycyI6W3siZWx0IjpbXSwibmFtZSI6ImVsdHIifV19LCJjdHIiOjYzfX0iV3sibmFtZSI6InN3dCIsInZhbHVlIjp7InNsbiI6eyJlaWQiOjI1MCwidHJzIjpbeyJlbHQiOlsxNTZdLCJuYW1lIjoiZWx0ciJ9XX0sImN0ciI6NzJ9fSIgeyJuYW1lIjoibXMiLCJ2YWx1ZSI6eyJjdHIiOjc1fX0iL3sibmFtZSI6ImdjYyIsInZhbHVlIjp7InNlbGVjdGlvbiI6MCwiY3RyIjo4MX19Ild7Im5hbWUiOiJzd3QiLCJ2YWx1ZSI6eyJzbG4iOnsiZWlkIjoyNTAsInRycyI6W3siZWx0IjpbMTUyXSwibmFtZSI6ImVsdHIifV19LCJjdHIiOjg4fX0iL3sibmFtZSI6ImdjYyIsInZhbHVlIjp7InNlbGVjdGlvbiI6MywiY3RyIjo5N319Ilh7Im5hbWUiOiJzd3QiLCJ2YWx1ZSI6eyJzbG4iOnsiZWlkIjo1NDMsInRycyI6W3siZWx0IjpbMTkyXSwibmFtZSI6ImVsdHIifV19LCJjdHIiOjExMX19IjB7Im5hbWUiOiJnY2MiLCJ2YWx1ZSI6eyJzZWxlY3Rpb24iOjQsImN0ciI6MTE4fX0iMHsibmFtZSI6ImdjYyIsInZhbHVlIjp7InNlbGVjdGlvbiI6MCwiY3RyIjoxMjJ9fToWOgIIAToECAIQBToECAMQAToECAQQBA==