Upgrade to Pro — share decks privately, control downloads, hide ads and more …

那些年我看過的反外掛爛梗

adr
March 31, 2018

 那些年我看過的反外掛爛梗

線上遊戲是許多人的童年,許多人在線上遊戲看到外掛只覺得他們破壞遊戲平衡,讓遊戲變得不公平、不再有趣,
而有些人則投入外掛的懷抱,成為外掛使用者一員。今天將以台灣最知名紅遍大街小巷的線上遊戲作為舉例,
以外掛開發者的立場做分析線上遊戲,分享一些常見的分析遊戲弱點技巧、遊戲架構設計帶來的隱憂、
與這樣的分析技巧曾經成功挖掘出哪些漏洞,解析以往這些看似黑科技的外掛技術如何被挖掘出來的。

議程末將會簡單的介紹一些反外掛技巧,對於記憶體層面的攻防如何讓駭客難以分析與破解的開發者思路。

adr

March 31, 2018
Tweet

More Decks by adr

Other Decks in Technology

Transcript

  1. [email protected]
    那些年年我看過的
    反外掛爛梗

    View Slide

  2. [email protected]
    ./Bio✨
    • ⾺馬聖豪, aaaddress1 aka adr
    • Chroot, TDOH
    • 精通 C/C++、Windows 特性、逆向⼯工程
    • Speaker: BlackHat Asia Arsenal 2018
    HITCON CMT 2015
    HITCON CMT 2016 Lightning
    HITCON CMT 2017
    SITCON 2016
    SITCON 2017
    iThome#Chatbot 2017
    BSidesLV 2016
    ICNC'17
    資訊安全基礎技術⼯工作坊
    資安實務攻防研習營

    View Slide

  3. 這是今天的當事⼈人
    為了了受害者權益,已經打上⾺馬賽克

    View Slide

  4. 沒有任何技術,
    我今天是來來講脫⼝口秀的
    都是⼀一些網路路上就可以搜集來來老歷史。我怕被吉

    View Slide

  5. [email protected]
    記憶體攻防戰
    • 線上遊戲怎麼運作的
    • 駭客如何分析線上遊戲
    1. 動態分析: OllyDBG, Cheat Engine
    2. 靜態分析: IDA Pro
    • 常⾒見見的遊戲保護⽅方式
    3. 產品保護?套殼就對啦!
    4. 套殼不夠?我上混淆跟虛擬機!
    5. 虛擬機不夠難?我⾃自幹整個 Ring0 防護
    • 通常如何解決那些保護
    • 傳說的⿊黑魔法怎麼被做出來來的?

    View Slide

  6. 先聊聊
    線上遊戲怎麼運作der?

    View Slide

  7. [email protected]
    boringGame.c
    int nowHp = 100;
    void getHurt(int HpCost) { nowHp -= HpCost; }
    int main(void) {
    char buf[0x30];
    while (1) {
    printf(
    "[1] hp -= 10\n[2] exit()\nopcode: ");
    gets(buf);
    if (buf[0] == '1') getHurt(10);
    else if (buf[0] == '2') break;
    else puts(":/ unknown opcode");
    printf("your hp: %d\n\n", nowHp);
    if (nowHp < 1) {
    puts("you die!");
    break;
    }
    }
    return 0;
    }

    View Slide

  8. [email protected]
    boringGame.c

    View Slide

  9. 我知道這時你會想說
    可是這只是單機遊戲啊(對啊!對啊!)

    View Slide

  10. [email protected]
    客⼾戶端為主遊戲架構
    int nowHp = 100;
    int main(void) {
    char buf[0x30];
    while (1) {
    printf("[1] hp -= 10\n[2] exit()\nopcode: ");
    gets(buf);
    if (buf[0] == '1') sendHurtCost(10);
    else if (buf[0] == '2') break;
    else puts(":/ unknown opcode");
    displyPalyerData(recvAllPlayerData());
    if (nowHp < 1) {
    puts("you die!");
    break;
    }
    }
    return 0;
    }

    View Slide

  11. [email protected]
    伺服端為主遊戲架構
    int nowHp = 100;
    int main(void) {
    char buf[0x30];
    while (1) {
    printf("[1] hp -= 10\n[2] exit()\nopcode: ");
    gets(buf);
    if (buf[0] == '1') iNeedToGetHurt();
    else if (buf[0] == '2') break;
    else puts(":/ unknown opcode");
    displyPalyerData(recvAllPlayerData());
    nowHp = recvMyHpData();
    if (nowHp < 1) {
    puts("you die!");
    break;
    }
    }
    return 0;
    }

    View Slide

  12. [email protected]
    架構⾯面來來說(客⼾戶為主)
    客⼾戶端
    伺服端
    現在有誰在線上啊?
    同張地圖
    玩家資訊
    欸欸,
    經過我計算結果
    本次要損⾎血 10 滴
    好ㄛ
    我知道ㄌ
    我要攻擊
    那隻 id 為 1 的怪物
    好ㄛ攻擊成功!
    你剛剛打他造成 10 點傷害

    View Slide

  13. [email protected]
    架構⾯面來來說(客⼾戶為主)
    客⼾戶端
    伺服端
    現在有誰在線上啊?
    同張地圖
    玩家資訊
    欸欸,
    經過我計算結果
    本次要損⾎血 10 滴
    好ㄛ
    我知道ㄌ
    我要攻擊
    那隻 id 為 1 的怪物
    好ㄛ攻擊成功!
    你剛剛打他造成 10 點傷害

    View Slide

  14. [email protected]
    架構⾯面來來說(伺服為主)
    客⼾戶端
    伺服端
    現在有誰在線上啊?
    同張地圖
    玩家資訊
    ㄟ,
    我現在狀狀況怎麼樣?
    你剛剛被BOSS海海K三下
    經由你裝備防護後
    你扣了了 1337 滴⾎血
    現在⾎血量量 50 滴
    ㄟ我要打那隻
    id 為 1 的怪物
    ㄜ...你只造成ㄌ
    1 點傷害

    View Slide

  15. [email protected]
    架構⾯面來來說(伺服為主)
    客⼾戶端
    伺服端
    現在有誰在線上啊?
    同張地圖
    玩家資訊
    ㄟ,
    我現在狀狀況怎麼樣?
    你剛剛被BOSS海海K三下
    經由你裝備防護後
    你扣了了 1337 滴⾎血
    現在⾎血量量 50 滴
    ㄟ我要打那隻
    id 為 1 的怪物
    ㄜ...你只造成ㄌ
    1 點傷害

    View Slide

  16. 聽你⼀一本正經地講廢話
    所以呢?知道這架構可以幹嘛?

    View Slide

  17. [email protected]
    boringGame.c
    int nowHp = 100;
    void getHurt(int HpCost) { nowHp -= HpCost; }
    int main(void) {
    char buf[0x30];
    while (1) {
    printf(
    "[1] hp -= 10\n[2] exit()\nopcode: ");
    gets(buf);
    if (buf[0] == '1') getHurt(10);
    else if (buf[0] == '2') break;
    else puts(":/ unknown opcode");
    printf("your hp: %d\n\n", nowHp);
    if (nowHp < 1) {
    puts("you die!");
    break;
    }
    }
    return 0;
    }

    View Slide

  18. [email protected]
    boringGame.c

    View Slide

  19. [email protected]
    Cheat Engine

    View Slide

  20. [email protected]
    Cheat Engine

    View Slide

  21. [email protected]
    Cheat Engine

    View Slide

  22. [email protected]
    Cheat Engine

    View Slide

  23. [email protected]
    Cheat Engine

    View Slide

  24. View Slide

  25. View Slide

  26. [email protected]
    線上遊戲外掛策略略
    1. 盡可能分析封包怎麼發送的
    • connect()、send()、recv()
    2. 不會分析封包?⼭山不轉路路轉,分析程式邏輯
    3. 若若有資料是在客⼾戶端算完才送出,嘗試篡改它
    • 怪物出⽣生座標、攻擊距離、損⾎血量量、是否要損⾎血、要換去哪個頻道
    • 吸怪、全圖打、MISS式無敵、完全無敵、⾃自動換頻、⾃自動過圖、全圖吸寶
    4. 無法竄改?我模擬單⼀一個攻擊封包狂丟
    • 吃我攻擊無延遲啦

    View Slide

  27. 對於遊戲公司⽽而⾔言 ...
    線上遊戲外掛多寡決定玩家情感上的品質啊(嘆)

    View Slide

  28. 遊戲公司好棒棒
    想出了了三種⼿手段制裁駭客
    1. 加殼保護
    2. 遊戲搭載反外掛系統
    3. ⾃自我程式碼校驗防篡改

    View Slide

  29. [email protected]
    Themida
    www.oreans.com/themida.php

    View Slide

  30. 簡直是救命仙丹丹啊!
    動動⼿手指幫程式套保護殼,
    就能保護原始碼呢,簡直比犀⽜牛盾還好⽤用!

    View Slide

  31. 原本駭客打算
    通通丟去餵 IDA!

    View Slide

  32. View Slide

  33. View Slide

  34. 沒有錯啦!
    IDA 只會看到⼀一堆「殼」本⾝身的實作,
    根本看不到實際遊戲本⾝身的內容惹。

    View Slide

  35. 打個岔,
    我得提提殼怎麼實作的

    View Slide

  36. [email protected]
    你以為殼的保護

    View Slide

  37. [email protected]
    你以為殼的保護
    ⼀一切充滿不確定性,
    反正能 Work 就好了了嘛?

    View Slide

  38. [email protected]
    Process
    Game Process
    Game.exe
    (PE)
    Reserved
    0x00 ~ 0x3fffff
    0x400000+
    ntdll.dll
    KUSER_SHARED_DATA
    0x7ffe000+
    user32.dll
    kernel32.dll
    ...
    ...

    View Slide

  39. [email protected]

    Game Process
    保留留空間
    Reserved
    0x00 ~ 0x3fffff
    0x400000+
    KUSER_SHARED_DATA
    0x7ffe000+
    ...
    殼的主程式
    加密並壓縮的內容

    View Slide

  40. [email protected]

    Game Process
    保留留空間
    Reserved
    0x00 ~ 0x3fffff
    0x400000+
    KUSER_SHARED_DATA
    0x7ffe000+
    ...
    殼的主程式
    加密並壓縮的內容
    原始程式內容
    (Game.exe)
    取出
    解密內容
    殼的主程式

    View Slide

  41. [email protected]

    Game Process
    保留留空間
    Reserved
    0x00 ~ 0x3fffff
    0x400000+
    KUSER_SHARED_DATA
    0x7ffe000+
    ...
    殼的主程式
    加密並壓縮的內容
    原始程式內容
    (Game.exe)
    覆寫回執⾏行行程式預期加載的位址
    並模擬實作 loader 需做的事情
    殼的主程式
    Game.exe
    (PE)

    View Slide

  42. [email protected]
    讀寫別⼈人的 Process
    boringGame.exe
    Process Id = 1
    0xdead: 01 23 00 00
    gameHacker.exe
    Process Id = 2
    我想寫入 Process id = 1 的 Process
    記憶體 0xdead 處連續 4 個 byte 的內容
    可以ㄇ
    WriteProcessMemory

    View Slide

  43. [email protected]
    boringGame.exe
    Process Id = 1
    0xdead: 01 23 00 00
    我想寫入 Process id = 1 的 Process
    記憶體 0xdead 處連續 4 個 byte 的內容
    可以ㄇ
    WriteProcessMemory
    不可以ㄛ
    跨 Process 讀/寫/創建 Thread
    要先申請權限ㄏㄏ
    gameHacker.exe
    Process Id = 2
    讀寫別⼈人的 Process

    View Slide

  44. [email protected]
    讀寫別⼈人的 Process
    boringGame.exe
    Process Id = 1
    0xdead: 01 23 00 00
    OpenProcess()
    gameHacker.exe
    Process Id = 2
    Token
    WriteProcessMemory(Token, ...)

    View Slide

  45. [email protected]
    讀寫別⼈人的 Process
    boringGame.exe
    Process Id = 1
    0xdead: 01 23 00 00
    OpenProcess()
    gameHacker.exe
    Process Id = 2
    Token
    ReadProcessMemory(Token, ...)
    0xdead = 01 23 00 00

    View Slide

  46. 所以呢?
    把程式執⾏行行起來來,跨 Process 讀取遊戲執⾏行行程式內容
    1. 修復讀取到的執⾏行行程式內容,就可以餵給 IDA 分析
    2. ⽤用動態分析⽅方式除錯遊戲,直接⼀一步步跟蹤遊戲指令

    View Slide

  47. [email protected]
    反外掛系統

    View Slide

  48. [email protected]
    讀寫別⼈人的 Process
    boringGame.exe
    Process Id = 1
    0xdead: 01 23 00 00
    OpenProcess()
    gameHacker.exe
    Process Id = 2
    Token
    ReadProcessMemory(Token, ...)
    0xdead = 01 23 00 00

    View Slide

  49. [email protected]
    讀寫別⼈人的 Process
    boringGame.exe
    ntdll!ZwOpenProcess
    gameHacker.exe
    Token

    View Slide

  50. [email protected]
    實際上運作 boringGame.exe
    Process Id = 1
    ntdll!ZwOpenProcess: 我可不可以申請 Process Id = 1 Process
    的存取寫入權限R?
    gameHacker.exe
    (Ring3)
    好R,你要 Token 就給你R,有何不可ㄋ
    Windows Kernel
    (Ring0)

    View Slide

  51. [email protected]
    實際上運作 boringGame.exe
    Process Id = 1
    ntdll!ZwOpenProcess: 我可不可以申請 Process Id = 1 Process
    的存取寫入權限R?
    gameHacker.exe
    (Ring3)
    ㄜ... 有個驅動程式控制ㄌ我,
    它跟我說不能給你 Token, Sorry :(
    Windows Kernel
    (Ring0)

    View Slide

  52. 你以為到此為⽌止,
    遊戲廠商⼤大獲全勝了了嗎
    三種奇淫技巧
    (⼀一)偽造 PEB!ImagePath
    (⼆二)DLL Hijacking / DLL Inject
    (三)乾脆把遊戲保護給殺掉

    View Slide

  53. [email protected]
    偽造 ImagePath boringGame.exe
    Process Id = 1
    ntdll!ZwOpenProcess:
    我可不可以申請 Process Id = 1 Process
    的存取寫入權限R?
    ㄜ...你是⼯工作管理理員喔,
    好ㄅ,可能遊戲當掉了了玩家想強制關閉遊戲,
    Token 給你吧
    Windows Kernel
    (Ring0)

    View Slide

  54. [email protected]
    偽造 ImagePath
    _asm{
    mov eax,fs:[0x30] //eax points to PEB
    mov eax,[eax+0x010] //eax points to _PEB->_RTL_USER_PROCESS_PARAMETERS
    add eax,0x38 //eax points to ImagePathName(UNICODE_STRING)
    add eax,0x4 //UNICODE_STRING.Buffer
    mov ebx,wszImagePathName
    mov [eax],ebx
    mov eax,[eax]
    }

    View Slide

  55. [email protected]
    寫⼀一⽀支 DLL 吧 :)
    BOOL APIENTRY DllMain( HMODULE hModule,
    DWORD ul_reason_for_call,
    LPVOID lpReserved)
    {
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    MessageBoxA(NULL, "hi there!", "info", NULL);
    break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
    break;
    }
    return TRUE;
    }

    View Slide

  56. [email protected]
    DLL Entry
    Game Process
    Game.exe
    (PE)
    0x400000+
    ntdll.dll
    KUSER_SHARED_DATA
    0x7ffe000+
    user32.dll
    kernel32.dll
    ...
    ...
    “An optional entry point into a
    dynamic-link library (DLL).
    When the system starts or
    terminates a process or thread,
    it calls the entry-point
    function for each loaded DLL
    using the first thread of the
    process. ”
    -- MSDN (DllMain entry point)
    .text: DllEntry
    .text: DllEntry
    .text: DllEntry

    View Slide

  57. [email protected]
    DLL Hijacking
    via SestWindowsHookEx
    resources.infosecinstitute.com/using-setwindowshookex-for-dll-injection-on-windows

    View Slide

  58. [email protected]
    DLL Hijacking
    via Side-Loading
    www.fireeye.com/content/dam/fireeye-www/global/en/current-threats/pdfs/
    rpt-dll-sideloading.pdf

    View Slide

  59. [email protected]
    DLL Hijacking
    via Side-Loading
    www.fireeye.com/content/dam/fireeye-www/global/en/current-threats/pdfs/
    rpt-dll-sideloading.pdf

    View Slide

  60. [email protected]
    DLL Hijacking
    via Side-Loading
    http://www.studentexplorer.net/soft/51651.html

    View Slide

  61. [email protected]
    DLL Inject
    https://arvanaghi.com/blog/dll-injection-using-loadlibrary-in-C/

    View Slide

  62. [email protected]
    把反外掛函數庫餵 IDA

    View Slide

  63. [email protected]
    把反外掛函數庫餵 IDA

    View Slide

  64. [email protected]
    把反外掛函數庫餵 IDA

    View Slide

  65. [email protected]
    把反外掛函數庫餵 IDA

    View Slide

  66. [email protected]
    還記得我們找到程式邏輯的嗎

    View Slide

  67. [email protected]
    把反外掛函數庫餵 IDA

    View Slide

  68. [email protected]
    把反外掛函數庫餵 IDA

    View Slide

  69. [email protected]
    把反外掛函數庫餵 IDA

    View Slide

  70. [email protected]
    老外寫了了 CE Script MSCRCBypass:

    push eax

    lea eax, [ecx]

    cmp eax, 00401000

    jb Normal

    cmp eax, 00BFE000

    ja Normal

    push ebx

    mov ebx, FakeDump

    sub eax, 00401000

    add eax, ebx

    movzx ecx, byte ptr [eax]

    pop ebx

    pop eax

    jmp Normal+04

    Normal:

    pop eax

    movzx ecx, byte ptr [ecx]

    mov edx, [ebp+14]

    jmp 00A11487

    00A11481:

    jmp MSCRCBypass

    nop
    CreateThread(MSmemcpy)

    MSmemcpy:

    mov edi, FakeDump

    mov esi, 00401000

    mov ecx, 001FF400

    repe movsd

    ret
    ccplz.net/threads/ems-v75-bypass-information.23009/

    View Slide

  71. ⼀一些都市傳說
    是怎麼被創造出來來的?

    View Slide

  72. Q0.
    可是真的沒辦法
    過記憶體校驗怎麼辦 :(

    View Slide

  73. [email protected]
    messagebox.c
    en.wikipedia.org/wiki/X86_calling_conventions
    #include
    const char *lptext = "hi there!";
    const char *lptitl = "info";
    int main()
    {
    MessageBoxA(0, lptext, lptitl, 0);
    return 0;
    }

    View Slide

  74. [email protected]
    messagebox.c
    en.wikipedia.org/wiki/X86_calling_conventions
    #include
    const char *lptext = "hi there!";
    const char *lptitl = "info";
    int main()
    {
    MessageBoxA(0, lptext, lptitl, 0);
    return 0;
    }

    View Slide

  75. [email protected]
    messagebox.exe
    en.wikipedia.org/wiki/X86_calling_conventions
    00F11000 00F11002 | push "info"
    00F11007 | push "hi there!"
    00F1100C | push 0
    00F1100E | call dword ptr ds:[]
    00F11014 | xor eax,eax
    00F11016 | ret

    View Slide

  76. [email protected]
    messagebox hijacking
    en.wikipedia.org/wiki/X86_calling_conventions

    View Slide

  77. [email protected]
    messagebox hijacking
    en.wikipedia.org/wiki/X86_calling_conventions

    View Slide

  78. [email protected]
    messagebox hijacking
    en.wikipedia.org/wiki/X86_calling_conventions

    View Slide

  79. Q1. 虛擬機?

    View Slide

  80. [email protected]
    Calling Convention
    int callee(int, int, int);
    int caller(void)
    {
    return callee(1, 2, 3) + 5;
    }
    caller:
    push ebp
    mov ebp, esp
    push 3
    push 2
    push 1
    call callee
    add eax, 5
    add esp, 12
    mov esp, ebp
    pop ebp
    ret
    en.wikipedia.org/wiki/X86_calling_conventions

    View Slide

  81. [email protected]
    殼本⾝身的虛擬機?
    int callee(int, int, int);
    int caller(void)
    {
    return callee(1, 2, 3) + 5;
    }
    callee:
    push xxxx
    jmp PackerVM
    en.wikipedia.org/wiki/X86_calling_conventions
    caller:
    push ebp
    mov ebp, esp
    push 3
    push 2
    push 1
    call callee
    ...

    View Slide

  82. [email protected]
    虛擬機構造
    www.blackhat.com/docs/asia-18/
    asia-18-Blazytko-Breaking-State-Of-The-Art-Binary-Code-Obfuscation-Via-Program-Synthesis.pdf

    View Slide

  83. [email protected]
    虛擬機構造
    www.blackhat.com/docs/asia-18/
    asia-18-Blazytko-Breaking-State-Of-The-Art-Binary-Code-Obfuscation-Via-Program-Synthesis.pdf

    View Slide

  84. [email protected]
    Themida
    www.oreans.com/themida.php

    View Slide

  85. [email protected]
    殼本⾝身的虛擬機?
    int callee(int, int, int);
    int caller(void)
    {
    return callee(1, 2, 3) + 5;
    }
    callee:
    push xxxx
    jmp PackerVM
    en.wikipedia.org/wiki/X86_calling_conventions
    caller:
    push ebp
    mov ebp, esp
    push 3
    push 2
    push 1
    >> call callee
    ...

    View Slide

  86. Q2. 全圖吸怪?
    https://www.youtube.com/watch?v=ORrd1C2vEq0

    View Slide

  87. [email protected]
    還記得嗎
    客⼾戶端
    伺服端
    現在有誰在線上啊?
    同張地圖
    玩家資訊
    欸欸,
    經過我計算結果
    本次要損⾎血 10 滴
    好ㄛ
    我知道ㄌ
    我要攻擊
    那隻 id 為 1 的怪物
    好ㄛ攻擊成功!
    你剛剛打他造成 10 點傷害

    View Slide

  88. 不過,
    外掛圈是這樣啦。
    程式碼開源共享?外掛圈才是龍頭啦!Github落落漆
    www.unknowncheats.me/wiki/Maple_Story:GMS_-_V66_Pointers/Addresses_%26_More

    View Slide

  89. 當然外掛製造商
    也會有鬧不平的時候。
    知⼰己知彼百戰百勝,東抄⻄西抄⾃自成⼀一家
    (你有我沒有的?我破解掉你的外掛來來分析看看)

    View Slide

  90. [email protected]
    怪物出⽣生
    void bornMob(mob inMob) {
    // ...
    sendMob(inMob.type, inMob.x, inMob.y, ...);
    // ...
    }
    void initMap() {
    /* ... 做了了很多進地圖的封包處理理 ... */
    wallData curr;
    recvWallData(&curr);
    mob mobArr[0xff];
    for (int i = 0; i < 255; i++) {
    if (cmpMobInWall(mobArr[i], curr))
    bornMob(mobArr[i]);
    else
    dieMob(mobArr[i]);
    }
    }
    struct wallData {
    int leftWall;
    int rightWall;
    int TopWall;
    int Bottom;
    }
    *年年代久遠、資料不可考,按照印象⼤大概是這樣啦。
    github.com/aaaddress1/CrackShield-MapleStory-Hack/blob/master/Script/GiFon_.h

    View Slide

  91. [email protected]
    全圖吸怪
    void bornMob(mob inMob) {
    // ...
    sendMob(inMob.type, inMob.x, inMob.y, ...);
    // ...
    }
    void initMap() {
    /* ... 做了了很多進地圖的封包處理理 ... */
    wallData curr;
    recvWallData(&curr);
    mob mobArr[0xff];
    for (int i = 0; i < 255; i++) {
    if (cmpMobInWall(mobArr[i], curr))
    bornMob(mobArr[i]);
    else
    dieMob(mobArr[i]);
    }
    }
    struct wallData {
    int leftWall;
    int rightWall;
    int TopWall;
    int Bottom;
    }
    *年年代久遠、資料不可考,按照印象⼤大概是這樣啦。
    github.com/aaaddress1/CrackShield-MapleStory-Hack/blob/master/Script/GiFon_.h

    View Slide

  92. Q3. 全圖攻擊?
    https://www.youtube.com/watch?v=0B8G2WlC-BA

    View Slide

  93. [email protected]
    技能攻擊判斷
    typedef struct _RECT {
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
    } RECT, *PRECT;
    typedef struct tagPOINT {
    LONG x;
    LONG y;
    } POINT, *PPOINT;
    *年年代久遠、資料不可考,按照印象⼤大概是這樣啦。
    github.com/aaaddress1/CrackShield-MapleStory-Hack/blob/master/Script/XY_Fix.h
    void CALLBACK attack() {
    // ...
    wallData curr;
    recvWallData(&curr);
    _RECT skillRange;
    mob arr[0xff];
    for (int i = 0; i < 255; i++) {
    // 確認技能施放 ...
    tagPOINT p = tagPOINT(arr[i].x, arr[i].y);
    if (PtinRect(&skillRange, &p))
    sendAttackMob(arr[i]);
    // ...
    }
    }

    View Slide

  94. [email protected]
    全圖攻擊
    typedef struct _RECT {
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
    } RECT, *PRECT;
    typedef struct tagPOINT {
    LONG x;
    LONG y;
    } POINT, *PPOINT;
    *年年代久遠、資料不可考,按照印象⼤大概是這樣啦。
    github.com/aaaddress1/CrackShield-MapleStory-Hack/blob/master/Script/XY_Fix.h
    void CALLBACK attack() {
    // ...
    wallData curr;
    recvWallData(&curr);
    _RECT skillRange;
    mob arr[0xff];
    for (int i = 0; i < 255; i++) {
    // 確認技能施放 ...
    tagPOINT p = tagPOINT(skillRange.left, skillRange.top);
    if (PtinRect(&skillRange, &p))
    sendAttackMob(arr[i]);
    // ...
    }
    }

    View Slide

  95. [email protected]
    技能攻擊判斷
    *反外掛
    *年年代久遠、資料不可考,按照印象⼤大概是這樣啦。
    github.com/aaaddress1/CrackShield-MapleStory-Hack/blob/master/Script/XY_Fix.h
    void CALLBACK attack() {
    // ...
    wallData curr;
    recvWallData(&curr);
    _RECT skillRange;
    mob arr[0xff];
    for (int i = 0; i < 255; i++) {
    // 確認技能施放 ...
    tagPOINT p = tagPOINT(arr[i].x, arr[i].y);
    if (PtinRect(&skillRange, &p))
    sendAttackMob(arr[i]);
    // ...
    while (!chkMob(arr[i], p.x, p.y));
    }
    }
    bool chkMob(mob mob, LONG x, LONG y){
    if (chkShownMob(mob, x, y))
    return 1;
    else
    killGame();
    }

    View Slide

  96. [email protected]
    技能攻擊判斷
    *反反外掛
    *年年代久遠、資料不可考,按照印象⼤大概是這樣啦。
    github.com/aaaddress1/CrackShield-MapleStory-Hack/blob/master/Script/XY_Fix.h
    void CALLBACK attack() {
    // ...
    wallData curr;
    recvWallData(&curr);
    _RECT skillRange;
    mob arr[0xff];
    for (int i = 0; i < 255; i++) {
    // 確認技能施放 ...
    tagPOINT p = tagPOINT(arr[i].x, arr[i].y);
    if (PtinRect(&skillRange, &p))
    sendAttackMob(arr[i]);
    // ...
    while (!chkMob(arr[i], p.x, p.y));
    }
    }
    bool chkMob(mob mob, LONG x, LONG y){
    chkShownMob(mob, x, y);
    return 1;
    }

    View Slide

  97. Q4.
    遊戲⽅方都沒啥保護嗎?

    View Slide

  98. [email protected]
    任意地圖⼤大概像這樣
    四四⽅方⽅方的,你懂我懂

    View Slide

  99. [email protected]
    地圖,你我回憶,對的!
    地板
    光點
    光點 你
    來來搶圖的玩家
    怪物

    View Slide

  100. [email protected]
    不對,你記錯了了,這才是真正的地圖
    地板
    光點
    光點 你
    來來搶圖的玩家
    怪物
    地圖外打不到的透明怪
    地圖外打不到的透明怪
    地圖外打不到的透明怪

    View Slide

  101. 你打到我
    就準備去⾒見見 GM 吧你。
    這也是為啥遊戲資料片裡⾯面
    可以翻到⼀一堆沒有圖片卻是怪物的資料,ㄎㄎ。

    View Slide

  102. Q5. 遇 GM ⾃自動下線

    View Slide

  103. [email protected]
    void initMap() {
    /* ... 做了了很多進地圖的封包處理理 ... */
    object arr[0xff];
    for (int i = 0; i < 255; i++) {
    if (arr[i].type != TYPE_PLAYER) continue;
    displyPlayer(arr[i], arr[i].name);
    }
    for (int i = 0; i < 255; i++) {
    if (arr[i].type != TYPE_CHEATMOB) continue;
    setCheatMobLocation(arr[i]);
    }
    wallData curr;
    recvWallData(&curr);
    for (int i = 0; i < 255; i++) {
    if (arr[i].type != TYPE_MOB) continue;
    if (cmpMobInWall(arr[i], curr))
    bornMob(arr[i]);
    else
    dieMob(arr[i]);
    }
    }
    地圖載入階段
    *年年代久遠、資料不可考,按照印象⼤大概是這樣啦。

    View Slide

  104. [email protected]
    void initMap() {
    /* ... 做了了很多進地圖的封包處理理 ... */
    object arr[0xff];
    for (int i = 0; i < 255; i++) {
    if (arr[i].type != TYPE_PLAYER) continue;
    displyPlayer(arr[i], arr[i].name);
    }
    for (int i = 0; i < 255; i++) {
    if (arr[i].type != TYPE_CHEATMOB) continue;
    setCheatMobLocation(arr[i]);
    }
    wallData curr;
    recvWallData(&curr);
    for (int i = 0; i < 255; i++) {
    if (arr[i].type != TYPE_MOB) continue;
    if (cmpMobInWall(arr[i], curr))
    bornMob(arr[i]);
    else
    dieMob(arr[i]);
    }
    }
    遇 GM 下線
    *年年代久遠、資料不可考,按照印象⼤大概是這樣啦。

    View Slide

  105. [email protected]
    Thanks!
    我剛剛什什麼都沒說,你們什什麼都沒聽到。

    View Slide