1 điểm bởi GN⁺ 2023-12-06 | 1 bình luận | Chia sẻ qua WhatsApp
  • Trong Magic: The Gathering Arena, có thể buộc đối thủ đầu hàng vào đúng thời điểm mong muốn, từ đó tạo ra trạng thái không thua trong các trận matchmaking
  • Trò chơi thẻ bài thường phù hợp với kiến trúc máy chủ toàn quyền, nơi máy chủ quản lý toàn bộ trạng thái, nhưng đối thủ bot Sparky của MTGA lại được triển khai bằng logic client cục bộ
  • Client MTGA viết bằng C# cho phép truy cập các đối tượng runtime và các trường private bằng reflection, và trong mã đã decompile có thể thấy các tên như JoinMatch, ConnectAndJoinMatch, HeadlessClient
  • Trong trận đấu với bot, cấu trúc kết nối dùng PersonaIDJWT của cùng một tài khoản cho cả hai ghế, và khi áp dụng điều này vào trận thường, có thể gắn một headless client vào ghế của đối thủ rồi gọi ConcedeGame()
  • Sau đó máy chủ đã được để ngăn hai ghế trong các trận matchmaking dùng cùng một tài khoản và JWT, trở thành một ví dụ cho thấy ranh giới giữa triển khai bot phía client và xác thực ghế ảnh hưởng trực tiếp đến bảo mật thực chiến

Vì sao game thẻ bài khó bị hack

  • Game thẻ bài theo lượt và lượng thông tin trao đổi giữa client và máy chủ không nhiều, nên rất phù hợp với kiến trúc máy chủ toàn quyền
  • Máy chủ quản lý toàn bộ trạng thái trò chơi và chỉ gửi cho client những thông tin cần thiết
    • Thông tin chưa được công khai như bài trên tay hoặc bộ bài của đối thủ sẽ không tồn tại trên máy cục bộ
    • Các kiểu can thiệp như đổi thứ tự bộ bài hoặc thao túng lá sắp rút rất khó thực hiện vì máy chủ xử lý hành động và chỉ thông báo kết quả
  • Khác với game bắn súng góc nhìn thứ nhất, hành vi của người chơi trong game thẻ bài bị giới hạn và chỉ xảy ra vào những thời điểm xác định
    • Trong FPS, thông tin như vị trí mô hình địch có thể được cache sẵn ở client nên có thể xuất hiện kiểu wallhack
    • Bài viết anti-wallhack của Riot nói về cách tiếp cận giảm dữ liệu vị trí người chơi ở phía client
  • Việc phát hiện hành vi sai trái cũng tương đối dễ
    • Đánh ra lá bài không có trên tay
    • Hành động khi chưa đến lượt của mình

Điểm khởi đầu phân tích: mạng và client C#

  • Tác giả chọn cách bắt đầu việc hack game bằng cách xem xét giao tiếp mạng
  • Bài nói chuyện DEF CON của Manfred về các trường hợp hack MMO cho thấy việc dịch ngược giao thức mạng là chìa khóa để phân tích nhiều lỗi
  • MTGA được viết bằng C#, nên thay vì hook các hàm gửi nhận traffic, có thể thao tác đối tượng ngay trong runtime
  • Các assembly .NET không bị làm rối mã cho phép xem tên hàm, biến, lớp ở dạng con người đọc được nhờ metadata token
    • Vì vậy kết quả decompile gần như tương đương với việc review mã nguồn

Cấu trúc Sparky được tìm thấy trong JoinMatch

  • Trong lúc tìm tên hàm liên quan để lần ra quá trình khởi tạo tham gia trận, tác giả phát hiện hàm JoinMatch
  • JoinMatch là một hàm dài hơn 200 dòng, và ở phần cuối có lời gọi ConnectAndJoinMatch
    • Hàm này có vẻ là luồng nhận thông tin cấu hình trận rồi kết nối tới máy chủ game
  • Trong cùng luồng mã có các nhánh MatchType.NPEMatchType.Familiar
    • NPE được suy đoán là các trận dạng hướng dẫn cho người chơi mới
    • Familiar là trận bot tiêu chuẩn
  • Logic được gọi trong nhánh này liên kết với Sparky, đối thủ bot của MTGA
    • Sparky là đối thủ mang tính biểu tượng của MTGA, dùng trong tutorial và đấu với bot
    • Trong trận bot, logic bot được chạy ngay bên trong game client trên máy cục bộ

