Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)

Akatsuki Hackers Labは株式会社アカツキが運営しています。

アカツキゲームスのクライアントエンジニアインターンに参加しました

はじめまして。アカツキゲームスのクライアントエンジニアインターンとしてお世話になりました、今井と申します。

今回とあるゲーム開発プロジェクトに6/6 ~ 6/24の3週間ほどお邪魔させていただきました。

本記事では、今回のインターンで私が取り組んだことや学んだことについて書きたいと思います。

インターン開始前

インターンの実施開始1ヶ月ほど前に、メンターの方とインターンの内容について事前面談を行う機会がありました。

その面談で、事前に勉強しておいてほしいものとしてUniRxとUniTaskがありこの本をアカツキゲームスさんから頂いて勉強しました。また、課題として触っておいてほしいゲーム等の指示もありゲーム会社らしさを感じました。

課題について

僕の配属されたプロジェクトでは、課題をあらかじめ用意してくださっていたのでそれに従って課題を進めることにしました。

提示された課題は大きく分けて2つで、1つがUIの裏地にぼかしをかける機能の実装、もう1つがデバッグ用のキャラクタービューワーの作成でした。

UIの裏地にぼかしをかける機能はある程度できていたものが既にあり、これの性能向上及び使用するアーティストの方の意見を参考に改修するといったものでした。

キャラクタービューワーの機能については、全く作成されていない状態から1から要件に従ってビューワーを作るといったタスクでした。

UIの裏地にぼかしをかける機能

既にUIの裏地にぼかしをかける機能はできていましたが、アーティストの方との意見交換で以下のような要望が得られました。

  • UIの裏にUIがあったときにぼかしをかけても裏にあるUIがぼけない
  • UIのぼかしのかかり具合を徐々にアニメーションさせたい

また、メンターの方とのミーティングで以下のような問題があることも分かりました。

  • 今のぼかし機能だとゲームが実行されてから毎フレームずっと実行されてしまっている処理であり重い

これらを解決するために以下のように既存のぼかし機能を改修しました。

  • ぼかしをかける前のテクスチャ取得タイミングをUIが表示された後にずらすことによってぼかし対象にUIまで含められるようにした
  • ぼかしのかかり具合はテクスチャのアルファ値を時間で変化させることによって徐々にぼけて見えるようにした
  • ぼかし処理がずっと実行されないように、ぼかしをかけるテクスチャを貼り付けるUIが表示される瞬間のタイミングを取ってきて一回だけぼかしの計算を行うように変更

キャラクタービューワーの作成

キャラクタービューワーの作成は、何も作成されていない状態からデバッグメニューに機能を追加するといったもので、エンジニアの方との用件定義で以下のような仕様にすると話しました。

  • エディタと実機の両方で確認したい
  • キャラリストやモーションリストをプルダウンから選んで再生できるようにしたい
  • マウスやスマホのタッチ操作でカメラの移動や回転を行いたい

プロジェクトの規模が大きかったのでデバッグメニューを扱うコードがどこにあるのか調べるのに時間がかかったり、機能をどこにどのように実装すればいいのかが分からず実装に苦戦しました。こういった開発経験は初めてなのでかなり勉強になる部分が多かったです。

インターン期間終盤に、自分なりに考えて1通り機能を実装したものを実機でビルドしてエンジニアの方にフィードバックを貰いました。

私が考えて実装していたカメラ操作は1本指スワイプでスワイプしている間ずっとカメラ移動をし、キャラクターの回転はボタンを押して操作するといったものでした。

フィードバックの結果、カメラ操作は2本指スワイプでスワイプした移動量分だけカメラ移動をするように、キャラクターの回転は1本指でスワイプした分だけ回転させるといった実装に変更することにしました。

この操作周りの仕様は、具体的な指示があったわけではないので自分で考えて実装した部分だったのですが、結果的に改修することになったので作り始める前にあらかじめ相談してどういった入力にするのかを決めた方が良かったと感じました。

キャラビューワーの方は、最終的に要件に加えて追加でアニメーションの再生やライティング調整の機能を追加しプルリクエストを本番環境ブランチにマージするところまで出来ました。

インターンを終えて

リモートワークならではの取り組み

今回のインターンは、初日と最終日のみ出社してそれ以外は完全にリモートで行いました。インターン前はリモートワークだとコミュニケーションが疎かになったりするのではないかという心配がありました。

しかし、私の所属したチームでは朝にあさまると呼ばれるチームのメンバーが各々が好きな物事を紹介する時間があったり、事前に趣味のアンケートをとって似たような趣味同士の人でブレイクアウトルームを作って話し合う機会などがあり、チームの色々な人を知ったり交流することができました。

また、チーム全体で誰かが発信したときにそれにリアクションするということが徹底されていると感じました。例えば、Zoomで発表する時があったのですが沢山メンバーの方がコメントをつけてくださり、リモートながらコミュニケーションを相互に行いつつ発表することが出来ました。

研究活動等でリモートで発表することは多々あるのですが、何も反応がつかず寂しい思いをするといったことが多くあったのでこういった文化が形成されていることは素晴らしいと思います。

