K-Shin07 2024/04/06 15:49

ウディタでのゲーム開発者が行えるセキュリティ対策

はじめに

本記事について

ウディタを利用するゲーム開発者が行えるセキュリティ対策について
いくつかまとめたいと思います。

ただし、対策例みたいものを出しているものもありますが
ここでは具体的な実装の話はあまりされません。
攻撃者側がこれを見れば対策されてしまう可能性があるからです。

基本的にはここで知った情報をもとに
各開発者が独自にアレンジや応用して対策していくことが重要です。

こういった対策は対応がどれも面倒で時間がかかるものが多いです。
自分の作品でそこまで対策を施すか必要があるかどうかはよく検討してみましょう。

また、ここで書かれている情報はウディタ用に書かれています。
ウディタ以外でのゲーム開発でも有効なものが含まれていますが
他の環境では手段として適切でない可能性があるので、ご了承ください。

ここで書かれない情報について

対策の手段が書いたままの1通りしかないような情報は
たとえここで記載した内容よりも有用であったとしてもあまり記載していません。

そういったものは各開発者がみんな同じように対策すれば、
攻撃者側も同じように対策を破ることで、一網打尽にされるためです。



セーブデータの改ざん対策

セーブデータの改ざんによる
不正なゲームプレイの対策についてです。

オンラインランキング機能を持つようなゲームを作る際は
特に重要になります。

ウディタのセーブ機能で保存されたセーブファイルも
中身を解析して簡単に読み込めるようなものではないのですが、
大多数のウディタ利用者が使用するため、
何者かによって解析されてしまう可能性が非常に高いです。

とは言え慣れた人でもない限り、
独自にセーブファイルを作るのは敷居が高いと思います。
雑な実装ならウディタのセーブファイルよりも簡単に突破されてしまうでしょう。

特にウディタはユーザーが独自の完全なバイナリファイルを出力できないようになっているため、
保存したファイルは中身が見られやすいテキストファイルになってしまいます。

ここではウディタのセーブ機能を使いつつも
改ざんをなるべく避ける対策方法について記載します。

チェックサムによる改ざん検知

チェックサムと呼ばれる改ざんを検知する一般的な手法があります。

例えばセーブデータとして保存したい複数の数値データがあったとしたら、
それらの数値を全て足して特定の数値で割った余り『検知用の数値』として追加でセーブファイルに保存します。

そしてセーブファイルを読み込んだときに、保存したときと同じように『検知用の数値』を求めなおして、セーブファイルに保存していた『検知用の数値』と比較します。

これで数値が同じじゃなければセーブファイルが改ざんされたことがわかります。

以下にわかりやすく図で示しました。

上記のようにセーブファイルに保存したHPやMPが書き換えられても
読み込み時に検知できるようになります。

もちろん上記例だと検知用の数値が±255の範囲しか取らないため
改ざんされていても偶然検知用数値が一致する可能性はあります。

実際に実装する場合は数値の計算方法をアレンジしましょう。
計算結果さえ一致する処理なら、何をしてもかまいません。

ウディタ特有の計算処理実装時の注意点

ウディタには数値計算が±20億の範囲に丸めこまれるパターンがあります。

例えば『デバッグ文』の隠しコマンドである【変数監視機能 ValWatch】を
使用していると数値計算が常に±20億の範囲に丸め込まれてしまい、
検知用の数値を求めるときにセーブファイルに保存した値との
数値誤差が発生する可能性があります。

これについては注意しておきましょう。
基本的には開発者がテストプレイしているときにしか遭遇しないはずなので
実際のゲームを遊ぶ側に影響はないと思います。

独自チェックサム機能を搭載したセーブコモンを配布したい場合の注意点

基本的には独自でコモンを作って自分自身で利用するほうが安全ではありますが
このような機能を持ったコモンを配布したい場合は
必ず利用者側で計算結果を調整できる機能や余地を用意してあげましょう。
(単体のコモン配布やRPG作成用の独自の大規模コモンにチェックサム機能付きセーブ機能を搭載する場合など)

例えば利用者が変更可能な『キーとなる数値』を用意して
その値次第で計算結果が変わるような仕組みです。

↑で例に挙げていたチェックサムの計算例でいうと割るときに使用していた
『256』を『キーとなる数値』とするようなイメージです。
利用者がこれを『600』にすれば計算結果が変わるので解析されにくくなります。
(みんなが同じものを使うと解析されてしまったときに全ての人に影響がでます)

ゲーム名に空白を入れる

ウディタのセーブ機能は同じゲームから出力されたセーブファイルが読み込めます。
これはゲーム名が一致するかどうかで判定されるので
同じ名前のゲームがあればセーブファイルが読み込めてしまう可能性があります。

ウディタの基本システムを利用している方や
他の人が作成した基本システムのような大規模コモンを使用している方は
同じようなCDB構造になる可能性が高いので読み込めてしまう可能性も高いです。

そこでゲーム名に見えない空白を含めることで
仮に同じ名前のゲームから出力したセーブファイルが読み込まれたとしても
弾ける可能性が高くなります。

もちろん開発中のゲームの名前を変更すると
以前のセーブファイルが読み込めなくなる
のでご注意ください。





不正なゲーム実行を阻止

配布版でのテストプレイ実行を阻止

人によってはテストプレイ中だけ実行される
デバッグ機能を実装している方もいるでしょう。

実装は主に『Sys112:[読]テストプレイ中?(1=YES) 』を判定して
デバッグ機能を動かすかどうかを判断していると思います。

これを無理やりONにされて実行されてしまうケースがあります。

以下のような対策が可能です。

  • .wolfファイルが存在する場合はテストプレイ中判定になっても
    デバッグ機能を実行しない
  • プロテクト用のファイルがあれば同様に実行しない

これについては自分が配布しているコモンがあるので、共有しておきます。
そのまま使ってもよいですし、中身を参考にして独自に組んでも構いません。
https://silsec.sakura.ne.jp/WolfRPGEditor/CommonList/html/tdv201.html#16806970059801

他のゲーム配信プラットフォームに勝手に配信されないようにする

このようなケースがどれだけ発生しているのかは
自分は把握していないのですが、ある程度こういったものを防止する手段はあります。
(この対策を入れても完全に防げる保証はありません)

Steam

Steamで実行する場合下記DLLがゲームに同梱されています。

  • steam_api.dll
    • ↑ウディタの場合はこちらだと思います
  • steam_api64.dll

Steamに配信する予定がないものはGame.exeと同じ階層に
これらのDLLが配置されていないかどうかをチェックするとよさそうです。
見つかったら即時ゲームが終了するようなイメージです。

PLiCy

PLiCy上で動作しているときは
下記システム文字列変数に専用の文字列が格納されるみたいです。

  • システム文字列変数SysS[19](9900019)

PLiCyで配信するつもりがないなら、
上記に『PLiCy』という文字列が含まれているか
チェックすることで弾くことができそうです。


▼ システム文字列変数についての詳細はPLiCyのよくある質問に記載されています
https://plicy.net/ToolFAQ/WolfRPGEditor#3275a3575c128f54cabb900c9d58c140




秘匿性の高い文字列や数値の暗号化

ゲーム実行中のコモンセルフやDBなどの変数に格納されているデータは
外部から読み取られる可能性があります。

チート対策が必要でもない限り、
基本的にはほとんどの変数は読み取られてもかまわないと思いますが
サーバーに接続する際に必要な文字列情報など外部に絶対流出させたくない値
なるべく生の値を変数に持たないようにしましょう。

例えば流出させたくない文字列が『ABC』だとして
使用していない間は暗号化を施して『DEF』に変えておくことで仮に読み取られても、復号化されない限りは問題になりません。

暗号化の方法

暗号化って言われても結局どうすればいいのか
まったく分からない人も多いと思います。

ここでいう暗号化というのは元の値に戻す復号化が可能なものを指します。
暗号化の手法はたくさんありますが
ここではビット演算を利用した暗号化について記載します。

ウディタではビット演算は論理積(AND)しか公式では対応していないため
コモンイベントでビット演算を実装するところから始まります。
(論理積は変数操作にある『ビット積』のことです)

一式実装したコモンイベントは下記で配布しているので
めんどくさくて自分で実装したくない方や参考にしたい方はこちらをどうぞ。
https://silsec.sakura.ne.jp/WolfRPGEditor/CommonList/html/tdv249.html#14400752006401

XOR暗号

ビット演算のXOR(排他的論理和)を使用することで簡単に暗号化と復号化ができます。
例えば以下のようなことができます。

『100』 →暗号化(XOR)→ 『65435』 →復号化(XOR)→ 『100』  

数値『100』と『65435』は一見関連性が無いように見えますが
2進数で表すと次のようになります。

0000 0000 0110 0100 ←100
1111 1111 1001 1011 ←65435

『0』と『1』がそれぞれ反転していることがわかると思います。
こういった反転をXORを使うことで簡単にできます。
反転しているだけなのでもう一度反転すると元の値に戻るという仕組みです。

またウディタで扱う数値は全て『32ビット』です。
これは2進数にすると『0』または『1』が32個並ぶ数値という意味です。

それを踏まえて実際のウディタの数値『100』『65435』を
正しく2進数で表示すると下記になります。

0000 0000 0000 0000 0000 0000 0110 0100 ←100
0000 0000 0000 0000 1111 1111 1001 1011 ←65435

左側にある『0』は反転していません。
XORは反転させたい範囲や位置を自分で決めることができるからです。

例えば上記例では次のようにXORされています。

計算式:100 XOR 65535 → 計算結果 65435

それぞれ2進数で表記
0000 0000 0000 0000 0000 0000 0110 0100 ←100
0000 0000 0000 0000 1111 1111 1111 1111 ←65535
0000 0000 0000 0000 1111 1111 1001 1011 ←65435

『100』に対して『65535』をXORした結果が『65435』になっています。
この『65535』の2進数は右半分が全て『1』です。

『1』になっている箇所が反転される位置を表しています。

XORに使用する値を変えることで反転する位置を自由に変えられます。
この使用する値がバレなければ暗号化された数値は元の値がわかりません。

これがXOR暗号です。

XORは別々の値で連続して反転させても復号化できます。
復号化したい場合は逆の順番でXORを行いましょう。

以下に例を置いておきます。

▼ 58 を暗号化
1回目:58 XOR 255 →計算結果 197
2回目:197 XOR 170 →計算結果 111
3回目 : 111 XOR 240 →計算結果 159
58 を暗号化した結果は 159

▼ 159 を復号化して58を求める
1回目:159 XOR 240 →計算結果 111
2回目:111 XOR 170 →計算結果 197
3回目 : 197 XOR 255 →計算結果 58
159 を復号化した結果は 58

↑で共有していたコモンイベントを導入すればそのまま動かせる
例と同じ処理が可能なイベントコードも貼っておきます。

WoditorEvCOMMAND_START
[103][0,1]<0>()("暗号化と復号化のサンプル")
[121][4,0]<0>(1600020,58,0,0)()
[106][0,1]<0>()("■■■■■■■■■■■■■■")
[106][0,1]<0>()("▼ \cself[20] を暗号化")
[300][5,1]<0>(0,16777218,1600020,255,1600021)("◇排他的論理和演算<XOR>")
[106][0,1]<0>()(">> \cself[21]")
[300][5,1]<0>(0,16777218,1600021,170,1600021)("◇排他的論理和演算<XOR>")
[106][0,1]<0>()(">> \cself[21]")
[300][5,1]<0>(0,16777218,1600021,240,1600021)("◇排他的論理和演算<XOR>")
[106][0,1]<0>()("暗号化結果:\cself[21]")
[103][0,1]<0>()(" ")
[106][0,1]<0>()("▼ \cself[21] を復号化")
[300][5,1]<0>(0,16777218,1600021,240,1600022)("◇排他的論理和演算<XOR>")
[106][0,1]<0>()(">> \cself[22]")
[300][5,1]<0>(0,16777218,1600022,170,1600022)("◇排他的論理和演算<XOR>")
[106][0,1]<0>()(">> \cself[22]")
[300][5,1]<0>(0,16777218,1600022,255,1600022)("◇排他的論理和演算<XOR>")
[106][0,1]<0>()("復号化結果:\cself[22]")
WoditorEvCOMMAND_END

ちなみにWindowsの電卓を使えば
100などの数値(10進数)を2進数にしたときの並びが確認できます。
もちろんXORの計算も可能です。(ビット演算は『ビット単位』を押すと可能)
2進数の並びをささっと確認したい場合に便利です。
(ウディタの処理上で確認したい場合は↑で共有していたコモンイベントに2進数文字列に変換するコモンが同梱されているのでそちらをご利用ください)

▼ 電卓の左上の『三』を押してプログラマーモードへ切り替えると2進数確認が可能


シフト演算による暗号化

上記ではXORによるビットの反転によって暗号化/復号化を実現していました。
ここではビットシフトを利用したビットの移動によって暗号化を行います。

以下に例を出します。

暗号化したい値:96
 0110 0000  ←96
 
 シフト演算でビットを右に3個ずらす
 0000 1100  ←12
 
 暗号化されて『12』が得られる
 
 『12』を復号化する(ビットを反対の左方向に3個ずらす)
 0110 0000  ←96
 
 復号化されて元の値『96』が得られる

このようにビットをシフトすることで暗号化と復号化を実現可能です。

↑で共有していたコモンイベントを利用する場合は
下記コモンで同じことを実現できます。
・◇左ローテートシフト演算
・◇右ローテートシフト演算

ローテートではないシフト演算コモンを利用すると
右端または左端で溢れてしまったビットの情報が無くなってしまうため
復号化できなくなるのでご注意ください。





ウディタ感を減らす

ゲームフォルダを開いて
ぱっと見でウディタとわかるような状態になっていると解析されやすいです。

効果としてはどれも弱いですが
ウディタ感無くすことで抽出されにくくできます。

Game.exeのアイコンを差し替える

これだけでウディタ感を取り除けます。

差し替えている人は多いと思うので、効果としてはかなり弱いですが
ゲーム開発ツール側に詳しくない人が何かしらの抽出ツールを利用している場合
ウディタと気づかれない可能性はあります。

ゲームランチャーを用意する

※この手法はウディコン投稿作品ではできませんのでご注意ください(外部のexeの同梱禁止)

ゲーム起動用のexeを別途用意して、
実際のウディタのゲームフォルダを隠して用意したexeからゲームを起動します。

こうすることでウディタ製のゲームフォルダに良くあるファイル構成が
見られにくくなり、ウディタと気づかれなくなる可能性が増えます。





抽出されてしまった場合を想定した対策

ゲームデータの抽出を完全に100%防ぐ手段は存在しません。
どうあがいても本気で解析されればいつかは抽出されてしまいます。

ゲーム会社が開発したAAAタイトルでも解析されてしまっています。
このことからわかるように個人がどれだけ頑張っても
目を付けれてしまったらいつかは破られてしまいます。

とはいえ何かしら対策が入っている限り、
すぐに破られることはなくなるため、
攻撃者が理解できなかったり、めんどくさくなったりして
あきらめる可能性も高くなります。

ここでは暗号化された.wolfファイルが復号化されてしまい、
全てのファイルが抽出されてしまったとしても
ある程度解析を防ぐ手段について記載します。
※ただしどれも対応が面倒なため安易におすすめできない手法です。

プログラム(コモンイベント)の難読化

難読化について

(最初に言っておきますが、難読化はかなりめんどくさいです。
効果としては理解されにくくなるだけですし、デメリットもあるので
初心者にはお勧めしません)

抽出されるとコモンイベントの中身も全て見られてしまいます。
コモンへの理解がある人に抽出されてしまったなら
改造されてしまう可能性もあります。

とはいえ他人のコードを読むのはそれなりに経験が必要です。
さらにコードが読みにくくなっていれば、なおのことです。

一般的な手法でウディタでもできそうな範囲であれば下記になると思います。

  • コモンイベント名やコモンセルフ/DB名など名前全般を意味のないものにする
  • コメント文/デバッグ文を無くす
  • 不要なイベントコマンドを挟む(意味のない処理)

ただこれらを行うと開発者自身も作業しにくくなります。
もしやるとしたら配布する前に自動で変換するような対応が現実的でしょう。
ただしゲームをアプデする度に
自動変換しなおさないといけないのでめんどくさいです。

CommonEvent.datを別のプログラムで読み込めるようにして
自動変換するプログラムを組めるレベルの人でない限り、
あまりおすすめできない手法となります。

難読化を自動で行う

私が下記で公開している処理最適化用の『WoditorOptimizer』で
似たようなことは可能です。
ただし9年ぐらい前に作った処理なため、やるのは自己責任でお願いします。(ウディタ3に対応済みではある)
https://alpha-stella.com/tools
(一応2025年~2026年あたりに処理を最新にする予定はあります。厳密には今開発中のゲームが完成したあと)

上記ツールで対応できるのは下記です。

  • コメント文を消す
  • デバッグ文を消す
  • コモンイベント名をぐちゃぐちゃにする
    • ※実行できる条件があります
      • \cself[5]など文字列変数でコモンイベントを呼び出していない
      • マップイベントからコモンイベントを名前呼び出ししているゲームでは使えない
  • コモンイベントのメモ欄を空にする
  • コモンセルフ名を空にする
  • コモンイベントの数値/文字列入力の各引数名および返り値名を空にする
  • コモンイベントの色をランダムに変更する
  • ラベル名を変える(ラベル名の最適化処理で変わる)

↓試しに開発中のゲームに上記難読化を適応させてみました。
こんな感じになります。相当解読しづらくなったと思います。
※もちろんゲームは正しく動作します

画像ファイルの対策

どうしても取り出されたくない画像ファイルがある場合は
画像ファイル自体に何かしらの変換をかけておいて
ウディタ上で表示するときに正しい画像に戻す
という手法が使えそうです。

ただし、取り出されたときに正しい画像に戻すのが
とてつもなくめんどくさくなるだけで完全に防ぐことはできないのでご注意ください。
(それにゲーム画面をスクショされれば正しい画像のまま取り出せてしまいます)

例としては以下のようなイメージです。
単に画像が分割されたり、90度刻みで回転しているだけならウディタ上でも元の画像に戻せます。

※改変禁止の素材サイトの画像を使用しているなら、場合によっては改変とみなされる可能性があるかもしれないのでご注意ください。


テキストファイル(CSVも含む)の暗号化

Dataフォルダ内のテキストファイルにゲーム上での会話などの
イベントを記載していることがあると思います。

特に開発に慣れてきた人は
だいたいこのような仕組みを実装している人が多いと思います。
(イベントが書かれたテキストファイル(スクリプト)を読み込んでゲーム内でイベントを動かすなど)

こういったデータの文字列に暗号化をかけておいて
ゲーム内で読み込んだあとに復号化する
ことで
テキストファイルを開かれてしまっても何が書いてあるかわからなくできます。

ただし多言語翻訳とかをする場合に障害になる可能性があるので
海外展開も視野に入れている方は翻訳に影響がないような仕組みを検討する必要があるかもしれません。
(スクリプト内にはテキストのIDだけ入っているようにして別途テキストを管理する対応など)


ファイルの拡張子偽装

.txt/.csvなどのテキストファイルの拡張子をそのままにせず
『.bin』などの別の拡張子に偽装することで何のデータか
ぱっと見わかりにくくなり、解析の手間が増えます。

もちろん拡張子を変えてもゲーム上で正しく動作するかは
よく確認するようにしましょう。


フォルダ構成を分かりにくくする

Dataフォルダ内の各フォルダ名を意味のないものに変えたり
不必要に階層化することで抽出されたときに判別しにくくなります。
※ファイルパスが長くなればなるほどゲーム上でファイルに対する処理負荷が微増していくのでご注意ください

この手法はファイルパスが変わってしまう上に
開発中もめんどくさくなるため、本当に対応する場合は
配布前に実行すれば勝手にやってくれるなどの自動化が必須だと思います。





GET送信とPOST送信の選択

ウディタのダウンロード機能でGET送信/POST送信を利用して
サーバーなどにアクセスすることができます。

サーバーにデータを送る場合にどちらの方法でも送信できますが
GET送信の場合はアクセス用のURLに全てのパラメータが載るため
情報が丸見えになりやすいです。

外部に漏らしたくないようなデータを送信する際は
POST送信でデータを送るようにすることで安全性が向上します。





WOLF RPG エディターPROの利用

プロ版には追加のプロテクト機能が搭載されています。
それを利用することで通常版よりもセキュリティを向上可能です。

ただし、通常の暗号化同様破られるリスクは存在するため
過信しすぎずゲーム開発者自身も何かしらの対策をすることが重要です。

▼ WOLF RPG エディターPRO
https://silversecond.booth.pm/items/4302198
※ウディコンなどでは利用できないのでご注意ください。





まとめ

ここに記載された対策はあくまでも対応例です。
各作者が『こういう風にアレンジすればもっと安全になるんじゃない?』
といった形でアプローチを変えたり応用したり、
複数の対策を組み合わせて自身の作品を守ることが重要です。
(POST送信でデータを送るときに暗号化してチェックサムで改ざん検知もするみたいなイメージ)

ここまで読んでくださり、ありがとうございます。

自身の作品に対する解析やチート行為で悩んでいる方や
対策したいけど何を考えればいいのかわからなかった方に
何かしらの気づきや対応のきっかけを与えられていたら幸いです。

記事のタグから探す

月別アーカイブ

限定特典から探す

記事を検索