株式会社Ninjastars 技術研究部

「もっと楽しく安全なゲームの世界を創る」

自作ゲーム:チートチャレンジ

代表の森島です。

 

近年、簡易的に扱えるチートツールが出回ることによってデバッガを用いたメモリチートは随分とカジュアルに行えるような環境になってきました。
gdbやstraceのようなデバッガが実際にどういう仕組みで動いているか疑問に思った方は少なからず、いらっしゃると思います。
実際、このようなデバッガはptraceと呼ばれるデバッグシステムコールをベースにして作られています。
こういったデバッガの仕組みをより多くの方に知っていただきたいと思い、このような形で皆様に共有いたします。
また実際に市場に出回っているゲームに対して、チート行為を行うと問題ですので今回は別にデモ用の自作ゲームを用意しました。

 今回の動作環境はUbuntu18.04を想定しております。

game.c

#include<stdio.h>

int money=100;

int main(void)
{
int player;
int computer;
int flag;
   
    while(1)
   {
    srand(time(0));
    printf("PLAYER(0:Rock、1:Scissors、2:Paper)");
    scanf("%d",&player);
    computer=rand()%3;
    flag=(player-computer+3)%3;
    printf("player:%d,computer:%d,money:%d\n",player,computer,money);

          if(flag==0)
         {
          printf("Draw\n");
         }
          else if(flag==1)
         {
          printf("Lose\n");
          money-=10;
         }
          else if(flag==2)
         {
          printf("Win\n");
          money+=10;
         }

          if(money<=0)
         {
          printf("Game Over\n");
          break;
         }
   }
return 0;
}

コードの不自然さやゲーム性についてはご了承ください。できるだけシンプルに書いております。

ゲームについての説明

簡単に言うと、コンピューターとじゃんけんをするだけのゲームです。
スタートは100のmoneyを持っていて、コンピューターに勝てば10増え、負ければ10減るという仕組みです。

f:id:Ninjastars:20181128173821p:plain
参考画像


ただ単純にじゃんけんをしているだけではmoneyをなかなか増やすことができません。
なのでこのmoneyの部分をptraceを用いて書き換えてみようというわけです。

次はptraceを用いたメモリの書き換えをするC言語のソースです。
hello_ptrace.c

#include<assert.h>
#include<sys/ptrace.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>

 int main(int argc,char **argv)
{
 assert(argc==4);
 pid_t pid=atoi(argv[1]);
 void *address=(void*)strtol(argv[2],NULL,0);
 void *data=(void*)strtol(argv[3],NULL,0);

 assert(ptrace(PTRACE_ATTACH,pid,NULL,NULL)==0);
 wait(NULL);
 assert(ptrace(PTRACE_POKEDATA,pid,address,data)==0);
 assert(ptrace(PTRACE_DETACH,pid,NULL,NULL)==0);
 return 0;
}

 こちらのプログラムは下記のリンクを参考にさせていただきました。

普通のやつらの下を行け: ptrace で実行中のプロセスにちょっかいを出す - bkブログ

hello_ptraceについての説明

今回はptrace関数のPTRACE_POKEDATA命令を使って特定のメモリアドレスを書き換えます。

直前のptrace(PTRACE_ATTACH,pid,NULL,NULL)が子プロセスを生成するため、wait関数を使って、子プロセスが終了するまでの時間稼ぎをしています。

使い方としては
「./hello_ptrace プロセスID 書き換え先アドレス 書き換えたいデータ」
で書き換えが実行できます。

hello_ptraceの実行はroot権限で行う必要があります。
またプロセスIDはプログラムを実行した状態で「ps -ax|grep game」といったコマンドなどで確認できます。

今回はプロセスIDが28903で、moneyが格納されているアドレスが0x0804a034でしたので以下のコマンドで実行しました。
「./hello_ptrace 28903 0x0804a034 0x10000000」
(とりあえず書き換え後のmoneyは0x10000000=268435456ぐらいにしました。)

書き換え後にもう一度、じゃんけんをするとこのように見事に書き換わっています。

f:id:Ninjastars:20181128173821p:plain
書き換え後

まとめ

この記事では普通の.dataセクションの書き換えを実際にやってみました。

しかし、ptraceの真価はこのようなものではありません。

(例)
通常は.rodataセクションを書き換えようとすると、Segmentation Faultを起こします。しかし、ptraceのようにカーネルの権限で書き換えると、何の異常もなく実行できる。

このような万能なptraceなので、もちろん実行にはroot権限が必要という訳です。



上記のコンパイル済みのプログラムはGoogle Driveに上げていますので、手元の環境で試してみたい方はこちらからダウンロードお願いします。
自作ゲーム:チートチャレンジ - Google ドライブ

なお、これらのソースは
gcc -m32 -no-pie -o game game.c」
gcc -m32 -no-pie -o hello_ptrace hello_ptrace.c」
というコマンドでコンパイルしました。
※hello_ptraceは自己責任にてコンパイルしてください。

注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。


本レポートについて

お問い合せ
E-mail:morishima@ninjastars-net.com

株式会社Ninjastars代表取締役
島健