• เกม RTS ปี 2001 Emperor: Battle for Dune มีปัญหาทั้งการรัน การติดตั้ง และการเล่นออนไลน์บน Windows สมัยใหม่ ส่วน EmperorLauncher คือแพตช์ที่ชุบชีวิตให้กลับมาเล่นได้อีกครั้ง
  • การปรับปรุงหลักมุ่งไปที่ การรองรับความละเอียดสูง, การจำกัดที่ 60 FPS, มัลติเพลเยอร์ออนไลน์แบบ IP โดยตรง, โหมดแคมเปญร่วมมือ และการข้ามขั้นตอนติดตั้งที่เสีย
  • การทำงานประกอบด้วยลอนเชอร์ที่แทนที่ Emperor.exe, การฉีด DLL ใส่ Game.exe, การแพตช์ฟังก์ชันด้วย Microsoft Detours, การ hook การเรนเดอร์ Direct3D 7, การดัก winsock และเซิร์ฟเวอร์ WOL แบบง่าย
  • การเล่นออนไลน์เปลี่ยนโครงสร้างเดิมที่เป็น P2P, ใช้พอร์ตสุ่ม และ NAT punching ให้ tunnel ผ่านการเชื่อมต่อเดียวแบบไคลเอนต์→เซิร์ฟเวอร์ ทำให้มีเพียงโฮสต์เซิร์ฟเวอร์ที่ต้องสนใจการตั้งค่าเครือข่าย
  • เครื่องมือนี้ดูแลตั้งแต่การคัดลอกไฟล์จาก CD ต้นฉบับ, แตกไฟล์ .cab, ใช้แพตช์ทางการ v1.09, ข้ามการลงทะเบียน COM ของ WOLAPI.DLL ไปจนถึง UI ลอนเชอร์ Win32 สำหรับติดตั้งและรันเกม

จุดที่ Emperor: Battle for Dune ติดขัด

  • Emperor: Battle for Dune เป็นเกมวางแผนแบบเรียลไทม์ที่ Westwood Studios สร้างในปี 2001 และเป็นภาคต่อของ Dune 2000
  • บนระบบสมัยใหม่ยังมีปัญหาหลายจุดที่ขัดขวางการเล่น
    • ไม่สามารถรันแบบ ความละเอียดสูง ให้เหมาะกับหน้าจอสมัยใหม่ได้
    • ในมัลติเพลเยอร์ ความเร็วการจำลองเกมไม่ได้ถูกจำกัด ทำให้เกมเร็วเกินไป
    • Westwood Online(WOL) ไม่ทำงานอีกแล้ว ทำให้มัลติเพลเยอร์นอก LAN ทำได้ยาก
    • แคมเปญร่วมมือเป็นฟีเจอร์เฉพาะออนไลน์ จึงใช้บน LAN ไม่ได้
    • โปรแกรมติดตั้งที่อยู่ในดิสก์เสีย
    • เอฟเฟกต์ภาพหลายอย่างพังเมื่อรันบน PC สมัยใหม่ที่มีเฟรมเรตสูง
  • EmperorLauncher เป็นแพตช์สำหรับแก้ปัญหาเหล่านี้ และมีไฟล์ดาวน์โหลดกับซอร์สโค้ดเผยแพร่แล้ว

