mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-16 17:05:03 +01:00
Progress with binary8 encoding
This commit is contained in:
58
entrance_server.go
Normal file
58
entrance_server.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/Andoryuuta/Erupe/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleEntranceServerConnection(conn net.Conn) {
|
||||||
|
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
||||||
|
nullInit := make([]byte, 8)
|
||||||
|
n, err := io.ReadFull(conn, nullInit)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
} else if n != len(nullInit) {
|
||||||
|
fmt.Println("io.ReadFull couldn't read the full 8 byte init.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cc := network.NewCryptConn(conn)
|
||||||
|
for {
|
||||||
|
pkt, err := cc.ReadPacket()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Got entrance server command:\n%s\n", hex.Dump(pkt))
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile("tw_server_list_resp.bin")
|
||||||
|
if err != nil {
|
||||||
|
print(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cc.SendPacket(data)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doEntranceServer(listenAddr string) {
|
||||||
|
l, err := net.Listen("tcp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
go handleEntranceServerConnection(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
6
main.go
6
main.go
@@ -2,16 +2,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"time"
|
||||||
_ "time"
|
|
||||||
|
|
||||||
"github.com/Andoryuuta/Erupe/network"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Starting!")
|
fmt.Println("Starting!")
|
||||||
|
|
||||||
go serveLauncherHTML(":80")
|
go serveLauncherHTML(":80")
|
||||||
|
go doEntranceServer(":53310")
|
||||||
go doSignServer(":53312")
|
go doSignServer(":53312")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -69,10 +69,40 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = combinedCheck
|
_ = combinedCheck
|
||||||
/*
|
|
||||||
fmt.Printf("cc %X, c0 %X, c1 %X, c2 %X\n", combinedCheck, check0, check1, check2)
|
|
||||||
fmt.Printf("cc %X, c0 %X, c1 %X, c2 %X\n", cph.PrevPacketCombinedCheck, cph.Check0, cph.Check1, cph.Check2)
|
|
||||||
fmt.Printf("cph: %+v\n", cph)
|
|
||||||
*/
|
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendPacket encrypts and sends a packet.
|
||||||
|
func (cc *CryptConn) SendPacket(data []byte) error {
|
||||||
|
keyRotDelta := byte(3)
|
||||||
|
|
||||||
|
if keyRotDelta != 0 {
|
||||||
|
cc.sendKeyRot = (uint32(keyRotDelta) * (cc.sendKeyRot + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt the data
|
||||||
|
encData, combinedCheck, check0, check1, check2 := crypto.Encrypt(data, cc.sendKeyRot, nil)
|
||||||
|
|
||||||
|
header := &CryptPacketHeader{}
|
||||||
|
header.Pf0 = byte(((uint(len(encData)) >> 12) & 0xF3) | 3)
|
||||||
|
header.KeyRotDelta = keyRotDelta
|
||||||
|
header.PacketNum = uint16(cc.sentPackets)
|
||||||
|
header.DataSize = uint16(len(encData))
|
||||||
|
header.PrevPacketCombinedCheck = cc.prevSendPacketCombinedCheck
|
||||||
|
header.Check0 = check0
|
||||||
|
header.Check1 = check1
|
||||||
|
header.Check2 = check2
|
||||||
|
|
||||||
|
headerBytes, err := header.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cc.conn.Write(headerBytes)
|
||||||
|
cc.conn.Write(encData)
|
||||||
|
|
||||||
|
cc.sentPackets++
|
||||||
|
cc.prevSendPacketCombinedCheck = combinedCheck
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,3 +65,26 @@ func NewCryptPacketHeader(data []byte) (*CryptPacketHeader, error) {
|
|||||||
|
|
||||||
return &c, nil
|
return &c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encode encodes the CryptPacketHeader into raw bytes.
|
||||||
|
func (c *CryptPacketHeader) Encode() ([]byte, error) {
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
var data = []interface{}{
|
||||||
|
c.Pf0,
|
||||||
|
c.KeyRotDelta,
|
||||||
|
c.PacketNum,
|
||||||
|
c.DataSize,
|
||||||
|
c.PrevPacketCombinedCheck,
|
||||||
|
c.Check0,
|
||||||
|
c.Check1,
|
||||||
|
c.Check2,
|
||||||
|
}
|
||||||
|
for _, v := range data {
|
||||||
|
err := binary.Write(buf, binary.BigEndian, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,77 @@ import (
|
|||||||
"github.com/Andoryuuta/byteframe"
|
"github.com/Andoryuuta/byteframe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func paddedString(x string, size uint) []byte {
|
||||||
|
out := make([]byte, size)
|
||||||
|
copy(out, x)
|
||||||
|
|
||||||
|
// Null terminate it.
|
||||||
|
out[len(out)-1] = 0
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint8PascalString(bf *byteframe.ByteFrame, x string) {
|
||||||
|
bf.WriteUint8(uint8(len(x) + 1))
|
||||||
|
bf.WriteNullTerminatedBytes([]byte(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint16PascalString(bf *byteframe.ByteFrame, x string) {
|
||||||
|
bf.WriteUint16(uint16(len(x) + 1))
|
||||||
|
bf.WriteNullTerminatedBytes([]byte(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSignInResp(username string) []byte {
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
|
bf.WriteUint8(1) // resp_code
|
||||||
|
bf.WriteUint8(0) // file/patch server count
|
||||||
|
bf.WriteUint8(1) // entrance server count
|
||||||
|
bf.WriteUint8(1) // character count
|
||||||
|
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
||||||
|
bf.WriteBytes(paddedString("logintokenstrng", 16)) // login_token (16 byte padded string)
|
||||||
|
bf.WriteUint32(1576761190)
|
||||||
|
|
||||||
|
// file patch server PascalStrings here
|
||||||
|
|
||||||
|
// Array(this.entrance_server_count, PascalString(Byte, "utf8")),
|
||||||
|
uint8PascalString(bf, "localhost:53310")
|
||||||
|
|
||||||
|
// Characters:
|
||||||
|
bf.WriteUint32(1039336769) // character ID
|
||||||
|
bf.WriteUint16(30)
|
||||||
|
bf.WriteUint16(7)
|
||||||
|
bf.WriteUint32(1576761172)
|
||||||
|
bf.WriteUint8(0)
|
||||||
|
bf.WriteUint8(0)
|
||||||
|
bf.WriteUint8(0)
|
||||||
|
bf.WriteUint8(0)
|
||||||
|
bf.WriteBytes(paddedString("username", 16))
|
||||||
|
bf.WriteBytes(paddedString("", 32))
|
||||||
|
|
||||||
|
bf.WriteUint8(0) // friends_list_count
|
||||||
|
bf.WriteUint8(0) // guild_members_count
|
||||||
|
bf.WriteUint8(0) // notice_count
|
||||||
|
bf.WriteUint32(0xDEADBEEF) // some_last_played_character_id
|
||||||
|
bf.WriteUint32(14) // unk_flags
|
||||||
|
uint8PascalString(bf, "") // unk_data_blob PascalString
|
||||||
|
|
||||||
|
bf.WriteUint16(51728)
|
||||||
|
bf.WriteUint16(20000)
|
||||||
|
uint16PascalString(bf, "1000672925")
|
||||||
|
|
||||||
|
bf.WriteUint8(0)
|
||||||
|
|
||||||
|
bf.WriteUint16(51729)
|
||||||
|
bf.WriteUint16(1)
|
||||||
|
bf.WriteUint16(20000)
|
||||||
|
uint16PascalString(bf, "203.191.249.36:8080")
|
||||||
|
|
||||||
|
bf.WriteUint32(1578905116)
|
||||||
|
bf.WriteUint32(0)
|
||||||
|
|
||||||
|
return bf.Data()
|
||||||
|
}
|
||||||
|
|
||||||
func handleSignServerConnection(conn net.Conn) {
|
func handleSignServerConnection(conn net.Conn) {
|
||||||
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
||||||
nullInit := make([]byte, 8)
|
nullInit := make([]byte, 8)
|
||||||
@@ -25,7 +96,7 @@ func handleSignServerConnection(conn net.Conn) {
|
|||||||
for {
|
for {
|
||||||
pkt, err := cc.ReadPacket()
|
pkt, err := cc.ReadPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bf := byteframe.NewByteFrameFromBytes(pkt)
|
bf := byteframe.NewByteFrameFromBytes(pkt)
|
||||||
@@ -33,9 +104,11 @@ func handleSignServerConnection(conn net.Conn) {
|
|||||||
username := string(bf.ReadNullTerminatedBytes())
|
username := string(bf.ReadNullTerminatedBytes())
|
||||||
password := string(bf.ReadNullTerminatedBytes())
|
password := string(bf.ReadNullTerminatedBytes())
|
||||||
unk := string(bf.ReadNullTerminatedBytes())
|
unk := string(bf.ReadNullTerminatedBytes())
|
||||||
fmt.Println("Got signin, type: %s, username: %s, password %s, unk: %s", loginType, username, password, unk)
|
fmt.Printf("Got signin, type: %s, username: %s, password %s, unk: %s", loginType, username, password, unk)
|
||||||
|
|
||||||
|
resp := makeSignInResp(username)
|
||||||
|
cc.SendPacket(resp)
|
||||||
|
|
||||||
//fmt.Printf("Got:\n%s", hex.Dump(pkt))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
88
test.py
88
test.py
@@ -68,6 +68,9 @@ class PacketCrypto(object):
|
|||||||
t_idx = data[i] ^ SHARED_CRYPT_KEY[shared_buf_idx]
|
t_idx = data[i] ^ SHARED_CRYPT_KEY[shared_buf_idx]
|
||||||
dec_key_byte = DECRYPT_KEY[t_idx]
|
dec_key_byte = DECRYPT_KEY[t_idx]
|
||||||
shared_buf_idx = ((unk_derived_cryptkey_rot >> 10) ^ dec_key_byte) & 0xFF
|
shared_buf_idx = ((unk_derived_cryptkey_rot >> 10) ^ dec_key_byte) & 0xFF
|
||||||
|
print('t_idx: {:X}'.format(t_idx))
|
||||||
|
print('i & 7: {:X}'.format(i & 7))
|
||||||
|
|
||||||
|
|
||||||
# Update the checksum accumulators.
|
# Update the checksum accumulators.
|
||||||
accumulator_0 = (accumulator_0 + ((t_idx << (i & 7)) & 0xFFFFFFFF))
|
accumulator_0 = (accumulator_0 + ((t_idx << (i & 7)) & 0xFFFFFFFF))
|
||||||
@@ -90,8 +93,91 @@ class PacketCrypto(object):
|
|||||||
return (output_data, combined_check, check_0, check_1, check_2)
|
return (output_data, combined_check, check_0, check_1, check_2)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
with open(sys.argv[1], 'rb') as f:
|
with open(sys.argv[1], 'rb') as f:
|
||||||
(output_data, combined_check, check_0, check_1, check_2) = PacketCrypto._general_crypt(f.read(), int(sys.argv[2], 16), 1)
|
(output_data, combined_check, check_0, check_1, check_2) = PacketCrypto._general_crypt(f.read(), int(sys.argv[2], 16), 1)
|
||||||
hexdump(output_data)
|
hexdump(output_data)
|
||||||
print("cc {:x}, c0 {:x}, c1 {:x}, c2 {:x}".format(combined_check, check_0, check_1, check_2))
|
print("cc {:x}, c0 {:x}, c1 {:x}, c2 {:x}".format(combined_check, check_0, check_1, check_2))
|
||||||
|
"""
|
||||||
|
|
||||||
|
from construct import *
|
||||||
|
|
||||||
|
Binary8Header = Struct(
|
||||||
|
"server_type" / Bytes(3),
|
||||||
|
"entry_count" / Int16ub,
|
||||||
|
"body_size" / Int16ub,
|
||||||
|
"checksum" / Int32ub,
|
||||||
|
)
|
||||||
|
|
||||||
|
BINARY8_KEY = bytes([0x01, 0x23, 0x34, 0x45, 0x56, 0xAB, 0xCD, 0xEF])
|
||||||
|
def decode_binary8(data, unk_key_byte):
|
||||||
|
cur_key = ((54323 * unk_key_byte) + 1) & 0xFFFFFFFF
|
||||||
|
|
||||||
|
output_data = bytearray()
|
||||||
|
for i in range(len(data)):
|
||||||
|
tmp = (data[i] ^ (cur_key >> 13)) & 0xFF
|
||||||
|
output_data.append(tmp ^ BINARY8_KEY[i&7])
|
||||||
|
cur_key = ((54323 * cur_key) + 1) & 0xFFFFFFFF
|
||||||
|
|
||||||
|
return output_data
|
||||||
|
|
||||||
|
def encode_binary8(data, unk_key_byte):
|
||||||
|
cur_key = ((54323 * unk_key_byte) + 1) & 0xFFFFFFFF
|
||||||
|
|
||||||
|
output_data = bytearray()
|
||||||
|
for i in range(len(data)):
|
||||||
|
output_data.append(data[i] ^ (BINARY8_KEY[i&7] ^ ((cur_key >> 13) & 0xFF)))
|
||||||
|
cur_key = ((54323 * cur_key) + 1) & 0xFFFFFFFF
|
||||||
|
|
||||||
|
return output_data
|
||||||
|
|
||||||
|
|
||||||
|
SUM32_TABLE_0 = bytes([0x35, 0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12])
|
||||||
|
SUM32_TABLE_1 = bytes([0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12, 0xDE, 0xDE, 0x35])
|
||||||
|
# BROKEN!!!:
|
||||||
|
def calc_sum32(data):
|
||||||
|
t0_i = len(data)
|
||||||
|
t1_i = data[(len(data) >> 1)+1]
|
||||||
|
out = bytearray(4)
|
||||||
|
for i in range(len(data)):
|
||||||
|
tmp = (SUM32_TABLE_1[t1_i % 9] ^ SUM32_TABLE_0[t0_i % 7]) ^ data[i]
|
||||||
|
out[i%4] = (out[i%4] + tmp) & 0xFF
|
||||||
|
|
||||||
|
t0_i += 1
|
||||||
|
t1_i += 1
|
||||||
|
|
||||||
|
|
||||||
|
return Int32ul.parse(out)
|
||||||
|
|
||||||
|
def read_binary8_part(stream):
|
||||||
|
# Read the header and decrypt the header first to get the size.
|
||||||
|
enc_bytes = bytearray(stream.read(12))
|
||||||
|
dec_header_bytes = decode_binary8(enc_bytes[1:], enc_bytes[0])
|
||||||
|
header = Binary8Header.parse(dec_header_bytes)
|
||||||
|
|
||||||
|
# Then read the body, append to the header, and decrypt the full thing.
|
||||||
|
body_bytes = stream.read(header.body_size)
|
||||||
|
enc_bytes.extend(body_bytes)
|
||||||
|
dec_bytes = decode_binary8(enc_bytes[1:], enc_bytes[0])
|
||||||
|
|
||||||
|
reenc_bytes = encode_binary8(dec_bytes, enc_bytes[0])
|
||||||
|
|
||||||
|
import zlib
|
||||||
|
print("Good: {}".format(zlib.crc32(enc_bytes[1:]) == zlib.crc32(reenc_bytes)))
|
||||||
|
print("calc_sum32: {:X}".format(calc_sum32(dec_bytes[11:])))
|
||||||
|
print("header checksum: {:X}".format(header.checksum))
|
||||||
|
|
||||||
|
# Then return the parsed header and just the raw body data.
|
||||||
|
return (header, dec_bytes[11:])
|
||||||
|
|
||||||
|
"""
|
||||||
|
with open('tw_server_list_resp.bin', 'rb') as f:
|
||||||
|
(header, data) = read_binary8_part(f)
|
||||||
|
from hexdump import hexdump
|
||||||
|
hexdump(data[:16])
|
||||||
|
print(len(data))
|
||||||
|
"""
|
||||||
|
|
||||||
|
with open('dec_bin8_data_dump.bin', 'rb') as f:
|
||||||
|
print("calc_sum32: {:X}".format(calc_sum32(f.read())))
|
||||||
|
print("want: 74EF4928")
|
||||||
Reference in New Issue
Block a user