From 12527d6a17a045dd4ce90c9ef4c30f137669b644 Mon Sep 17 00:00:00 2001 From: Madeorsk Date: Sun, 14 Apr 2019 20:13:52 +0200 Subject: [PATCH] First try, decoding games and players from sniffed connections. --- ConspiracyDecoder.iml | 9 ++ binary/binary_coder.go | 11 ++ binary/binary_decoder.go | 21 +++ go.mod | 3 + main.go | 72 +++++++++ requetes/donnees/info_joueur.go | 48 ++++++ requetes/donnees/info_partie.go | 152 +++++++++++++++++++ requetes/entete.go | 73 +++++++++ requetes/reqconst.go | 66 ++++++++ requetes/requete_broker.go | 71 +++++++++ requetes/requete_connectee.go | 16 ++ requetes/resultat_info_inscription_partie.go | 35 +++++ todecode1.log | Bin 0 -> 244 bytes todecode2.log | Bin 0 -> 227 bytes todecode3.log | Bin 0 -> 50 bytes todecode4.log | Bin 0 -> 244 bytes 16 files changed, 577 insertions(+) create mode 100644 ConspiracyDecoder.iml create mode 100644 binary/binary_coder.go create mode 100644 binary/binary_decoder.go create mode 100644 go.mod create mode 100644 main.go create mode 100644 requetes/donnees/info_joueur.go create mode 100644 requetes/donnees/info_partie.go create mode 100644 requetes/entete.go create mode 100644 requetes/reqconst.go create mode 100644 requetes/requete_broker.go create mode 100644 requetes/requete_connectee.go create mode 100644 requetes/resultat_info_inscription_partie.go create mode 100644 todecode1.log create mode 100644 todecode2.log create mode 100644 todecode3.log create mode 100644 todecode4.log diff --git a/ConspiracyDecoder.iml b/ConspiracyDecoder.iml new file mode 100644 index 0000000..eacc75a --- /dev/null +++ b/ConspiracyDecoder.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/binary/binary_coder.go b/binary/binary_coder.go new file mode 100644 index 0000000..2429182 --- /dev/null +++ b/binary/binary_coder.go @@ -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:] +} \ No newline at end of file diff --git a/binary/binary_decoder.go b/binary/binary_decoder.go new file mode 100644 index 0000000..331a0f8 --- /dev/null +++ b/binary/binary_decoder.go @@ -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]) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bbd4bb0 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module ConspiracyDecoder + +go 1.12 diff --git a/main.go b/main.go new file mode 100644 index 0000000..0ccbf52 --- /dev/null +++ b/main.go @@ -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() +} diff --git a/requetes/donnees/info_joueur.go b/requetes/donnees/info_joueur.go new file mode 100644 index 0000000..15852da --- /dev/null +++ b/requetes/donnees/info_joueur.go @@ -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 +} diff --git a/requetes/donnees/info_partie.go b/requetes/donnees/info_partie.go new file mode 100644 index 0000000..1d99225 --- /dev/null +++ b/requetes/donnees/info_partie.go @@ -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 + } +} diff --git a/requetes/entete.go b/requetes/entete.go new file mode 100644 index 0000000..c6267bc --- /dev/null +++ b/requetes/entete.go @@ -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 +} \ No newline at end of file diff --git a/requetes/reqconst.go b/requetes/reqconst.go new file mode 100644 index 0000000..178494e --- /dev/null +++ b/requetes/reqconst.go @@ -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 +) \ No newline at end of file diff --git a/requetes/requete_broker.go b/requetes/requete_broker.go new file mode 100644 index 0000000..1f9f2d6 --- /dev/null +++ b/requetes/requete_broker.go @@ -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, + })...) +} diff --git a/requetes/requete_connectee.go b/requetes/requete_connectee.go new file mode 100644 index 0000000..077e2cb --- /dev/null +++ b/requetes/requete_connectee.go @@ -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) +} diff --git a/requetes/resultat_info_inscription_partie.go b/requetes/resultat_info_inscription_partie.go new file mode 100644 index 0000000..ef442ef --- /dev/null +++ b/requetes/resultat_info_inscription_partie.go @@ -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 +} diff --git a/todecode1.log b/todecode1.log new file mode 100644 index 0000000000000000000000000000000000000000..c4e426fa2e6016cb31eaf4ccaa099d21541f8dbf GIT binary patch literal 244 zcmWIl6}WN|<39%m1_llw76H*OXPzj2bGGxskAO{k&riH{2r6d=q>nH#GB7Z2H)3OA z4M{C7DP~}rV8Ou12tw?Q2ROWoi}F)b5_5qn!2qb3fgxs-P%-lh28KhuAd;mAB*qBR z$l;rqlA2#soNdUY$l)`A$qlGnZFXextu<`dflBfa${82~8^AdEKN^VH^wR_;1X2Ms VB4&%%7N8IV0}z2RBLj$s006*VPsjiO literal 0 HcmV?d00001 diff --git a/todecode2.log b/todecode2.log new file mode 100644 index 0000000000000000000000000000000000000000..7dfa5aed041b7935729cfe06fed57df5787886f7 GIT binary patch literal 227 zcmd0jyK<{suP%v!fq?^v6+!gNnJ0?hob9}@<9Xk!MDs8ksGKs8-ps(rAenZCfqA?%aA-H!Yljfq?^vML_h+nJ0?hob9}jrt(99V{eTgRL%@YA7Nl*U|`;E#Ky!L zl3H9+%)m6kf`O3{gxDJoaCjFN<)@}3<^olM0Z=gmL(C?jV&)eN42OC_BufuSj1i=f z!#6P{HNU7h+mK0-!)F4M8&J90?8xL>YuK&>mE<9mGcX1=fN}DFG!V1trwL35qylI} U%oeXLKp_SOAOd4X1`rPc0FxO_DgXcg literal 0 HcmV?d00001