《Deadlock》區域偽裝器 快速閱讀精華
🚀 想跟國外朋友組隊或跳過特定地區配隊?這款區域偽裝器幫你強制指定連線區域 💪 運作原理:DLL注入遊戲 程序,攔截封包並即時修改區域代碼 🔑 核心特色 :
支援6大地區:全球、歐洲、東南亞、南美洲、俄羅斯、大洋洲 熱更新設定:遊戲內直接修改 config.json 無需重啟 即時生效:儲存後 0.5 秒內自動載入新設定 ⚠️ 重要提醒 :僅限離線練習或私人對戰使用,線上競技有帳號風險
本文章目錄
前言介紹
《Deadlock》目前採用區域分流配對機制,玩家預設會被分配到延遲最低的地區伺服器。但這也帶來一個困擾:想跟國外朋友同場競技,卻被系統拆散到不同區域;或是特定時段某些地區人數過少,配隊等很久。
這款區域偽裝器透過DLL注入技術 ,在遊戲發送配對請求時即時修改封包內的區域代碼,讓伺服器誤以為你身處指定地區。最棒的是設定檔支援熱更新 ——遊戲開著也能改區域,完全不用重啟程式。
支援區域代碼一覽
代碼 區域名稱 適用情境 0 ROW(全球/不限) 想最大化配對池,縮短等待時間 1 Europe(歐洲) 連線歐服與當地玩家對戰 2 SEAsia(東南亞) 亞洲時段玩家集中連線 3 SAmerica(南美洲) 美洲時段或特定社羣活動 4 Russia(俄羅斯) 預設值,俄區獨立配對池 5 Oceania(大洋洲) 澳洲紐西蘭玩家專用
安裝與注入教學
注入步驟
準備任意一款DLL注入器(推薦 GG修改器免root權限使用+GG修改器框架下載 或常見的 Process Hacker、Extreme Injector 等工具) 下載 RegionSpoof.dll 檔案(見文末下載點) 將 DLL 與自訂的 config.json 放在同一資料夾 內 啟動《Deadlock》並進入大廳 使用注入器將 RegionSpoof.dll 注入 client.dll 所在的進程 注入成功後會自動產生 region_spoof.log 記錄檔供除錯
👉 GM後台版 遊戲 推薦 ⬇️⬇️⬇️ 快速玩各種二次元動漫手遊app
設定檔說明
注入成功後,工具會在 DLL 所在目錄尋找 config.json ,格式極簡:
region :填入 0–5 的整數,對應上方區域代碼表預設值為 4(俄羅斯) ,首次使用記得改成你要的地區 遊戲進行中直接編輯存檔,0.5 秒內自動生效 ——完全不用重開
核心源碼整理
底下是完整 C++ 源碼,採用 MinHook 函數攔截框架。核心邏輯分為三步:定位 BSend 發送函數、整理 Protobuf 封包結構、即時覆寫 region_mode 欄位。
// Regions (ECitadelRegionMode):
// 0 ROW, 1 Europe, 2 SEAsia, 3 SAmerica, 4 Russia, 5 Oceania
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <cstdarg>
#include <cstdlib>
#include <atomic>
#include <vector>
#include "MinHook/MinHook.h"
namespace
{
constexpr uint32_t kMsgStartMM = 9010;
constexpr const char* kBSendSig =
"48 89 5C 24 ? 48 89 6C 24 ? 56 41 56 41 57 48 83 EC ? 8B DA "
"48 8B F1 8B 89 54 02 00 00 0F BA F3 1F";
using FnBSend = unsigned char (__fastcall*)(void* self, unsigned int msgType, void* buf, unsigned int size);
FnBSend g_oBSend = nullptr;
std::atomic<int> g_region{ 4 };
std::atomic<bool> g_ready{ false };
wchar_t g_configPatd[MAX_PATH]{};
wchar_t g_logPatd[MAX_PATH]{};
FILETIME g_lastWrite{};
CRITICAL_SECTION g_cs;
void Log(const char* fmt, ...)
{
char line[512];
va_list ap;
va_start(ap, fmt);
vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
OutputDebugStringA(line);
OutputDebugStringA("");
EnterCriticalSection(&g_cs);
FILE* f = nullptr;
if (_wfopen_s(&f, g_logPatd, L"a") == 0 && f)
{
fputs(line, f);
fputc('', f);
fclose(f);
}
LeaveCriticalSection(&g_cs);
}
bool GetDllDir(wchar_t* out, size_t cch)
{
HMODULE hm = nullptr;
if (!GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&GetDllDir), &hm))
return false;
wchar_t patd[MAX_PATH]{};
if (!GetModuleFileNameW(hm, patd, MAX_PATH))
return false;
wchar_t* slash = wcsrchr(patd, L'\\');
if (!slash) return false;
slash[1] = 0;
wcsncpy_s(out, cch, patd, _TRUNCATE);
return true;
}
bool ParseRegionFromJson(const char* text, int* outRegion)
{
if (!text || !outRegion) return false;
const char* p = strstr(text, "region");
if (!p) return false;
p = strchr(p, ':');
if (!p) return false;
++p;
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '') ++p;
char* end = nullptr;
long v = strtol(p, &end, 10);
if (end == p) return false;
if (v < 0) v = 0;
if (v > 255) v = 255;
*outRegion = static_cast<int>(v);
return true;
}
void ReloadConfig(bool force)
{
WIN32_FILE_ATTRIBUTE_DATA fad{};
if (!GetFileAttributesExW(g_configPatd, GetFileExInfoStandard, &fad))
return;
if (!force &&
fad.ftLastWriteTime.dwLowDateTime == g_lastWrite.dwLowDateTime &&
fad.ftLastWriteTime.dwHighDateTime == g_lastWrite.dwHighDateTime)
return;
g_lastWrite = fad.ftLastWriteTime;
FILE* f = nullptr;
if (_wfopen_s(&f, g_configPatd, L"rb") != 0 || !f)
return;
char buf[2048]{};
const size_t n = fread(buf, 1, sizeof(buf) - 1, f);
fclose(f);
buf[n] = 0;
int region = 0;
if (ParseRegionFromJson(buf, ®ion))
{
const int prev = g_region.exchange(region);
if (prev != region || force)
Log("[RegionSpoof] config region=%d (was %d)", region, prev);
}
}
bool ReadVarint(const uint8_t* p, const uint8_t* end, uint64_t* out, const uint8_t** next)
{
uint64_t v = 0;
int shift = 0;
const uint8_t* cur = p;
while (cur < end && shift < 64)
{
const uint8_t b = *cur++;
v |= static_cast<uint64_t>(b & 0x7F) << shift;
if ((b & 0x80) == 0)
{
*out = v;
*next = cur;
return true;
}
shift += 7;
}
return false;
}
bool WriteVarint1(uint8_t* p, uint32_t v)
{
if (v > 0x7F) return false;
*p = static_cast<uint8_t>(v);
return true;
}
bool PatchRegionInBody(uint8_t* body, size_t bodySize, uint32_t newRegion)
{
if (!body || bodySize < 4 || newRegion > 0x7F)
return false;
const uint8_t* end = body + bodySize;
uint8_t* p = body;
while (p < end)
{
uint64_t tag = 0;
const uint8_t* next = nullptr;
if (!ReadVarint(p, end, &tag, &next))
break;
const uint32_t field = static_cast<uint32_t>(tag >> 3);
const uint32_t wt = static_cast<uint32_t>(tag & 7);
p = const_cast<uint8_t*>(next);
if (wt == 0)
{
uint64_t dummy = 0;
if (!ReadVarint(p, end, &dummy, &next)) break;
p = const_cast<uint8_t*>(next);
}
else if (wt == 1)
{
if (p + 8 > end) break;
p += 8;
}
else if (wt == 5)
{
if (p + 4 > end) break;
p += 4;
}
else if (wt == 2)
{
uint64_t len = 0;
if (!ReadVarint(p, end, &len, &next)) break;
p = const_cast<uint8_t*>(next);
if (p + len > end) break;
if (field == 3)
{
uint8_t* mi = p;
uint8_t* miEnd = p + static_cast<size_t>(len);
while (mi < miEnd)
{
uint64_t t2 = 0;
const uint8_t* n2 = nullptr;
if (!ReadVarint(mi, miEnd, &t2, &n2)) break;
const uint32_t f2 = static_cast<uint32_t>(t2 >> 3);
const uint32_t w2 = static_cast<uint32_t>(t2 & 7);
uint8_t* afterTag = const_cast<uint8_t*>(n2);
if (w2 == 0)
{
uint64_t val = 0;
const uint8_t* n3 = nullptr;
if (!ReadVarint(afterTag, miEnd, &val, &n3)) break;
if (f2 == 8)
{
if (n3 == afterTag + 1)
{
const uint32_t old = static_cast<uint32_t>(val);
if (WriteVarint1(afterTag, newRegion))
{
Log("[RegionSpoof] patched region_mode %u -> %u", old, newRegion);
return true;
}
}
else
{
Log("[RegionSpoof] region varint multi-byte, skip");
}
return false;
}
mi = const_cast<uint8_t*>(n3);
}
else if (w2 == 2)
{
uint64_t l2 = 0;
if (!ReadVarint(afterTag, miEnd, &l2, &n2)) break;
mi = const_cast<uint8_t*>(n2) + static_cast<size_t>(l2);
}
else if (w2 == 1)
{
mi = afterTag + 8;
}
else if (w2 == 5)
{
mi = afterTag + 4;
}
else
{
break;
}
}
Log("[RegionSpoof] match_info present but region field not found");
return false;
}
p += static_cast<size_t>(len);
}
else
{
break;
}
}
Log("[RegionSpoof] match_info field not found in body");
return false;
}
bool PatchStartMatchmakingBuffer(void* buf, unsigned int size, uint32_t region)
{
if (!buf || size < 12)
return false;
auto* p = static_cast<uint8_t*>(buf);
uint32_t type = 0, hdrSize = 0;
memcpy(&type, p, 4);
memcpy(&hdrSize, p + 4, 4);
if ((type & 0x7FFFFFFFu) != kMsgStartMM)
return false;
if (8u + hdrSize >= size)
return false;
uint8_t* body = p + 8 + hdrSize;
const size_t bodySize = size - 8u - hdrSize;
return PatchRegionInBody(body, bodySize, region);
}
unsigned char __fastcall hk_BSend(void* self, unsigned int msgType, void* buf, unsigned int size)
{
ReloadConfig(false);
const uint32_t id = msgType & 0x7FFFFFFFu;
if (id == kMsgStartMM && buf && size)
{
const int region = g_region.load();
if (PatchStartMatchmakingBuffer(buf, size, static_cast<uint32_t>(region)))
{
}
else
{
}
}
return g_oBSend ? g_oBSend(self, msgType, buf, size) : 0;
}
struct SigByte
{
uint8_t value = 0;
bool any = false;
};
bool ParseSig(const char* sig, std::vector<SigByte>& out)
{
out.clear();
if (!sig) return false;
const char* p = sig;
while (*p)
{
while (*p == ' ' || *p == '\t') ++p;
if (!*p) break;
SigByte b{};
if (*p == '?')
{
b.any = true;
++p;
if (*p == '?') ++p;
}
else
{
char* end = nullptr;
const unsigned long v = strtoul(p, &end, 16);
if (end == p || v > 0xFF) return false;
b.value = static_cast<uint8_t>(v);
p = end;
}
out.push_back(b);
}
return !out.empty();
}
void* FindPattern(HMODULE mod, const char* sig)
{
if (!mod || !sig) return nullptr;
std::vector<SigByte> pat;
if (!ParseSig(sig, pat))
{
Log("[RegionSpoof] bad signature string");
return nullptr;
}
auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(mod);
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return nullptr;
auto* nt = reinterpret_cast<IMAGE_NT_HEADERS*>(
reinterpret_cast<uint8_t*>(mod) + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) return nullptr;
const auto* sec = IMAGE_FIRST_SECTION(nt);
for (unsigned i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++sec)
{
if ((sec->Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0)
continue;
auto* start = reinterpret_cast<uint8_t*>(mod) + sec->VirtualAddress;
const size_t size = sec->Misc.VirtualSize;
if (size < pat.size()) continue;
const size_t last = size - pat.size();
for (size_t off = 0; off <= last; ++off)
{
bool ok = true;
for (size_t j = 0; j < pat.size(); ++j)
{
if (pat[j].any) continue;
if (start[off + j] != pat[j].value)
{
ok = false;
break;
}
}
if (ok)
return start + off;
}
}
return nullptr;
}
bool InstallHook()
{
HMODULE client = nullptr;
for (int i = 0; i < 120 && !client; ++i)
{
client = GetModuleHandleA("client.dll");
if (!client)
Sleep(100);
}
if (!client)
{
Log("[RegionSpoof] client.dll not found");
return false;
}
void* target = FindPattern(client, kBSendSig);
if (!target)
{
Log("[RegionSpoof] BSend signature not found");
return false;
}
const auto rva = static_cast<unsigned>(
reinterpret_cast<uintptr_t>(target) - reinterpret_cast<uintptr_t>(client));
Log("[RegionSpoof] BSend found @ client+0x%X (%p)", rva, target);
if (MH_Initialize() != MH_OK)
{
Log("[RegionSpoof] MH_Initialize failed");
return false;
}
if (MH_CreateHook(target, reinterpret_cast<LPVOID>(&hk_BSend),
reinterpret_cast<LPVOID*>(&g_oBSend)) != MH_OK)
{
Log("[RegionSpoof] MH_CreateHook failed");
return false;
}
if (MH_EnableHook(target) != MH_OK)
{
Log("[RegionSpoof] MH_EnableHook failed");
return false;
}
Log("[RegionSpoof] hooked BSend (sig), region=%d", g_region.load());
g_ready = true;
return true;
}
DWORD WINAPI MainThread(LPVOID)
{
InitializeCriticalSection(&g_cs);
wchar_t dir[MAX_PATH]{};
if (!GetDllDir(dir, MAX_PATH))
{
OutputDebugStringA("[RegionSpoof] GetDllDir failed");
return 0;
}
_snwprintf_s(g_configPatd, _TRUNCATE, L"%sconfig.json", dir);
_snwprintf_s(g_logPatd, _TRUNCATE, L"%sregion_spoof.log", dir);
Log("[RegionSpoof] starting, config=%ls", g_configPatd);
ReloadConfig(true);
if (!InstallHook())
{
Log("[RegionSpoof] hook install failed");
return 0;
}
while (true)
{
ReloadConfig(false);
Sleep(500);
}
return 0;
}
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
HANDLE td = CreateThread(nullptr, 0, MainThread, nullptr, 0, nullptr);
if (td) CloseHandle(td);
}
return TRUE;
}
檔案下載點
所有站內附件皆會附上安全掃描報告 請會員查看純淨度百分比後判斷使用 相關檔案須知: 取得檔案前,請先詳細閱讀文章內容 避免不必要錯誤與誤會發生。 也可多參考文章討論樓層內容 了解附件檔案相關討論資訊。
常見問題Q&A
Q:注入後遊戲閃退怎麼辦?
確認注入時機——必須等 client.dll 完全載入後再注入,建議進到大廳畫面再操作。若仍閃退,檢查注入器是否以管理員身分執行。
Q:修改 config.json 後沒反應?
設定檔必須與 DLL 放在同一資料夾 ,且編碼為 UTF-8 無 BOM 格式。儲存後最慢 0.5 秒會生效,可觀察 region_spoof.log 確認是否有載入記錄。
Q:可以指定特定國家而非大區嗎?
不行,這款工具僅支援 Valve 定義的 6 個 ECitadelRegionMode 大區,無法細分到國家層級。
Q:使用這個會被VAC封鎖嗎?
《Deadlock》目前尚未啟用 VAC,但任何記憶體修改行為都有風險。強烈建議僅用於離線練習或私人對戰,正式競技模式請勿使用。
Q:遊戲更新後失效怎麼辦?
Valve 更新可能改變 BSend 函數位址,導致特徵碼匹配失敗。需等待作者釋出新版 DLL,或自行用除錯工具重新定位函數簽名。
Q:Mac 或 Steam Deck 能用嗎?
這是 Windows 專用的 DLL 注入方案,Mac 與 Linux 環境不支援。Steam Deck 的 Proton 層也可能無法正常運作。