Bot cục bộ và HeadlessClient

  • Bộ xử lý logic thực tế của bot nằm trong lớp HeadlessClient
  • HeadlessClient là một headless client không render bàn chơi nhưng vẫn kết nối máy chủ để chơi trận đấu
  • Client bot được tạo cục bộ dùng cùng thông tin xác thực người dùng với game client
    • PersonaID đóng vai trò ID người dùng
    • JSON web token được cấp cho game sau khi đăng nhập
  • Trong trận bot, hệ thống được thiết kế sao cho máy chủ không coi việc cùng một client thực chất kết nối vào cả hai phía của trận là vấn đề
  • Ghế (seat) được dùng để phân biệt người chơi nào là ai trong trận
    • Trong trận bot, có thể điền các ghế khác nhau bằng cùng một thông tin xác thực

Cách chiếm quyền trận đấu thường

  • Tác giả thử áp dụng logic trận bot vào trận thường để xem có thể kết nối vào cả hai ghế hay không
  • Các thông tin cần thiết được lấy từ các đối tượng game trong runtime
    • Cấu hình trận hiện tại
    • Host và cổng của máy chủ trận
    • controllerFabricUri
    • matchId
    • PersonaID
    • Jwt
    • Cơ sở dữ liệu thẻ bài
    • Trình quản lý trận
    • Hệ thống tra cứu asset
  • Ghế của đối thủ được tính dựa trên ghế của chính mình
    • Trong mã, ghế còn lại được tính bằng man.LocalPlayerSeatId % 2U + 1U
  • Dùng UnityFamiliar.SpawnFamiliar_DEBUG(...) để kết nối bot vào ghế của đối thủ
  • Sau đó tìm đối tượng UnityFamiliar đã tạo và gọi cheatbot.Client.Gre.ConcedeGame() để buộc đầu hàng ngay lập tức

Kết quả và bản vá

  • Cách này hoạt động cả trong trận thường
    • Nó vẫn hoạt động ngay cả khi đối thủ đã kết nối và trận đấu đang diễn ra
    • Vì đây là game matchmaking, người khai thác vẫn nhận được phần thưởng như thể đã thắng người chơi thật
  • Cốt lõi của lỗ hổng là máy chủ trận thường cho phép cùng một tài khoản và JWT kết nối vào cả hai ghế
  • Sau đó máy chủ đã được vá để trong các trận matchmaking, hai ghế không thể dùng cùng một tài khoản và JWT
  • Đoạn mã InstaWin trong phụ lục là một MonoBehaviour của Unity, tạo nút GUI và khi bấm sẽ thu thập thông tin trận hiện tại, gắn bot vào ghế đối thủ rồi buộc đầu hàng

1 bình luận

 
GN⁺ 2023-12-06
Các ý kiến trên Hacker News
  • Lần đầu tôi thực sự đào sâu vào Linux là khi dùng ShowEQ cho EverQuest để xem lưu lượng mạng
    Khi đó lưu lượng chưa được mã hóa và chứa rất nhiều thông tin hữu ích. Tôi sao chép lưu lượng sang một máy Linux bằng hub để vẽ bản đồ thời gian thực của khu vực, hiển thị vị trí quái vật, NPC và người dùng, thậm chí còn cho thấy cả chiến lợi phẩm mà quái vật mang theo, nhờ đó có thể chọn đúng quái vật cần săn. Ưu điểm là cách này hoàn toàn thụ động nên không thể bị phát hiện; cuối cùng SOE cũng nhận ra và bắt đầu mã hóa lưu lượng.

    • Dữ liệu rốt cuộc vẫn phải được giải mã để đọc, nên người ta sẽ đảo ngược kỹ thuật client để tìm cách giải mã theo thời gian thực
      Khi đó phía bên kia sẽ đưa vào chữ ký dựa trên khóa, rồi lại có người cố đánh cắp khóa từ client để phá mã hóa, và sau đó khi hệ thống chống gian lận xuất hiện thì trò mèo vờn chuột bắt đầu.
    • Hồi tuổi teen tôi cũng từng làm việc tương tự với Dark Age of Camelot; việc đó rất hữu ích để học network sniffing, khác biệt giữa hub và switch, cũng như Linux
      Nó có đáng để kiếm lợi thế trong game không? Không. Tôi không chơi nghiêm túc, nên chỉ mất 2 tuần cài đặt rồi dùng khoảng 1 tuần, nhưng xét như trải nghiệm học tập thì rất tuyệt.
    • Tôi nhớ ShowEQ từng được dùng để chứng minh nhiều giả thuyết và lỗi mà Verant/Sony cứ phủ nhận
      Ví dụ như sự tồn tại của hell level, việc halfling chứ không phải human nhận bonus kinh nghiệm và trên thực tế có chênh lệch kinh nghiệm theo chủng tộc/nghề, hay việc thuật giả kim Shaman thời kỳ đầu thực sự bị hỏng. Và tôi nghĩ chuyện này đã dẫn tới eqemulator.org.
    • Tôi thắc mắc mã hóa giúp ích thế nào. Dù sao client cũng phải giải mã, vậy chẳng phải chỉ cần bám theo chỗ đó là được sao?
    • ShowEQ hiện vẫn hoạt động, và còn có MySEQ chạy trên Windows bằng cách đọc bộ nhớ
      Có vẻ chủ sở hữu EQ hiện tại không bận tâm lắm nên dùng cũng khá ổn. Tuy vậy, cả hai ứng dụng đều chưa từng hiển thị chiến lợi phẩm của quái vật ngoài trang bị có thể nhìn thấy. Qua thời gian, một số dữ liệu cũng đã thay đổi; trước đây máu chính xác của quái vật được gửi đi, còn giờ chỉ gửi phần trăm.
  • Tôi không hiểu lắm đoạn “một bot gần như hoàn chỉnh có thể chơi bất kỳ ván Magic: The Gathering nào đủ nhỏ để chạy trên máy cục bộ”
    Nếu AI MTG nặng đến mức khó chạy trên máy khách hàng, có lẽ họ cũng sẽ không chạy nó trên máy chủ. Các trận bot trong game bài thường không tính phí theo từng ván nên chi phí sẽ lớn. Máy chủ cũng không phải phép màu; phần lớn dùng CPU x86 giống máy cục bộ, và thường xung nhịp còn thấp hơn desktop. Muốn giảm thời gian lượt của bot so với chạy cục bộ thì phải dùng nhiều lõi hơn hẳn so với khách hàng, nhưng nếu cấp 8–16 lõi cho mỗi người chơi thì giờ cao điểm đồng thời sẽ như ác mộng. Nếu người chơi CPU không hỗ trợ đa lõi thì chạy cục bộ dù sao cũng phải nhanh hơn.

    • Ý ở đây không phải là hiệu năng xử lý mà là mức sử dụng bộ nhớ
      Tức là engine luật của bot đủ nhỏ để vừa trong bộ nhớ của iPhone đời cũ hoặc thiết bị Android. Trên máy chủ, bạn có thể giữ nhiều máy trạng thái hoặc engine luật trong bộ nhớ, và việc thực thi một yêu cầu cụ thể có thể gần như không tốn năng lực xử lý.
    • Nếu là desktop thì đúng, nhưng cũng cần nhớ rằng MTGA chạy được trên điện thoại.
    • MTG là một trò chơi có hệ thống luật cực kỳ phức tạp.
  • Daniel, một lần nữa chúc mừng vì bài viết lên top. Sau khi bài này được đăng, tôi đã thử hack MTGA và cũng trao đổi một chút trên GitHub [0]
    Với những ai quan tâm: hiện tôi đang làm một client MTGA không chính thức, gần như chưa có tính năng, trong trạng thái không hoạt động tích cực. Mục tiêu là tự động hóa việc chơi ranked và cung cấp đối thủ bot mạnh hơn. Gần đây tôi bị các việc khác cuốn đi, và vẫn chưa biết phải làm một UI tốt, dễ nhìn như thế nào, nên rất khó dự đoán khi nào nó mới hữu dụng tối thiểu. Ngoài ra tôi vẫn muốn nghe thêm các câu chuyện hack game từ Daniel hoặc những người khác: cách tìm những lỗi kiểu này, làm sao không lo bị ban sau khi hack, cách công bố lỗi như @aethros nói, hay cách cấu trúc một client game bài không chính thức.
    [0] https://github.com/MayerDaniel/mayerdaniel.github.io/issues/...

  • Nói rằng mình tưởng việc tạo đối thủ AI cho một game phức tạp như MTG sẽ có overhead lớn là một cách nói giảm khá mạnh
    Trò chơi gần như Turing-complete, và ngay cả nếu loại trừ vòng lặp vô hạn thì người ta vẫn nghịch được rất nhiều thứ với nó. Dù vậy, có lẽ phần nghiên cứu về chiến lược AI đã được làm khá nhiều rồi. Và vì đây là bài do chính tác giả đăng, có lẽ tiêu đề nên được gắn “Show HN:”.

    • Show HN dành cho các dự án mà mọi người có thể trực tiếp dùng thử, không phải bài blog: https://news.ycombinator.com/showhn.html
    • “Có thể mã hóa một máy Turing bằng trò chơi” và “khó viết một chương trình có thể thực hiện hành động hợp lệ trong mọi tình huống” hoàn toàn không giống nhau
      Điều khó với người viết bot có vẻ là vế sau.
    • Thực sự là Turing-complete: https://arxiv.org/abs/1904.09828
    • Khi các set mới luân phiên thay đổi, game liên tục đổi khác, nhưng tôi hiểu là trong một số tương tác cụ thể có thể tạo ra vòng lặp vô hạn, hoặc đã từng có thể làm như vậy ở nhiều thời điểm
      Ví dụ như dùng hiệu ứng vào sân/rời sân/untap để tạo và giết sinh vật token. Những vòng lặp như vậy cũng không phải lúc nào cũng gây sát thương cho một bên nào đó.
    • Tôi tò mò liệu có chiến thuật thực tế nào gần với độ phức tạp này không
      Tôi chưa từng chơi MTG, nhưng lập luận này có vẻ nói rằng nếu không nhằm mục đích thắng mà cố tình tạo ra một cấu trúc có trạng thái thì về mặt kỹ thuật là có thể.
  • Chơi Old School Magic 93/94 bằng bài giấy cùng con trai thật sự rất vui
    Hằng năm tôi đến Madrid để tham dự Giải vô địch thế giới 7pts Singleton. Mùa hè này con trai tôi xếp hạng 9 nên tôi rất tự hào. 7pts Singleton là một thể thức tuyệt vời, cho phép xây dựng nhiều kiểu bộ bài khác nhau và đem lại lối chơi cân bằng với chi phí tương đối nhẹ nhàng (https://7pts-singleton.com)

    • Khó mà gọi một format cho phép dùng Black Lotus, Ancestral Recall, Moxen là rẻ
      Dù trong 7pts Singleton không thể dùng tất cả, chỉ một lá thôi cũng đã tốn từ vài nghìn đến vài chục nghìn USD. Dù vậy, thật tốt khi bạn được chơi cùng con trai, và chúc mừng vì đã vào nhóm có thứ hạng cao
  • Logic game thuần phía client cơ à. Trước đây khi làm một game nhỏ, có lúc tôi phải đưa logic game vào client để tăng độ phản hồi, nhưng đâu có luật nào cấm chạy lại cùng logic đó trên server, nên tôi đã làm vậy
    Với game thời gian thực như FPS hay RTS thì có thể khó, nhưng với game bài thì không có lý do bào chữa. Với dạng game bài này, đúng ra không nên gửi cho client nhiều thông tin hơn những gì người chơi thật có thể thấy. Ví dụ, đừng gửi nội dung các lá bài trên tay đối thủ, chỉ gửi số lượng lá bài thôi. Các hành động gửi lên server cũng chỉ nên liên quan đến chính mình, vì vậy không được phép tuyên bố đối thủ đầu hàng. Nếu tôi nói “Đầu hàng!” thì server phải diễn giải đó là việc tôi đầu hàng

    • Có thể hiểu bài viết nói rằng game đã được viết đúng theo cách đó. Chỉ nhận thông tin cần thiết vào thời điểm cần thiết, và chỉ gửi hành động của chính mình
      Lỗ hổng là có thể mở client thứ hai và kết nối vào ván đang diễn ra ở ghế của đối thủ. Một khi làm được vậy, có thể gửi hành động của đối thủ, bao gồm cả đầu hàng
    • Như bài viết nêu rõ, toàn bộ gameplay được xử lý phía server
      Thứ tác giả khai thác không phải logic game phía client, mà là vấn đề trong mã phân quyền và kết nối vào game. Câu “không được phép tuyên bố đầu hàng thay đối thủ” chỉ là lặp lại đúng cách exploit mà tác giả tìm ra hoạt động
    • Ở 1/3 đầu bài viết có nói rõ game được triển khai như vậy
  • Trong League of Legends từng có lỗi chia cho 0 với một tổ hợp tướng và trang bị cụ thể, khiến server đá tất cả người chơi ra rồi crash
    Vì kẻ khai thác bị đá ra cuối cùng, đội của người đó được xử thắng, còn đội đối phương nhận Loss Prevented thay vì kết quả bình thường

    • Nếu đối thủ không nhận kết quả bình thường mà cũng không bị tính thua, thì đội thắng cũng không nên được tính thắng
  • Bài viết này dễ tiếp cận mà vẫn có các chi tiết rất sâu sắc
    Tuy nhiên tôi không hiểu trường hợp kết nối bot vào trong lúc trận đấu đang diễn ra. Vì sao game lại cho phép tham gia giữa trận, và khi bot đầu hàng thì vì sao việc đó lại được xử lý thành đối thủ đầu hàng? Nếu đang tạo một game 3 người, thì người chơi số 3 đầu hàng cũng không được tính là người chơi số 2 đầu hàng

    • Nó không trở thành game 3 người, mà vẫn là game 2 người
      Mã tìm ra chỉ số ghế của tài khoản của tôi, rồi cho bot tham gia bằng chỉ số ghế khác. Vấn đề là không kiểm tra người dùng tham gia có đúng là người dùng phải ngồi ở ghế đó hay không. Cũng có thể nghĩ rằng không nên cho phép kết nối lại vào một ghế đã có kết nối, nhưng nếu game hỗ trợ cả mobile thì có lẽ bạn sẽ muốn thời gian chờ khi mất kết nối khá dài. Vì bạn không muốn chặn một người chơi chỉ bị ngắt kết nối chốc lát rồi kết nối lại trước khi thời gian chờ nhận ra kết nối cũ đã chết. Tôi từng thấy ở game khác, khi cố kết nối lại thì khoảng 10 giây hiện “không thể tham gia game đang diễn ra”, rồi sau đó mới kết nối lại được. Ví von thì giống như xuất hiện ở một giải MTG tại cửa hàng bài địa phương, đẩy ai đó khỏi ghế rồi ngồi xuống hô “Tôi đầu hàng!”, và trọng tài công nhận người ngồi trên chiếc ghế đó đã tuyên bố nên người chơi B bỏ cuộc
      1. Nhiều khả năng đây là cách xử lý kết nối lại sau khi mất kết nối. Kiểu như đóng kết nối cũ
      2. Bot thay thế người chơi 2 rồi gửi lệnh đầu hàng, và server ghi nhận đó là việc người chơi 2 đầu hàng
    • MTG: Arena không cho phép game 3 người, nên bot chen vào ghế của đối thủ
      Vì vậy có vẻ nó có thể đầu hàng thay đối thủ. Có thể các nhà phát triển không nghĩ rằng ai đó lại có lý do để cố làm như vậy nên đã không kiểm tra
  • Nhớ lại thời Diablo 2, từng có lúc có thể tái sử dụng cùng gói tin đăng nhập để đưa nhân vật server mở (LAN) vào server Internet bnet chính thức
    Vì dữ liệu server mở được lưu toàn bộ cục bộ, người ta có thể tạo đủ loại vật phẩm lẽ ra không được tồn tại, và server chính thức cũng chấp nhận chúng

  • Gần đây tôi quay lại với MTG qua MTGA. Đây là game Unity và không dùng il2cpp nên tôi đã decompile nhanh, mà kể cả có dùng il2cpp thì có lẽ cũng không bảo vệ được nhiều, và tôi đã tìm thấy vài thứ khá thú vị
    Có những thứ như key cho bản build Epic Launcher hoặc API chưa được tài liệu hóa. Tôi không muốn dùng để gian lận, chỉ là muốn có lịch sử trận đấu. Chẳng hạn như đã thắng thua những trận nào, hay chức năng xem lại trạng thái bàn đấu của một game đã kết thúc. Tôi cũng sẽ xem bài viết này, và hy vọng nó sớm được vá

    • Lỗ hổng này đã được vá trước khi bài viết được viết, và đã được công bố với MTG
      Nếu muốn xem lịch sử, bạn có thể xem https://untapped.gg/en. Tôi đã trao đổi với họ một chút, và về cơ bản nó làm đúng việc bạn muốn. Phần lớn thông tin được lấy từ log debug MTG trong thư mục ứng dụng MTGA, nên nếu muốn bạn cũng có thể tự làm tracker. Trang web cũng giải thích điều này: https://help.hearthsim.net/en/articles/3620440-how-do-i-supp...
    • https://www.17lands.com/ thu thập thống kê thắng/thua của game Limited, đồng thời lưu lịch sử game theo từng lượt cho cả Limited lẫn Constructed. Tôi đã tham gia với tư cách người đóng góp
    • Tôi biết https://mtgaassistant.net/ là một trong những công cụ khá phổ biến để thu thập dữ liệu chơi
    • Tôi nghĩ tính năng này đã có rồi. Có lẽ là player log
      Tôi nhớ là có khá nhiều ứng dụng dùng log đó để theo dõi