Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ThalissonTMora/shaiya-chat-native-re/llms.txt

Use this file to discover all available pages before exploring further.

Live wire capture is the final validation step after static RE — it confirms that byte offsets, field sizes, and padding behavior match what was decompiled from the binary. Use static analysis findings first to know exactly what you are looking for on the wire, then use this guide to set up the capture and verify each claim.
No live capture is performed automatically by the corpus tools. Use this checklist on your own client/server pair running Game.exe MD5 c1edd966… and ps_game.exe MD5 91b212af…. Game port is typically 30800/tcp — confirm in your server.ini / client connect.cfg.

Prerequisites

ToolRole
Wireshark / tcpdumpTCP payload capture after game handshake (post-0xA101 if encryption enabled)
x64dbg on ps_game.exe or Game.exeHook plaintext at SConnection_Send / recv dispatch
Optional Frida / custom DLLStable logging of [opcode, len, hex] at 0x004ED0E0
Preferred plaintext tap for chat: hook after Connection_DecryptInbound @ 0x00464FA0 (server) or client PacketPayload_Decrypt @ 0x00401080, before Chat_ProcessIncoming @ 0x0047F400 / client PacketDispatcher @ 0x005F1E10.
Encrypted sessions produce ciphertext on the wire. Unless you export session keys (not stock) or use in-process hooks, Wireshark alone will not show plaintext chat bytes for an encrypted session. Use x64dbg hooks at the plaintext boundary instead.

Wireshark / tcpdump BPF Filter

Replace SERVER_IP and PORT with your server’s IP and game port:
host SERVER_IP and tcp port PORT
After opcode mapping is available, Wireshark display filter examples:
tcp.payload[0:2] == 11:04    # guild 0x1104 LE — adjust if framing includes length prefix
If a length-prefixed outer frame exists (check SConnection_EnqueueWrite @ 0x004EF080), strip that header in a Lua dissector before matching 0x11xx.

Capture Triggers by Opcode

OpcodeChannelTypical trigger
0x1101NormalAny chat message in the default channel
0x1102Whisper/w PlayerName message — expect two S→C frames (dir=0 to target, dir=1 echo)
0x1103TradeChat in trade channel
0x1104GuildChat in guild channel
0x1108MegaphoneMegaphone item use
0x1111AreaArea/zone channel message
0x1109 / 0x110ANPC/script zone pushTrigger zone script or NPC dialog — server-side only (S→C)
0x110BChannel labelEntity label push — S→C only
Stock client kicks the connection if it receives 0x1109 / 0x110A / 0x110B from the client side (Chat_ProcessIncoming default → SConnection_Close(9,0) @ 0x0047FC24). These must be S→C only captures.

char[21] Tail Capture — D1 Artifact

Pattern B packets (0x1103, 0x1104, 0x1108, 0x1111) carry a char[21] sender name field. Static analysis of Chat_BroadcastGuild @ 0x00432530 confirms no memset before SConnection_Send — the tail bytes after the NUL terminator are uninit stack residue.
1

Set breakpoint at SConnection_Send

In x64dbg (32-bit), attach to ps_game.exe and set:
bp 004ED0E0
On hit (stdcall): [ESP+4] = SConnection*, [ESP+8] = payload pointer, [ESP+0xC] = size.
2

Filter for guild packets

Add a conditional log command:
?po payload
?po size
?by payload l min(size,40)
Filter hits where *(u16*)payload == 0x1104 and size == 0x18 + *(payload+23).
3

Hexdump the name[21] field

Log the 21 bytes starting at payload+2:
?by [buf+2] l 21
For a guild packet from a player named “JACKSON” (6 chars): bytes 0x000x05 = 4A41434B534F4E, byte 0x06 = 0x00 (NUL), bytes 0x070x14 = tail.
4

Classify tail bytes

Inspect bytes at indices strlen(name)+1 through 20:
OutcomeTag
All 0x00Emulator zero-fill — not stock
Non-zero stable garbageCONFIRMED stack junk — zero-fill in emulator
Varies per sendLog multiple sends to characterize
5

Validate with offline tool

Save the capture log and run:
python3 tools/wire/validate_d1_padding.py test/captures/live_capture.log

Pattern B Field Offsets

OpcodeDirsize formulaname[21] regionLen byteText start
0x1104 guildS→Clen+0x18payload+2payload+22payload+23payload+24
0x1103 tradeS→Clen+0x18payload+2payload+22payload+23payload+24
0x1108 megaphoneS→Clen+0x18payload+2payload+22payload+23payload+24
0x1111 areaS→Clen+0x18payload+2payload+22payload+23payload+24
0x1102 whisperS→Clen+0x19payload+3payload+23payload+24payload+25
0x1102 whisperC→Slen+0x18payload+2payload+22 (target[21])payload+23payload+24
0x0812 allianceS→Clen+0x1Cpayload+2payload+22payload+23payload+24

