From 8f3f31d3540bd0e74b7b1ef337d4846646608cb3 Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 7 Mar 2023 01:20:36 -0600 Subject: [PATCH] server: implement REP_LOGIN key exchange --- protocol/encrypt.go | 19 ++++++++++-- server/client.go | 70 +++++++++++++++++++++++++++++++++------------ server/server.go | 4 +-- tools/genstructs.py | 16 +++++------ 4 files changed, 78 insertions(+), 31 deletions(-) diff --git a/protocol/encrypt.go b/protocol/encrypt.go index ecf846d..6b30fa1 100644 --- a/protocol/encrypt.go +++ b/protocol/encrypt.go @@ -1,8 +1,12 @@ package protocol +import ( + "encoding/binary" +) + const ( - E_KEY = "m@rQn~W#" - KEY_LENGTH = 8 + DEFAULT_KEY = "m@rQn~W#" + KEY_LENGTH = 8 ) func encrypt_byte_change_A(ERSize int, data []byte) int { @@ -43,3 +47,14 @@ func DecryptData(buff, key []byte) { size := encrypt_byte_change_A(ERSize, buff) xorData(buff, key, size) } + +func CreateNewKey(uTime uint64, iv1, iv2 uint64) []byte { + num := iv1 + 1 + num2 := iv2 + 1 + + dEKey := uint64(binary.LittleEndian.Uint64([]byte(DEFAULT_KEY))) + key := dEKey * (uTime * num * num2) + buf := make([]byte, 8) + binary.LittleEndian.PutUint64(buf, uint64(key)) + return buf +} diff --git a/server/client.go b/server/client.go index 5ec01e7..e3f0189 100644 --- a/server/client.go +++ b/server/client.go @@ -5,21 +5,30 @@ import ( "fmt" "log" "net" + "time" "github.com/CPunch/GopenFusion/protocol" ) +const ( + USE_E = iota + USE_FE +) + type Client struct { - server *Server - conn net.Conn - key []byte + server *Server + conn net.Conn + e_key []byte + fe_key []byte + whichKey int } -func newClient(server *Server, conn net.Conn, key []byte) *Client { +func newClient(server *Server, conn net.Conn) *Client { return &Client{ - server: server, - conn: conn, - key: key, + server: server, + conn: conn, + e_key: []byte(protocol.DEFAULT_KEY), + whichKey: USE_E, } } @@ -41,7 +50,12 @@ func (client *Client) Send(data interface{}, typeID uint32) { tmp = append(tmp, pkt.Buf...) // encrypt typeID & body - protocol.EncryptData(tmp, client.key) + switch client.whichKey { + case USE_E: + protocol.EncryptData(tmp, client.e_key) + case USE_FE: + protocol.EncryptData(tmp, client.fe_key) + } // write packet body if _, err := client.conn.Write(tmp); err != nil { @@ -50,6 +64,34 @@ func (client *Client) Send(data interface{}, typeID uint32) { log.Printf("sent!") } +func (client *Client) Login(pkt *protocol.Packet) { + var loginPkt protocol.SP_CL2LS_REQ_LOGIN + pkt.Decode(&loginPkt) + log.Printf("Got packet: %#v", loginPkt) + + // !! TODO + resp := &protocol.SP_LS2CL_REP_LOGIN_SUCC{ + SZID: loginPkt.SZID, + ICharCount: 0, + ISlotNum: 1, + IPaymentFlag: 1, + IOpenBetaFlag: 0, + UISvrTime: uint64(time.Now().Unix()), + } + + client.Send(resp, protocol.P_LS2CL_REP_LOGIN_SUCC) + client.e_key = protocol.CreateNewKey( + resp.UISvrTime, + uint64(resp.ICharCount+1), + uint64(resp.ISlotNum+1), + ) + client.fe_key = protocol.CreateNewKey( + binary.LittleEndian.Uint64([]byte(protocol.DEFAULT_KEY)), + uint64(loginPkt.IClientVerC), + 1, + ) +} + func (client *Client) ClientHandler() { defer func() { if err := recover(); err != nil { @@ -78,22 +120,14 @@ func (client *Client) ClientHandler() { } // decrypt && grab typeID - protocol.DecryptData(tmp[:sz], client.key) + protocol.DecryptData(tmp[:sz], client.e_key) typeID := int(binary.LittleEndian.Uint32(tmp[:4])) log.Printf("Got packet ID: %x, with a sizeof: %d\n", typeID, sz) pkt := protocol.NewPacket(tmp[4:sz]) switch typeID { case protocol.P_CL2LS_REQ_LOGIN: - var loginPkt protocol.SP_CL2LS_REQ_LOGIN - pkt.Decode(&loginPkt) - log.Printf("Got packet: %#v", loginPkt) - - client.Send(&protocol.SP_LS2CL_REP_LOGIN_FAIL{ - IErrorCode: protocol.LOGIN_FAIL_EULA_ERROR, - SZID: loginPkt.SZID, - }, protocol.P_LS2CL_REP_LOGIN_FAIL) - + client.Login(pkt) default: log.Printf("[WARN] unsupported packet ID: %x\n", typeID) } diff --git a/server/server.go b/server/server.go index 9e7cfd0..a5c380d 100644 --- a/server/server.go +++ b/server/server.go @@ -4,8 +4,6 @@ import ( "fmt" "log" "net" - - "github.com/CPunch/GopenFusion/protocol" ) type Server struct { @@ -42,7 +40,7 @@ func (server *Server) Start() { return } - client := newClient(server, conn, []byte(protocol.E_KEY)) + client := newClient(server, conn) server.clients[client] = true go client.ClientHandler() fmt.Printf("Client %p connected\n", client) diff --git a/tools/genstructs.py b/tools/genstructs.py index 0d782c5..6d611b1 100755 --- a/tools/genstructs.py +++ b/tools/genstructs.py @@ -5,7 +5,7 @@ Takes raw structures from a decompiled 'Assembly - CSharp.dll' from a main.unity3d fusionfall beta client, and transpiles them to gopenfusion's custom packet structure & tags. This requires a compiler installed, since struct field padding is grabbed via the `offsetof()` C macro. Some manual rearranging of structures - from the disassembled source might be needed. This script can also be used to generate c-style structures + from the disassembled source might be needed. This script can also be modified to generate c-style structures (because it already does!) usage: ./genstructs.py [IN.cs] > structs.go @@ -173,10 +173,8 @@ class StructTranspiler: def toCStyle(self) -> str: source = "typedef struct {\n" - for field in self.fields: source += "\t%s %s;\n" % (field.ctype, field.cname) - source += "} %s;\n" % self.name for field in self.fields: @@ -229,7 +227,6 @@ class StructTranspiler: source += "\n}\n" return source - if __name__ == '__main__': inFilePath = sys.argv[1] @@ -245,6 +242,7 @@ if __name__ == '__main__': except: break + # check for undefined types in structures and patch the sizes for i in range(len(structs)): struct = structs[i] f = struct.needsPatching() @@ -252,10 +250,10 @@ if __name__ == '__main__': name = struct.fields[f].type for s in structs: if s.name == name: - if struct.fields[f].size > 1: + if struct.fields[f].size > 1: # was it an array? structs[i].fields[f].type = ("[%d]" % struct.fields[f].size) + struct.fields[f].type - structs[i].fields[f].size *= s.size - structs[i].fields[f].needsPatching = False + structs[i].fields[f].size *= s.size # field's size was set to 1 even if it wasn't an array + structs[i].fields[f].needsPatching = False # mark done f = struct.needsPatching() @@ -272,10 +270,12 @@ if __name__ == '__main__': compiler.compile(['tmp.c']) compiler.link_executable(['tmp.o'], 'tmp') + # patch padding bytes lines = subprocess.getoutput(['./tmp']).splitlines() for i in range(len(structs)): structs[i].populatePadding(lines[i].split(" ")) - + + # emit structures for struct in structs: print(struct.toGoStyle())