TSecType — 防作弊安全型別
本篇定位
這是 Phase 9 的第 6 篇,也是最關鍵的安全型別解析。 讀完本篇你會了解:為什麼用 Cheat Engine 找到的 HP 位址直接改值會讓遊戲崩潰,以及如何用正確的方式讀寫遊戲的加密數值。
一個現實問題:Cheat Engine 找到的 HP 是假的
如果你曾嘗試用 Cheat Engine 掃描 MapleStory 的 HP,你可能發現:
- 搜尋到一個地址,顯示值是 9999
- 你改成 1
- 遊戲沒有死,有時候直接崩潰,有時候什麼都沒發生
原因:你改的不是 HP,你改的是 HP 的加密版本。
想像這個情境
銀行保險庫的門上有一個「假把手」——看起來像把手,摸起來像把手,但轉動它什麼都不會發生(或是觸發警報)。真正的開門機制在別處,而且是加密的。
TSecType<T>就是那個假把手。遊戲把重要數值藏在一層加密後面,讓外部的修改工具(包括 Cheat Engine)無法直接生效。
為什麼不能直接讀寫 HP/MP/座標?
MapleStory 對所有重要的數值(HP、MP、玩家座標、物品數量等)都使用 TSecType<T> 加密儲存。直接用 Cheat Engine 找到的「HP 位址」其實存的是加密後的值,不是你看到的數字。
直接寫入加密欄位 = 立即崩潰
若不透過
SetData()/GetData()操作,而是直接對TSecType的記憶體位址寫入原始數值,checksum 驗證失敗後,GetData()回傳NULL,幾乎必然導致遊戲崩潰或無聲的防作弊觸發。
TSecData — 加密資料結構
template <typename T>
class TSecData {
public:
T data; // 加密後的數值(不是原始值!)
BYTE bKey; // XOR 加密金鑰
BYTE FakePtr1; // 取自外層 TSecType::FakePtr1 的低位元組
BYTE FakePtr2; // 取自外層 TSecType::FakePtr2 的低位元組
WORD wChecksum; // 完整性驗證碼
};
// sizeof(TSecData<long>) == 0x0C(12 bytes)TSecType — 安全數值包裝器
template <typename T>
class TSecType {
private:
DWORD FakePtr1; // 隨機 DWORD(初始化時 rand())
DWORD FakePtr2; // 隨機 DWORD(初始化時 rand())
TSecData<T>* m_secdata; // 指向加密資料
};
// sizeof(TSecType<long>) == 0x0C(12 bytes)FakePtr1 / FakePtr2 的低位元組(LOBYTE)會存入 m_secdata,作為指標驗證:如果有人複製記憶體區塊,FakePtr 的值不會匹配,GetData() 就能偵測到篡改。
加密演算法(SetData)
void SetData(T data) {
m_secdata->bKey = LOBYTE(rand()); // 每次寫入都重新產生隨機 key
// 初始 checksum 依型別大小設定
m_secdata->wChecksum = sizeof(T) > 1 ? 39525 : (WORD)(-26011);
for (BYTE i = 0, key = m_secdata->bKey; i < sizeof(T) + 1; i++) {
if (i > 0) {
// key 根據上一個明文位元組滾動演進
key = (key ^ plaintext_byte[i-1]) + key + 42;
// checksum 用循環移位更新
wChecksum = (8 * wChecksum) | (key + (wChecksum >> 13));
}
if (i < sizeof(T)) {
if (!key) key = 42; // key 不能為 0
encrypted[i] = plaintext[i] ^ key; // XOR 加密
}
}
}關鍵特徵:
- 滾動 XOR:每個位元組的 key 都基於前一個位元組計算,不是固定 key
- 位置依賴:加密結果與每個位元組在數值中的位置有關
- 每次寫入重新加密:
rand()的 key 讓靜態掃描無法預測
解密演算法(GetData)
T GetData() {
T decrypted;
WORD wChecksum = 0;
for (BYTE i = 0, key = m_secdata->bKey; i < sizeof(T) + 1; i++) {
if (i > 0) {
key = encrypted[i-1] + key + 42; // 注意:用加密值而非明文滾動 key
wChecksum = i > 1
? (8 * wChecksum) | (key + (wChecksum >> 13))
: (key + 4) | 0xD328;
}
if (i < sizeof(T)) {
if (!key) key = 42;
decrypted[i] = encrypted[i] ^ key; // XOR 解密
}
}
// 三重驗證
if (m_secdata->wChecksum != wChecksum
|| LOBYTE(FakePtr1) != m_secdata->FakePtr1
|| LOBYTE(FakePtr2) != m_secdata->FakePtr2)
{
return NULL; // 完整性驗證失敗 → 回傳 0
}
return decrypted;
}運算子多載(用起來像普通數值)
TSecType<long> hp(100);
long current = hp; // operator T() → GetData()
hp = 200; // operator=(T) → SetData()
hp += 50; // operator+= → GetData() + 50 → SetData()
hp -= 10; // operator-=
hp *= 2; // operator*=
hp /= 3; // operator/=
bool same = (hp == &hp2); // operator==SECPOINT — 安全座標型別
class SECPOINT {
public:
TSecType<long> y; // Y 座標(加密儲存)
TSecType<long> x; // X 座標(加密儲存)
};MapleStory 的玩家、怪物座標都用 SECPOINT 儲存,並非直接可讀的整數。
// 讀取玩家座標
SECPOINT* pos = reinterpret_cast<SECPOINT*>(playerBase + POSITION_OFFSET);
long x = pos->x.GetData();
long y = pos->y.GetData();
// 設定玩家座標(DLL 中)
pos->x.SetData(500);
pos->y.SetData(300);ZtlSecure — Thread-local 安全型別
ZtlSecure.h 提供另一種更輕量的加密,使用 ZtlSecureTear(寫入)和 ZtlSecureFuse(讀取):
#define ZTLSECURE_CHECKSUM 0xBAADF00D
#define ZTLSECURE_ROTATION 5
// 加密(tear):產生 key + 加密資料,回傳 checksum
unsigned int checksum = ZtlSecureTear(&storage, value);
// 解密(fuse):驗證 checksum,回傳明文
T value = ZtlSecureFuse(&storage, checksum);ZtlSecure vs TSecType 差異:
| TSecType | ZtlSecure | |
|---|---|---|
| 用途 | 長期存活的物件欄位(HP/MP) | 短期的 stack/register 數值 |
| checksum 存放 | 在 TSecData 內 | 由呼叫者保管 |
| 再加密時機 | 每次 SetData() | 每次 Tear() |
如何在 DLL 中安全修改遊戲數值
// ❌ 錯誤:直接寫記憶體 → checksum 失效 → 崩潰
*(long*)(playerBase + HP_OFFSET) = 9999;
// ✅ 正確:透過 TSecType API
TSecType<long>* pHP = reinterpret_cast<TSecType<long>*>(playerBase + HP_OFFSET);
pHP->SetData(9999);
// ✅ 正確:讀取時
long currentHP = pHP->GetData();本篇重點整理
讀完這篇你應該記住的事
- TSecType 不是普通整數:遊戲裡的 HP/MP/座標都被加密存放
- 一定要用 GetData() / SetData():直接讀寫記憶體必崩潰
- SECPOINT 是座標:x 和 y 都是
TSecType<long>,不是 int- Cheat Engine 找到的值是假的:那是加密後的版本,不能直接改
相關筆記
- 03_AddyLocations.h_地址表解析 — 取得 MapleStory 物件的位址(Phase 3)
- 04_AutoTypes.h_MapleStory型別定義 — 其他 MapleStory 型別定義(Phase 3)
- 01_MapleClientCollectionTypes目錄概覽 — Phase 9 目錄總覽