การแทนที่ Emperor.exe และการเริ่มต้น Game.exe

  • Emperor.exe ของเกมไม่ใช่ไฟล์รันเกมจริง แต่เป็น wrapper บาง ๆ ที่เปิด Game.exe
  • ถ้ารัน Game.exe โดยตรงจะไม่มีอะไรเกิดขึ้น จึงต้องวิเคราะห์ขั้นตอน initialization ที่ Emperor.exe เคยทำ แล้วจำลองใหม่ในลอนเชอร์ตัวแทน
  • ใช้ IDA ในการวิเคราะห์
    • IDA สามารถ disassemble ไฟล์ executable และ decompile โค้ดบางส่วนให้อยู่ในรูปแบบ C ได้
    • ต้องไล่ตามการเรียกฟังก์ชันและการใช้ Windows API จากไบนารีที่ข้อมูล type และข้อมูล struct หายไปแล้ว
  • ก่อนรัน Game.exe, Emperor.exe จะสร้าง mutex กับ handle ของ anonymous file mapping จากนั้นประมวลผลข้อมูลที่อ่านจาก Emperor.dat แล้วคัดลอกลง mapping
  • โปรเซสแม่ส่งข้อความ Windows ไปยัง ID ของเธรดหลักของโปรเซสลูกที่ได้จาก CreateProcessA เพื่อส่งค่าของ file mapping handle
    • ใช้ 0xBEEF เป็น ID ข้อความแบบกำหนดเอง
    • ข้อมูลใน file mapping คือสตริงสามตัว "UIDATA,3DDATA,MAPS" และถูกส่งต่อให้โค้ดโหลด asset ของเกม
  • แทนที่จะ reimplement โค้ดถอดรหัสเอง ผู้พัฒนาใส่เครื่องมือ dump แทนตำแหน่ง Game.exe เพื่อบันทึกข้อมูลที่ถูกส่งมาลงดิสก์ แล้วให้ลอนเชอร์ทำ sequence เดียวกัน

วิธีฉีด DLL และแพตช์ฟังก์ชัน

  • เพื่อใช้แพตช์ ต้องรันโค้ดของผู้ใช้ภายในโปรเซส Game.exe และใช้วิธี CreateRemoteThread + LoadLibrary เพื่อทำสิ่งนี้
  • ขั้นตอนการฉีดดำเนินตามลำดับนี้
    • จัดสรร buffer ในหน่วยความจำของโปรเซสเป้าหมายด้วย VirtualAllocEx
    • คัดลอกสตริง path ของ DLL ด้วย WriteProcessMemory
    • ส่ง address ของ LoadLibrary และ buffer path ของ DLL ให้ CreateRemoteThread เพื่อโหลด DLL ในโปรเซสเป้าหมาย
    • เมื่อ DllMain ของ DLL ทำงาน โค้ดแพตช์ก็เริ่มทำงาน
  • เริ่มโปรเซสในสถานะ suspended แล้วฉีด DLL เพื่อให้แน่ใจว่าโค้ดทำงานก่อน main
  • ใช้ Microsoft Detours เพื่อแก้ไขฟังก์ชันเดิม
    • Detours เปลี่ยนคำสั่งเริ่มต้นของฟังก์ชันเดิมเป็นคำสั่ง jump เพื่อส่งการเรียกไปยังฟังก์ชันทดแทน
    • คำสั่งเดิมที่ถูกเขียนทับจะถูกคัดลอกไปยังหน่วยความจำแยกต่างหาก แล้วสร้าง wrapper ที่ jump ต่อไปยังตำแหน่งที่เหลือของฟังก์ชันเดิม ทำให้ยังเรียกฟังก์ชันต้นฉบับได้
  • หน้าโค้ดของฟังก์ชันเขียนไม่ได้ด้วยเหตุผลด้านความปลอดภัย จึงต้องเปลี่ยน permission ด้วย VirtualProtect และเรียก FlushInstructionCache หลังแก้ไข

