Use this file to discover all available pages before exploring further.
The 0xA101 key blob originates exclusively from ps_login.exe — it is absent from ps_game.exe and every other server binary in the Shaiya Core V9 tree. A static key table of 16 pre-generated BigInt-derived slots is built at process startup; each outbound 0xA101 picks a random slot and serializes its 64-byte and 128-byte limb arrays directly into the wire frame. This page documents the full outbound send chain, the key-table slot structure, and the BigInt RSA-like generation pipeline, as confirmed by E8 cross-reference scans and Ghidra decompilation of ps_login.exe (MD5 de5b348cca36e0585f06be93f013fa6d, ImageBase 0x00400000).
The 0xA101 send path begins at process startup when the key table is populated, and is triggered per-connection when the login state machine fires at 0x00401CDF.
The key table lives at DAT_00454C74 with a fixed stride of 0x84 (132 bytes) per slot. There are exactly 16 slots ((0x4554B4 − 0x454C74) / 0x84 = 16). Each slot is a dense array of approximately 11 GMP-style bigint objects (12-byte header each: capacity, limb_count, limbs*).
block_a and block_b on the wire are the raw limb arrays of the first and third BigInt objects in the slot — not separately malloc-ed buffers. SendKeyBlob_A101 copies limb_count × 4 bytes little-endian directly from the slot’s in-place limb storage into the stack send frame.
KeyTable_SlotInit @ 0x00409AE0 performs what is inferred to be a 512-bit-per-prime RSA-like key generation. The exact semantic of the final powMod + second modMul is opaque in isolation, but the wire-level behavior — reading limbs_a[+0x08] and limbs_b[+0x20] — is fully confirmed.
Prototype (CONFIRMED @ 0x409AEA):void __fastcall KeyTable_SlotInit(int bit_param, int alt_seed) where EAX = slot pointer; bit_param = 0x400 at all production call sites; alt_seed = 0 in production.
Step
VA / Callee
Action
Status
0
KeyTable_PreSlotInit @ 0x409750
Zero-initializes 11 BigInt objects (FUN_00421DD0 × 11) before crypto
Random at slot+0x24 (p): clear bits bitlen−1, bitlen−2, 0
CONFIRMED
5
BigInt_assign @ 0x422910
Force odd; quick prime screen via BigInt_bitTestMod5 @ 0x424D40
INFERRED
6
Repeat 4–5 at slot+0x30
Generate second prime q
CONFIRMED
7
BigInt_exportCopy @ 0x422100
slot+0x3C ← p+1; slot+0x48 ← q+1 (add limb 1)
CONFIRMED
8
BigInt_modMul @ 0x421EF0
N = p·q → slot+0x0C
CONFIRMED
9a
param_2 == 0 (production)
Generate random odd e at slot+0x00; trial-division loop: BigInt_mod while result is zero → e *= 2
CONFIRMED @ 0x409BA4–0x409C68
9b
param_2 != 0
KeyTable_SlotAltInit(e, param_2) — inject seed limb from alt_seed
CONFIRMED
10
BigInt_cmp @ 0x4236B0 ×2
Guard comparisons before CRT exponentiation step
CONFIRMED
11
BigInt_powMod @ 0x421E50
CRT preparation on slot+0x3C, slot+0x48 vs N
INFERRED (modexp is opaque)
12
BigInt_modMul @ 0x421EF0
slot+0x18 ← p·q — overwrites bigint #2 → these become the block_b limbs
CONFIRMED
Key generation confidence is approximately 90% based on 16 captured KeyTable slots showing consistent 512-bit half-prime structure and 1024-bit composite results. The full RSA math hypothesis (512-bit primes, 1024-bit N, coprime e) is sound but the exact semantic of the final powMod step is not required for wire replay.
Allocates a single new slot and appends it to the live table. Called from the dynamic growth path when the connection load exceeds the 16 pre-generated slots.
Serializes a slot to wire format. Currently has zero direct E8 cross-references in the binary — it is a dead/orphaned function. SendKeyBlob_A101 performs inline serialization via rep movsd instead.
KeyDerivPRNG provides the per-send random index: idx = rand() % DAT_454C70. The slot content is not regenerated per send — only the selection index changes.
cmp/sub against 0xA101 (and branches for 0xA200, 0xA100)
0x004042F0
Login_OpcodeDispatch — switch-table dispatch for all login opcodes
0x00404611, 0x004049A5, 0x004049F2
mov word [esp+disp], 0xA102 — outbound error/status replies inside dispatcher
ps_login.exe is the only binary in the server tree that contains a mov word [...], 0xA101 writer. The three occurrences of 0xA101 as byte patterns in ps_game.exe are pointer addresses, not opcode writes.
# Break @ 0x404E3E (just before Connection_Send)# For each slot index, dump:# [slot+0x08] for *(slot+0x04)*4 bytes → block_a# [slot+0x20] for *(slot+0x1C)*4 bytes → block_b# Repeat 16 times (once per startup-init slot in GlobalInit loop @ 0x4065D9)
Replay fixed 16 slots captured from real ps_login.exe run
Yes — CONFIRMED
Send path only reads +0x04/+0x08/+0x1C/+0x20; no per-connection re-roll
Reimplement KeyTable_SlotInit
Optional — INFERRED
Requires GMP-like layer (BigInt_*), time()-seeded pool, matching rand() if growth path activates
Arbitrary random 64+128 bytes
No
Client Crypto_ProcessKeyPacket derives session keys and counter from exact octet values
Supplying arbitrary random bytes as block_a / block_b will cause the client’s HMAC-SHA256 derivation to produce a different counter value than the server expects, breaking the stream cipher and dropping the connection. Only captured limb dumps or a faithful reimplementation of KeyTable_SlotInit will produce a wire-compatible 0xA101.
The following items in the 0xA101 / counter derivation chain are NOT MAPPED without a live runtime capture:
D3: End-to-end 0xA101 counter — the exact bytes at ctx+0xF4 after Crypto_CounterLoad cannot be pre-computed offline because Crypto_PRNGFill state depends on the session time() seed plus all prior PRNG calls. Capture by breaking at 0x401740 and dumping 0x23038E4..0x23038F3.
Server-side 0xA101 inbound ack handling — the ps_login.exe branch at 0x00404375 that processes the client’s 131-byte follow-up 0xA101 is mapped at dispatcher level but its full ack body parse and state transition have not been decompiled.