NPC Script Push Capture — D4 Artifact

Captures of 0x1109 / 0x110A / 0x110B require triggering the right in-game event.
1

Identify the trigger

OpcodeTypical triggerBuilder VA
0x1109NPC/script zone chat0x004C6A80 (flag 0) / 0x004C6F50 (flag 1)
0x110AUnion message ID push0x004C8310
0x110BChannel label on entity0x004C8520
2

Break at SConnection_Send and filter

bp 004ED0E0
On hit, read: opcode = *(u16*)payload, size, and the first min(size, 64) bytes.
3

Verify 1109 payload structure

Expected Pattern D (0x1109) layout:
+0x00  u16  opcode = 0x1109
+0x02  u8   flag   (0 = party/zone path; 1 = spatial)
+0x03  u32  charId
+0x07  u8   len    (1..0x7F)
+0x08  u8   text[len]
Total size = len + 8.
4

Verify 110A payload structure

Expected 0x110A layout:
+0x00  u16  opcode = 0x110A
+0x02  u32  charId
+0x06  u16  message_id  (biased by or eax, 0xC00 in wrapper)
Total size = 8.
5

Correlate with Wireshark

Note the server port and player session time window. Filter server→client payloads during NPC dialog. Match opcode + size signature and save a .pcapng slice under captures/ (gitignored) with the build MD5 in the filename.

Finding Opcodes in Captures with offline tools

extract_plaintext_opcodes.py

Use this tool on any decrypted binary blob or packet dump:
# Scan a binary capture for all known chat opcodes
python3 tools/wire/extract_plaintext_opcodes.py --file capture.bin

# Scan for specific opcodes
python3 tools/wire/extract_plaintext_opcodes.py \
  --file capture.bin --opcodes 1104,1109,110A

# Show 48 bytes of context after each hit
python3 tools/wire/extract_plaintext_opcodes.py \
  --file capture.bin --context 48

scan_capture_logs.py

Scan existing MITM/session log files for opcode=0xXXXX markers and raw hex patterns:
python3 tools/wire/scan_capture_logs.py /mnt/c/ShaiyaServer/ps_session_redis/captures
python3 tools/wire/scan_capture_logs.py test/captures/ui_session_20260526_static.log

0xA101 Counter Capture — D3 Artifact

Capturing the 0xA101 login packet for counter derivation requires an additional step to obtain the PRNG seed.
1

Capture the login packet

Run tcpdump or Wireshark on the login port (typically ~30900) at the moment the client connects:
tcpdump -i eth0 -w login_capture.pcap tcp port 30900
2

Extract the 197-byte plaintext

The 0xA101 packet is plaintext on the login socket. Export the 197-byte body as hex.
3

Capture the PRNG seed from memory

In x64dbg (32-bit) attached to Game.exe, set two breakpoints:
bp 00404610   ; Crypto_PRNGFill — fires when PRNG buffer is filled
bp 005E3D60   ; Handler_Packet_A101_KeyMaterial — fires on A101 recv
On hit at 0x00404610, dump the 128-byte PRNG buffer from the buffer pointer passed in EAX.
4

Validate offline

python3 tools/crypto/validate_a101_counter.py \
  --hex <197-byte-plaintext-hex> \
  --prng-hex <128-byte-prng-hex>

Whisper C→S vs S→C (0x1102)

The whisper opcode has different layouts in each direction:
DirectionLayout (after opcode)sizeNotes
C→Starget[21] @ +0x02 · len @ +0x17 · text @ +0x18len+0x18PacketSend_Whisper @ 0x005ED160
S→Cdir @ +0x02 · name[21] @ +0x03 · len @ +0x18 · text @ +0x19len+0x19Dual send @ 0x0047F685, 0x0047F6F9
Server clears payload+0x16 (last byte of C→S target[21]) @ 0x0047F608 before World_FindUserByName. One whisper A→B produces two S→C 0x1102 frames: dir=0 to target, dir=1 echo back to sender.

Limitations

  • Encryption: NAT/port forwarding changes endpoint IPs — BPF must follow the actual TCP stream.
  • Script-only opcodes: 0x1109/0x110A/0x110B require triggering the right NPC or zone script. Empty captures usually mean wrong trigger, not wrong VA.
  • Session vs game port: existing ps_session_redis/captures/ logs cover the session port only — they contain 0xA101 but not game-channel 0x11xx opcodes.

Build docs developers (and LLMs) love