การกู้คืน debug log

  • ในไบนารีมี call ที่ดูเหมือน debug log แต่ฟังก์ชันปลายทางจริงเป็นฟังก์ชันว่างที่มีแค่ ret
  • ดูเหมือนว่าใน release build มีฟังก์ชันว่างหลายตัวถูก merge ให้เป็นโค้ดเดียวกัน และหนึ่งในนั้นคือ debug logger
  • ตอนแรกใช้ heuristic โดยตีความ argument แรกเป็น pointer ไปยังสตริง แล้วตรวจว่ามีอักขระ ASCII ที่พิมพ์ได้หรือไม่
    • การเข้าถึง pointer ที่ผิดพลาดถูกจับและข้ามด้วย SEH exception ของ Windows
    • วิธีนี้ใช้งานได้ในระดับหนึ่ง แต่ยังมี false positive และ false negative
  • ต่อมาใช้ฟีเจอร์ patch ของ IDA และสคริปต์ Python เพื่อย้าย call site ของ log ไปยังฟังก์ชันว่างอีกตัวหนึ่ง
    • บางส่วนหาได้ด้วย heuristic และบางส่วนหาจาก pattern ที่ push string constant แล้วตามด้วย call
    • call site ที่เหลืออีกหลายร้อยจุดต้องใส่ comment ด้วยมือ
  • log ที่กู้คืนได้ช่วยในการ debug มัลติเพลเยอร์ WOL
    • ขณะประมวลผล SC_MESSAGE_YOUR_DETAILS พบ assert log "MyId == INVALID_ID" และตรวจสอบจาก dump ของ Wireshark ได้ว่ากำลังส่งคำสั่ง GAMEOPT ไปยังผู้เล่นทุกคนอย่างผิดพลาด

แพตช์กราฟิก Direct3D 7

  • Emperor เป็นเกมที่ใช้ Direct3D 7 และการรองรับ Direct3D 7 บน Windows สมัยใหม่ไม่สมบูรณ์
  • ปัญหาความละเอียดสูงเกี่ยวข้องกับขีดจำกัดขนาด texture สูงสุด 2048 ในชั้น wrapper ของ Direct3D 7 และแก้ได้โดยใช้โค้ด LegacyD3DResolutionHack ของ UCyborg
  • เกมจัดการหน้าจอที่ไม่ใช่อัตราส่วน 4:3 ได้ไม่ถูกต้อง
    • ตัวการเรนเดอร์เองทำงานได้ แต่ UI พังเหมือนถูกขยายมากเกินไป
    • offset การเรนเดอร์เมาส์ในเกมก็คลาดเคลื่อนตามระยะจากกึ่งกลางหน้าจอ
  • วิธีแก้คือ letterboxing แบบ 4:3
    • เมื่อรันเกมในโหมดหน้าต่าง จะใช้ความละเอียดใดก็ได้
    • ลบสไตล์ขอบหน้าต่างออก แล้วกำหนด parent ของหน้าต่างเกมใหม่ให้อยู่บนหน้าต่างสีดำแบบเต็มจอ
    • เพิ่ม mouse capture เพื่อไม่ให้ edge scrolling พังในโหมดหลายจอหรือโหมดหน้าต่าง
  • การจำกัดเฟรมเรตทำโดยแพตช์ IDirect3DDevice7::EndScene ให้ตรงกับ 60 FPS
    • EndScene ถูกเรียกหนึ่งครั้งตอนท้ายเฟรม จึงเหมาะกับการคำนวณเวลาหน่วงและ sleep เธรด
    • pointer ของ EndScene ไม่ได้ export โดยตรง จึง hook การเรียก DirectDrawCreateEx และ IDirect3D7::CreateDevice ตามลำดับเพื่อดึง function pointer จาก vtable

มัลติเพลเยอร์ออนไลน์และการแทนที่ WOL

  • เป้าหมายคือ มัลติเพลเยอร์แบบ IP โดยตรง ที่เชื่อมต่อได้ด้วย port forwarding และการกรอก IP โดยไม่ต้องมี lobby หรือโครงสร้างพื้นฐานสำหรับโฮสต์
  • โหมด LAN ทำงานได้ แต่ใช้ UDP broadcast เพื่อหาเซิร์ฟเวอร์ จึงไม่เหมาะกับการเล่นผ่านอินเทอร์เน็ต
    • เมนู LAN ไม่มีฟีเจอร์กรอก IP ด้วยมือ
    • ตอนแรกลองแพตช์ LAN chat เพื่อระบุ IP แต่หยุดหลังพบว่าแคมเปญร่วมมือเป็นฟีเจอร์เฉพาะ WOL
  • ถ้าจะชุบชีวิต WOL ต้องมีสองสิ่ง
    • WOL master server ปลอม เพื่อให้เกมรู้ว่าต้องเชื่อมต่อที่ไหนและเริ่มเกมใด
    • proxy เพื่อให้ packet ของเกมทำงานบนการเชื่อมต่อ IP โดยตรง
  • โครงสร้าง WOL เดิมมี master server และเซิร์ฟเวอร์ “mangler” โดย mangler น่าจะทำหน้าที่ประสาน NAT punching
    • เซิร์ฟเวอร์ mangler เดิมหายไปแล้ว และเกมค้างขณะรอ response ดังกล่าว
    • ในแพตช์จึงลบการเรียก mangler ออก
  • Emperor ใช้โมเดลเครือข่ายแบบ P2P เปิดการเชื่อมต่อสองทิศทางสำหรับผู้เล่นแต่ละคู่ และเลือกพอร์ตแบบสุ่ม
    • เป็นโครงสร้างที่ไคลเอนต์ทุกคนต้องรับพอร์ตที่เปิดอยู่ จึงไม่เหมาะกับสภาพแวดล้อมอินเทอร์เน็ตสมัยใหม่
  • วิธีแก้คือดักฟังก์ชัน winsock ทั้งหมด แล้ว tunnel การเชื่อมต่อทุกอย่างผ่าน การเชื่อมต่อเดียวแบบไคลเอนต์→เซิร์ฟเวอร์
    • ดักข้อความที่ไคลเอนต์พยายามส่งไปยังเซิร์ฟเวอร์หรือไคลเอนต์อื่น ห่อด้วย header แล้วส่งผ่านการเชื่อมต่อเดียว
    • เธรดฝั่งเซิร์ฟเวอร์รับข้อความแล้วกระจายไปยังปลายทาง
    • เกมยังเชื่อว่าตัวเองทำงานแบบ P2P แต่ในความเป็นจริงมีเพียงโฮสต์เซิร์ฟเวอร์ที่ต้องจัดการการตั้งค่าเครือข่าย
  • ด้วยโครงสร้างนี้ สามารถเริ่มและเข้าร่วมเกมแคมเปญร่วมมือได้

การทำเซิร์ฟเวอร์ WOL แบบง่าย

  • WOL master server มีโครงสร้างใกล้เคียงกับเซิร์ฟเวอร์ IRC
  • xwis.net มีเซิร์ฟเวอร์ที่ดูเหมือนชุมชนแฟน ๆ เป็นผู้ดูแล และในเวลาที่เขียน ดูเหมือนว่าจะมีสิทธิ์เข้าถึง DNS entry ดั้งเดิมของเกม servserv.westwood.com ด้วย
    • Emperor ไม่ได้ทำงานบน xwis ได้สมบูรณ์แบบเดิม แต่ใช้เป็นข้อมูลอ้างอิงสำหรับการสร้างและเข้าร่วม lobby ได้
  • ใช้ implementation ของเซิร์ฟเวอร์ WOL แบบเปิดเผย handle_wol.cpp ของ pvpgn-server เป็นเอกสารอ้างอิงด้วย
  • เหตุผลที่สร้างเซิร์ฟเวอร์เองคือไม่มีการรับประกันว่าเซิร์ฟเวอร์ภายนอกจะถูกดูแลต่อไป
    • เป้าหมายไม่ใช่การดำเนินงานชุมชนแข่งขัน แต่คือการให้ฟังก์ชันขั้นต่ำที่จำเป็นต่อการรันเกมมัลติเพลเยอร์
  • WOL ผสมระหว่าง IRC มาตรฐานกับพฤติกรรม custom
    • lobby เกมเป็น channel พิเศษ
    • ข้อมูล lobby ใช้ IRC topic
    • แชตใน lobby ใช้ PAGE แทน PRIVMSG
    • การ sync การตั้งค่าเกมใช้ข้อความ GAMEINFO ที่มีเนื้อหาไม่ใช่ ASCII
  • implementation พื้นฐานของเซิร์ฟเวอร์ WOL ทำสำเร็จด้วย trial and error และถึงแม้จะไม่ robust เมื่อออกนอกเส้นทางปกติ แต่ก็ใช้งานได้

