First try, decoding games and players from sniffed connections.

This commit is contained in:
Madeorsk 2019-04-14 20:13:52 +02:00
parent 6621626b23
commit 12527d6a17
16 changed files with 577 additions and 0 deletions

9
ConspiracyDecoder.iml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

11
binary/binary_coder.go Normal file
View File

@ -0,0 +1,11 @@
package binary
import (
"encoding/binary"
)
func BinarySetInt(val int64, length byte) []byte {
bytes := make([]byte, 8)
binary.BigEndian.PutUint64(bytes, uint64(val))
return bytes[8-length:]
}

21
binary/binary_decoder.go Normal file
View File

@ -0,0 +1,21 @@
package binary
func BinaryGetInt(bytes []byte, s, length int) (res int64) {
res = 0
for i := 0; i < length; i++ {
res += (int64) (bytes[s + i]) << (8*uint(length - i - 1))
}
return res
}
func BinaryGetBool(bytes []byte, s int) bool {
return bytes[s] == 1
}
func BinaryGetString(bytes []byte, s int) (string, int) {
strStart := s+1
strEnd := strStart + int(bytes[s])
return string(bytes[strStart:strEnd]), 1+int(bytes[s])
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module ConspiracyDecoder
go 1.12

72
main.go Normal file
View File

@ -0,0 +1,72 @@
package main
import (
"ConspiracyDecoder/requetes"
"crypto/tls"
"fmt"
"io/ioutil"
"os"
)
func testDecode(filename string) {
bytes, err := ioutil.ReadFile(filename)
if (err != nil) {
fmt.Println("Impossible de lire le fichier.")
os.Exit(1)
}
requetes.ReadRequete(bytes)
}
func moreTestDecode() {
testDecode("todecode2.log")
testDecode("todecode3.log")
testDecode("todecode4.log")
}
func testAutoDecodeRequest() {
requeteBytes := requetes.CreateRequetteConnecteeBasique(requetes.NUMREQ_REQUET_MES_PARTIES)
requetes.ReadRequete(requeteBytes)
}
func testServerRequest() {
conf := &tls.Config{
InsecureSkipVerify: true, //TODO Voir pour ajouter le certificat Badfrog.
}
var n int
var err error
var conn *tls.Conn
conn, err = tls.Dial("tcp", "conspiracy.badfrog.info:26677", conf)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer conn.Close()
n, err = conn.Write(requetes.CreateRequetteConnecteeBasique(requetes.NUMREQ_REQUET_MES_PARTIES))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
buf := make([]byte, 4096)
n, err = conn.Read(buf)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
requetes.ReadRequete(buf[:n])
}
func main() {
//testDecode("todecode1.log")
moreTestDecode()
testAutoDecodeRequest()
// Tentative de connexion au serveur pour envoi d'une requête perso.
testServerRequest()
}

View File

@ -0,0 +1,48 @@
package donnees
import (
"ConspiracyDecoder/binary"
"fmt"
)
type InfoJoueur struct {
Id int
Pseudo string
AvatarId int64
Grade byte
Fiabilite int32
DateCreation int32
EloClassic int16
RankingClasic int32
EloGunboat int16
RankingGunboat int32
Bloque bool
}
func ReadInfoJoueur(bytes []byte, s *int) (*InfoJoueur, error) {
joueur := new(InfoJoueur)
readSize := 0
joueur.Id = int(binary.BinaryGetInt(bytes, *s, 4)); *s += 4
joueur.Pseudo, readSize = binary.BinaryGetString(bytes, *s); *s += readSize
joueur.AvatarId = binary.BinaryGetInt(bytes, *s, 8); *s += 8
joueur.Grade = bytes[*s]; *s++
joueur.Fiabilite = int32(binary.BinaryGetInt(bytes, *s,4)); *s += 4
joueur.DateCreation = int32(binary.BinaryGetInt(bytes, *s,4)); *s += 4
joueur.EloClassic = int16(binary.BinaryGetInt(bytes, *s,2)); *s += 2
joueur.RankingClasic = int32(binary.BinaryGetInt(bytes, *s,4)); *s += 4
joueur.EloGunboat = int16(binary.BinaryGetInt(bytes, *s,2)); *s += 2
joueur.RankingGunboat = int32(binary.BinaryGetInt(bytes, *s,4)); *s += 4
joueur.Bloque = binary.BinaryGetBool(bytes, 1); *s++
fmt.Printf("%+v\n", *joueur)
*s += 3 // Octets de séparation ?
return joueur, nil
}

View File

@ -0,0 +1,152 @@
package donnees
import (
"ConspiracyDecoder/binary"
)
type InfoPartie struct {
Id int32
TypeCarte byte
NbJoueurs byte
Nom string
IdCreateur int32
Classee bool
Privee bool
TirageAleatoire bool
Anonyme bool
Langue byte
Joueurs []*InfoJoueur
DureeMvt int32
DureeAjust int32
NbAnneeFin int16
HeureDemarrage int32
HeureRepriseJeu int32
DureeJouable int32
DateCreation int32
DateDebut int32
DateFin int32
DatePause int32
DateNegoFin int32
NbPhase int16
DateResolution int32
IdEmpireReqMatchNul byte
PhaseReqNul int16
EmpirePause byte
ResultatJoueur []byte
AnneeFinJoueur []byte
DeltaClassement []int16
JoueurIdEmpire byte
OrdresAttendus bool
resolImm bool
Ordres []int64
MasqueEmpiresBloques int16
MasqueEmpiresAyantBloqueJoueur int16
JoueurNbMsg int32
MessagesNonLus []int64
}
func ReadInfoPartie(bytes []byte) (InfoPartie, error) {
partie := InfoPartie{}
readSize := 0
s := 0
partie.Id = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.TypeCarte = bytes[s]; s += 1
partie.NbJoueurs = bytes[s]; s += 1
partie.Nom, readSize = binary.BinaryGetString(bytes, s); s += readSize
partie.IdCreateur = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.Classee = binary.BinaryGetBool(bytes, s); s++
partie.Privee = binary.BinaryGetBool(bytes, s); s++
partie.TirageAleatoire = binary.BinaryGetBool(bytes, s); s++
partie.Anonyme = binary.BinaryGetBool(bytes, s); s++
partie.Langue = bytes[s]; s++
// Lecture des joueurs.
partie.Joueurs = make([]*InfoJoueur, partie.NbJoueurs)
for i := 0; i < int(partie.NbJoueurs); i++ {
infosJoueurExistent := binary.BinaryGetBool(bytes, s); s++
if infosJoueurExistent {
infosJoueur, err := ReadInfoJoueur(bytes, &s) // Lecture des informations du joueur.
if err != nil {
partie.Joueurs[i] = nil // Les informations sur le joueur `i` n'existent pas.
} else {
partie.Joueurs[i] = infosJoueur // Les informations sur le joueur `i` existent, on les sauvegarde.
}
} else {
partie.Joueurs[i] = nil // Les informations sur le joueur `i` n'existent pas.
}
}
partie.DureeMvt = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.DureeAjust = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.NbAnneeFin = int16(binary.BinaryGetInt(bytes, s, 2)); s += 2
partie.HeureDemarrage = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.HeureRepriseJeu = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.DureeJouable = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.DateCreation = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.DateDebut = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.DateFin = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.DatePause = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.DateNegoFin = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.NbPhase = int16(binary.BinaryGetInt(bytes, s, 2)); s += 2
partie.DateResolution = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.IdEmpireReqMatchNul = bytes[s]; s++
partie.PhaseReqNul = int16(binary.BinaryGetInt(bytes, s, 2)); s += 2
partie.EmpirePause = bytes[s]; s++
partie.ResultatJoueur = make([]byte, partie.NbJoueurs)
partie.AnneeFinJoueur = make([]byte, partie.NbJoueurs)
partie.DeltaClassement = make([]int16, partie.NbJoueurs)
for i := 0; i < int(partie.NbJoueurs); i++ {
partie.ResultatJoueur[i] = bytes[s]; s++
partie.AnneeFinJoueur[i] = bytes[s]; s++
partie.DeltaClassement[i] = int16(binary.BinaryGetInt(bytes, s, 2)); s += 2
}
// Ordres du joueur.
nbBytes := GetOrdresNbBytes(partie.TypeCarte)
nbOrdres := int16(binary.BinaryGetInt(bytes, s, 2)); s += 2
partie.Ordres = make([]int64, nbOrdres)
for i := 0; i < int(nbOrdres); i++ {
partie.Ordres[i] = binary.BinaryGetInt(bytes, s, nbBytes); s += nbBytes
}
partie.MasqueEmpiresBloques = int16(binary.BinaryGetInt(bytes, s, 2)); s += 2
partie.MasqueEmpiresAyantBloqueJoueur = int16(binary.BinaryGetInt(bytes, s, 2)); s += 2
partie.JoueurNbMsg = int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
nbMessagesNonLus := int32(binary.BinaryGetInt(bytes, s, 4)); s += 4
partie.MessagesNonLus = make([]int64, nbMessagesNonLus)
for i := 0; i < int(nbMessagesNonLus); i++ {
partie.MessagesNonLus[i] = binary.BinaryGetInt(bytes, s, 8); s += 8
}
s += 4 // Octets de séparation ?
return partie, nil
}
func GetOrdresNbBytes(typeCarte byte) int {
if typeCarte == 1 {
return 4
} else {
return 5
}
}

73
requetes/entete.go Normal file
View File

@ -0,0 +1,73 @@
package requetes
import (
"ConspiracyDecoder/binary"
"errors"
)
const VERSION int32 = 8
var PREAMBULES []int64 = []int64{
2323970988526936351,
-3592950365806191536,
-5122968913806141892,
8933515957217044033,
6339469214218321472,
3767780935981252362,
7646783252089175650,
5853113887207920170,
-394272200419613097,
7360280507499510862,
4357950641317498845,
6624737535948636999,
-553746225865565502,
661090631784662623,
}
type Entete struct {
Preambule int64 `json:"preambule"`
Version int32 `json:"version"`
NumReq int32 `json:"num_req"`
TailleReq int32 `json:"taille_req"`
}
func isPreambuleValide(preambule int64) bool {
for _, el := range PREAMBULES {
if preambule == el {
return true
}
}
return false
}
func isVersionValide(version int32) bool {
return version == VERSION
}
func ReadEntete(bytes []byte) (Entete, error) {
entete := Entete{}
entete.Preambule = binary.BinaryGetInt(bytes, 0, 8)
if !isPreambuleValide(entete.Preambule) {
return Entete{}, errors.New("invalid preambule")
}
entete.Version = int32(binary.BinaryGetInt(bytes, 8, 4))
if !isVersionValide(entete.Version) {
return Entete{}, errors.New("invalid version")
}
entete.NumReq = int32(binary.BinaryGetInt(bytes, 12, 4))
entete.TailleReq = int32(binary.BinaryGetInt(bytes, 16, 4))
return entete, nil
}
func WriteEntete(entete Entete) []byte {
bytes := binary.BinarySetInt(entete.Preambule, 8)
bytes = append(bytes, binary.BinarySetInt(int64(entete.Version), 4)...)
bytes = append(bytes, binary.BinarySetInt(int64(entete.NumReq), 4)...)
bytes = append(bytes, binary.BinarySetInt(int64(entete.TailleReq), 4)...)
return bytes
}

66
requetes/reqconst.go Normal file
View File

@ -0,0 +1,66 @@
package requetes
const (
NUMREQ_RESULT_EXCEPTION = iota
NUMREQ_RESULT_REQUETE = iota
NUMREQ_REQUET_CREATION_COMPTE = iota
NUMREQ_REQUET_CONNEXION = iota
NUMREQ_REQUET_CONNEXION_GOOGLE = iota
NUMREQ_REQUET_DECONNEXION = iota
NUMREQ_RESULT_CONNEXION = iota
NUMREQ_REQUET_ADMINISTRATEUR = iota
NUMREQ_REQUET_SERVEUR_STAT = iota
NUMREQ_RESULT_SERVEUR_STAT = iota
NUMREQ_REQUET_SUPPR_REG_ID = iota
NUMREQ_REQUET_DEPRECATED_CREATION_PARTIE = iota
NUMREQ_REQUET_REJOINDRE_PARTIE = iota
NUMREQ_REQUET_GERE_CONTACT = iota
NUMREQ_REQUET_MES_CONTACTS = iota
NUMREQ_REQUET_INFO_JOUEUR = iota
NUMREQ_RESULT_INFO_JOUEUR = iota
NUMREQ_REQUET_MES_STATISTIQUES = iota
NUMREQ_REQUET_INFO_STATISTIQUE = iota
NUMREQ_RESULT_INFO_STATISTIQUE = iota
NUMREQ_REQUET_MES_PARTIES = iota
NUMREQ_REQUET_DEPRECATED_INFO_PARTIE = iota
NUMREQ_RESULT_DEPRECATED_INFO_PARTIE_V2 = iota
NUMREQ_REQUET_DONNEES_PARTIE = iota
NUMREQ_RESULT_DONNEES_PARTIE = iota
NUMREQ_REQUET_DEPRECATED_TRANS_ORDRE = iota
NUMREQ_REQUET_MATCH_NUL = iota
NUMREQ_REQUET_PAUSE_PARTIE = iota
NUMREQ_REQUET_TRANS_MSG = iota
NUMREQ_REQUET_ACQUITTE_MSG_LU = iota
NUMREQ_REQUET_RECUP_MSG = iota
NUMREQ_RESULT_RECUP_MSG = iota
NUMREQ_REQUET_TRANS_INVIT = iota
NUMREQ_REQUET_MES_INVIT = iota
NUMREQ_REQUET_INFO_INVIT = iota
NUMREQ_RESULT_INFO_INVIT = iota
NUMREQ_REQUET_REPONSE_INVIT = iota
NUMREQ_REQUET_AJOUTE_OBSERVATION = iota
NUMREQ_REQUET_MES_OBSERVATIONS = iota
NUMREQ_REQUET_CHANGE_PROFIL = iota
NUMREQ_REQUET_DEPRECATED_CHANGE_PARAM_PARTIE = iota
NUMREQ_REQUET_RETRAIT_PARTIE = iota
NUMREQ_REQUET_ENVOI_NOTIF_INFO_MSG = iota
NUMREQ_REQUET_DEPRECATED_INFO_PARTIE_2 = iota
NUMREQ_REQUET_ABANDON_PARTIE = iota
NUMREQ_REQUET_VALIDE_ACHAT = iota
NUMREQ_REQUET_CREATION_PARTIE = iota
NUMREQ_REQUET_CHANGE_PARAM_PARTIE = iota
NUMREQ_REQUET_SIGNAL_TRICHE = iota
NUMREQ_REQUET_BANNISSEMENT = iota
NUMREQ_REQUET_TRANS_ORDRE = iota
NUMREQ_REQUET_INFO_PARTIE = iota
NUMREQ_REQUET_DEPRECATED_INSCRIPTION_PARTIE = iota
NUMREQ_REQUET_DESINSCRIPTION_PARTIE = iota
NUMREQ_RESULT_INFO_INSCRIPTION_PARTIE = iota
NUMREQ_REQUET_MODIFIER_JOUEUR = iota
NUMREQ_REQUET_DEPRECATED_INSCRIPTION_PARTIE_2 = iota
NUMREQ_REQUET_DEPRECATED_INSCRIPTION_PARTIE_3 = iota
NUMREQ_REQUET_INFO_PARAM_SERVEUR = iota
NUMREQ_RESULT_INFO_PARAM_SERVEUR = iota
NUMREQ_REQUET_VALIDE_DEMARRAGE = iota
NUMREQ_REQUET_INSCRIPTION_PARTIE = iota
)

View File

@ -0,0 +1,71 @@
package requetes
import (
"ConspiracyDecoder/binary"
"fmt"
"os"
)
func ReadRequete(bytes []byte) {
bytesEntete := bytes[:20]
// Lecture de l'en-tête.
entete, err := ReadEntete(bytesEntete)
if (err != nil) {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("%+v\n", entete)
switch entete.NumReq {
case NUMREQ_REQUET_MES_PARTIES:
fmt.Println("Requête : Mes parties.")
// Lecture d'une requête connectée basique.
reqConnectee := ReadRequeteConnectee(bytes[20:28])
fmt.Printf("ID de connexion : %d\n", reqConnectee.IdConnexion)
if len(bytes) > 28 {
fmt.Println("\nLecture de la réponse.")
fmt.Println("\n === \n")
ReadRequete(bytes[28:]) // Lecture de la réponse, directement après dans le buffer.
}
break
case NUMREQ_RESULT_INFO_INSCRIPTION_PARTIE:
fmt.Println("Réponse : Informations inscriptions / parties.")
parties, err := ReadResultatInfoInscriptionPartie(bytes[20:])
if err != nil {
fmt.Println(err)
os.Exit(1)
}
for index, partie := range parties {
fmt.Printf("%d: %+v\n", index, partie)
}
break
case NUMREQ_RESULT_EXCEPTION:
fmt.Println("Réponse : Exception.")
fmt.Printf("CodeException : %d\n", binary.BinaryGetInt(bytes, 20, 4))
break
case NUMREQ_RESULT_REQUETE:
fmt.Println("Réponse : RésultatRequête.")
fmt.Printf("Message acquité : %d\nCode erreur : %d\n", binary.BinaryGetInt(bytes, 20, 4), binary.BinaryGetInt(bytes, 24, 4))
break
default:
fmt.Println("Type de requête non géré.")
}
}
func CreateRequetteConnecteeBasique(typeRequete int) []byte {
return append(WriteEntete(Entete{
Preambule: PREAMBULES[0],
Version: VERSION,
NumReq: int32(typeRequete),
TailleReq: 8,
}), WriteRequeteConnectee(RequeteConnectee{
IdConnexion: -1614038591215924784,
})...)
}

View File

@ -0,0 +1,16 @@
package requetes
import "ConspiracyDecoder/binary"
type RequeteConnectee struct {
IdConnexion int64
}
func ReadRequeteConnectee(bytes []byte) (reqConnectee RequeteConnectee) {
reqConnectee.IdConnexion = binary.BinaryGetInt(bytes, 0, 8)
return
}
func WriteRequeteConnectee(reqConnectee RequeteConnectee) []byte {
return binary.BinarySetInt(reqConnectee.IdConnexion, 8)
}

View File

@ -0,0 +1,35 @@
package requetes
import (
"ConspiracyDecoder/requetes/donnees"
"errors"
"fmt"
"os"
)
func ReadResultatInfoInscriptionPartie(bytes []byte) ([]donnees.InfoPartie, error) {
size := ((int) (bytes[0] & 255) << 8) + ((int) (bytes[1] & 255))
// (size détermine le nombre de parties / inscriptions, on ne traite ici que des parties.)
// On crée un tableau de `size` parties.
parties := make([]donnees.InfoPartie, size)
if size > 0 {
//TODO Faire une boucle pour lire toutes les parties.
// 1 = InfoInscription, 0 = InfoPartie.
if bytes[2] == 0 {
// Dans le cas des informations d'une partie...
partie, err := donnees.ReadInfoPartie(bytes[3:])
if err != nil {
fmt.Println(err)
os.Exit(1)
}
parties[0] = partie
} else {
return nil, errors.New("infoinscription not handled")
}
}
return parties, nil
}

BIN
todecode1.log Normal file

Binary file not shown.

BIN
todecode2.log Normal file

Binary file not shown.

BIN
todecode3.log Normal file

Binary file not shown.

BIN
todecode4.log Normal file

Binary file not shown.