Skip to content

Instantly share code, notes, and snippets.

@mq1n
Last active February 14, 2026 09:14
Show Gist options
  • Select an option

  • Save mq1n/95b059eddcb6110e381b74fb6a9790f0 to your computer and use it in GitHub Desktop.

Select an option

Save mq1n/95b059eddcb6110e381b74fb6a9790f0 to your computer and use it in GitHub Desktop.
fuzzer
#include "../include/PacketFuzzer.hpp"
#include "../include/desc.h"
#include "../include/char.h"
#include "../include/log.h"
#include <net/Type.hpp>
#include <base/Serialization.hpp>
#include <corelib/boost_compat.hpp>
#include <xxhash.h>
#include <cstring>
#include <random>
#include <algorithm>
#include <limits>
#include <fstream>
#include <sstream>
using asio::const_buffer;
namespace
{
// Build a valid PacketHeader with a payload, computing the hash
PacketHeader MakeHeader(uint16_t id, uint32_t payloadSize)
{
PacketHeader h{};
h.magic = PacketHeader::kMagic;
h.version = PacketHeader::kVersion;
h.length = static_cast<uint32_t>(sizeof(PacketHeader) + payloadSize);
h.id = id;
h.order_security_id = 0;
h.flags = 0;
// hash computed by caller with actual payload bytes
return h;
}
// Writes header (little-endian) and payload into a contiguous buffer matching Socket::ProcessData expectations
std::vector<uint8_t> BuildWirePacket(const PacketHeader& header, const std::vector<uint8_t>& payload)
{
std::vector<uint8_t> buf;
buf.resize(sizeof(PacketHeader) + payload.size());
// Write header in little endian as on the wire
auto* out = reinterpret_cast<PacketHeader*>(buf.data());
out->magic = corelib::boost_compat::endian::native_to_little(header.magic);
out->version = header.version;
out->length = corelib::boost_compat::endian::native_to_little(header.length);
out->id = corelib::boost_compat::endian::native_to_little(header.id);
out->hash = corelib::boost_compat::endian::native_to_little(header.hash);
out->order_security_id = corelib::boost_compat::endian::native_to_little(header.order_security_id);
out->flags = header.flags;
if (!payload.empty())
{
std::memcpy(buf.data() + sizeof(PacketHeader), payload.data(), payload.size());
}
return buf;
}
std::vector<uint8_t> RandomPayload(std::mt19937& rng, uint32_t maxSize)
{
std::uniform_int_distribution<uint32_t> sizeDist(0, std::min<uint32_t>(maxSize, PacketHeader::kMaxDataSize));
const uint32_t size = sizeDist(rng);
std::vector<uint8_t> data(size);
std::uniform_int_distribution<uint32_t> byteDist(0, 255);
for (auto& b : data) b = static_cast<uint8_t>(byteDist(rng));
return data;
}
// Mutate a payload using several strategies
std::vector<std::vector<uint8_t>> MutatePayloads(const std::vector<uint8_t>& base,
std::mt19937& rng,
uint32_t maxVariants)
{
std::vector<std::vector<uint8_t>> out;
out.reserve(8 + maxVariants);
// 1) Bit flip single bits across bytes
for (size_t i = 0; i < std::min<size_t>(base.size(), 16); ++i)
{
for (int bit = 0; bit < 8; ++bit)
{
auto m = base;
m[i] ^= static_cast<uint8_t>(1u << bit);
out.emplace_back(std::move(m));
if (out.size() >= maxVariants) return out;
}
}
// 2) Insert random byte at random position
{
std::uniform_int_distribution<size_t> posDist(0, base.size());
for (uint32_t k = 0; k < 8 && out.size() < maxVariants; ++k)
{
auto m = base;
const auto pos = posDist(rng);
m.insert(m.begin() + static_cast<std::ptrdiff_t>(pos), static_cast<uint8_t>(rng() & 0xFF));
out.emplace_back(std::move(m));
}
}
// 3) Truncate to random shorter length
if (!base.empty())
{
std::uniform_int_distribution<size_t> cutDist(0, base.size() - 1);
for (uint32_t k = 0; k < 8 && out.size() < maxVariants; ++k)
{
auto m = base;
m.resize(cutDist(rng));
out.emplace_back(std::move(m));
}
}
// 4) Over-extend certain leading length fields (first 2/4 bytes)
if (base.size() >= 2)
{
auto m = base;
m[0] = 0xFF; m[1] = 0xFF;
out.emplace_back(std::move(m));
}
if (base.size() >= 4)
{
auto m = base;
m[0] = 0xFF; m[1] = 0xFF; m[2] = 0xFF; m[3] = 0xFF;
out.emplace_back(std::move(m));
}
return out;
}
// Crafted edge cases for strings/optionals/vector counts, etc.
std::vector<std::vector<uint8_t>> EdgeCasePayloads(uint32_t maxSize)
{
std::vector<std::vector<uint8_t>> cases;
// Empty payload
cases.emplace_back();
// 1 byte
cases.emplace_back(1, 0x00);
// Max size payload (capped by maxSize)
cases.emplace_back(std::min<uint32_t>(maxSize, PacketHeader::kMaxDataSize), 0xFF);
// String length overrun (len says large, but body small)
{
std::vector<uint8_t> p;
// Write uint16_t length = 0xFFFF, no bytes following
p.resize(2);
p[0] = 0xFF; p[1] = 0xFF;
cases.emplace_back(std::move(p));
}
// OptionalFields bitmask huge but no data
{
std::vector<uint8_t> p;
// OptionalFields bits are uint64_t (implementation detail: see Serialization OptionalFields::BitsType)
p.resize(8, 0xFF); // all bits set, but nothing else
cases.emplace_back(std::move(p));
}
return cases;
}
// Meaningless arbitrary payloads with common test patterns and varied sizes
std::vector<std::vector<uint8_t>> BuildArbitraryPayloads(uint32_t maxSize, std::mt19937& rng)
{
const uint32_t cap = std::min<uint32_t>(maxSize, PacketHeader::kMaxDataSize);
std::vector<uint32_t> lens = {
0, 1, 2, 3, 4, 7, 8, 9, 15, 16, 31, 32, 63, 64,
127, 128, 255, 256, 512, 768, 1024, 2048, 4096
};
// Trim to cap
for (auto& L : lens) if (L > cap) L = cap;
lens.erase(std::remove_if(lens.begin(), lens.end(), [&](uint32_t L){ return L > cap; }), lens.end());
std::vector<std::vector<uint8_t>> out;
out.reserve(lens.size() * 6);
auto makePattern = [&](uint32_t len, uint8_t v){ std::vector<uint8_t> p(len, v); return p; };
for (uint32_t len : lens)
{
// All zeros
out.emplace_back(makePattern(len, 0x00));
// All 0xFF
out.emplace_back(makePattern(len, 0xFF));
// 0xAA pattern
out.emplace_back(makePattern(len, 0xAA));
// 0x55 pattern
out.emplace_back(makePattern(len, 0x55));
// Incrementing bytes
{
std::vector<uint8_t> inc(len);
for (uint32_t i = 0; i < len; ++i) inc[i] = static_cast<uint8_t>(i);
out.emplace_back(std::move(inc));
}
// Random bytes
{
std::vector<uint8_t> rnd(len);
std::uniform_int_distribution<uint32_t> byteDist(0, 255);
for (auto& b : rnd) b = static_cast<uint8_t>(byteDist(rng));
out.emplace_back(std::move(rnd));
}
}
// A few large cases near cap
if (cap >= 1)
{
auto addNear = [&](uint32_t L){ if (L <= cap) out.emplace_back(makePattern(L, 0xEE)); };
addNear(cap);
addNear(cap > 1 ? cap - 1 : cap);
addNear(cap > 64 ? cap - 64 : cap);
}
return out;
}
template <typename TPacket>
std::vector<uint8_t> SerializePacket(const TPacket& p)
{
const auto objSize = GetObjectSize(p);
std::vector<uint8_t> payload(objSize);
asio::mutable_buffer buf(payload.data(), payload.size());
Write(buf, p);
return payload;
}
// Create a long unicode string (UTF-8) with mixed plane chars
std::string BuildUnicodeString()
{
// Mixed ASCII, BMP, and some 4-byte UTF-8 sequences
std::string s = "ASCII äöü ĄŻźć中華人民共和國 ";
// Add emojis / surrogate pairs in UTF-8
const char* emojis[] = {"\xF0\x9F\x98\x80", "\xF0\x9F\x9A\x80", "\xF0\x9F\x8E\x89"};
for (int i = 0; i < 128; ++i)
{
s += emojis[i % 3];
}
return s;
}
// Load BLNS (Big List of Naughty Strings) from disk if provided; otherwise return a built-in subset
std::vector<std::string> LoadNaughtyStrings(const std::string& path)
{
std::vector<std::string> out;
if (!path.empty())
{
std::ifstream f(path);
if (f)
{
std::string line;
while (std::getline(f, line))
{
if (line.empty() || line[0] == '#') continue;
out.push_back(line);
}
}
}
if (out.empty())
{
// Minimal embedded subset if file unavailable
out = {
"\t\n\r", // control chars
"\x00", // null byte
"'\"<>/&", // HTML special
"..\\..\\..\\", // path traversal
"😀🚀🎉", // emojis
"零一二三四五六七八九十", // CJK numerals
"%s %x %n %p", // printf tokens
"${7*7}", // template injection
"DROP TABLE users;--", // SQLi
};
}
return out;
}
// Python-centric injection strings commonly seen in client script contexts
std::vector<std::string> BuildPythonInjectionStrings()
{
return {
"__import__('os').system('calc')",
"__import__('subprocess').Popen(['calc'])",
"(lambda: __import__('os').system('echo PY'))()",
"__class__.__mro__",
"globals()",
"locals()",
"__import__('builtins').eval('__import\\x28\\'os\\'\\x29.system(\'echo X\')')",
"""'; __import__('os').system('echo INJ'); '""",
"{.__class__.__mro__[1].__subclasses__()}",
"__import__('sys').modules",
"().__class__.__base__.__subclasses__()",
"print.__class__.__mro__",
"open('/etc/passwd')",
"__import__('sys').setrecursionlimit(10**6)",
};
}
// Structured payload builders for commonly used packets
std::vector<std::vector<uint8_t>> BuildStructuredPayloads(uint16_t id, bool unicodeHeavy)
{
using namespace std;
vector<vector<uint8_t>> out;
switch (id)
{
case HEADER_CG_KEY_LOGIN:
{
CgKeyLoginPacket k{};
k.login = "testlogin"; k.sessionId = 1; k.ac_sessionId = "ac"; k.hwid_1 = "HW1"; k.hwid_2 = "HW2"; k.hwid_3 = "HW3"; k.discord_id = "0"; k.client_version = 0; out.emplace_back(SerializePacket(k));
k.login.assign(32, 'L'); k.sessionId = 0xFFFFFFFFFFFFFFFFull; k.ac_sessionId.assign(64, 'A'); k.hwid_1.assign(64, '1'); k.hwid_2.assign(64, '2'); k.hwid_3.assign(64, '3'); k.discord_id.assign(64, 'D'); k.client_version = 0xFFFFFFFF; out.emplace_back(SerializePacket(k));
break;
}
case HEADER_CG_CHARACTER_SELECT:
{
TPacketCGPlayerSelect sel{}; sel.index = 0; sel.localeName = "en"; out.emplace_back(SerializePacket(sel)); sel.index = 3; sel.localeName = "tr"; out.emplace_back(SerializePacket(sel));
break;
}
case HEADER_CG_CHARACTER_CREATE:
{
TPacketCGPlayerCreate cr{}; cr.index = 0; cr.name = "Hero"; cr.job = 0; cr.shape = 0; cr.Con = 3; cr.Int = 3; cr.Str = 3; cr.Dex = 3; cr.empire = 1; out.emplace_back(SerializePacket(cr));
cr.name.assign(20, 'N'); cr.job = 3; cr.shape = 1; cr.empire = 3; out.emplace_back(SerializePacket(cr));
break;
}
case HEADER_CG_CHARACTER_DELETE:
{
TPacketCGPlayerDelete del{}; del.index = 0; del.private_code = "000000"; out.emplace_back(SerializePacket(del));
del.index = 3; del.private_code = std::string(14, '9'); out.emplace_back(SerializePacket(del));
break;
}
case HEADER_CG_ENTERGAME:
{
TPacketCGEnterGame eg{}; eg.client_version = 0; eg.file_hash = 0; eg.mem_hash = 0; eg.mem_corrupted = 0; eg.ac_init = 1; out.emplace_back(SerializePacket(eg));
eg.client_version = 0xFFFFFFFF; eg.file_hash = 0xFFFFFFFF; eg.mem_hash = 0xFFFFFFFF; eg.mem_corrupted = 1; eg.ac_init = 0; out.emplace_back(SerializePacket(eg));
break;
}
case HEADER_CG_MARK_CRCLIST:
{
TPacketCGMarkCRCList ml{}; ml.imgIdx = 0; memset(ml.crclist, 0, sizeof(ml.crclist)); out.emplace_back(SerializePacket(ml));
break;
}
case HEADER_CG_MARK_IDXLIST:
{
TPacketCGMarkIDXList mid{}; out.emplace_back(SerializePacket(mid));
break;
}
case HEADER_CG_MARK_UPLOAD:
{
TPacketCGMarkUpload mu{}; mu.gid = 1; // fill some bytes in image
for (size_t i = 0; i < sizeof(mu.image); i += 4) { mu.image[i] = 0xFF; }
out.emplace_back(SerializePacket(mu));
break;
}
case HEADER_CG_GUILD_SYMBOL_UPLOAD:
{
TPacketCGGuildSymbolUpload gs{}; gs.guild_id = 1; gs.data.assign(256, 0x7F); out.emplace_back(SerializePacket(gs));
gs.data.assign(64 * 1024, 0xFF); out.emplace_back(SerializePacket(gs));
break;
}
case HEADER_CG_SYMBOL_CRC:
{
TPacketCGSymbolCRC sc{}; sc.guild_id = 1; sc.crc = 0; sc.size = 0; sc.lastRequest = 1; out.emplace_back(SerializePacket(sc));
sc.crc = 0xDEADBEEF; sc.size = 4096; sc.lastRequest = 0; out.emplace_back(SerializePacket(sc));
break;
}
case HEADER_CG_CHARACTER_MOVE:
{
TPacketCGMove m{};
// minimally valid and boundary variations
m.bFunc = FUNC_MOVE; m.lX = 0; m.lY = 0; m.dwTime = 1; out.emplace_back(SerializePacket(m));
m.lX = numeric_limits<int32_t>::max(); m.lY = numeric_limits<int32_t>::max(); out.emplace_back(SerializePacket(m));
m.lX = numeric_limits<int32_t>::min(); m.lY = numeric_limits<int32_t>::min(); out.emplace_back(SerializePacket(m));
m.lX = -1; m.lY = -1; out.emplace_back(SerializePacket(m));
m.lZ = numeric_limits<int32_t>::max(); out.emplace_back(SerializePacket(m));
m.lZ = numeric_limits<int32_t>::min(); out.emplace_back(SerializePacket(m));
m.bRot = 255; m.color = 0xFFFFFFFF; m.motionKey = 0xFFFFFFFF; out.emplace_back(SerializePacket(m));
m.loopCount = 255; m.isMovingSkill = 1; out.emplace_back(SerializePacket(m));
break;
}
case HEADER_CG_ATTACK:
{
TPacketCGAttack a{};
a.bType = 0; a.dwVictimVID = 1; out.emplace_back(SerializePacket(a));
a.dwVictimVID = 0; a.attackTime = 0; out.emplace_back(SerializePacket(a));
a.attackTime = 0xFFFFFFFF; out.emplace_back(SerializePacket(a));
a.motionKey = 0; out.emplace_back(SerializePacket(a));
a.motionKey = 0xFFFFFFFF; out.emplace_back(SerializePacket(a));
a.x = std::numeric_limits<uint32_t>::max(); a.y = 0; out.emplace_back(SerializePacket(a));
a.pushX = 0xFFFFFFFF; a.pushY = 0xFFFFFFFF; out.emplace_back(SerializePacket(a));
break;
}
case HEADER_CG_CHAT:
{
TPacketCGChat c{};
c.type = 0; c.lang = 0; c.message = "hi"; out.emplace_back(SerializePacket(c));
c.message.assign(1024, 'A'); out.emplace_back(SerializePacket(c));
c.message.clear(); out.emplace_back(SerializePacket(c));
if (unicodeHeavy)
{
c.message = BuildUnicodeString(); out.emplace_back(SerializePacket(c));
}
break;
}
case HEADER_CG_ITEM_MOVE:
{
TPacketCGItemMove im{};
out.emplace_back(SerializePacket(im));
im.count = static_cast<CountType>(0); out.emplace_back(SerializePacket(im));
im.count = std::numeric_limits<CountType>::max(); out.emplace_back(SerializePacket(im));
break;
}
case HEADER_CG_ITEM_DROP:
{
TPacketCGItemDrop d{};
d.gold = 0; out.emplace_back(SerializePacket(d));
d.gold = static_cast<Gold>(-1); out.emplace_back(SerializePacket(d));
d.gold = std::numeric_limits<Gold>::max(); out.emplace_back(SerializePacket(d));
break;
}
case HEADER_CG_ITEM_DROP2:
{
TPacketCGItemDrop2 d{}; d.Cell = TItemPos{0,0}; d.gold = 0; d.count = 0; out.emplace_back(SerializePacket(d));
d.gold = std::numeric_limits<Gold>::max(); d.count = std::numeric_limits<CountType>::max(); out.emplace_back(SerializePacket(d));
break;
}
case HEADER_CG_EXCHANGE:
{
TPacketCGExchange e{};
// Try all subheaders minimally
for (uint8_t sh = EXCHANGE_SUBHEADER_CG_START; sh <= EXCHANGE_SUBHEADER_CG_CANCEL; ++sh)
{
e.sub_header = sh;
out.emplace_back(SerializePacket(e));
}
break;
}
case HEADER_CG_USE_SKILL:
{
TPacketCGUseSkill us{};
us.dwVnum = 0; us.dwVID = 0; out.emplace_back(SerializePacket(us));
us.dwVnum = 0xFFFFFFFF; us.dwVID = 0xFFFFFFFF; out.emplace_back(SerializePacket(us));
break;
}
case HEADER_CG_CHOOSE_SKILL_GROUP:
{
TPacketCGChooseSkillGroup csg{}; csg.skillGroup = 0; out.emplace_back(SerializePacket(csg)); csg.skillGroup = 2; out.emplace_back(SerializePacket(csg));
break;
}
case HEADER_CG_ON_CLICK:
{
TPacketCGOnClick oc{}; oc.vid = 0; out.emplace_back(SerializePacket(oc)); oc.vid = 0xFFFFFFFF; out.emplace_back(SerializePacket(oc));
break;
}
case HEADER_CG_TARGET:
{
TPacketCGTarget t{}; t.dwVID = 0; out.emplace_back(SerializePacket(t)); t.dwVID = 0xFFFFFFFF; out.emplace_back(SerializePacket(t));
break;
}
case HEADER_CG_ITEM_USE:
{
TPacketCGItemUse u{}; u.Cell = TItemPos{0, 0}; out.emplace_back(SerializePacket(u));
u.Cell = TItemPos{1, 200}; out.emplace_back(SerializePacket(u));
break;
}
case HEADER_CG_ITEM_USE_TO_ITEM:
{
TPacketCGItemUseToItem u{}; u.Cell = TItemPos{0,0}; u.TargetCell = TItemPos{1,0}; out.emplace_back(SerializePacket(u));
break;
}
case HEADER_CG_ITEM_SPLIT:
{
TPacketCGItemSplit sp{}; sp.Cell = TItemPos{0,0}; sp.count = 0; out.emplace_back(SerializePacket(sp));
sp.count = std::numeric_limits<CountType>::max(); out.emplace_back(SerializePacket(sp));
break;
}
case HEADER_CG_ITEM_PICKUP:
{
TPacketCGItemPickup pu{}; pu.vid = 0; pu.time = 0; out.emplace_back(SerializePacket(pu));
pu.vid = 0xFFFFFFFF; pu.time = 0xFFFFFFFF; out.emplace_back(SerializePacket(pu));
break;
}
case HEADER_CG_ITEM_GIVE:
{
TPacketCGGiveItem gi{}; gi.dwTargetVID = 0; gi.ItemPos = TItemPos{0,0}; gi.byItemCount = 0; out.emplace_back(SerializePacket(gi));
gi.dwTargetVID = 0xFFFFFFFF; gi.byItemCount = std::numeric_limits<CountType>::max(); out.emplace_back(SerializePacket(gi));
break;
}
case HEADER_CG_ITEM_DESTROY:
{
TPacketCGItemDestroy d{}; d.Cell = TItemPos{0,0}; out.emplace_back(SerializePacket(d));
break;
}
case HEADER_CG_ITEM_COMBINATION:
{
TPacketCGItemCombiation ic{}; ic.mediumPos = TItemPos{0,0}; ic.basePos = TItemPos{1,0}; ic.materialPos = TItemPos{2,0}; out.emplace_back(SerializePacket(ic));
break;
}
case HEADER_CG_REFINE:
{
TPacketCGRefine r{}; r.pos = 0; r.type = 0; out.emplace_back(SerializePacket(r)); r.pos = 0xFFFF; r.type = 0xFF; out.emplace_back(SerializePacket(r));
break;
}
case HEADER_CG_FISHING:
{
TPacketCGFishing f{}; f.dir = 0; out.emplace_back(SerializePacket(f)); f.dir = 255; out.emplace_back(SerializePacket(f));
break;
}
case HEADER_CG_FISHING_GAME:
{
TPacketCGFishingGame fg{}; fg.hitCount = 0; fg.time = 0.f; out.emplace_back(SerializePacket(fg)); fg.hitCount = 255; fg.time = 9999.9f; out.emplace_back(SerializePacket(fg));
break;
}
case HEADER_CG_FLY_TARGETING:
{
TPacketCGFlyTargeting ft{}; ft.dwTargetVID = 0; ft.x = 0; ft.y = 0; out.emplace_back(SerializePacket(ft));
ft.dwTargetVID = 0xFFFFFFFF; ft.x = std::numeric_limits<int32_t>::max(); ft.y = std::numeric_limits<int32_t>::min(); out.emplace_back(SerializePacket(ft));
break;
}
case HEADER_CG_ADD_FLY_TARGETING:
{
TPacketCGFlyTargeting ft{}; ft.dwTargetVID = 0; ft.x = 1; ft.y = 1; out.emplace_back(SerializePacket(ft));
break;
}
case HEADER_CG_SHOOT:
{
TPacketCGShoot s{}; s.type = 0; s.motionKey = 0; out.emplace_back(SerializePacket(s)); s.type = 0xFF; s.motionKey = 0xFFFFFFFF; out.emplace_back(SerializePacket(s));
break;
}
case HEADER_CG_QUICKSLOT_ADD:
{
TPacketCGQuickslotAdd qa{}; qa.pos = 0; qa.slot = TQuickslot{}; out.emplace_back(SerializePacket(qa)); qa.pos = 255; out.emplace_back(SerializePacket(qa));
break;
}
case HEADER_CG_QUICKSLOT_DEL:
{
TPacketCGQuickslotDel qd{}; qd.pos = 0; out.emplace_back(SerializePacket(qd)); qd.pos = 255; out.emplace_back(SerializePacket(qd));
break;
}
case HEADER_CG_QUICKSLOT_SWAP:
{
TPacketCGQuickslotSwap qs{}; qs.pos = 0; qs.change_pos = 1; out.emplace_back(SerializePacket(qs)); qs.pos = 255; qs.change_pos = 254; out.emplace_back(SerializePacket(qs));
break;
}
case HEADER_CG_CHAT_FILTER:
{
TPacketCGChatFilter cf{}; cf.bFilterList = {0,1,0,1,1,0,0,1}; out.emplace_back(SerializePacket(cf));
cf.bFilterList.assign(32, 1); out.emplace_back(SerializePacket(cf));
break;
}
case HEADER_CG_SYNC_POSITION:
{
TPacketCGSyncPosition sp{}; out.emplace_back(SerializePacket(sp));
sp.elems.push_back({0, 0, 0}); out.emplace_back(SerializePacket(sp));
sp.elems.push_back({0xFFFFFFFF, std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min()}); out.emplace_back(SerializePacket(sp));
break;
}
case HEADER_CG_SAFEBOX_ITEM_MOVE:
{
TPacketCGItemMove mv{}; mv.Cell = TItemPos{0,0}; mv.CellTo = TItemPos{1,0}; mv.count = 1; out.emplace_back(SerializePacket(mv));
break;
}
case HEADER_CG_SAFEBOX_CHECKOUT:
{
TPacketCGSafeboxCheckout so{}; so.bSafePos = 0; so.ItemPos = TItemPos{0,0}; out.emplace_back(SerializePacket(so));
break;
}
case HEADER_CG_SAFEBOX_CHECKIN:
{
TPacketCGSafeboxCheckin si{}; si.bSafePos = 0; si.ItemPos = TItemPos{0,0}; out.emplace_back(SerializePacket(si));
break;
}
case HEADER_CG_TARGET_LOAD:
{
TPacketCGTargetLoad tl{}; tl.dwVID = 0; out.emplace_back(SerializePacket(tl)); tl.dwVID = 0xFFFFFFFF; out.emplace_back(SerializePacket(tl));
break;
}
case HEADER_CG_CHANGE_SKILL_COLOR:
{
TPacketCGChangeSkillColorPacket cscp{}; cscp.vnum = 0; cscp.color = 0; out.emplace_back(SerializePacket(cscp)); cscp.vnum = 0xFFFFFFFF; cscp.color = 0xFFFFFFFF; out.emplace_back(SerializePacket(cscp));
break;
}
case HEADER_CG_CHANGE_EMPIRE:
{
CgChangeEmpirePacket cep{}; cep.slot = 0; cep.empire = 0; out.emplace_back(SerializePacket(cep)); cep.slot = 3; cep.empire = 3; out.emplace_back(SerializePacket(cep));
break;
}
case HEADER_CG_BLOCK_MODE:
{
CgBlockModePacket bm{}; bm.blockMode = 0; out.emplace_back(SerializePacket(bm)); bm.blockMode = 0xFFFFFFFF; out.emplace_back(SerializePacket(bm));
break;
}
case HEADER_CG_SET_TITLE:
{
CgSetTitlePacket st{}; st.color = 0; st.szTitle = "Title"; out.emplace_back(SerializePacket(st));
if (unicodeHeavy) { st.szTitle = BuildUnicodeString(); out.emplace_back(SerializePacket(st)); }
break;
}
case HEADER_CG_CHANGE_NAME:
{
CgChangeNamePacket chn{}; chn.index = 0; chn.name = "NewName"; out.emplace_back(SerializePacket(chn));
break;
}
case HEADER_CG_DUNGEON_WARP:
{
TPacketCGDungeonWarp dw{}; dw.bDungeonID = 0; out.emplace_back(SerializePacket(dw)); dw.bDungeonID = 255; out.emplace_back(SerializePacket(dw));
break;
}
case HEADER_CG_DUNGEON_RANKING:
{
TPacketCGDungeonRanking dr{}; dr.bDungeonID = 0; dr.bRankingType = 0; out.emplace_back(SerializePacket(dr)); dr.bDungeonID = 255; dr.bRankingType = 255; out.emplace_back(SerializePacket(dr));
break;
}
case HEADER_CG_SWITCHBOT:
{
CgSwitchbotPacket sb{}; sb.subheader = SWITCHBOT_SUBHEADER_CG_SET_ATTRIBUTE; sb.updateAttr = CgSwitchbotAttributeUpdatePacket{0,0,0,{0,0}}; out.emplace_back(SerializePacket(sb));
sb = CgSwitchbotPacket{}; sb.subheader = SWITCHBOT_SUBHEADER_CG_START_SLOT; sb.status = CgSwitchbotStatusPacket{0}; out.emplace_back(SerializePacket(sb));
break;
}
case HEADER_CG_BATTLE_PASS:
{
TPacketCGBattlePassAction ba{}; ba.bAction = 0; out.emplace_back(SerializePacket(ba)); ba.bAction = 1; out.emplace_back(SerializePacket(ba));
break;
}
case HEADER_CG_SCRIPT_BUTTON:
{
TPacketCGScriptButton sbn{}; sbn.idx = 0; out.emplace_back(SerializePacket(sbn)); sbn.idx = 0xFFFFFFFF; out.emplace_back(SerializePacket(sbn));
break;
}
case HEADER_CG_SCRIPT_SELECT_ITEM:
{
TPacketCGScriptSelectItem ssel{}; ssel.selection = 0; out.emplace_back(SerializePacket(ssel)); ssel.selection = 0xFFFFFFFF; out.emplace_back(SerializePacket(ssel));
break;
}
case HEADER_CG_SCRIPT_ANSWER:
{
TPacketCGScriptAnswer sans{}; sans.answer = 0; sans.qIndex = 0; out.emplace_back(SerializePacket(sans)); sans.answer = 1; sans.qIndex = -1; out.emplace_back(SerializePacket(sans));
break;
}
case HEADER_CG_QUEST_INPUT_STRING:
{
TPacketCGQuestInputString qis{}; qis.msg = ""; qis.qIndex = 0; out.emplace_back(SerializePacket(qis)); qis.msg.assign(256, 'Q'); qis.qIndex = -1; out.emplace_back(SerializePacket(qis));
break;
}
case HEADER_CG_QUEST_RECEIVE:
{
TPacketCGQuestRcv qrcv{}; qrcv.questID = 0; qrcv.msg = "hi"; out.emplace_back(SerializePacket(qrcv)); qrcv.questID = 0xFFFFFFFF; qrcv.msg.clear(); out.emplace_back(SerializePacket(qrcv));
break;
}
case HEADER_CG_QUEST_CONFIRM:
{
TPacketCGQuestConfirm qc{}; qc.answer = 0; qc.requestPID = 0; out.emplace_back(SerializePacket(qc)); qc.answer = 1; qc.requestPID = 0xFFFFFFFF; out.emplace_back(SerializePacket(qc));
break;
}
case HEADER_CG_PARTY_REMOVE:
{
TPacketCGPartyRemove pr{}; pr.pid = 0; out.emplace_back(SerializePacket(pr)); pr.pid = 0xFFFFFFFF; out.emplace_back(SerializePacket(pr));
break;
}
case HEADER_CG_PARTY_PARAMETER:
{
TPacketCGPartyParameter pp{}; pp.bDistributeMode = 0; out.emplace_back(SerializePacket(pp)); pp.bDistributeMode = 2; out.emplace_back(SerializePacket(pp));
break;
}
case HEADER_CG_ANSWER_MAKE_GUILD:
{
TPacketCGAnswerMakeGuild amg{}; amg.guild_name = "Guild"; out.emplace_back(SerializePacket(amg));
break;
}
case HEADER_CG_HACK:
{
TPacketCGHack hk{}; hk.szBuf = ""; hk.szInfo = ""; out.emplace_back(SerializePacket(hk)); hk.szBuf.assign(64, 'H'); hk.szInfo.assign(64, 'I'); out.emplace_back(SerializePacket(hk));
break;
}
case HEADER_CG_MYSHOP:
{
CgMyShopOpen ms{}; ms.sign = "Shop"; ms.bundleItem = TItemPos{0,0}; ms.count = 0; out.emplace_back(SerializePacket(ms));
break;
}
case HEADER_CG_MESSENGER:
{
// Simple TPacketCGMessenger add_by_name/remove coverage
TPacketCGMessenger m{}; m.subheader = MESSENGER_SUBHEADER_CG_ADD_BY_NAME; m.name = std::string("Friend"); out.emplace_back(SerializePacket(m));
m = TPacketCGMessenger{}; m.subheader = MESSENGER_SUBHEADER_CG_REMOVE; m.name = std::string("Friend"); out.emplace_back(SerializePacket(m));
break;
}
case HEADER_CG_WIKI_REQUEST:
{
CgRecvWikiPacket wk{}; wk.ret_id = 0; wk.vnum = 0; wk.is_mob = 0; out.emplace_back(SerializePacket(wk)); wk.ret_id = 1; wk.vnum = 12345; wk.is_mob = 1; out.emplace_back(SerializePacket(wk));
break;
}
case HEADER_CG_SHOP:
{
// Cover shop subheaders with minimal optional fields
TPacketCGShop sp{};
// BUY
sp = TPacketCGShop{}; sp.subheader = SHOP_SUBHEADER_CG_BUY; sp.buyAction = CgShopActionBuy{0, 1}; out.emplace_back(SerializePacket(sp));
// SELL
sp = TPacketCGShop{}; sp.subheader = SHOP_SUBHEADER_CG_SELL; sp.sellAction = CgShopActionSell{TItemPos{0, 0}, 1}; out.emplace_back(SerializePacket(sp));
// SELL2
sp = TPacketCGShop{}; sp.subheader = SHOP_SUBHEADER_CG_SELL2; sp.sellAction = CgShopActionSell{TItemPos{1, 2}, 255}; out.emplace_back(SerializePacket(sp));
// END
sp = TPacketCGShop{}; sp.subheader = SHOP_SUBHEADER_CG_END; out.emplace_back(SerializePacket(sp));
break;
}
case HEADER_CG_ACCE:
{
// ACCE subheader coverage
TPacketCGAcce ap{};
// CHECKIN
ap = TPacketCGAcce{}; ap.subheader = ACCE_SUBHEADER_CG_REFINE_CHECKIN; ap.checkin = TPacketCGAcceCheckin{0, TItemPos{0, 0}, 0}; out.emplace_back(SerializePacket(ap));
// CHECKOUT
ap = TPacketCGAcce{}; ap.subheader = ACCE_SUBHEADER_CG_REFINE_CHECKOUT; ap.checkout = TPacketCGAcceCheckout{0}; out.emplace_back(SerializePacket(ap));
// ACCEPT
ap = TPacketCGAcce{}; ap.subheader = ACCE_SUBHEADER_CG_REFINE_ACCEPT; ap.accept = TPacketCGAcceRefineAccept{0}; out.emplace_back(SerializePacket(ap));
// CANCEL
ap = TPacketCGAcce{}; ap.subheader = ACCE_SUBHEADER_CG_REFINE_CANCEL; out.emplace_back(SerializePacket(ap));
break;
}
case HEADER_CG_CHANGELOOK:
{
// CHANGELOOK subheader coverage
CgChangeLookPacket cl{};
// CHECKIN
cl = CgChangeLookPacket{}; cl.subheader = CHANGELOOK_SUBHEADER_CG_REFINE_CHECKIN; cl.checkin = CgChangeLookCheckinPacket{TItemPos{0, 0}, 0}; out.emplace_back(SerializePacket(cl));
// CHECKOUT
cl = CgChangeLookPacket{}; cl.subheader = CHANGELOOK_SUBHEADER_CG_REFINE_CHECKOUT; cl.checkout = static_cast<uint8_t>(0); out.emplace_back(SerializePacket(cl));
// ACCEPT
cl = CgChangeLookPacket{}; cl.subheader = CHANGELOOK_SUBHEADER_CG_REFINE_ACCEPT; out.emplace_back(SerializePacket(cl));
// CANCEL
cl = CgChangeLookPacket{}; cl.subheader = CHANGELOOK_SUBHEADER_CG_REFINE_CANCEL; out.emplace_back(SerializePacket(cl));
break;
}
case HEADER_CG_LEVEL_PET:
{
// LEVEL PET subheader coverage
CgLevelPetPacket lp{};
// OPEN
lp = CgLevelPetPacket{}; lp.subheader = LEVELPET_SUBHEADER_CG_OPEN; lp.index = static_cast<uint32_t>(0); out.emplace_back(SerializePacket(lp));
// CLOSE
lp = CgLevelPetPacket{}; lp.subheader = LEVELPET_SUBHEADER_CG_CLOSE; out.emplace_back(SerializePacket(lp));
// PLUS_ATTR
lp = CgLevelPetPacket{}; lp.subheader = LEVELPET_SUBHEADER_CG_PLUS_ATTR; lp.itemPos = TItemPos{0, 0}; out.emplace_back(SerializePacket(lp));
break;
}
#ifdef ENABLE_IKASHOP_RENEWAL
case HEADER_CG_NEW_OFFLINESHOP:
{
using namespace std;
// Ikarus Shop: fully cover subheaders
TPacketCGIkarusShop pk{};
// CREATE_NEW (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_CREATE_NEW; out.emplace_back(SerializePacket(pk));
// FORCE_CLOSE (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_FORCE_CLOSE; out.emplace_back(SerializePacket(pk));
// REQUEST_SHOPLIST (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_REQUEST_SHOPLIST; out.emplace_back(SerializePacket(pk));
// OPEN (ownerid)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_OPEN; pk.shopOpen = TSubPacketCGShopOpen{0}; out.emplace_back(SerializePacket(pk));
// OPEN_OWNER (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_OPEN_OWNER; out.emplace_back(SerializePacket(pk));
// BUY_ITEM
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_BUY_ITEM; pk.buyItem = TSubPacketCGShopBuyItem{0u, DEFAULT_ITEM_UUID, false, 0}; out.emplace_back(SerializePacket(pk));
// ADD_ITEM
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_ADD_ITEM; pk.addItem = TSubPacketCGAddItem{TItemPos{0,0}, TPriceInfo{0}, 0}; out.emplace_back(SerializePacket(pk));
// REMOVE_ITEM
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_REMOVE_ITEM; pk.removeItem = TSubPacketCGRemoveItem{DEFAULT_ITEM_UUID}; out.emplace_back(SerializePacket(pk));
// EDIT_ITEM
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_EDIT_ITEM; pk.editItem = TSubPacketCGEditItem{DEFAULT_ITEM_UUID, TPriceInfo{0}}; out.emplace_back(SerializePacket(pk));
// FILTER_REQUEST
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_FILTER_REQUEST; pk.filterRequest = TSubPacketCGFilterRequest{TFilterInfo{0,0,"", TPriceInfo{0}, TPriceInfo{0}, 0, 0, {}, 0, 0, false}}; out.emplace_back(SerializePacket(pk));
// SEARCH_FILL_REQUEST (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_SEARCH_FILL_REQUEST; out.emplace_back(SerializePacket(pk));
// OFFER_CREATE
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_OFFER_CREATE; pk.offerCreate = TSubPacketCGOfferCreate{}; out.emplace_back(SerializePacket(pk));
// OFFER_ACCEPT
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_OFFER_ACCEPT; pk.offerAccept = TSubPacketCGOfferAccept{0}; out.emplace_back(SerializePacket(pk));
// OFFER_CANCEL
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_OFFER_CANCEL; pk.offerCancel = TSubPacketCGOfferCancel{0,0}; out.emplace_back(SerializePacket(pk));
// REQUEST_OFFER_LIST (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_REQUEST_OFFER_LIST; out.emplace_back(SerializePacket(pk));
// SAFEBOX_OPEN (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_SAFEBOX_OPEN; out.emplace_back(SerializePacket(pk));
// SAFEBOX_GET_ITEM
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_SAFEBOX_GET_ITEM; pk.getItemSafebox = TSubPacketCGShopSafeboxGetItem{DEFAULT_ITEM_UUID}; out.emplace_back(SerializePacket(pk));
// SAFEBOX_GET_VALUTES (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_SAFEBOX_GET_VALUTES; out.emplace_back(SerializePacket(pk));
// SAFEBOX_CLOSE (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_SAFEBOX_CLOSE; out.emplace_back(SerializePacket(pk));
// AUCTION_LIST_REQUEST (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_AUCTION_LIST_REQUEST; out.emplace_back(SerializePacket(pk));
// AUCTION_OPEN_REQUEST
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_AUCTION_OPEN_REQUEST; pk.auctionOpenRequest = TSubPacketCGAuctionOpenRequest{0}; out.emplace_back(SerializePacket(pk));
// MY_AUCTION_OPEN_REQUEST (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_MY_AUCTION_OPEN_REQUEST; out.emplace_back(SerializePacket(pk));
// MY_AUCTION_CLOSE (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_MY_AUCTION_CLOSE; out.emplace_back(SerializePacket(pk));
// MY_AUCTION_CANCEL (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_MY_AUCTION_CANCEL; out.emplace_back(SerializePacket(pk));
// CREATE_AUCTION
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_CREATE_AUCTION; pk.auctionCreate = TSubPacketCGAuctionCreate{TItemPos{0,0}, TPriceInfo{0}}; out.emplace_back(SerializePacket(pk));
// AUCTION_ADD_OFFER
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_AUCTION_ADD_OFFER; pk.auctionAddOffer = TSubPacketCGAuctionAddOffer{0, TPriceInfo{0}}; out.emplace_back(SerializePacket(pk));
// EXIT_FROM_AUCTION
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_EXIT_FROM_AUCTION; pk.auctionExitFrom = TSubPacketCGAuctionExitFrom{0}; out.emplace_back(SerializePacket(pk));
// CLOSE_MY_SHOP_BOARD (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_CLOSE_MY_SHOP_BOARD; out.emplace_back(SerializePacket(pk));
// CLOSE_OFFER_LIST (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_CLOSE_OFFER_LIST; out.emplace_back(SerializePacket(pk));
// CLOSE_SHOP_GUEST (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_CLOSE_SHOP_GUEST; out.emplace_back(SerializePacket(pk));
#ifdef ENABLE_IKASHOP_ENTITIES
// CLICK_ENTITY
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_CLICK_ENTITY; pk.clickEntity = TSubPacketCGShopClickEntity{0}; out.emplace_back(SerializePacket(pk));
#ifdef EXTEND_IKASHOP_PRO
// MOVE_SHOP_ENTITY (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_MOVE_SHOP_ENTITY; out.emplace_back(SerializePacket(pk));
#endif
#endif
#ifdef EXTEND_IKASHOP_PRO
// NOTIFICATION_LIST_REQUEST (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_NOTIFICATION_LIST_REQUEST; out.emplace_back(SerializePacket(pk));
// NOTIFICATION_LIST_CLOSE (no additional)
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_NOTIFICATION_LIST_CLOSE; out.emplace_back(SerializePacket(pk));
#endif
#ifdef EXTEND_IKASHOP_ULTIMATE
// PRICE_AVERAGE_REQUEST
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_PRICE_AVERAGE_REQUEST; pk.priceAverageRequest = TSubPacketCGPriceAverageRequest{0, 1}; out.emplace_back(SerializePacket(pk));
// SHOP_DECORATION_USE
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_DECORATION_USE; pk.decoUse = 0; out.emplace_back(SerializePacket(pk));
// SHOP_MOVE_ITEM
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_MOVE_ITEM; pk.moveItem = TSubPacketCGShopMoveItem{0, 1}; out.emplace_back(SerializePacket(pk));
#endif
// CHANGE_NAME
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_CHANGE_NAME; pk.changeName = TSubPacketCGShopChangeName{"Name"}; out.emplace_back(SerializePacket(pk));
// FILTER_WITH_VNUM_REQUEST
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_FILTER_WITH_VNUM_REQUEST; pk.filterWithVnumRequest = TSubPacketCGFilterWithVnumRequest{0}; out.emplace_back(SerializePacket(pk));
// FILTER_WITH_SELLER_REQUEST
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_FILTER_WITH_SELLER_REQUEST; pk.filterWithSellerRequest = TSubPacketCGFilterWithSellerRequest{0}; out.emplace_back(SerializePacket(pk));
// FILTER_WITH_SELLER_NAME_REQUEST
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_FILTER_WITH_SELLER_NAME_REQUEST; pk.filterWithSellerNameRequest = TSubPacketCGFilterWithSellerNameRequest{"Seller"}; out.emplace_back(SerializePacket(pk));
// BUY_ALL_ITEMS
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_SHOP_BUY_ALL_ITEMS; {
TSubPacketCGShopBuyAllItems b{}; b.itemcount = 0; b.seenprice = 0; pk.buyAllItems = b; out.emplace_back(SerializePacket(pk));
}
// TELEPORT_TO_SHOP
pk = TPacketCGIkarusShop{}; pk.subheader = SUBHEADER_CG_TELEPORT_TO_SHOP; pk.teleportToShop = TSubPacketCGTeleportToShop{0, 0}; out.emplace_back(SerializePacket(pk));
break;
}
#endif // ENABLE_IKASHOP_RENEWAL
default:
break;
}
return out;
}
}
namespace fuzz
{
void Run(DESC* desc, const FuzzOptions& opts)
{
if (!desc)
return;
auto* proc = desc->GetInputProcessor();
if (!proc)
{
SPDLOG_ERROR("Fuzzer: no input processor for desc {}", desc->GetHandle());
return;
}
auto blns = LoadNaughtyStrings(opts.naughtyStringsPath);
std::mt19937 rng;
if (opts.seed != 0)
rng.seed(opts.seed);
else
rng.seed(static_cast<uint32_t>(std::random_device{}()));
std::vector<uint16_t> ids;
if (!opts.headerIds.empty())
{
ids = opts.headerIds;
}
else
{
ids.reserve(256);
for (uint16_t i = 0; i < 256; ++i) ids.push_back(i);
}
auto edgeCases = EdgeCasePayloads(opts.maxPayloadSize);
for (auto id : ids)
{
// Try edge cases first
for (const auto& ec : edgeCases)
{
try
{
PacketHeader h = MakeHeader(id, static_cast<uint32_t>(ec.size()));
const auto computedHash = XXH32(ec.data(), ec.size(), PacketHeader::kHashSeed);
h.hash = computedHash;
auto wire = BuildWirePacket(h, ec);
const_buffer fullBuf(wire.data(), wire.size());
const_buffer dataBuf = fullBuf + sizeof(PacketHeader);
(void)proc->Analyze(desc, h, dataBuf);
}
catch (const std::exception& ex)
{
SPDLOG_ERROR("Fuzzer edge case id {} failed: {}", id, ex.what());
}
}
// Structured cases for known packets
if (opts.enableStructured)
{
auto structured = BuildStructuredPayloads(id, opts.unicodeHeavy);
for (auto& sc : structured)
{
try
{
PacketHeader h = MakeHeader(id, static_cast<uint32_t>(sc.size()));
h.hash = XXH32(sc.data(), sc.size(), PacketHeader::kHashSeed);
auto wire = BuildWirePacket(h, sc);
const_buffer fullBuf(wire.data(), wire.size());
const_buffer dataBuf = fullBuf + sizeof(PacketHeader);
(void)proc->Analyze(desc, h, dataBuf);
}
catch (const std::exception& ex)
{
SPDLOG_ERROR("Fuzzer structured id {} failed: {}", id, ex.what());
}
}
// Inject BLNS (naughty strings) into known string-bearing packets
if (opts.enableNaughtyStrings)
{
if (!blns.empty())
{
uint32_t injected = 0;
// Chat message
if (id == HEADER_CG_CHAT)
{
for (auto& s : blns)
{
if (injected++ >= opts.maxNaughtyPerField) break;
TPacketCGChat c{}; c.type = 0; c.lang = 0; c.message = s;
auto sp = SerializePacket(c);
try {
PacketHeader h = MakeHeader(id, static_cast<uint32_t>(sp.size()));
h.hash = XXH32(sp.data(), sp.size(), PacketHeader::kHashSeed);
auto wire = BuildWirePacket(h, sp);
const_buffer fullBuf(wire.data(), wire.size());
const_buffer dataBuf = fullBuf + sizeof(PacketHeader);
(void)proc->Analyze(desc, h, dataBuf);
} catch(const std::exception& ex) {
SPDLOG_ERROR("Fuzzer BLNS chat failed: {}", ex.what());
}
}
}
// Whisper (name + message)
if (id == HEADER_CG_WHISPER)
{
injected = 0;
for (auto& s : blns)
{
if (injected++ >= opts.maxNaughtyPerField) break;
TPacketCGWhisper w{}; w.szNameTo = s; w.message = s;
auto sp = SerializePacket(w);
try {
PacketHeader h = MakeHeader(id, static_cast<uint32_t>(sp.size()));
h.hash = XXH32(sp.data(), sp.size(), PacketHeader::kHashSeed);
auto wire = BuildWirePacket(h, sp);
const_buffer fullBuf(wire.data(), wire.size());
const_buffer dataBuf = fullBuf + sizeof(PacketHeader);
(void)proc->Analyze(desc, h, dataBuf);
} catch(const std::exception& ex) {
SPDLOG_ERROR("Fuzzer BLNS whisper failed: {}", ex.what());
}
}
}
}
}
}
// Arbitrary meaningless payloads irrespective of expected struct
{
auto anyPayloads = BuildArbitraryPayloads(opts.maxPayloadSize, rng);
for (auto& ap : anyPayloads)
{
try
{
PacketHeader h = MakeHeader(id, static_cast<uint32_t>(ap.size()));
h.hash = XXH32(ap.data(), ap.size(), PacketHeader::kHashSeed);
auto wire = BuildWirePacket(h, ap);
const_buffer fullBuf(wire.data(), wire.size());
const_buffer dataBuf = fullBuf + sizeof(PacketHeader);
(void)proc->Analyze(desc, h, dataBuf);
}
catch (const std::exception& ex)
{
SPDLOG_ERROR("Fuzzer arbitrary id {} failed: {}", id, ex.what());
}
}
}
// Random iterations
for (uint32_t it = 0; it < opts.iterationsPerId; ++it)
{
try
{
auto payload = RandomPayload(rng, opts.maxPayloadSize);
if (opts.enableMutations)
{
auto variants = MutatePayloads(payload, rng, opts.maxMutationsPerBase);
for (auto& v : variants)
{
PacketHeader h2 = MakeHeader(id, static_cast<uint32_t>(v.size()));
h2.hash = XXH32(v.data(), v.size(), PacketHeader::kHashSeed);
auto wire2 = BuildWirePacket(h2, v);
const_buffer fb(wire2.data(), wire2.size());
const_buffer db = fb + sizeof(PacketHeader);
(void)proc->Analyze(desc, h2, db);
}
}
PacketHeader h = MakeHeader(id, static_cast<uint32_t>(payload.size()));
const auto computedHash = XXH32(payload.data(), payload.size(), PacketHeader::kHashSeed);
h.hash = computedHash;
auto wire = BuildWirePacket(h, payload);
const_buffer fullBuf(wire.data(), wire.size());
const_buffer dataBuf = fullBuf + sizeof(PacketHeader);
(void)proc->Analyze(desc, h, dataBuf);
}
catch (const std::exception& ex)
{
SPDLOG_ERROR("Fuzzer random id {} failed: {}", id, ex.what());
}
}
}
SPDLOG_INFO("Fuzzer finished.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment