ゲームセキュリティ再考 ~スピードハック(Speed Hack)の原理とその対策~
株式会社Ninjastars
セキュリティエンジニア:一瀬健二郎
CheatEngine・うさみみハリケーン等のプロセスメモリエディタツールにはゲーム内時間を加速・減速させる機能が付属しています。
このような機能は一般的にスピードハック(Speed Hack)と呼ばれています。
この機能は脅威となる場合もあるため、簡単な原理の紹介と対策をご紹介いたします。
※詳細な実装などについては触れません。


原理
様々な手法がありますが、Cheat Engine等では時間関連のAPIを改変することで実現します。
Windows上でCheat Engineの場合Speed Hackを実現するにあたり改変するWindowsAPIは以下の3つです。
GetTickCount
GetTickCount64
QueryPerformanceCounter
これらの時間関連のAPIの戻り値を以下の計算式で改変します。
start_time =スピードハック開始時間 current_time = 現在時間 return_time = 戻り値 speed = 加速・減速率 return_time = start_time + ((current_time - start_time)*speed)
このタイプのSpeed Hackは上記のように経過時間をシステムに誤認させることで加速・減速を実現させます。
またAPIの改変は一般的に以下のような手法で実現されます。
・先頭処理をjmp命令に書き換え、偽の関数に置き換える。
・関数アドレステーブル(IAT等)の値を偽の関数のアドレスに置き換える。
etc
対策
一例としてCheat EngineのSpeed Hackを検知するシステムを考えます。
上記に記載した通り、Cheat Engineは3つの時間関連のAPIのみ改変します。
つまりこの3つに依存しない別の時間関連の関数で経過時間を計測し、比較することでSpeed Hackを検知することが可能です。
以下のプログラムをVisualStudio等でビルドし、Cheat EngineでアタッチしてSpeed Hackを有効にしてみましょう。
Speed Hackを検知してシステムが終了するはずです。
DetectSpeedHack.cpp
#include <windows.h> #include <iostream> using namespace std; int main() { DWORD start_time = 0; DWORD elapsed_time = 0; while (true) { //開始時刻 start_time = GetTickCount(); //1秒待機 Sleep(1000); //GetTickCountによる経過時間を取得 elapsed_time = GetTickCount() - start_time; //GetTickCountが改変されSpeed Hackされてる場合プロセス終了する。 if (elapsed_time < 900 || elapsed_time > 1100) { ExitProcess(0); } else { cout << elapsed_time << endl; } } return 0; }
※勿論Sleep関数も同時に改変された場合、上記は有効ではありません。
Speed Hackに対しては様々な対策があると思いますが、以下は例です。
・定期的なサーバとの経過時間の整合性チェック。
・時間関連の関数の実行プログラムのハッシュチェック(CRC等)。
・関数に依存しない経過時間の取得。
・DLLインジェクション等の阻止(一般的に偽の関数の用意のためにDLLインジェクションが行われるため)。
etc
まとめ
セキュリティ対策全般において、敵を知ることにより様々な視点からの対策が可能になると思います。
今回の記事が読者の皆様のセキュリティ対策の一助となれば幸いです。
注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。
本レポートについて
お問い合せ
E-mail:ichise@ninjastars-net.com
株式会社Ninjastarsエンジニア
一瀬健二郎
pwnの歩き方 ハリネズミ本を読む
株式会社Ninjastars
セキュリティエンジニア:一瀬健二郎
今回は通称ハリネズミ本と呼ばれる、「セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方」のpwnの項目について書かせていただきます。
素晴らしい内容の本ですが、独学で学ばれてる方などの場合躓いてしまう部分などもあると思うため補助的な解説をさせていただきます。

相違点
gdb-peda
python3の対応も進んだので、longld/pedaを利用します。
gdb-peda超入門 - Qiita
バッファオーバーフロー時の挙動
本の通りのpayloadだとmain関数内でbofを発生させると上手くeipが書き変わらないようです。
bofさせる処理を関数化しましょう。
バッファオーバーフローでeipが書き換えられない - fr33f0r4ll
#include <stdio.h> void bof(){ /* バッファオーバーフローさせる処理を書く。 */ return; } int main(int argc, char** argv){ { bof(); return 0; }
バッファオーバーフローについては当ブログでも記事にしています。
ソフトウェアバグ -Segmentation Fault- - 株式会社Ninjastars 技術研究部
gccのコンパイルオプション
gccのver7.4.0ではコンパイル時のデフォルトの挙動が変わっています。
・PIEについて
デフォルトでPIEが有効になっているため「-no-pie」オプションをつける必要がある。
gcc -m32 -no-pie -fno-stack-protector -o bof3 bof3.c
・RELROについて
gccのデフォルトではFull RELROとなりますが、-no-pieを付ければPartial RELROとなるためGOT Overwriteは実行可能です。
gccのオプションとセキュリティ機構の関係は下記の方のブログを参照ください。
セキュエン備忘録: [Linux] GCCのオプションとセキュリティ機構の関係
当ブログの記事でもgccのコンパイルオプションについて説明しています。
自作ゲーム:チートチャレンジ2 - 株式会社Ninjastars 技術研究部
Tips
pattoのoffsetについて
gdb-peda$ patto AFAA AFAA found at offset: 44
pattoのオフセットの位置は0から始まっています。
オフセットが44の場合最初の'A'は44文字です。
objdumpについて
最初はオプションが難しく感じるかもしれません。
コマンドのオプションの意味を理解し、覚えてしまいましょう。
Man page of objdump
ret2pltが分からない
sample.c
//gcc -m32 -no-pie -fno-stack-protector -o sample sample.c #include<stdio.h> char buffer[32]; int main(int argc,char * argv[]) { fgets(buffer,32,stdin); printf(buffer); return 0; }
理解のために、上記サンプルを例にします。
gdbでsampleを動かしてみましょう。
gdb -q sample b printf@plt r //標準入力からhelloと入力

0x80484bc (<main+70>: add esp,0x10)
スタックの最初に積まれている上記部分は、call命令でprintf@pltを呼び出した次の命令となります。
つまり正常にprintf@pltを呼び出したときのスタック状態は
リターンアドレス(printf@pltから戻った時の、次の命令のアドレス)
引数1
ret2pltは上記を再現しているという訳です。
ここで重要なのは何かわからない部分があったら実際に手を動かしてみる、理解できるまで可能な範囲で考えてみるというアプローチの仕方であると思います。
注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。
本レポートについて
お問い合せ
E-mail:ichise@ninjastars-net.com
株式会社Ninjastarsエンジニア
一瀬健二郎
「Victor of Cyberists」 問題紹介
株式会社Ninjastars
セキュリティエンジニア:一瀬健二郎
2019年6月30日にセキュ塾 x Ninjastarsが共同開催したCTF大会「Victor of Cyberists」での問題に関しての紹介をさせていただきます。
Reversing 7問
Game 3問
Web 2問
Forensics 1問
という問題構成でした。
Gameというジャンルですが、ゲームセキュリティを題材とした問題のジャンルとなります。
これは実際のゲームにおける攻撃・防御手法を題材にした問題として作問・調整させていただきました。
オフラインCTFという性質上、今回は問題の紹介に留めさせていただきます。
Reversing
全てLinux ELF 32bitバイナリ。
IDA・Ghidraやgdbを利用して解くことを想定し、シンプルながら時間をかければ解けるように調整しました。
crkme
crkme2
crkme2a
crkme3
please_decrypt_me
hidden_password
baby_ransomware
Game
全てWindows用のEXE。
IDA・Ghidra、dnspy、CheatEngineなどを利用しての解法を想定。
left_5_seconds
5秒以内に30個のパネルをクリックするとフラグが現れる。

Gacha
12個の誕生石を全てコンプするとフラグが現れる。所持オーブが120で消費が10なので理論的には偶然クリアも可能。
全問題通して唯一解かれなかった問題。