反省点

チームによって方針がかなり違う部分があり、僕のチームはかなり自主性を重んじる文化で基本的に個人の裁量に任せてくれる部分が大きかったです。

したがって、仕様をどうしたらいいかよく分からない部分は基本的に自分から聞きに行く必要がありました。しかし、あまり自分から聞きに行くことができず結果として時間の無駄になってしまう部分があったことが反省です。

しかし、この個人の裁量に任せてくれるという部分はプラスに働いていた部分もあると感じていて、どのように実装するのかといった部分から自分で考えて自由に実装することができたところもありました。特にキャラビューワーは自分でこうやりたいと考えた機能を追加できたので良かったです。

良かった点

私にとって、実際に機能を使うアーティストの方から意見をもらいつつ自分で改善案を考えて実装するといったサイクルを体験できたことは非常に価値のあるものでした。

私は今まで個人で開発することが多かったので、ブランチを切って実装、プルリクを上げてレビューしてもらう、修正する、マージするといった一通りの流れを経験できたことも新鮮でした。特にレビュー内容は気付かされることが多くかなり学びになったと思います。

また、インターンを通して自分のスキルで足りていない部分と通用していそうな部分が分かり、社会に出る前にどういったスキルを学ぶべきかの方針がついたことも良かったです。

ゲーム系のインターンに関して、実際の環境で業務を行うといった内容を実施している企業さんが少ないためアカツキゲームスさんのインターンは貴重だと感じます。

最後に

メンターの方をはじめとして、関わってくださるチームメンバーの方々がとても暖かく楽しくインターンを行うことができました。3週間という短い期間でしたがありがとうございました。

AWS Device Farm で Airtest を動かす方法

AWS Device Farmとは?

https://docs.aws.amazon.com/ja_jp/devicefarm/latest/developerguide/welcome.html

Device Farm は、アマゾン ウェブ サービス (AWS) によりホストされている実際の物理的な電話やタブレットで、Android や iOS、およびウェブアプリケーションをテストしてやり取りできるアプリケーションテストサービスです。

  • フレームワークを使用したアプリケーションの自動テスト
  • リアルタイムリモートアクセス

の二つのサービスがあり、今回は前者の自動テストについての内容です
自動テストサービスの一連の流れは以下の記事がわかりやすいです
AWS Device Farm で Appium を使って Androidネイティブアプリの自動テストを試してみた

対応フレームワークは以下があります

  • Appium
  • Calabash
  • Instrumentation(Android)
  • UI Automator(Android)
  • UI Automation(iOS)
  • XCTest(iOS)

Airtest とは?

https://airtest.netease.com/

With image recognition and UI hierarchy, our automation framework support game testing (訳: 画像認識とUI階層化により、ゲームテストをサポートする自動化フレームワークです。)

CEDEC2020ではその利用例が紹介されています
AirtestとPocoとOpenSTFによるUnity製スマートフォン向けゲームの実機自動テスト環境構築とその利用方法

一般的なモバイルアプリケーションの自動化は、アクセシビリティや自動化がサポートされたUI要素を指定しアクションを行います
しかし、ゲーム(正確にはゲームエンジン)のUI要素はレンダリングエンジンが描画したただのピクセルなため、外部スクリプトから直接UI要素を指定することができません
そのため、画像認識や内部にエージェントを組み込むなどしてUIを指定します

AWS Device Farmでサポートされているフレームワークの中ではAppiumが画像認識でのテストをオプションでサポートしています。しかし、Device Farmでは現在実行することができません(2022/06時点で確認済み)

AWS Device Farm 上で Airtest を動かすには

公式にサポートはされていないフレームワークでも、YAML 形式のテスト仕様 (テストスペック) にスクリプトを書くことで実行することが可能です

対応フレームワークに見せかける

テスト一式(test_bundle.zip)をアップロードする際にはフレームワークごとの形式に沿っている必要があるので、Appium Python と認識されるために必要最低限の体裁を整えます

構成例(pytestっぽくする)

.
├── requirements.txt(Appiumが含まれたもの)
└── tests(「tests」という名前のディレクトリ)
    ├── smoke.py(任意のAirtestファイル)
    └── test_main.py(test_hoge or hoge_test という形式の名前.py)

test_main.pyはこのようにAirtestのファイルを呼び出すようにします

import sys

def run_test():
    import smoke  # Run top-level commands

print("[TEST_SCRIPT]Start Airtest", file=sys.stderr) # Device Farmで標準出力がバッファリングされて見にくいのでstderrへ
run_test()
print("[TEST_SCRIPT]Finish Airtest", file=sys.stderr)
# 起動確認程度の簡単なスモークテスト
from airtest.core.api import *

CWD = os.getcwd()
PKG = os.environ['APP_PACKAGE']
LOG_DIR = os.environ['LOG_DIR']
auto_setup(__file__, logdir=LOG_DIR, devices=["Android:///", ])

if PKG not in device().list_app():
    install(CWD + "/" + os.environ['APP_PATH'])
