mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-04-26 10:18:29 -05:00
Send SBCM exploit to client + support dev auth after login
This commit is contained in:
parent
978a7d3956
commit
5710b20b28
|
|
@ -3,10 +3,11 @@ package database
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/jackc/pgx/v4/pgxpool"
|
|
||||||
"github.com/logrusorgru/aurora/v3"
|
|
||||||
"wwfc/common"
|
"wwfc/common"
|
||||||
"wwfc/logging"
|
"wwfc/logging"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
|
"github.com/logrusorgru/aurora/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsbrcd string, profileId uint32, ngDeviceId uint32) (User, bool) {
|
func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsbrcd string, profileId uint32, ngDeviceId uint32) (User, bool) {
|
||||||
|
|
@ -52,7 +53,7 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
|
||||||
user.LastName = *lastName
|
user.LastName = *lastName
|
||||||
}
|
}
|
||||||
|
|
||||||
if expectedNgId != nil {
|
if expectedNgId != nil && user.NgDeviceId != 0 {
|
||||||
user.NgDeviceId = *expectedNgId
|
user.NgDeviceId = *expectedNgId
|
||||||
if ngDeviceId != 0 && user.NgDeviceId != ngDeviceId {
|
if ngDeviceId != 0 && user.NgDeviceId != ngDeviceId {
|
||||||
logging.Error("DATABASE", "NG device ID mismatch for profile", aurora.Cyan(user.ProfileId), "- expected", aurora.Cyan(fmt.Sprintf("%08x", user.NgDeviceId)), "but got", aurora.Cyan(fmt.Sprintf("%08x", ngDeviceId)))
|
logging.Error("DATABASE", "NG device ID mismatch for profile", aurora.Cyan(user.ProfileId), "- expected", aurora.Cyan(fmt.Sprintf("%08x", user.NgDeviceId)), "but got", aurora.Cyan(fmt.Sprintf("%08x", ngDeviceId)))
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ package database
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/jackc/pgx/v4/pgxpool"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -85,6 +86,15 @@ func (user *User) UpdateProfileID(pool *pgxpool.Pool, ctx context.Context, newPr
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) UpdateDeviceID(pool *pgxpool.Pool, ctx context.Context, newDeviceId uint32) error {
|
||||||
|
_, err := pool.Exec(ctx, UpdateUserNGDeviceID, user.ProfileId, newDeviceId)
|
||||||
|
if err == nil {
|
||||||
|
user.NgDeviceId = newDeviceId
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func GetUniqueUserID() uint64 {
|
func GetUniqueUserID() uint64 {
|
||||||
// Not guaranteed unique but doesn't matter in practice if multiple people have the same user ID.
|
// Not guaranteed unique but doesn't matter in practice if multiple people have the same user ID.
|
||||||
return uint64(rand.Int63n(0x80000000000))
|
return uint64(rand.Int63n(0x80000000000))
|
||||||
|
|
|
||||||
|
|
@ -169,9 +169,7 @@ func (g *GameSpySession) authAddFriend(command common.GameSpyCommand) {
|
||||||
func (g *GameSpySession) setStatus(command common.GameSpyCommand) {
|
func (g *GameSpySession) setStatus(command common.GameSpyCommand) {
|
||||||
status := command.CommandValue
|
status := command.CommandValue
|
||||||
|
|
||||||
if g.QR2IP != 0 {
|
qr2.ProcessGPStatusUpdate(g.User.ProfileId, g.QR2IP, status)
|
||||||
qr2.ProcessGPStatusUpdate(g.QR2IP, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
statstring, ok := command.OtherValues["statstring"]
|
statstring, ok := command.OtherValues["statstring"]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
|
|
@ -213,6 +213,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||||
sessions[g.User.ProfileId] = g
|
sessions[g.User.ProfileId] = g
|
||||||
mutex.Unlock()
|
mutex.Unlock()
|
||||||
|
|
||||||
|
g.AuthToken = authToken
|
||||||
g.LoginTicket = common.MarshalGPCMLoginTicket(g.User.ProfileId)
|
g.LoginTicket = common.MarshalGPCMLoginTicket(g.User.ProfileId)
|
||||||
g.SessionKey = rand.Int31n(290000000) + 10000000
|
g.SessionKey = rand.Int31n(290000000) + 10000000
|
||||||
g.GameCode = gamecd
|
g.GameCode = gamecd
|
||||||
|
|
@ -224,7 +225,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||||
g.ModuleName += "/" + common.CalcFriendCodeString(g.User.ProfileId, "RMCJ")
|
g.ModuleName += "/" + common.CalcFriendCodeString(g.User.ProfileId, "RMCJ")
|
||||||
|
|
||||||
// Notify QR2 of the login
|
// Notify QR2 of the login
|
||||||
qr2.Login(g.User.ProfileId, ingamesn, cfc, g.Conn.RemoteAddr().String(), g.NeedsExploit, g.DeviceAuthenticated)
|
qr2.Login(g.User.ProfileId, gamecd, ingamesn, cfc, g.Conn.RemoteAddr().String(), g.NeedsExploit, g.DeviceAuthenticated)
|
||||||
|
|
||||||
payload := common.CreateGameSpyMessage(common.GameSpyCommand{
|
payload := common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||||
Command: "lc",
|
Command: "lc",
|
||||||
|
|
@ -245,6 +246,42 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||||
g.sendFriendRequests()
|
g.sendFriendRequests()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GameSpySession) exLogin(command common.GameSpyCommand) {
|
||||||
|
payloadVer, payloadVerExists := command.OtherValues["payload_ver"]
|
||||||
|
signature, signatureExists := command.OtherValues["wwfc_sig"]
|
||||||
|
deviceId := uint32(0)
|
||||||
|
|
||||||
|
if !payloadVerExists || payloadVer != "1" {
|
||||||
|
g.replyError(GPError{
|
||||||
|
ErrorCode: ErrLogin.ErrorCode,
|
||||||
|
ErrorString: "The payload version is invalid.",
|
||||||
|
Fatal: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !signatureExists {
|
||||||
|
g.replyError(GPError{
|
||||||
|
ErrorCode: ErrLogin.ErrorCode,
|
||||||
|
ErrorString: "Missing authentication signature.",
|
||||||
|
Fatal: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if deviceId = verifySignature(g.AuthToken, signature); deviceId == 0 {
|
||||||
|
g.replyError(GPError{
|
||||||
|
ErrorCode: ErrLogin.ErrorCode,
|
||||||
|
ErrorString: "The authentication signature is invalid.",
|
||||||
|
Fatal: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.DeviceAuthenticated = true
|
||||||
|
qr2.SetDeviceAuthenticated(g.User.ProfileId)
|
||||||
|
}
|
||||||
|
|
||||||
func IsLoggedIn(profileID uint32) bool {
|
func IsLoggedIn(profileID uint32) bool {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ type GameSpySession struct {
|
||||||
LoggedIn bool
|
LoggedIn bool
|
||||||
DeviceAuthenticated bool
|
DeviceAuthenticated bool
|
||||||
Challenge string
|
Challenge string
|
||||||
|
AuthToken string
|
||||||
LoginTicket string
|
LoginTicket string
|
||||||
SessionKey int32
|
SessionKey int32
|
||||||
GameCode string
|
GameCode string
|
||||||
|
|
@ -89,7 +90,7 @@ func (g *GameSpySession) closeSession() {
|
||||||
if g.LoggedIn {
|
if g.LoggedIn {
|
||||||
qr2.Logout(g.User.ProfileId)
|
qr2.Logout(g.User.ProfileId)
|
||||||
if g.QR2IP != 0 {
|
if g.QR2IP != 0 {
|
||||||
qr2.ProcessGPStatusUpdate(g.QR2IP, "0")
|
qr2.ProcessGPStatusUpdate(g.User.ProfileId, g.QR2IP, "0")
|
||||||
}
|
}
|
||||||
g.sendLogoutStatus()
|
g.sendLogoutStatus()
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +106,7 @@ func (g *GameSpySession) closeSession() {
|
||||||
|
|
||||||
// Handles incoming requests.
|
// Handles incoming requests.
|
||||||
func handleRequest(conn net.Conn) {
|
func handleRequest(conn net.Conn) {
|
||||||
session := GameSpySession{
|
session := &GameSpySession{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
User: database.User{},
|
User: database.User{},
|
||||||
ModuleName: "GPCM",
|
ModuleName: "GPCM",
|
||||||
|
|
@ -172,6 +173,7 @@ func handleRequest(conn net.Conn) {
|
||||||
session.Conn.Write([]byte(`\ka\\final\`))
|
session.Conn.Write([]byte(`\ka\\final\`))
|
||||||
})
|
})
|
||||||
commands = session.handleCommand("login", commands, session.login)
|
commands = session.handleCommand("login", commands, session.login)
|
||||||
|
commands = session.handleCommand("wwfc_exlogin", commands, session.exLogin)
|
||||||
commands = session.ignoreCommand("logout", commands)
|
commands = session.ignoreCommand("logout", commands)
|
||||||
|
|
||||||
if len(commands) != 0 && session.LoggedIn == false {
|
if len(commands) != 0 && session.LoggedIn == false {
|
||||||
|
|
|
||||||
48
qr2/group.go
48
qr2/group.go
|
|
@ -2,11 +2,12 @@ package qr2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/logrusorgru/aurora/v3"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"wwfc/common"
|
"wwfc/common"
|
||||||
"wwfc/logging"
|
"wwfc/logging"
|
||||||
|
|
||||||
|
"github.com/logrusorgru/aurora/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Group struct {
|
type Group struct {
|
||||||
|
|
@ -112,11 +113,48 @@ func ProcessGPResvOK(cmd common.MatchCommandDataResvOK, senderIP uint64, senderP
|
||||||
return processResvOK(moduleName, cmd, from, to)
|
return processResvOK(moduleName, cmd, from, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessGPStatusUpdate(senderIP uint64, status string) {
|
func ProcessGPStatusUpdate(profileID uint32, senderIP uint64, status string) {
|
||||||
if status == "0" || status == "1" || status == "3" || status == "4" {
|
moduleName := "QR2/GPStatus:" + strconv.FormatUint(uint64(profileID), 10)
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
login, exists := logins[profileID]
|
||||||
|
if !exists || login == nil {
|
||||||
|
logging.Error(moduleName, "Received status update for non-existent profile ID", aurora.Cyan(profileID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session := login.Session
|
||||||
|
if session == nil {
|
||||||
|
if senderIP == 0 {
|
||||||
|
logging.Info(moduleName, "Received status update for profile ID", aurora.Cyan(profileID), "but no session exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login with this profile ID
|
||||||
|
session, exists = sessions[senderIP]
|
||||||
|
if !exists || session == nil {
|
||||||
|
logging.Info(moduleName, "Received status update for profile ID", aurora.Cyan(profileID), "but no session exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !session.setProfileID(moduleName, strconv.FormatUint(uint64(profileID), 10)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the client message exploit if not received yet
|
||||||
|
if status != "0" && status != "1" && !session.ExploitReceived && session.Login != nil && session.Login.NeedsExploit {
|
||||||
|
sessionCopy := *session
|
||||||
|
|
||||||
|
mutex.Unlock()
|
||||||
|
logging.Notice(moduleName, "Sending SBCM exploit to DNS patcher client")
|
||||||
|
sendClientExploit(moduleName, sessionCopy)
|
||||||
|
mutex.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == "0" || status == "1" || status == "3" || status == "4" {
|
||||||
session := sessions[senderIP]
|
session := sessions[senderIP]
|
||||||
if session == nil || session.GroupPointer == nil {
|
if session == nil || session.GroupPointer == nil {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@ package qr2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"github.com/logrusorgru/aurora/v3"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"wwfc/common"
|
"wwfc/common"
|
||||||
"wwfc/logging"
|
"wwfc/logging"
|
||||||
|
|
||||||
|
"github.com/logrusorgru/aurora/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func heartbeat(moduleName string, conn net.PacketConn, addr net.Addr, buffer []byte) {
|
func heartbeat(moduleName string, conn net.PacketConn, addr net.Addr, buffer []byte) {
|
||||||
|
|
@ -16,13 +17,20 @@ func heartbeat(moduleName string, conn net.PacketConn, addr net.Addr, buffer []b
|
||||||
values := strings.Split(string(buffer[5:]), "\u0000")
|
values := strings.Split(string(buffer[5:]), "\u0000")
|
||||||
|
|
||||||
payload := map[string]string{}
|
payload := map[string]string{}
|
||||||
|
unknowns := []string{}
|
||||||
for i := 0; i < len(values); i += 2 {
|
for i := 0; i < len(values); i += 2 {
|
||||||
if len(values[i]) == 0 || values[i][0] == '+' {
|
if len(values[i]) == 0 || values[i][0] == '+' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
payload[values[i]] = values[i+1]
|
|
||||||
logging.Info(moduleName, aurora.Cyan(values[i]).String()+":", aurora.Cyan(values[i+1]))
|
logging.Info(moduleName, aurora.Cyan(values[i]).String()+":", aurora.Cyan(values[i+1]))
|
||||||
|
|
||||||
|
if values[i] == "unknown" {
|
||||||
|
unknowns = append(unknowns, values[i+1])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
payload[values[i]] = values[i+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
realIP, realPort := common.IPFormatToString(addr.String())
|
realIP, realPort := common.IPFormatToString(addr.String())
|
||||||
|
|
@ -41,7 +49,8 @@ func heartbeat(moduleName string, conn net.PacketConn, addr net.Addr, buffer []b
|
||||||
|
|
||||||
lookupAddr := makeLookupAddr(addr.String())
|
lookupAddr := makeLookupAddr(addr.String())
|
||||||
|
|
||||||
if statechanged, ok := payload["statechanged"]; ok {
|
statechanged, ok := payload["statechanged"]
|
||||||
|
if ok {
|
||||||
if statechanged == "1" {
|
if statechanged == "1" {
|
||||||
// TODO: This would be a good place to run the server->client message exploit
|
// TODO: This would be a good place to run the server->client message exploit
|
||||||
// for DNS patcher games that require code patches. The status code should be
|
// for DNS patcher games that require code patches. The status code should be
|
||||||
|
|
@ -62,9 +71,29 @@ func heartbeat(moduleName string, conn net.PacketConn, addr net.Addr, buffer []b
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if payload["gamename"] == "mariokartwii" && len(unknowns) > 0 {
|
||||||
|
// Try to login using the first unknown as a profile ID
|
||||||
|
// This makes it possible to execute the exploit on the client sooner
|
||||||
|
profileId := unknowns[0]
|
||||||
|
logging.Notice(moduleName, "Attempting to use unknown as profile ID", aurora.Cyan(profileId))
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
session, sessionExists := sessions[lookupAddr]
|
||||||
|
if !sessionExists {
|
||||||
|
logging.Error(moduleName, "Session not found")
|
||||||
|
} else {
|
||||||
|
session.setProfileID(moduleName, profileId)
|
||||||
|
}
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
if !session.Authenticated {
|
if !session.Authenticated {
|
||||||
logging.Notice(moduleName, "Sending challenge")
|
logging.Notice(moduleName, "Sending challenge")
|
||||||
sendChallenge(conn, addr, session, lookupAddr)
|
sendChallenge(conn, addr, session, lookupAddr)
|
||||||
return
|
return
|
||||||
|
} else if !session.ExploitReceived && session.Login != nil && session.Login.NeedsExploit && statechanged == "1" {
|
||||||
|
logging.Notice(moduleName, "Sending SBCM exploit to DNS patcher client")
|
||||||
|
sendClientExploit(moduleName, session)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package qr2
|
||||||
|
|
||||||
type LoginInfo struct {
|
type LoginInfo struct {
|
||||||
ProfileID uint32
|
ProfileID uint32
|
||||||
|
GameCode string
|
||||||
InGameName string
|
InGameName string
|
||||||
ConsoleFriendCode uint64
|
ConsoleFriendCode uint64
|
||||||
GPPublicIP string
|
GPPublicIP string
|
||||||
|
|
@ -12,11 +13,12 @@ type LoginInfo struct {
|
||||||
|
|
||||||
var logins = map[uint32]*LoginInfo{}
|
var logins = map[uint32]*LoginInfo{}
|
||||||
|
|
||||||
func Login(profileID uint32, inGameName string, consoleFriendCode uint64, publicIP string, needsExploit bool, deviceAuthenticated bool) {
|
func Login(profileID uint32, gameCode string, inGameName string, consoleFriendCode uint64, publicIP string, needsExploit bool, deviceAuthenticated bool) {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
|
|
||||||
logins[profileID] = &LoginInfo{
|
logins[profileID] = &LoginInfo{
|
||||||
ProfileID: profileID,
|
ProfileID: profileID,
|
||||||
|
GameCode: gameCode,
|
||||||
InGameName: inGameName,
|
InGameName: inGameName,
|
||||||
ConsoleFriendCode: consoleFriendCode,
|
ConsoleFriendCode: consoleFriendCode,
|
||||||
GPPublicIP: publicIP,
|
GPPublicIP: publicIP,
|
||||||
|
|
|
||||||
17
qr2/main.go
17
qr2/main.go
|
|
@ -2,11 +2,12 @@ package qr2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"github.com/logrusorgru/aurora/v3"
|
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
"wwfc/common"
|
"wwfc/common"
|
||||||
"wwfc/logging"
|
"wwfc/logging"
|
||||||
|
|
||||||
|
"github.com/logrusorgru/aurora/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -21,6 +22,8 @@ const (
|
||||||
KeepAliveRequest = 0x08
|
KeepAliveRequest = 0x08
|
||||||
AvailableRequest = 0x09
|
AvailableRequest = 0x09
|
||||||
ClientRegisteredReply = 0x0A
|
ClientRegisteredReply = 0x0A
|
||||||
|
|
||||||
|
ClientExploitReply = 0x10
|
||||||
)
|
)
|
||||||
|
|
||||||
var masterConn net.PacketConn
|
var masterConn net.PacketConn
|
||||||
|
|
@ -116,6 +119,10 @@ func handleConnection(conn net.PacketConn, addr net.Addr, buffer []byte) {
|
||||||
|
|
||||||
case ClientMessageAckRequest:
|
case ClientMessageAckRequest:
|
||||||
logging.Notice(moduleName, "Command:", aurora.Yellow("CLIENT_MESSAGE_ACK"))
|
logging.Notice(moduleName, "Command:", aurora.Yellow("CLIENT_MESSAGE_ACK"))
|
||||||
|
|
||||||
|
// In case ClientExploitReply is lost, this can be checked as well
|
||||||
|
// This would be sent either after the payload is downloaded, or the client is already patched
|
||||||
|
session.ExploitReceived = true
|
||||||
return
|
return
|
||||||
|
|
||||||
case KeepAliveRequest:
|
case KeepAliveRequest:
|
||||||
|
|
@ -131,7 +138,13 @@ func handleConnection(conn net.PacketConn, addr net.Addr, buffer []byte) {
|
||||||
return
|
return
|
||||||
|
|
||||||
case ClientRegisteredReply:
|
case ClientRegisteredReply:
|
||||||
logging.Notice(moduleName, "Command:", aurora.Cyan("CLIENT_REGISTERED"))
|
logging.Notice(moduleName, "Command:", aurora.Yellow("CLIENT_REGISTERED"))
|
||||||
|
break
|
||||||
|
|
||||||
|
case ClientExploitReply:
|
||||||
|
logging.Notice(moduleName, "Command:", aurora.Yellow("CLIENT_EXPLOIT_ACK"))
|
||||||
|
|
||||||
|
session.ExploitReceived = true
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
"wwfc/common"
|
"wwfc/common"
|
||||||
"wwfc/logging"
|
"wwfc/logging"
|
||||||
|
|
||||||
|
|
@ -271,3 +273,55 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
||||||
logging.Error(moduleName, "Error sending message:", err.Error())
|
logging.Error(moduleName, "Error sending message:", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendClientExploit(moduleName string, sessionCopy Session) {
|
||||||
|
if len(sessionCopy.Login.GameCode) != 4 || !common.IsUppercaseAlphanumeric(sessionCopy.Login.GameCode) {
|
||||||
|
logging.Error(moduleName, "Invalid game code:", aurora.Cyan(sessionCopy.Login.GameCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exploit, err := os.ReadFile("payload/sbcm/" + "payload." + sessionCopy.Login.GameCode + ".bin")
|
||||||
|
if err != nil {
|
||||||
|
logging.Error(moduleName, "Error reading exploit file", aurora.Cyan(sessionCopy.Login.GameCode), "-", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
session, sessionExists := sessions[makeLookupAddr(sessionCopy.Addr.String())]
|
||||||
|
if !sessionExists {
|
||||||
|
logging.Error(moduleName, "Session not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
packetCount := session.PacketCount + 1
|
||||||
|
session.PacketCount = packetCount
|
||||||
|
mutex.Unlock()
|
||||||
|
|
||||||
|
// Now send the exploit
|
||||||
|
payload := createResponseHeader(ClientMessageRequest, sessionCopy.SessionID)
|
||||||
|
payload = append(payload, []byte{0, 0, 0, 0}...)
|
||||||
|
binary.BigEndian.PutUint32(payload[len(payload)-4:], packetCount)
|
||||||
|
payload = append(payload, exploit[0xB:]...)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
_, err = masterConn.WriteTo(payload, sessionCopy.Addr)
|
||||||
|
if err != nil {
|
||||||
|
logging.Error(moduleName, "Error sending message:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resend the message if no ack after 2 seconds
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
session, sessionExists := sessions[makeLookupAddr(sessionCopy.Addr.String())]
|
||||||
|
if !sessionExists || session.ExploitReceived || session.Login == nil || !session.Login.NeedsExploit {
|
||||||
|
mutex.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.Unlock()
|
||||||
|
logging.Notice(moduleName, "Resending SBCM exploit to DNS patcher client")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,18 +20,19 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
SessionID uint32
|
SessionID uint32
|
||||||
SearchID uint64
|
SearchID uint64
|
||||||
Addr net.Addr
|
Addr net.Addr
|
||||||
Challenge string
|
Challenge string
|
||||||
Authenticated bool
|
Authenticated bool
|
||||||
Login *LoginInfo
|
Login *LoginInfo
|
||||||
LastKeepAlive int64
|
ExploitReceived bool
|
||||||
Endianness byte // Some fields depend on the client's endianness
|
LastKeepAlive int64
|
||||||
Data map[string]string
|
Endianness byte // Some fields depend on the client's endianness
|
||||||
PacketCount uint32
|
Data map[string]string
|
||||||
ReservationID uint64
|
PacketCount uint32
|
||||||
GroupPointer *Group
|
ReservationID uint64
|
||||||
|
GroupPointer *Group
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user