※web、forensicsについては省略
感想.
今回メンバー全員で事前準備を入念にした甲斐があってか、予想以上に大好評で多くの参加者の皆様にご満足いただけたようです。
特にGameのジャンルが高評価だったようで、ゲームセキュリティ専門企業としての期待に応えられたことを非常に嬉しく思いました。
こういったCTF大会は定期的に開催する予定であり、問題に関してのアイデア等はほぼ無尽蔵に所持しておりますので乞うご期待ください。
今後もメンバー一同で常に勉強・研究を重ね問題作成・情報提供などで一層お役に立てるよう邁進していきます。
注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。
本レポートについて
お問い合せ
E-mail:ichise@ninjastars-net.com
株式会社Ninjastarsエンジニア
一瀬健二郎
「Victor of Cyberists」大会レポート
株式会社Ninjastars
エンジニア:高橋遥
皆様こんにちは、Ninjastarsの高橋です。
本記事では大会の様子のご紹介と次回セミナーの告知をさせていただきます。
6月30日に弊社オフィス:TUNNEL TOKYOにてCTF大会を開催致しました。
参加された皆様につきましては、ご来場いただき誠にありがとうございました。
また、6時間以上にも及ぶ問題との格闘、お疲れ様でした。

今回のVictor of Cyberistsでは当日知り合った参加者同士でチームを組み、
大会に臨むという形式で互いの技術を競い合っていただきました。
問題を解き進めていくにつれてチームメンバー同士で打ち解け合い、情報の共有を行っているといったような光景が多く見られました。
このような形でゲームセキュリティを通して新たなコミュニティ形成の一助になれば幸いに思います。


6時間の大会時間を経て優勝したのは「TEAM_HOSEI」でした!
第2位だった「One」の追い上げを見事振り切っての優勝でした
おめでとうございます!
大会後は「女神転生」シリーズを手がけたゲームプロデューサー:鈴木一也様と
セキュリティコンサルタント:中澤講師(セキュ塾)、
そしてNInjastars代表取締役の森島の三名でゲームセキュリティの歴史と現状、今後について
パネルディスカッションを行いました。

今後もNinjastarsではゲームセキュリティの意識向上と普及を目指して大会やセミナーを実施致します。
次回の開催は8月末〜9月上旬を予定、
第2回Defense against Game Hackです!
今回のCTF大会とは違い、セミナー形式でゲームセキュリティをお伝えする場となる予定です。
皆様のご参加、お待ちしております!
注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。
本レポートについて
お問い合せ
E-mail:takahashi.haruka@ninjastars-net.com
株式会社Ninjastars
エンジニア:高橋遥
ランサムウェアへの挑戦 -選ばれし者たちへ
株式会社Ninjastars
取締役:齊藤和輝
ランサムウェアについて
ランサムウェアとはマルウェアの一種であり、
感染すると攻撃対象のマシン(PC/スマートフォン)上でのユーザー操作が制限されてしまいます。
現在、ランサムウェアは暗号化タイプと非暗号化タイプの2種類に分類できます。
暗号化ランサムウェアでは攻撃対象の外部記憶装置のデータを暗号化し、ファイルへのアクセスを制限します。
それに対して、非暗号化ランサムウェアではポルノ画像やグロ画像を表示し続けることでシステムへのアクセスを制限してきます。
どちらのタイプであってもPCやファイルを人質にとり、制限を解除したければ金を払えという風に金銭を要求します。
今回の記事では「Reversing.Kr」のRansomwareを題材にランサムウェアについて解説していきます。
Reversing.Krとはリバースエンジニアリングの技術を学ぶための演習問題を公開しているサイトです。
問題によっては配布されるプログラムがマルウェアとして認識されてしまう場合もありますが、
あくまでソフトウェア解析技術の向上を目的としたプログラムなのでご安心ください。
Reversing.Krのリンクはこちら
reversing.kr
本記事は常設CTFの問題のネタバレを多く含みます。ご覧いただく際はご注意ください。
今回の演習問題では元々実行可能ファイルであったはずの「file」がランサムウェアに人質として捕らえられ、実行不能なファイルになっています。
問題回答までの流れとしては、ランサムウェア本体であるrun.exeを解析して、fileがどのように暗号化されているのかを明らかにしたあと、元に戻して実行可能な状態にまで持っていきます。
まずはrun.exeを解析していきましょう!
実際にrun.exeを実行してみると、文字化けしています。 見事フラグを出すことに成功しましたね!お疲れ様でした! 本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。 本レポートについて 株式会社Ninjastars
元が韓国語なので私にとってはどちらも解読不可能な文字列ですが...
ここで注目すべきはProgram Trees内のUPXという文字です。
これはUPXと呼ばれるパッカーでパッキングされていることを表しています。
マルウェアはお決まりのパッキング手法というものがなく、独自の手法でパッキングされていることが多いのでマニュアルアンパッキング技術が必要になります。
こちら⬇の記事でマニュアルアンパッキングの基礎と手法をご紹介しているので、こちらを参考にしながら実際にアンパッキングに挑戦してみましょう。
www.ninjastars-net.com
ここからはアンパッキングしたrun.exeを見ていきます。
このプログラムではKeyを要求する際に入力した文字列を取得している関数が必ず存在するので
Symbol Tree->Imports->MSVCR100.DLLから探してみましょう。
scanf関数があるのでXREF[1]から見ていくと、0x44A792で呼び出されていることがわかりますね。
次に動的解析ツールのOllyDbgを使って0x44A792にブレークポイントを設定していきます。
この処理の周囲(0x44A7E2)にfopen関数があるのでfileを操作しているのであろうことが予想できます。
getc関数の戻り値はEAXに格納されているので、EAXをいじっている命令を探していきましょう。
すぐ近くの0x44A87Cでの
MOV BYTE PTR DS:[ECX+5415B8],AL
という命令でEAXのデータをECX+5415B8に展開していることがわかります。
展開が終了する0x44A897にブレークポイントを設定し、中身をすべて展開してみましょう。
次にメモリ上に展開されているバイナリをいじっている命令を探しにいきます。
0x44A8CF付近にて入力した文字コードとバイナリのXORをとり、
さらに0xFFとXORをとっている命令群があります。
おそらくこの命令群が復号化ルーチンだと予想できます。

試しに「abcde」を入力してみます。
先ほどの式では以下のように変換されるはずです。

また、入力した文字は5文字でしたがfileのバイナリ全体が変換されているので
5文字を循環させて復号化していることが想像できます。
5バイト目を計算してみると
「0x0D=0x93 xor 0x61 xor 0xFF」であることがわかります。
これで循環していることが確認できました。
次に何文字で復号できるのかを見つけていきましょう。
前提条件としてfileをPE形式の実行ファイルにしなければいけません。
そのために適当な実行ファイルをStirlingで確認しましょう。
そこを揃えることを考えます。
入力すべき文字は次の式で求めることができます。
「letsplaychesslet」となりますが、「let」という文字列が2回登場しているので
「letsplaychess」だけでいいことがわかります。
これをKeyに入力しましょう。

file.exeにリネームして実行してみましょう。
本記事で学んだ技術、知識をさらなる学びの足がかりにしていただければ幸いです。
それではまた次回!
注意事項
お問い合せ
E-mail:saito@ninjastars-net.com
取締役:齊藤和輝
NinjastarsにとってのCTFとは
株式会社Ninjastars
代表取締役の森島です。
近年、loTや仮想通貨といった分野でのデータ流通量が増加することに伴い、セキュリティの需要が急速に拡大しています。
そのため、IT全般において幅広い知識を持った優秀なセキュリティ人材(ハッカー)を育成する手段として、CTF(Capture The Flag)と呼ばれるものが注目を集めています。
CTFとは
まずCTFとは簡単に言うと「情報セキュリティの技術を競うコンテスト」です。
情報セキュリティに関する実践的な練習台として、私も初めて参加してからもう3年が経ちました。
CTFは1年中、世界各地で開催されています。
具体的な大会スケジュールはこちらをご覧ください。
ctftime.org
SECCON CTFとは
こういった数多くのCTFの中でも「SECCON CTF」は日本最高峰と呼ばれている大会です。
私自身、ここ2年はチームで予選に参加し、両年とも無事予選通過することができました。
(2017年は決勝大会にも参加させていただきました。)
まとめ
弊社では数人の社員で1つのチームを作り、こういったCTFに定期的に参加しています。
CTFでは日頃から蓄えた情報セキュリティに関するあらゆる知識を総動員して挑むこととなり、社員間の結束も強まっていると感じております。
当ブログではCTFの問題などを題材に実際に脆弱性を突くまでの流れや、それを元に攻撃を失敗に終わらせるための防御手法などについて解説いたします。
弊社のブログを通して、攻撃手法を正しく理解し、最適なセキュリティ対策を考えるためのきっかけにしていただければ幸いです。
本レポートについて
お問い合せ
E-mail:morishima@ninjastars-net.com
CTFから学ぶ! Harekaze CTF 2018 15 Puzzle
株式会社Ninjastars
セキュリティエンジニア:一瀬健二郎
今回は2018年に開かれたHarekaze CTF 2018のReversingの問題「15 Puzzle」について記載します。
この問題を通して.NET製アプリケーションがどのような構造を持っているか解析してみたいと思います。
解析ツールとしてCheat Engineとdnspyを利用します。
またCheat Engineについてはツールの詳細な使い方は省略します。
製作者のst98様のwriteup
Harekaze CTF 2018 で出題した問題 (Obfuscated Password Checker, Sokosoko Secure Uploader, 15 Puzzle, Logger) の解説 - st98 の日記帳
問題について
dnspyでデコンパイル->改変->再コンパイルを行い実行すると、実行プログラムのILのバイト列を元にフラグを生成しているため文字化けして正しい答えが現れません。
C#のソースコードを解析し正しい改変を行い解くのが正攻法であると思われますが、別の解き方をしてみます。
解析
プログラムを起動し何度かパネルのボタンをクリックします。
Cheat Engineでアタッチし、Memory View->View->Enumrate DLL's and Symbols
これはプロセスにロードされた全モジュールと全シンボルを列挙してくれる機能であり、以下のような結果となります。

Form1::IsPuzzleSolvedの項目をダブルクリックしてみましょう。
するとILからアセンブリに変換された、実際に内部で動作しているプログラムを見ることが出来ます。

isPuzzleSolvedなので戻り値が必ず1になるように関数を改竄します。
mov eax,1 ret
パネルをクリックするとScoreが1ずつ増えていくことが分かります。
またILを直接改竄しているわけではないので、CRCによるチェックとも無関係です。
通常の解法と同じでパネルを1000回クリックすればフラグが現れます。
このように状況によってはdnspyでデコンパイル->改変->再コンパイルをする必要はなく、動的に生成されたアセンブリを直接改竄する場合の方が早いことがあります。
今回は関数名から改竄すべき処理がすぐわかりましたが、関数名が難読化等されている場合のアプローチを考えてみましょう。
解析2
private int[] panels = new int[16];
dnspyのデコンパイル結果からpanelsはint型の配列であるので、16進バイト列でメモリ検索してみます。
左上から右に行く順番でデータが配置されてると予想して、下のような画像の場合数値はリトルエンディアンなので以下のバイト列で検索します。
バイト列:03 00 00 00 0f 00 00 00 ** ** ** ** 09 00 00 00(10進:3 15 * 9)
数字がない場合の数値は予想できないので、ワイルドカード*を利用して検索します。
下記の画像のように1つ検索が見つかりますが、この数値がパネルの情報を保持しているアドレスとなります。
※今回はint型配列であると分かっている前提ですが、仮にpanels変数自体も難読化され分からなくてもプログラミングのセオリー的なことからある程度データ構造の予想をすることはできます。

ここからのアプローチは下のように考えてみます。
1.クリック時に各パネルが正しい位置にあるか判定する処理が存在するだろうと予想する。
2.パネル情報を保持しているメモリアドレスにハードウェアブレークポイントを設置する(参照、書き込みを検知)。
3.パネルの数値の比較判定条件を常にtrueにする。または元の処理を探り改竄する。
etc
左上のパネルに該当するアドレスにブレークポイントを設置しパネルをクリックしてみます。
下記のような処理がヒットしたので一番上の命令のアドレスに逆アセンブル画面を移動させます。

mov eax,[ecx+esi*4+08] lea edx,[esi+01] cmp eax,edx je 007C20A5
上記を
cmp eax,edx=>cmp eax,eax
に書き換えます。
実際にパネルをクリックすると常にScoreが+1されていくことが分かりました。
このように断片的な情報からデバッガなどを利用して解析していくアプローチは様々な場面で有効です。
また逆に対策する側としても関数名さえ分からなければ解析できない等と安易に考えることは危険です。
注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。
本レポートについて
お問い合せ
E-mail:ichise@ninjastars-net.com
株式会社Ninjastarsエンジニア
一瀬健二郎