start_app(PKG)

assert_exists(Template(r"tpl0000000000.png", "スタート画面があること")

uninstall(PKG)

テストスペックは以下のようにします

version: 0.1

phases:
  install:
    commands:
      - export PYTHON_VERSION=3
      - cd $DEVICEFARM_TEST_PACKAGE_PATH
      - . bin/activate
      - pip install --upgrade pip
      - pip install -r requirements.txt

  pre_test:
    commands:
      - cd $DEVICEFARM_TEST_PACKAGE_PATH
      - echo "Pre Test. OK"

  test:
    commands:
      - echo "Test Start"
      - export APP_PACKAGE='jp.hoge.huga.dev'
      - export LOG_DIR=$DEVICEFARM_LOG_DIR
      - python3 tests/test_main.py
      - echo "Test Done"

  post_test:
    commands:
      - echo "Post Test. OK"

artifacts:
  - $DEVICEFARM_LOG_DIR

しかし、このままではエラーで終了してしまいます

adb server version (39) doesn't match this client (40); killing...
ADB server didn't ACK

どうやらadbがDevice Farmのホストマシン上のものとAirtest内蔵のものでversionが異なり、既に起動しているDevice FarmのものをAirtest側がkillしようとして失敗するようです

adbをホストマシンのに差し替える

AirtestのFAQによると、競合した際はpackage内のadbを任意のものに差し替えるとのことです
なのでtestspecのpre_testでinstallしたAirtest package内のadbをDevice Farmのものに差し替えます

~略~

  pre_test:
    commands:
      - cd $DEVICEFARM_TEST_PACKAGE_PATH
      # Airtest package 内の adb を Device Farm のものに差し替える
      - echo "adb setup start"
      - rm ./lib/python3.7/site-packages/airtest/core/android/static/adb/linux/adb
      - $adb_path=$(which adb)
      - cp adb_path ./lib/python3.7/site-packages/airtest/core/android/static/adb/linux/adb
      - sed -i -e s#^\$ADB#$adb_path.orig#g ./lib/python3.7/site-packages/airtest/core/android/static/adb/linux/adb

      - echo "Pre Test. OK"

~略~

これでAirtestが動作するようになりますが、今度はテストシナリオは終了しているのに (testspecでいう)post_testに進行しなくなります
test 内 python3 tests/test_main.py 後の echo "Test Done" すら実行されていません. (uninstallや終了処理はされているようです)

[DEBUG]<airtest.core.android.adb> /tmp/scratchP4PNu8.scratch/test-packageXI1AKZ/lib/python3.7/site-packages/airtest/core/android/static/adb/linux/adb -s XXXXX uninstall 'jp.hoge.huga.dev
[DEBUG]<airtest.core.android.adb> /tmp/scratchP4PNu8.scratch/test-packageXI1AKZ/lib/python3.7/site-packages/airtest/core/android/static/adb/linux/adb -s XXXXX forward --remove tcp:14967
[07:16:55][DEBUG]<airtest.utils.nbsp> [rotation_server]b''
[07:16:55][DEBUG]<airtest.core.android.adb> /tmp/scratchP4PNu8.scratch/test-packageXI1AKZ/lib/python3.7/site-packages/airtest/core/android/static/adb/linux/adb -s XXXXX forward --remove tcp:18833

終了できていないプロセスを強制終了する

Airtestでは自動操作や画面のキャプチャのためにAndroid側やAirtestを実行しているマシンにいくつかのアプリケーションをインストールして、最後に終了するようになっています
その中の rotationwatcher という端末の回転を検知するプロセスがうまく終了できていなさそうでした

Airtest側でうまく終了できなそうだったので、強引ですがプロセスをkillする処理をAirtestの終了処理に差し込むことでDevice Farmが正常に終了しました

import sys

def register_force_kill():
    import time
    import subprocess

    def run_shell(cmd):
            print(cmd, file=sys.stderr)
            process = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
            stdout, stderr = process.communicate(timeout=150)
            exit_code = process.returncode
            print("stdout: ", stdout, " stderr: ", stderr, " exit_code: ", exit_code, file=sys.stderr)

    def teardown():
        print("[TEST_SCRIPT]teardown", file=sys.stderr)
        #run_shell("ps aux | grep rotationwatcher") # 確認用
        run_shell("pkill -f rotationwatcher")
        time.sleep(1) # Wait for rotationwatcher to terminate
        #run_shell("ps aux | grep rotationwatcher") # 確認用

    from airtest.utils.snippet import reg_cleanup
    reg_cleanup(teardown) # Insert into airtest termination process


def run_test():
    import smoke  # Run top-level commands

print("[TEST_SCRIPT]Registration of forced termination", file=sys.stderr)
register_force_kill()
print("[TEST_SCRIPT]Start Airtest", file=sys.stderr)
run_test()
print("[TEST_SCRIPT]Finish Airtest", file=sys.stderr)

無理やりkillしてるので本来の終了プロセスで参照してエラーがでていますが、 例外は無視されていてDevice Farm自体は正しく動いています

Exception ignored in: <module 'threading' from '/usr/local/lib/python3.7/threading.py'>
Traceback (most recent call last):
  File "/tmp/scratchnIe8tr.scratch/test-package8h1e4P/lib/python3.7/site-packages/airtest/utils/snippet.py", line 92, in exitfunc
    _cleanup()
~略~
ValueError: Invalid file object: <_io.BufferedReader name=7>

動画やログ、成果物の保存なども正常にできています

Device Farm の画面
ダウンロードしたレポート

まとめ

  • 対応フレームワークに見せかけて
  • AirtestのadbをDevice Farmのものに差し替えて
  • 終了に失敗するプロセスを自前で止めることで

AWS Device Farm上でAirtestが動きました! (サポート外の方法かつ無理やりな部分もあるのであくまで一例として…)

【Akatsuki Geek Live 2022 vol.1】開催レポート

みなさまこんにちは!アカツキゲームス新卒採用担当の藤元です。

2022年2月24日に開催した、学生向けイベント「Akatsuki Geek Live 2022 Vol.1」についてのレポートを公開します

Geek Liveの開催はなんと今年で4年目!エンジニアメンバーの中でも恒例イベントとなりました。

 

2019年に開催された第一回目では30名の参加者でしたが、4年目となる今回は70名を越える参加者となりました。

今回もオンライン配信だったため、全国から多くの方が参加してくださり大変嬉しいです!

ご参加いただいたみなさま、ありがとうございます。

「Akatsuki Geek Live」とは?

エンジニアを志す学生さんと、アカツキエンジニアの交流を目的としたイベントです。

フリーテーマで7分間のLTを実施することで、楽しみながらアカツキのことを知れる場、そして学生さん同士の横のつながりも作れる場として実施しております。

※ LTとは:Lightning Talkの略で短いプレゼンテーションのこと 

▼イベント概要はこちらから

aktsk.connpass.com

今回の登壇者はアカツキメンバー3名と内定者3名の計6名でした。

さて、ここからは、時間いっぱいギークな話を発表してくださった登壇者のスライドを紹介しながら

当日の様子を少し紹介させていただければと思います!

当日の様子

続きを読む

ペンテスターはApple Silicon Macに夢を見るか?

この記事は、Akatsuki Advent Calendar 2021の24日目の記事です。

こんにちは、セキュリティエンジニアの小竹 泰一(aka tkmru)です。 アカツキでは、アプリケーションに対する脆弱性診断や社内ネットワークに対するペネトレーションテスト、ツール開発/検証を担当しています。

Apple Silicon MacをiOSアプリの脆弱性診断に使用する際にどのような利点、欠点があるのか調査した結果を書きたいと思います。2019年のAkatsuki Advent Calendarから続く、大好評企画(?)「ペンテスターは〇〇に夢を見るか」シリーズ第二弾です。 第一弾はこちら

Apple Silicon MacでiOSアプリが動作するように!

M1搭載MacBookが登場して、iOSアプリがエミュレータを使わずともmacOS上で動作するようになりました。

iPhoneおよびiPad向けAppが、Appleシリコン搭載のMac上のMac App Storeでも提供されるようになりました。AppをMac向けに修正する必要はなく、ユーザーとデベロッパの双方にとって新たな可能性が広がります。iPhoneおよびiPad向けAppで利用可能な既存の機能を使って、キーボード、ウインドウ、タッチ入力のジェスチャと連携して動作するようAppを最適化することができます。さらに既存の機能を確認し、必要に応じて機能を有効化/無効化することで、AppのMac上での動作を調整することも可能です。

developer.apple.com

JailbreakしていないiPhoneはAndroidと比べ、プラットフォーム側の制限が厳しく、かゆいところに手が届かないがちです。 そこでApple Silicon Macを使うことでiOSアプリの脆弱性診断が簡単になるのではと考え、検証しました。 この記事はその検証結果をまとめたものです。

iOSアプリのインストール方法

IPAファイルをダブルクリックすることでインストールできます。 Apple Silicon Mac上では、IPAファイルはインストーラとして振る舞います。 どんなIPAファイルでもインストールできるわけではなく、iOSデバイスと同等の制限があり、macOSのUUIDを実行許可している証明書でリザインされたものしかインストールできません。

f:id:TAKEmaru:20211223211535p:plain
インストール画面

tap1000000.appは検証用に作成したアプリの名前です。この記事内では度々tap1000000.appが登場します。

各種Tips

検証する過程で分かった各種Tipsを紹介します。 デスクトップアプリと同じようにiOSアプリを扱えました。

アクティビティモニタが便利

macOSにデフォルトでインストールされているアクティビティモニタを開き、実行しているプロセスを選択することで、プロセスの詳細な情報を取得できます。 PIDや使用しているファイルを確認できます。

f:id:TAKEmaru:20211220152731p:plain
アクティビティモニタの画面例

これはiOSアプリのためのTipsではなく、macOS上で動作する全てのアプリケーションで使えるテクニックです。

メモリマップの取得方法

vmmapコマンドでPIDで指定したプロセスのメモリマップを取得できます。

$ vmmap 10299
Process:         tap1000000 [10299]
Path:            /Volumes/VOLUME/*/tap1000000.app/tap1000000
Load Address:    0x1025e8000
Identifier:      jp.hoge.tap1000000
Version:         0.1 (0)
Code Type:       ARM64
Platform:        iOS
Parent Process:  ??? [1]
...

==== Non-writable regions for process 10299
REGION TYPE                    START - END         [ VSIZE  RSDNT  DIRTY   SWAP] PRT/MAX SHRMOD PURGE    REGION DETAIL
__TEXT                      1025e8000-1025f0000    [   32K    16K     0K     0K] r-x/r-x SM=COW          /var/folders/*/tap1000000.app/tap1000000
__LINKEDIT                  1025f4000-1025fc000    [   32K     0K     0K     0K] r--/r-- SM=COW          /var/folders/*/tap1000000.app/tap1000000
VM_ALLOCATE                 102604000-102608000    [   16K    16K    16K     0K] r--/rwx SM=PRV  
shared memory               102608000-10260c000    [   16K    16K    16K     0K] r--/r-- SM=SHM  
MALLOC metadata             10260c000-102610000    [   16K    16K    16K     0K] r--/rwx SM=COW          MallocHelperZone_0x10260c000 zone structure
MALLOC guard page           102614000-102618000    [   16K     0K     0K     0K] ---/rwx SM=ZER  
MALLOC guard page           102620000-102624000    [   16K     0K     0K     0K] ---/rwx SM=ZER  
MALLOC guard page           102624000-102628000    [   16K     0K     0K     0K] ---/rwx SM=NUL  
MALLOC guard page           102630000-102638000    [   32K     0K     0K     0K] ---/rwx SM=NUL  
...

これもiOSアプリのためのTipsではなく、macOS上で動作する全てのアプリケーションで使えるテクニックです。

中間者攻撃の方法

システム環境設定からネットワーク -> 詳細設定 を開き、使用しているネットワークを選択した後、 詳細 -> プロキシの順にメニューを開くことでプロキシの設定画面を開けます。 HTTP/HTTPS通信を取得するには構成するプロトコルにWebプロキシ(HTTP)保護されたWebプロキシ(HTTPS)を選択し、プロキシツールが動いているアドレス/ポートを指定してください。

f:id:TAKEmaru:20211220153633p:plain
プロキシ設定例

ただ、PCから出る全ての通信が指定したプロキシツールを通るようになるので、プロキシツール側でうまくフィルタしてあげる必要があります。 この辺りもiPhoneの場合と変わりませんが、PC上では診断対象のアプリと並行してブラウザなども動かすと思うのでフィルタリングがより大切になります。

各種パス

インストールされたアプリは次のパスに配置されます。

  • /Applications/tap1000000.app/Wrapper/tap1000000.app

また、用途は特定できていませんが、アプリの実行中は次のパスにもアプリが配置されます。

  • /private/var/folders/hc/XXXXXXXXnsfn1_c9n20jxw40000gq/X/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/d/Wrapper/tap1000000.app

アプリが使用するアプリケーションコンテナは次のパスに配置されます。

  • /Users/taichi.kotake/Library/Containers/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Data/

このあたりのパスは前述のアクティビティモニタから確認できるので、興味がある方は見てみてください。

欠点

現状、Apple Silicon Mac上でWindows VMを動かせないのは欠点です。 ゲームアプリの脆弱性診断では、Unity製アプリのバイナリのシンボルを復元するIl2CppDumperというツールを動かすためにWindows VMをしばしば使います。

github.com

まず、VMware FusionがApple Silicon Mac上では動作しません。 VMware FusionのApple Silicon Mac対応は進んでいるようですが、Intel版のOSをサポートはしないようで、Apple Silicon Mac対応VMware Fusionがリリースされたとしても、Intel版Windowsは実行できない見込みです。 Microsoftは仮想環境向けにARM版Windowsを販売しないようで、当分はApple Silicon Mac上でWindows VMを動かせなさそうです。

news.mynavi.jp

内製メモリ改ざんツールをApple Silicon Macに対応させた話

アカツキのセキュリティチームでは脆弱性診断のためのツールをいくつか内製し、OSSとして公開しています。 その中の一つにipa-meditというiOSアプリ向けのメモリ改ざんツールがあります。 このツールをApple Silicon Mac上で動作するiOSアプリに対応させました。ここでは、その解説をしたいと思います。

github.com

Apple Silicon Macに対応する以前のバージョンのipa-meditは以前、Black Hat USA 2021 Arsenalで発表しました。 この記事はApple Silicon Macの解説のための記事なので、iPhone、iPad上で動作するiOSアプリに対してメモリ改ざんするロジックの解説はここではしません。興味がある方は次のスライドをご覧ください。

speakerdeck.com

メモリ改ざんとは

チートの方法の1つにUI上に表示されている値を端末のメモリ上から検索し、見つけた値を改ざんする、「メモリ改ざん」という手法があります。 これはゲームへのチート方法の中で最も簡単な方法で、脆弱性診断の際には実際にメモリ上のデータを改ざんをすることでチートできるかどうか確認しています。 対策としては、XOR等を使ってメモリ上ではエンコードされた状態で値を保持し、UI上に表示されている値を検索されても見つからないようにする方法があります。 ipa-meditでは次のようにメモリ改ざんを行えます。

LinuxとmacOSの違いに困惑...

要はmacOS上で動作するアプリケーションをデバッグすればいいだけなので、シュッと対応できるのではと開発時には考えていました。 しかし、慣れ親しんでいたLinux環境でのシステムプログラミングとは違い、macOS固有の知識が要求されるため、いくつか戸惑うことがありました。

Linuxとの違いに戸惑う....

apk-meditというAndroid向けのメモリ改ざんツールを以前作成したことがあったので、このツールと同じロジックで作れるのではないかと考えていましたが、かなり勝手が違いました。 LLDBやGolang製デバッガのDelveなどのmacOSでも動作するデバッガのコードが実装の参考になりました。

hackerslab.aktsk.jp

/proc がない....

Linux系OSの場合は/proc/$pid/mapsから、特定のプロセスのメモリマップを取得し、/proc/$pid/memよりメモリを読み書きできます。 しかし、macOSには/procがありません。 そのため、専用のAPIを用いて、メモリマップの取得とメモリの読み書きを行う必要があります。 ipa-meditではメモリマップの取得には前述のvmmapコマンドを使っていますが、 メモリの読み込みにはmach_vm_readを、メモリへの書き込みにはmach_vm_writeを使っています。

ptraceでメモリの読み書きができない....

ptraceは、デバッガを実装する際によく用いられるシステムコールで、第一引数にrequestという動作モードを表す定数を指定することで動作を切り替えられます。 Linux系OSの場合はptraceを使ってプロセスへアタッチし、プロセスを停止させたり、メモリを読み書きできます。

ptrace(int request, pid_t pid, caddr_t addr, int data);

しかし、macOSの場合はメモリの読み書きがサポートされておらず、第一引数にメモリを読むためのPTRACE_PEEKDATA、メモリへ書き込むためのPTRACE_POKEDATAを指定できません。そのため、前述のmach_vm_readmach_vm_writeを使ってメモリの読み書きを行っています。 macOSでもアタッチ/デタッチにはptraceを使えるので、ipa-meditではアタッチ/デタッチにのみptraceを使っています。

署名まわりで戸惑う....

macOSでは、署名されていないプログラムをデバッガとして使うことはできません。 デバッガとして使うプログラムには、com.apple.security.cs.debuggerという属性を有効にするようentitlements.plistで指定し、署名しておく必要があります。 ipa-meditではmake buildコマンドでビルドする際に署名の処理も行っています。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.debugger</key>
    <true/>
</dict>
</plist>

また、デバッグ対象のアプリではcom.apple.security.get-task-allow属性を有効にしておく必要があります。 この属性はデバッガによるアタッチを許可します。 Apple Silicon Macを使ってiOSアプリの脆弱性診断をする際には、リザインの際にこの属性を有効にしておかないとメモリ改ざんを行えません。 デバック対象のアプリでcom.apple.security.get-task-allow属性が有効になっているかどうかはcodesignコマンドで確認できます。

$ codesign -d --entitlements :- 47071
Executable=/private/var/folders/hc/XXXXXXXXnsfn1_c9n20jxw40000gq/X/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/d/Wrapper/tap1000000.app/tap1000000
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>application-identifier</key>
    <string>XXXXXXXXXX.jp.hoge.tap1000000</string>
    <key>com.apple.developer.team-identifier</key>
    <string>XXXXXXXXXX</string>
    <key>get-task-allow</key>
    <true/> <-- ここがtrueになっている必要がある
    <key>keychain-access-groups</key>
    <array>
        <string>XXXXXXXXXX.jp.hoge.tap1000000</string>
    </array>
</dict>
</plist>

まとめ

検証の結果、Apple Silicon Mac上で動作するiOSアプリに対しても問題なく脆弱性診断ができそうだと分かりました。 Apple Silicon Mac上のiOSアプリに対して脆弱性診断っぽいことを行うまとまったドキュメントは、インターネット上にまだなく(多分)、MITMする方法すら検索しても出てこないので、公開する意義のあるいいドキュメントになったかなと思います。

また、PC上で動作しているため、アプリケーションコンテナ内のファイルへの操作をXcodeを介さず行える点や、デバッガのアタッチをデスクトップアプリと同じ方法で行える点から、一部の診断は実機のiPhoneを使うよりやりやすくなる印象を受けました。 iPhoneをJailBreakしなくとも、ターミナルが動作している環境でiOSアプリを動かせるのは便利かなーと思います。 今後、Apple Silicon Mac環境特有の何かを用いたiOSアプリへのハック方法が出てくると面白いなと思います。

関連情報

Apple Silicon Macには、Intel Mac向けにビルドされたアプリケーションをApple Silicon Mac上で動作させるRosetta 2という仕組みが搭載されています。 この動作を明らかにするProject ChampollionというリサーチをFFRIが行っており面白いです。 iOSアプリとは関係ないですが、Apple Silicon Mac特有の仕組みに興味がある方はこちらもおすすめです。

github.com

Engineering Managerの役割を無くしてみた

こんにちは、ゆのん(id:yunon_phys)です。この記事は Akatsuki Advent Calendar 2021 の22日目の記事です。

アカツキでは数年前よりEngineering Managerの役割をおいているのですが、この度、Engineering Manager(EM)の役割を公式に無くしました。私の個人的な活動で、EMによるEMのためのPodcastをやっている*1、社内でもそういう活動を知っている人がいるし、世間からするとお前がそれを言うのか〜と言われそうなのでなかなか勇気のいる決断でした。ただ、今後の会社の方針を考えたときに、今見直すタイミングなんじゃないかと思い、思い切って無くしてみました。

本記事ではなぜ無くすことにしたのか、これからどうしていくのかを書いていきます。

EMのやることが消滅したわけではない

アカツキのEMはエンジニアのマネジメントと、プロジェクトマネジメントが主で、techのマネジメントも必要とあらば入っていました。

なぜこれだけいろいろなことをやる役割が必要なのかでいうと、プロダクトには、エンジニアだけでなく、プランナー、ディレクター、プロデューサー、デザイナー、QA、マーケターなど様々な職種が入っているからです。それらの様々な職種が混在するプロダクトチームが一体となって開発・運用を行うためには、狭い領域の専門性を磨く人も必要でしたし、広くプロダクトチームをカバーする人も必要で、EMは後者の役割でした。

ただ、EMといえどスーパーマンではないので、人事領域に強い人もいれば、プロジェクトマネジメント領域に強い人もいて、同じEMと言えども強みの発揮のやり方は違っていました。近年ではモバイルゲームがより成熟化してきていて、高度な専門性が求められるようになってきました。高度な専門性が求められれば求められるほど、同じEMと言いながら異なる強みを利用して価値を発揮しているようなEMに対して、EMは何をする人なのか、という質問が至るところで湧いてくるようになりました。さらに、Engineering Managerという言葉の響きからtechのリードとしての期待が暗黙のうちに持たれることもあり、その期待とのずれから、それぞれのEMの強みが否定されかねない状態になってきました。

私は、EMを社内にも社外にも広めた立場であるものの、EMという名称を使うことでこういったネガティブな状態になるのは本意ではありませんでした。一方で、EMのやっていたことがいらなくなったわけではないです。どの業務も必要だったからEMという役割が存在していたのは間違いありません。そこで、EMという名称を社内から無くしはするが、これまでEMが担っていた役割をどうすればより良い形で組織で実現出来るか、もう一度役割を整理し直しました。そのために改めて、エンジニア組織をどういう組織にしたいのか、ゼロベースで考えました。

f:id:yunon_phys:20211221232924p:plain

より技術力を高められる組織にしたい

まず、考えたのは、アカツキの技術力をもっと高めたいというものです。技術力がもっと高まれば、より豊かなゲームの表現が出来るようになり、プレイヤーにもっと満足していただけるゲームが作れると信じています。

そのためには、未来の技術を考え、技術を引っ張るリーダーの存在と、技術を正当に評価する体制が必要だと考えました。

技術リーダーを設置した

これまでのエンジニア組織はCTOとVPoEの体制になっていて、CTOが全社の技術を引っ張る役割、VPoEである私がエンジニア組織の組織マネジメントを行う役割として分かれていました。しかし、技術発展は著しいため、それぞれの技術の専門性をもった技術リーダーの存在が必要となってきました。

そこで、モバイルアプリケーション側を専門とするClient Engineering Leadと、サーバー・インフラ側を専門とするServer Engineering Leadの2人の技術リーダーをエンジニア組織に設置することにしました。この2人の技術リーダーが存在することで、これまでCTOに全ての技術の相談が行っていたところを、2人の技術リーダーに分散することができ、さらに、より深く、未来の技術に向けた話が出来るようになりました。

f:id:yunon_phys:20211221235015p:plain

評価体制を変更した

元々はエンジニアの納得感を優先して、評価されるエンジニアが評価者を選ぶという仕組みにしていました。EMがプロダクトチームのエンジニアのマネジメントを行う機会が多かったので、評価者としても選ばれやすかったです。これはこれでうまくいっていたところは多かったですが、技術的に組織全体を引き上げるということに関しては、目標設定・評価の段階で組織だって動けていませんでした。

今回2人の技術リーダーを設置したことで、より未来の技術の方向性がクリアになっていくため、評価もこの2人が最終意思決定出来るようにするのが良いと考えました。とはいえ、現在いるエンジニア全体を2人が評価するのはさすがに無理があるので、プロダクト単位で評価が行えるようにTeam Leadを各プロダクトに設置しました

f:id:yunon_phys:20211221234833p:plain

Engineering LeadはTeam Leadと、Team Leadはエンジニアメンバーと目標設定のすり合わせと評価のすり合わせをします。さらに、Engineering Leadはエンジニアメンバーの目標設定の内容も確認し、高い目標を掲げられているか、伸ばしたい技術の方向性として正しいかもチェックしていきます。

Team Leadがエンジニアメンバーの技術評価をする責任を持ったことで、今後プロダクトチームで技術的な課題が出たときは、EMではなくTeam Leadが担うことになります。これにより、EMの役割が一つ分解できました。

よりエンジニア組織の人事領域を強化したい

エンジニアが増えていく中で、採用ニーズが複雑化、エンジニアのキャリア形成が多様化などが課題となってきました。

これまで、VPoEである私を中心に、各プロダクトに配属されているEMたちと定期的にやりとりを行ってこのあたりの整理を行ってきました。しかし、プロダクト増にEMの採用が追いつかなかったり、1人分の仕事がそもそも無かったりと、エンジニアのマネジメントを満足に行えていない現状がありました。

Engineering Officeの立ち上げ

そこで、Engineering Officeの部署をエンジニア組織内に立ち上げ、エンジニアの採用ニーズやキャリア形成のための相談をサービス化することにしました。また、これまで全社の人事部門にあったエンジニアの採用部隊をこのEngineering Officeに統合することで、エンジニアの人事全般を一手に引き受けるようにしました。

f:id:yunon_phys:20211221235735p:plain

Engineering Officeには人事領域に強みのあるEMが入ることになりました。さらに、人事領域に元々興味のあったエンジニアメンバーも入ることにもなりました。Engineering Officeが設置されたことで、エンジニアの一つのキャリアパスに人事が加わったのは、組織的に一つ深みを増せたと思います。

ちなみに、Engineering Officeはメルカリさんの影響を大いに受けているので、この場を持って感謝の言葉をお伝えさせていただきます。ありがとうございます!!

よりプロジェクトマネジメントの領域を広く、深くしたい

EMはこれまでスクラムマスターとしての動きをしていたり、開発チームの課題発見・解決を行ってきました。しかし、いざゲームの運用となると、開発チームのマネジメントだけでなく、アセットを作ってデプロイする運用チームのマネジメントであったり、開発した機能をうまく運用にのせる必要がありました。これまではなんとなく、いろんなやり方でうまく回してきたのですが、モバイルゲームがより成熟化していくにつれ、プロダクトチーム全体の動きをうまく統合してマネジメントしていくやり方が求められるようになってきました

また、開発チームのプロセスをマネジメントしたり、運用チームのプロセスをマネジメントする人は、今後のプロダクトが増えていくときに無くてはならない存在です。一方、大規模ゲーム開発・運用経験 × プロジェクトマネジメントというのは、専門性も要求されるばかりか、そういった経験を積んでいる人はあまりにレアな存在であるため採用も難しいという課題がありました。

プロセスマネジメント職の立ち上げ

そこで、開発のプロセスや、運用アセットの製作プロセスをマネジメントするための専門職を立ち上げました*2。エンジニアだけでなく、プランナーやデザイナー、QAなどの様々なバックグラウンドを持つ人がこのプロセスマネジメント職*3に就くことで、それぞれの経験や人脈を活かしながら、プロダクトチームの課題を解決出来るようにしていきます。そして、このプロセスマネジメント職を社内で育成できる体制を整えることで、採用に左右されることなく、長期的な目線でプロダクトの増加に備えることになります。

f:id:yunon_phys:20211221235918p:plain

プロジェクトマネジメントに強みを持っていたEMがこの職に就くことで、よりプロセスマネジメントの専門性を深めながら、プロダクトチームに影響力を発揮していくことになります。

実はこの職が出来ることでもう一つ嬉しいことがあって、スクラムマスターを採用出来るようになりました。今まで、EMがスクラムマスターとしての役割も担っていたと書きましたが、逆にスクラムマスターに対してもEMとしての役割も期待していたが故に、より広範な知識や経験を要求してしまい、採用が難しかったという側面があります。これからはスクラムマスターにはスクラムマスターとしての業務のみを期待するようになり、よりスクラムマスターとしての専門性を評価出来るようになるので、良い方向に向かうと確信しています。

最後に

以上のように、EMという役割を無くすところから始まり、エンジニア組織を一から考え直してみました。結果として、今考えられる中で一番良い形になったのではないかと思います。

また数年後にはやっぱりEM復活しました、と言ってそうですが、組織はそのときに一番あった形に最適化していくのが当然やることだと思っているのでそのときはご容赦ください:)

*1:更新が滞っていてすみません

*2:正確にはこれからではあるのですが

*3:プロジェクトマネジメント職ではなくプロセスマネジメント職。プロジェクトマネジメントとすると、これまたいろんな業務を期待されてしまうことがわかった。いろいろ話を聞いていくと、プロジェクトマネジメントに期待しているのはほぼほぼプロセスマネジメントだとわかったので、あえてプロセスマネジメントとすることで業務の幅を想像しやすくなるようにした