โปรแกรมติดตั้งและการใช้แพตช์ v1.09

  • โปรแกรมติดตั้งต้นฉบับเสีย ผู้ใช้จึงต้องเลี่ยงด้วยการคัดลอกเนื้อหา CD ลงฮาร์ดดิสก์แล้วเขียนทับด้วย setup ทดแทน
  • EmperorLauncher มีฟีเจอร์ติดตั้งที่คัดลอกไฟล์จาก CD ต้นฉบับและแตกไฟล์ .cab
    • .cab เป็นรูปแบบ archive คล้าย zip และ Windows มี interface สำหรับแตกไฟล์
  • การใช้ v1.09 ซึ่งเป็นแพตช์ทางการตัวสุดท้ายยุ่งยากกว่า
    • วิธีแตก EM109EN.EXE ด้วย 7zip แบบตรง ๆ เพื่อให้ได้ไบนารีล่าสุดใช้ไม่ได้
    • พบ resource ขนาดใหญ่ใน Windows resource ที่เห็น header ของไฟล์ executable
    • 4 ไบต์แรกของ resource นั้นคือขนาดไฟล์ และหลังจากนั้นคือไฟล์จริง
  • EM109EN.EXE แตก DLL ที่ฝังอยู่เป็นไฟล์ชั่วคราวแล้วโหลด จากนั้นรันฟังก์ชัน RTPatch32@12 ภายใน DLL
    • RTPatch เป็นเครื่องมือ binary diff patch
    • อ้างอิงเครื่องมือ myRTP เพื่อโหลดและรัน DLL ที่ฝังอยู่โดยตรง
  • เนื่องจาก DLL อ่าน path จาก registry ไม่ใช่ path ติดตั้งที่รับมาเป็น argument จึงสร้าง registry key ปลอมตามที่คาดไว้เพื่อใช้แพตช์

การจัดการ Westwood Online Shared Internet Components

  • การติดตั้งต้นฉบับมี Westwood Online Shared Internet Components แยกจากตัว Emperor หลัก
  • หากไม่มี component นี้ WOL จะไม่ทำงาน และไฟล์หลักคือ WOLAPI.DLL
  • WOLAPI.DLL เป็น COM class library และ Emperor สร้าง COM object ภายใน DLL ด้วย CoCreateClass
  • การลงทะเบียน COM ปกติจะลงทะเบียน CLSID และ path ของ DLL แบบทั้งระบบไว้ใต้ HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
    • วิธีนี้ต้องใช้สิทธิ์ผู้ดูแลระบบ
    • ส่งผลต่อทั้งระบบเกินขอบเขตของโปรเซสเกม
  • ในแพตช์ใช้ registry redirect และ OaEnablePerUserTLibRegistration เพื่อจัดการแบบลงทะเบียนรายผู้ใช้
    • ไม่พบวิธีลงทะเบียน class library เฉพาะในโปรเซสเดียว
    • ความพยายามใช้ DllGetClassObject โดยตรงไม่ทำงาน

UI ลอนเชอร์และผลลัพธ์สุดท้าย

  • ขั้นตอนสุดท้ายคือ UI ลอนเชอร์เรียบง่ายสำหรับกรอก IP และเปลี่ยนการตั้งค่าพื้นฐาน
  • UI เขียนด้วย Win32 controls แบบ raw
    • เพียงพอสำหรับ UI แบบ static และเรียบง่าย แต่ประสบการณ์การทำ Win32 UI โดยตรงค่อนข้างสมบุกสมบัน
  • สุดท้าย EmperorLauncher กลายเป็นเครื่องมือที่ครอบคลุมการรันบนระบบสมัยใหม่, ความละเอียดสูง, การจำกัด 60 FPS, มัลติเพลเยอร์แบบ IP โดยตรง, แคมเปญร่วมมือ รวมถึงการติดตั้งและการใช้แพตช์

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น