プログラム系統備忘録ブログ

記事中のコードは自己責任の下でご自由にどうぞ。

LINE CTF 2024 write-up & Binary Ninja FreeとIDA Freeの比較

LINE CTF 2024へ、一人チームrotationで参加しました。そのwrite-up記事です。またおまけとして、Binary Ninjaを初めて触ったのでIDAとのふんわり比較も記述しています。

また、今回からお試しとして、IDAの解析結果ファイル*.i64等をGitHubで公開してみることにしました。https://github.com/Tan90909090/ctf-writeups/tree/main/LINE_CTF_2024をご参照ください。

2024/04/02(火) 01:00頃追記: BrownFlagChecker問題の元ソースコードが公開されています: Black-Frost/LineCtf2024

コンテスト概要

2024/03/23(土) 09:00 +09:00 - 2024/03/24(日) 09:00 +09:00 の24時間開催でした。他ルールはトップページから引用します:

Information
- Name: LINE CTF 2024
- Schedule: March 23, 2024, 09:00 AM ~ March 24, 2024, 09:00 AM (UTC+9)
- Style: Jeopardy-style (Team Competition @ Online)
- Organizer: Security team at LY Corporation
- Discord: https://discord.gg/jqFhg4TVpK
- Contact: dl_ctf@linecorp.com
- Bugbounty: https://hackerone.com/line
- Prizes:
  - 🥇1st: $5,000
  - 🥈2nd: $3,000
  - 🥉3rd: $2,000

Rules
- Do not participate and play on multiple teams.
- Do not share flags or any hints.
- Do not ask other players who are not on the same team for hints on challenges via DM, etc.
- Do not attack scoreboard server or our infra. (e.g. DoS)
- Do not do scanning.
- The flag format is LINECTF{[0-9a-f]{32}}
- There is no team size limit

- The displayed amount of prize money will be transferred in USD. LINE will bear the transfer cost.
- We will ask for your PII for payment of rewards and checking of anti-social forces
  - For residents in Japan, we will ask your My Number for payment records
- We will not support the multiple transfer accounts. Only one bank account can receive the rewards

- We reserve the right to ban and disqualify any teams breaking any of these rules
- The Top 3 will need to send us (dl_ctf@linecorp.com) a writeup until 2024.03.25 09:00 (UTC+9)

- Unfortunately, due to current geopolitical events, participants from Russia are NOT eligible to receive prize money

上記ルール中では出題ジャンルの言及はありませんが、Discordサーバーへ参加するとCATEGORIESとしてpwnwebrevcryptomiscチャンネルが最初からあったため、推測はできました。なお競技開始後に分かったことですが、ジャンルごとの出題問題数には大きな差がありました:

  • web: 12問
  • misc: 3問(welcome問題含む)
  • pwn: 2問
  • rev: 1問
  • crypto: 1問

結果

正の得点を得ている865チーム中、222点で107位でした。

得点

灰色背景: 解けた問題

環境

WindowsのWSL2(Ubuntu 22.04)などを使って取り組みました。また、rev問題でバイナリ実行用のWindowsマシンには、Download a Windows virtual machine - Windows app development | Microsoft Developerのものを使いました。

Windows

c:\>ver

Microsoft Windows [Version 10.0.19045.4170]

c:\>wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu-22.04           Running         2
  kali-linux             Stopped         2
  docker-desktop         Stopped         2
  docker-desktop-data    Stopped         2

c:\>

他ソフト

  • IDA Free Version 8.4.240320 Windows x64 (64-bit address size)(Free版IDAでもversion 7頃からx64バイナリを、version 8.2からはx86バイナリもクラウドベースの逆コンパイルができます)
  • Binary Ninja Free 4.0.4958-Stable
  • Microsoft Visual Studio Community 2022 (64-bit) - Current Version 17.8.3

WSL2(Ubuntu 22.04)

$ cat /proc/version
Linux version 5.15.146.1-microsoft-standard-WSL2 (root@65c757a075e2) (gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37) #1 SMP Thu Jan 11 04:09:03 UTC 2024
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
$ python3 --version
Python 3.10.12
$ python3 -m pip show pycryptodome | grep Version
Version: 3.14.1
$

解けた問題

[misc] welcome (821 teams solved, 1 point)

Welcome to the LINE CTF 2024! We hope you enjoy.

LINECTF{6240995c64f58fe13d72d80760551344}

書かれていたフラグを提出して正解できました: LINECTF{6240995c64f58fe13d72d80760551344}

[rev] BrownFlagChecker (17 teams solved, 221 points)

Brown the Bear just learned about how people protect their games from cheaters. He wants to try those techniques, but he doesn't know how to make a game...

配布ファイルとして、バイナリ2つと、起動方法等の説明がありました:

$ file *
BrownFlagChecker.exe: PE32+ executable (console) x86-64, for MS Windows
BrownProtector.sys:   PE32+ executable (native) x86-64, for MS Windows
README.md:            ASCII text, with CRLF line terminators
$ cat README.md
This challenge should be run in a Windows 10 or 11 VM, with `Secure Boot` disabled.

You must enable `Test mode` in the VM (See what is `Test mode` here: https://learn.microsoft.com/en-us/windows-hardware/drivers/install/the-testsigning-boot-configuration-option)

To enable `Test mode`, run the following command in an administrator command prompt.
```
bcdedit.exe -set TESTSIGNING ON
```
After that, reboot your VM.

The binary must also be run in an administrator command prompt.
$

はい、.sysファイルがあります。WindowsのKernel Mode Driver(KMD)を使った問題です!以降、私の本問題の理解を記述していきますが、私はKMDの知識がほとんどありません。誤り等あればコメント等で教えていただければ嬉しいです。

実行環境の準備

README.mdにあるとおり、本問題のバイナリ群は、Test modeに設定したWindows VMでの実行が想定されています。私は普段、細々とした実行にはWindows Sandboxを使っていますが、Test mode設定にするため再起動が必要だったり、そもそもThe boot configuration data store could not be opened. The requested system device cannot be found.エラーでだめでした。

「確かMicrosoft社は評価用のものを配布していたはず」とGoogle検索するとDownload a Windows virtual machine - Windows app development | Microsoft Developerを見つけました。VirtualBox用ovaファイルをダウンロードしてインポートして起動しました。README.mdの通りにTest mode設定も成功して、無事に本問題のバイナリを起動できました:

c:\Users\User\Desktop\work>BrownFlagChecker.exe
NtLoadDriver failed with code: 0xc000010e

c:\Users\User\Desktop\work>BrownFlagChecker.exe
Welcome! Give me the key and I will give you the flag: test
Wrong! But here is a clue just for you: https://rb.gy/i6drqn

c:\Users\User\Desktop\work>

たまにNtLoadDriverなどでエラーになることがありますが、何度か実行すれば正常にフラグ入力ができました。これで動作検証できる環境を用意できました。

ひたすら読解

BrownFlagChecker.exeBrownProtector.sysの処理を、頑張ってひたすら読み解いていきました。

BrownFlagChecker.exeは一般的なEXEなので、Free版IDAで問題なく解析できました。ただBrownProtector.sysはKMDなのでDRIVER_OBJECT型などのKMD用の構造体を多用していますが、Free版IDAではそれらの構造体定義が含まれていないようでした。そこで最近公開されたBinary NinjaのFree版を試しにインストールして読ませてみると、DRIVER_OBJECTは適切に認識してくれました。もう少し調べると、Binary Ninjaはntoskrnlからインポートしている型は認識してくれましたが、WDFLDRからインポートしている型(WDF_BIND_INFO)は含まれていないようでした(Microsoftドキュメントも特段無さそうでした)。WDFLDR関連の型は少なかったので、ググってReactOSでの型定義を参考に、自分で定義しました。一方で、逆コンパイル結果はFree版IDAの方が見やすい傾向であったため、BrownProtector.sys処理内容の解析にはFree版IDAとFree版Binary Ninjaを併用しました。比較して思ったことは本記事最後に参考として記述します。

制御やデータの流れの理解

ひたすら読解した結果、BrownFlagChecker.exe起動後は以下の流れであることが分かりました:

  1. (BrownFlagChecker.exe0x140003B36付近)IsDebuggerPresentFALSEを返す場合の分岐の後で、CreateProcessABrownFlagChecker.exeをもう1つ起動します。この際dwCreationFlagsDEBUG_ONLY_THIS_PROCESSを指定しています。そのためか、子プロセス側は一旦サスペンド状態で起動するようです。以降、大本の起動側を「親プロセス」、CreateProcessAにより新たに起動された側を「子プロセス」と表現します。
  2. (親プロセス、BrownFlagChecker.exe140001AF0付近)CREATE_PROCESS_DEBUG_EVENT時の分岐で呼び出す関数の中で、BrownProtector.sysを、サービス登録したり、SeLoadDriverPrivilege特権を取得してからのNtLoadDriverで読み込んだりします。
  3. (親プロセス、BrownProtector.sys0x140001034付近)DRIVER_OBJECTメンバーの、DriverUnloadMajorFunction[IRP_MJ_DEVICE_CONTROL]MajorFunction[IRP_MJ_CREATE]MajorFunction[IRP_MJ_CLOSE]に関数ポインターを設定したり、デバイスの初期設定を行ったりしているようです。本問題では、MajorFunction[IRP_MJ_DEVICE_CONTROL]へ登録している関数(0x140001180)が重要です。
  4. (親プロセス、BrownFlagChecker.exe0x14000340C付近)CreateFileAでデバイスを開き、DeviceIoControlで制御コード0x224000を送信します。
  5. (親プロセス、BrownProtector.sys0x14000142b付近)PsGetCurrentProcessIdで親プロセスのPIDを取得して保持します。また、耐解析チェックを行います。チェック結果に問題なければ引数のバッファへ0x1337を書き込みます。(問題があれば0xdeadを書き込み、BrownFlagChecker.exe側で判定しています。以降も同様です。)
  6. (親プロセス、BrownFlagChecker.exe0x14000346D付近)VirtualAllocで確保したバッファ20個へ固定値を格納します。ここでは格納するだけで、後で使います。ContinueDebugEventで子プロセスを続行させます(実質ここで子プロセスは始めて動作開始するはずです)。WaitForDebugEventで子プロセスのイベントを待ちます。
  7. (子プロセス、BrownFlagChecker.exe0x140003A79付近)IsDebuggerPresentTRUEを返す場合の分岐の後で、0x1400031E0の関数を呼び出します。
  8. (子プロセス、BrownFlagChecker.exe0x140003260付近)CreateFileAでデバイスを開き、DeviceIoControlで制御コード0x224004を送信します。
  9. (子プロセス、BrownProtector.sys0x1400013e4付近)自身のプロセスの親プロセスのPIDが、先ほど取得した親プロセス側のPIDであることを検証します。PsGetCurrentProcessIdPsGetCurrentThreadIdで、子プロセス側のPIDやTIDを取得して保持します。ObRegisterCallbacksを使った何かの耐解析用らしいコールバックの設定が成功すれば、引数のバッファへ0x1337を書き込みます。
  10. (子プロセス、BrownFlagChecker.exe0x14000328B付近)RAXレジスタに1を設定した状態で、idiv命令で0除算を行います。例外が発生し、Debuggerである親プロセス側へ移行します。
  11. (親プロセス、BrownFlagChecker.exe0x140003966付近)STATUS_INTEGER_DIVIDE_BY_ZERO時の分岐の中で、printfでユーザー入力を促し、scanfでユーザー入力を取得します。ユーザー入力が64文字である場合は、ユーザー入力内容と先ほど構築した固定値を含むバッファを、DeviceIoControlで制御コード0x224008で送信します。
  12. (親プロセス、BrownProtector.sys0x140001387付近)PIDチェックや耐解析チェックを行った後、受信データをグローバル変数に保持します。その後にCR3レジスタの内容とMmGetVirtualForPhysicalを使って、何かをしています(詳細分からず)。SetThreadContextで子プロセスのRIPを調整してから、ContinueDebugEventで子プロセスを続行させます。WaitForDebugEventで子プロセスのイベントを待ちます。
  13. (子プロセス、BrownFlagChecker.exe0x1400032BE付近)DeviceIoControlで制御コード0x22400Cを送信します。
  14. (子プロセス、BrownProtector.sys0x1400012eb付近)PIDチェックやTIDチェックを行った後、先ほど保持した親プロセスからの入力を、MmAllocateContiguousMemoryMmGetPhysicalAddressMmGetVirtualForPhysicalなどを使って、何かをしています(詳細分からず)。引数のバッファへ0x1337を書き込みます。
  15. (子プロセス、BrownFlagChecker.exe0x140001E80付近)DeviceIoControlで制御コード0x224010を送信します。
  16. (子プロセス、BrownProtector.sys0x140001265付近)PIDチェックやTIDチェック、耐解析チェックをした後、CR3レジスタの内容とMmGetVirtualForPhysicalrdtsc命令を使って何かのアドレスを計算しています(詳細分からず)。計算結果を引数のバッファへ書き込みます。
  17. (子プロセス、BrownFlagChecker.exe0x140001F1C付近)DeviceIoControlによりバッファに書き込まれたアドレスから計算したアドレスをsrcとして、memcpyでコピーします。その後、DeviceIoControlで制御コード0x224014を送信します。
    • このあたりから「3文字長の文字列をスタック上に構築」しているように見える処理がありますが、実際は文字列ではなく8バイト整数型として使用します。
  18. (子プロセス、BrownProtector.sys0x14000123f付近)CR3レジスタの内容とMmGetVirtualForPhysicalを使って何かをしています(詳細分からず)。
  19. (子プロセス、BrownFlagChecker.exe0x140001F50付近)「DeviceIoControlで制御コード0x224010を送信して、バッファに書き込まれたアドレスから計算したアドレスから読み込んで色々して、DeviceIoControlで制御コード0x224014を送信」を9回行います。
  20. (子プロセス、BrownFlagChecker.exe0x140002741付近)「DeviceIoControlで制御コード0x224010を送信して、バッファに書き込まれたアドレスから計算したアドレスを第2引数としてmemcmpで比較して正誤判定し、DeviceIoControlで制御コード0x224014を送信」を行います。
  21. (子プロセス、BrownFlagChecker.exe0x1400032E5付近)EAXの内容を、正解なら2に、誤りなら3に設定してから、idiv命令で0除算を行います。例外が発生し、Debuggerである親プロセス側へ移行します。
  22. (親プロセス、BrownFlagChecker.exe0x14000393F付近)STATUS_INTEGER_DIVIDE_BY_ZERO時の分岐の中で、子プロセスの例外発生時のEAXレジスタ内容が2なら、入力内容を使ってフラグを復号して表示します。
  23. その後、諸々の終了時処理を行います。省略します。

このように、親プロセスであるDebuggerと子プロセスであるDebuggeeが、DeviceIoControl経由でアドレスをやり取りしている、ものすごい問題でした。CR3レジスタに関連する処理を全く理解できていませんが、もしかしたら手動でページングのようなことをしているのかもしれません。

なお、BrownFlagChecker.exe0x140003A05付近には「子プロセスがSTATUS_ACCESS_VIOLATIONになった場合には、SetThreadContextRIPを8増やしてから続行させる」処理が存在します。どこかでAccess Violationが起こることを前提としているようですが、具体的にどこなのかは分かりませんでした。

耐解析機構の理解

本問題には多数の耐解析機構が存在します。それらにより、本問題の難易度が上がっています。

  • BrownFlagChecker.exeの子プロセス側は、Debuggeeとして動作します。1プロセスにアタッチできるDebuggerは1つだけであるため、「他にデバッガーを起動して子プロセスへアタッチ」が出来ません。
  • KMDには署名が必須なはずです。BrownProtector.sysにもデジタル署名が付与されており、コード部分等を改ざんしようものなら署名が無効になってしまって読み込めなくなるはずです。
  • BrownProtector.sysには、多数の耐解析機構が実装されています:
    • 適切な順番でDeviceIoControlの各種制御コードが送られることや、各種制御コード送信時のPIDやTIDを検証します。
    • 0x140001808の関数では、ZwQueryInformationProcessProcessDebugPortを指定してデバッグポート番号を取得し、それが0であることを検証しています。(NtQueryInformationProcess functionによると、ring 3デバッガー(=ユーザーランドデバッガー?)がアタッチされている場合に非0になるようです。)
    • 0x140001864の関数では、CR0レジスタの値とCR4レジスタの値が特定の状態であることを検証しています(詳細分からず)。より具体的には、CR0 < 0 && (CR4 & 0x1000) == 0LLtrueであることを検証しています。
    • 0x140001554の関数では、ObRegisterCallbacksを使って、ObjectTypeがPsProcessTypePsThreadTypeであるものについて、何かコールバックを設定しています(詳細分からず)。
    • 0x14000165Cの関数では、以下のようにBrownFlagChecker.exeの改ざん検知を行っています:
      1. PEB::ImageBaseAddress(=BrownFlagChecker.exeの読み込みアドレス)を取得
      2. 取得結果からPE構造をたどって、.textセクションの位置とサイズを取得
      3. .textセクション全体からハッシュ値を計算して、特定の値であることを検証

0x14000165Cの関数で計算するハッシュ値は、0x140001000の以下の関数で計算しています:

unsigned int __fastcall __pure SomeHashProc(const BYTE *pByteArray, __int64 qwLength)
{
  unsigned int dwHash; // r9d
  unsigned __int64 byteCurrent; // r8

  for ( dwHash = -1; qwLength; --qwLength )
  {
    byteCurrent = *pByteArray++;
    dwHash = g_dwValueArraySize256[(unsigned __int8)dwHash ^ byteCurrent] ^ (dwHash >> 8);
  }
  return ~dwHash;
}

暗号学的ハッシュ関数ではないはずですが、テーブルを使った値変換を含むため、「.textセクションの一部を改ざんつつ、ハッシュ値が特定の値になるよう帳尻を合わせる」ことは難しいか面倒なように思います。 ← 終了後のDiscord書き込みで気付きましたが、CRC32を計算しているようです。CRC32なら意図的な衝突方法もあるかもしれません。

DLL Injection + IAT Hookingを使ったデータ内容確認

さて本問題を解くためには、子プロセスが最後の方に行っている、「DeviceIoControl等経由で取得、計算したアドレスからの読み込み」の内容を是非確認したいです。前述したように本問題では多数の耐解析機構が搭載されていますが、「DLL InjectionしてDLLを読み込ませてBrownFlagChecker.exeのIAT内容をDLL内部の自作関数へ改ざんすれば、.textセクション内容はそのままに、BrownFlagChecker.exeのAPI呼び出し時に色々状態を確認できそう」と考えました。以前にDLL Injection + IAT Hookingを試した事があったので、その時のソースコードを引っ張ってきて試したら無事に動いてくれました。使ったソースコードはGitHubに置いています。

本手法を使う上で注意が必要だった点です:

  • 前述した通り、親プロセスが子プロセスを起動する際、CreateProcessAdwCreationFlags引数にDEBUG_ONLY_THIS_PROCESSを指定しています。また、DLL Injectionする際は、CREATE_SUSPENDEDを指定して起動することが定石です。ただ、CREATE_SUSPENDED | DEBUG_ONLY_THIS_PROCESSを指定してしまうと、CreateRemoteThread用途で起動したスレッドもサスペンド状態になってしまうようでした。最終的に「CreateProcessACREATE_SUSPENDEDのみを指定して起動して、CreateRemoteThreadでDLL Injectionして、その後DebugActiveProcessでアタッチ」することで、うまく動作しました。
  • 通常のCreateProcessAによる子プロセスの起動では、親子でコンソールを共有します。ただ親子それぞれの出力が出力が1文字単位で混ざることがあったため、CreateProcessAdwCreationFlags引数にCREATE_NEW_CONSOLEを追加することで、親子でコンソールを分離しました。
  • よく分かっていませんが、アクセス予定のメモリ領域をVirtualQuery関数で調べるとPAGE_NOACCESSらしかったのですが、正常にアクセスできました。どういうことか全然分かっていません。
  • 親プロセスが子プロセスをTerminateProcessしようとしたりするため、その前にSleep(INFINITE)させるようにしました。
  • 本手法を施した状態で正しくフラグ入力できる状況が、10回起動して1回あるかどうかでした。正しく動かない状況では、NtLoadDriver failed with code: 0xc000010eとなったり、どこかでフリーズしたり、scanfによる入力箇所を通っているはずなのに入力がないものと扱われて即失敗ルートを取ったりしました。うまくいかない理由が全然分かっていませんが、何回か実行すればそのうち動いたのであまり気にしませんでした。
  • 稀に、実行途中にGuest VMが丸ごとフリーズすることがありました。その場合はVMを再起動しました。

本手法を使って分かった点です:

  • 子プロセスが、1回目の制御コード0x224010経由で取得する内容は、親プロセスで入力した内容そのものした。その内容をmemcpyでコピーします。
  • 子プロセスが、2回目~11回目の制御コード0x224010経由で取得する内容は、完全固定値でした。親プロセスが最初の方に構築している固定内容バッファの内容が特定の順序で入れ替わって反映されているようでした。
  • 子プロセス最後のmemcmpで比較している内容は、第1引数が入力に依存した内容で、第2引数は固定値でした。
  • memcpyの第1引数のアドレスを保持しておいて、制御コード0x224014DeviceIoControl時にそのアドレスを確認することで、9段階の加工中の状態を確認できました。最終的なソルバー開発時に役立ちました。

本手法を使った出力の例です(x64/DebugビルドのBrownFlagCheckerInjector.exeBrownFlagCheckerHook.dllを使っています):

  • 親プロセス側:
C:\Users\User\Desktop\work>dir
 Volume in drive C is Windows
 Volume Serial Number is ACBD-83FF

 Directory of C:\Users\User\Desktop\work

03/26/2024  11:07 PM    <DIR>          .
03/23/2024  12:25 PM    <DIR>          ..
03/23/2024  08:51 AM            26,112 BrownFlagChecker.exe
03/25/2024  03:10 AM         2,622,976 BrownFlagCheckerHook.dll
03/24/2024  02:29 AM         2,113,536 BrownFlagCheckerInjector.exe
03/11/2024  06:40 AM            15,640 BrownProtector.sys
               4 File(s)      4,778,264 bytes
               2 Dir(s)  83,701,641,216 bytes free

C:\Users\User\Desktop\work>BrownFlagCheckerInjector.exe
this is main.cpp
(DEBUG) VirtualAllocEx
(DEBUG) WriteProcessMemory
(DEBUG) GetModuleHandleW
(DEBUG) GetProcAddress
(DEBUG) pLoadLibraryW : 00007FFB699A8BE0
(DEBUG) CreateRemoteThread
(DEBUG) threadId : 7612
(DEBUG) WaitForSingleObject
after GetModuleFileNameW, result: c:\Users\User\Desktop\work\BrownFlagCheckerHook.dll
exeImage
exeFile
Succeeded to hook DeviceIoControl
Succeeded to hook GetThreadContext
Succeeded to hook SetThreadContext
Succeeded to hook CreateProcessA
Succeeded to hook memcpy
Succeeded to hook memcmp
Succeeded to hook puts
Succeeded to hook TerminateProcess
Succeeded to hook ExitProcess
(DEBUG) GetExitCodeThread
CreateProcessA before
CreateProcessA after, result = 1
CreateProcessA hook child process
, lpProcessInformation->dwProcessId: 7636
(DEBUG) VirtualAllocEx
(DEBUG) WriteProcessMemory
(DEBUG) GetModuleHandleW
(DEBUG) GetProcAddress
(DEBUG) pLoadLibraryW : 00007FFB699A8BE0
(DEBUG) CreateRemoteThread
(DEBUG) threadId : 7644
(DEBUG) WaitForSingleObject
(DEBUG) GetExitCodeThread
CreateProcessA DebugActiveProcess
CreateProcessA DebugActiveProcess, result = 1
CreateProcessA ResumeThread
CreateProcessA ResumeThread, result = 2
CreateProcessA complete
DeviceIoControl after, received: 0x37, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
GetThreadContext after, lpContext->Rax: 1, lpContext->Rip: 5368722059
Welcome! Give me the key and I will give you the flag: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
DeviceIoControl after, received:
SetThreadContext before, lpContext->Rax: 0, lpContext->Rip: 5368722065
GetThreadContext after, lpContext->Rax: 3, lpContext->Rip: 5368722149
Wrong! But here is a clue just for you: https://rb.gy/i6drqn
puts! Wait!
  • 子プロセス側:
after GetModuleFileNameW, result: c:\Users\User\Desktop\work\BrownFlagCheckerHook.dll
exeImage
exeFile
Succeeded to hook DeviceIoControl
Succeeded to hook GetThreadContext
Succeeded to hook SetThreadContext
Succeeded to hook CreateProcessA
Succeeded to hook memcpy
Succeeded to hook memcmp
Succeeded to hook puts
Succeeded to hook TerminateProcess
Succeeded to hook ExitProcess
DeviceIoControl after, received: 0x37, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl after, received: 0x37, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl after, received: 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x7e0595547000 (GUY): 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
memcpy, dest(        0x180000) = src(0x7e0595547000) : 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
DeviceIoControl after, received:
DeviceIoControl after, received: 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:    0x68574f43000 (COW): 0x62, 0x4e, 0xb3, 0x30, 0xd7, 0x63, 0x9b, 0x94, 0x5c, 0xec, 0x45, 0x15, 0xc7, 0xf5, 0x53, 0x58,
DeviceIoControl(code: 0x224010) addr:    0x68544142000 (BAT): 0xd9, 0xae, 0xcb, 0x5a, 0xd8, 0xfc, 0x84, 0xf3, 0xaa, 0xc5, 0x8b, 0x9d, 0x08, 0x2d, 0x1d, 0x8f,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0x2e, 0xb1, 0x78, 0xfe, 0x4e, 0xe6, 0x32, 0x34, 0x4e, 0x89, 0x19, 0xf0, 0x0b, 0x58, 0x91, 0xd7, 0x89, 0x2e, 0x8a, 0xb0, 0x83, 0x45, 0x9e, 0xb8, 0x48, 0x8d, 0x1e, 0x44, 0xd6, 0x66, 0x1d, 0x00, 0x19, 0x90, 0x1a, 0xe2, 0x8d, 0xaf, 0xe1, 0xa4, 0x59, 0x41, 0x65, 0x61, 0xf3, 0x8c, 0xd7, 0x88, 0xc1, 0xad, 0x34, 0xb9, 0x10, 0x7b, 0x8a, 0x68, 0x99, 0x55, 0xc1, 0x95, 0x7a, 0x31, 0x56, 0xbe,
DeviceIoControl after, received:
DeviceIoControl after, received: 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x460455945000 (EYE): 0xf5, 0xd1, 0x5c, 0x5a, 0xb2, 0xe9, 0x6b, 0x21, 0xe7, 0x2c, 0x74, 0xfa, 0x11, 0x00, 0x02, 0xdc,
DeviceIoControl(code: 0x224010) addr:   0x460455053000 (SPE): 0xf5, 0x18, 0xb9, 0x72, 0x69, 0x4c, 0x79, 0xcc, 0xc4, 0x13, 0x8b, 0xe0, 0x39, 0x66, 0x9e, 0x59,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0xca, 0x1e, 0xb2, 0xc0, 0xda, 0xc3, 0x82, 0xbf, 0x04, 0x82, 0x14, 0xdb, 0xcc, 0x02, 0xcc, 0xa8, 0xbd, 0x6f, 0xb0, 0xc8, 0x1c, 0xc0, 0xf0, 0x56, 0x9d, 0x9d, 0xa0, 0xa1, 0x4a, 0x8a, 0x49, 0xaf, 0x8c, 0x77, 0x75, 0x45, 0x36, 0x4e, 0x74, 0x03, 0xc0, 0x1c, 0xc5, 0x6c, 0x78, 0x09, 0x78, 0xb9, 0x18, 0x06, 0x5a, 0x1b, 0x4a, 0x9a, 0x52, 0x11, 0x6f, 0xc4, 0xa8, 0x75, 0x3f, 0xf1, 0xef, 0xc3,
DeviceIoControl after, received:
DeviceIoControl after, received: 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x1a8434241000 (ABC): 0xe4, 0x49, 0x63, 0x70, 0xe9, 0x36, 0x53, 0x8d, 0x9c, 0x14, 0xca, 0xf0, 0x03, 0xbc, 0x2b, 0x3d,
DeviceIoControl(code: 0x224010) addr:   0x1a84e4957000 (WIN): 0xcf, 0x74, 0x4d, 0xef, 0x49, 0xbb, 0x74, 0xaa, 0x96, 0x6e, 0xe7, 0xf5, 0x63, 0x09, 0x67, 0x89,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0x32, 0xeb, 0xb1, 0xbe, 0xce, 0x6c, 0xd7, 0x75, 0xf2, 0x8d, 0x40, 0x73, 0x07, 0xfe, 0x7a, 0x73, 0x65, 0x2d, 0xa2, 0x05, 0xff, 0xde, 0x21, 0xa7, 0x04, 0x3c, 0xaf, 0x6f, 0x8a, 0xe1, 0x71, 0xc5, 0x5f, 0xa2, 0x87, 0x73, 0xf6, 0x09, 0x22, 0xe2, 0x0a, 0xb9, 0x34, 0xd5, 0x58, 0xf2, 0xd2, 0x0b, 0xeb, 0xea, 0xdc, 0x2c, 0x26, 0xf9, 0x5e, 0xfb, 0xe8, 0x25, 0x2e, 0xf4, 0x4e, 0x64, 0x4e, 0x07,
DeviceIoControl after, received:
DeviceIoControl after, received: 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x4b8454349000 (ICE): 0x9e, 0xd6, 0x67, 0xb3, 0x8d, 0x13, 0x8d, 0xe7, 0x49, 0x73, 0x52, 0xf8, 0xb4, 0x56, 0xf8, 0x58,
DeviceIoControl(code: 0x224010) addr:   0x4b8595243000 (CRY): 0x69, 0x9e, 0xbb, 0x61, 0x9f, 0xab, 0x1c, 0x14, 0x3c, 0x8e, 0x09, 0xc3, 0x36, 0xfc, 0xf8, 0xf8,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0x11, 0xc2, 0x60, 0xe3, 0xf4, 0x27, 0xf3, 0x41, 0x5f, 0x48, 0xd2, 0xe3, 0xd2, 0x55, 0x6c, 0x94, 0x13, 0x2c, 0xf0, 0xf7, 0x6b, 0xe6, 0x56, 0x8b, 0xfc, 0x5a, 0x43, 0xb3, 0x9f, 0x57, 0x4b, 0x63, 0xea, 0x44, 0x52, 0x3c, 0x51, 0x99, 0xb7, 0x03, 0x66, 0x34, 0x42, 0x52, 0xb3, 0x80, 0x9e, 0xab, 0xad, 0x19, 0xe9, 0x68, 0xba, 0xb6, 0xfd, 0x10, 0x08, 0xc7, 0x63, 0x1d, 0x45, 0x17, 0xc8, 0x0b,
DeviceIoControl after, received:
DeviceIoControl after, received: 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x798474f44000 (DOG): 0xbb, 0xbd, 0xaa, 0x45, 0x34, 0xcf, 0x75, 0xca, 0xd7, 0xd3, 0x1c, 0x13, 0x05, 0x3e, 0x63, 0x4e,
DeviceIoControl(code: 0x224010) addr:   0x798544143000 (CAT): 0x60, 0x3a, 0xe7, 0x7b, 0x24, 0xda, 0x70, 0x6f, 0x99, 0x2a, 0xee, 0xb2, 0x1a, 0x96, 0xc6, 0x33,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0xae, 0x29, 0xd2, 0x05, 0x32, 0x06, 0x4c, 0x3a, 0x74, 0x35, 0x66, 0xce, 0x43, 0x4c, 0xb4, 0x99, 0xec, 0x56, 0xaf, 0x2f, 0xdf, 0x50, 0x79, 0x29, 0x21, 0x17, 0x0c, 0xdd, 0xe3, 0xe7, 0xed, 0xdf, 0x67, 0x9d, 0x14, 0x48, 0x78, 0xad, 0xf3, 0xb6, 0x35, 0x4f, 0xe5, 0xb9, 0x0b, 0x7c, 0xd8, 0x20, 0x05, 0xb6, 0x9c, 0xba, 0xa5, 0xcc, 0xb4, 0x6e, 0x5a, 0x0d, 0xd8, 0x7c, 0x97, 0xc8, 0xd2, 0x15,
DeviceIoControl after, received:
DeviceIoControl after, received: 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x4084b5441000 (ATK): 0x01, 0x50, 0xb0, 0x60, 0x47, 0xe2, 0x0c, 0xf6, 0xa3, 0xbd, 0x9c, 0x41, 0xb0, 0x61, 0x9e, 0x34,
DeviceIoControl(code: 0x224010) addr:   0x4084d4956000 (VIM): 0x6e, 0x31, 0xfa, 0x55, 0xe0, 0x39, 0x27, 0xfa, 0x2b, 0x90, 0x78, 0xb6, 0xc6, 0x72, 0x69, 0xdf,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0x93, 0x40, 0xbc, 0x5a, 0x76, 0x2b, 0xb4, 0xd0, 0x7a, 0xe5, 0x4b, 0x46, 0x5c, 0x4b, 0xd7, 0xbe, 0xfc, 0xf5, 0x74, 0xd0, 0x56, 0x61, 0xb4, 0xd3, 0xb7, 0xdf, 0x85, 0xda, 0xf1, 0xf9, 0xf9, 0x84, 0x3c, 0x78, 0x00, 0xa9, 0x5b, 0xe6, 0xaa, 0x72, 0x73, 0x5f, 0x6b, 0xa0, 0x62, 0x66, 0xb7, 0xe5, 0xc1, 0x06, 0xbd, 0x73, 0x5f, 0x6e, 0xdd, 0x36, 0x3a, 0x3b, 0xcf, 0x80, 0x8a, 0x80, 0x88, 0x71,
DeviceIoControl after, received:
DeviceIoControl after, received: 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x660444552000 (RED): 0xef, 0xaf, 0xa1, 0x44, 0x7b, 0xc8, 0xdd, 0xf8, 0x1f, 0x9c, 0x3a, 0xf8, 0xcc, 0xc2, 0x57, 0x8b,
DeviceIoControl(code: 0x224010) addr:   0x66050495a000 (ZIP): 0xcb, 0xdd, 0x86, 0x3e, 0x1f, 0x03, 0xe5, 0x2f, 0xa3, 0xb1, 0xd7, 0xf4, 0x6e, 0xa8, 0x30, 0xd0,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0x2e, 0x39, 0xc9, 0x98, 0x40, 0x03, 0xdc, 0x52, 0xb6, 0x7d, 0xf3, 0x33, 0x9a, 0x81, 0xbb, 0x32, 0x0a, 0x9e, 0xfd, 0x7a, 0x78, 0xa6, 0x1c, 0x1e, 0x9c, 0x16, 0x25, 0xc0, 0x26, 0xe0, 0x0f, 0xd3, 0xe9, 0xc1, 0x0e, 0x35, 0xbd, 0x10, 0x55, 0xe8, 0xbf, 0x2c, 0x3d, 0x93, 0xa0, 0x82, 0xc7, 0x45, 0x0b, 0x21, 0xeb, 0x3a, 0xc2, 0xab, 0x30, 0x1b, 0xfb, 0x87, 0xc7, 0xd2, 0xa2, 0x27, 0x7a, 0xf8,
DeviceIoControl after, received:
DeviceIoControl after, received: 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x760535251000 (QRS): 0x71, 0x29, 0x2c, 0x0e, 0x6b, 0x2f, 0x3f, 0x2f, 0xd9, 0x71, 0x11, 0xb2, 0x4b, 0x63, 0x42, 0xf4,
DeviceIoControl(code: 0x224010) addr:   0x760464544000 (DEF): 0x2e, 0x44, 0xd1, 0x84, 0xe7, 0x05, 0x4a, 0x7a, 0xa7, 0x67, 0x53, 0x8a, 0xd3, 0xca, 0x61, 0xd9,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0x08, 0x20, 0xbd, 0x5a, 0x2e, 0x1f, 0x31, 0x80, 0x0f, 0x14, 0x99, 0x77, 0xa8, 0x8a, 0xa9, 0xb7, 0x53, 0xed, 0xfb, 0x26, 0x37, 0x84, 0x47, 0xce, 0x90, 0x04, 0x64, 0xc8, 0xce, 0xae, 0x3c, 0x93, 0xa4, 0xb6, 0x75, 0xcc, 0x5f, 0x85, 0x1b, 0x00, 0x89, 0x06, 0x47, 0x29, 0x3f, 0xa5, 0x08, 0x27, 0xe3, 0xb3, 0xd3, 0x97, 0x3d, 0x9b, 0x7b, 0x3f, 0x7a, 0xb4, 0x64, 0x78, 0x4b, 0x8a, 0xa3, 0x2e,
DeviceIoControl after, received:
DeviceIoControl after, received: 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:    0x804f4f4d000 (MOO): 0xfb, 0xa7, 0x3c, 0xa3, 0xe8, 0x8d, 0x3c, 0x74, 0xda, 0xcb, 0x32, 0x84, 0x85, 0xec, 0x2a, 0xed,
DeviceIoControl(code: 0x224010) addr:    0x80524941000 (AIR): 0x72, 0x5e, 0xa8, 0x4d, 0x26, 0x47, 0x8d, 0xaf, 0xa6, 0x8e, 0xad, 0x9b, 0xfd, 0xbe, 0x52, 0x6f,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0xa8, 0x7d, 0xf8, 0xac, 0x32, 0xd3, 0x77, 0x1a, 0x35, 0x8b, 0x21, 0x83, 0x4a, 0xd1, 0x1c, 0x92, 0xd0, 0x92, 0x16, 0x20, 0x6a, 0x07, 0x17, 0x57, 0xc1, 0xc0, 0xdd, 0x14, 0x54, 0x10, 0x1e, 0x23, 0xbf, 0x7c, 0x07, 0xe8, 0xea, 0xd9, 0x64, 0x69, 0x0a, 0x13, 0x74, 0x4f, 0xb4, 0x65, 0x56, 0x72, 0x0b, 0x70, 0xfb, 0xa7, 0xb2, 0xc6, 0xce, 0x06, 0xe7, 0x5e, 0x22, 0xb4, 0x2c, 0x01, 0xa4, 0x6f,
DeviceIoControl after, received:
DeviceIoControl after, received: 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
DeviceIoControl(code: 0x224010) addr:   0x1a0474745000 (EGG): 0xe2, 0x42, 0xf1, 0x42, 0x1d, 0x2a, 0xae, 0xc7, 0xc1, 0xec, 0x19, 0x6a, 0x43, 0x45, 0x77, 0xe2,
memcmp,
buffer1: 0xa8, 0x7d, 0xf8, 0xac, 0x32, 0xd3, 0x77, 0x1a, 0x35, 0x8b, 0x21, 0x83, 0x4a, 0xd1, 0x1c, 0x92, 0xd0, 0x92, 0x16, 0x20, 0x6a, 0x07, 0x17, 0x57, 0xc1, 0xc0, 0xdd, 0x14, 0x54, 0x10, 0x1e, 0x23, 0xbf, 0x7c, 0x07, 0xe8, 0xea, 0xd9, 0x64, 0x69, 0x0a, 0x13, 0x74, 0x4f, 0xb4, 0x65, 0x56, 0x72, 0x0b, 0x70, 0xfb, 0xa7, 0xb2, 0xc6, 0xce, 0x06, 0xe7, 0x5e, 0x22, 0xb4, 0x2c, 0x01, 0xa4, 0x6f,
buffer2: 0xe2, 0x42, 0xf1, 0x42, 0x1d, 0x2a, 0xae, 0xc7, 0xc1, 0xec, 0x19, 0x6a, 0x43, 0x45, 0x77, 0xe2, 0x05, 0xd3, 0x91, 0xe3, 0x72, 0x59, 0x6b, 0xaa, 0x96, 0x41, 0x08, 0x4d, 0x8e, 0x91, 0x46, 0xf3, 0x5f, 0x86, 0xb2, 0x2a, 0x05, 0xb2, 0x2a, 0x8a, 0x08, 0x9b, 0xfc, 0x66, 0x2b, 0x07, 0xe4, 0x3d, 0xec, 0x57, 0xfa, 0x1c, 0x8a, 0xfd, 0xbb, 0x08, 0x78, 0x06, 0xdb, 0x78, 0x35, 0x4f, 0x5b, 0xe0,
DeviceIoControl before, dwIoControlCode: 224014, current dest content: 0xa8, 0x7d, 0xf8, 0xac, 0x32, 0xd3, 0x77, 0x1a, 0x35, 0x8b, 0x21, 0x83, 0x4a, 0xd1, 0x1c, 0x92, 0xd0, 0x92, 0x16, 0x20, 0x6a, 0x07, 0x17, 0x57, 0xc1, 0xc0, 0xdd, 0x14, 0x54, 0x10, 0x1e, 0x23, 0xbf, 0x7c, 0x07, 0xe8, 0xea, 0xd9, 0x64, 0x69, 0x0a, 0x13, 0x74, 0x4f, 0xb4, 0x65, 0x56, 0x72, 0x0b, 0x70, 0xfb, 0xa7, 0xb2, 0xc6, 0xce, 0x06, 0xe7, 0x5e, 0x22, 0xb4, 0x2c, 0x01, 0xa4, 0x6f,
DeviceIoControl after, received:

あとは、入力内容がどのように加工されるかを解明できれば、正解となる入力内容を逆算できそうです。

子プロセスの入力内容の加工処理の解析

結論から書くと、0x140001F50以降で、入力をAES-128-CBCで9回暗号化していました。鍵およびIVは、制御コード0x224010経由で取得する固定値を使用しています。AES-128-CBCと判断した経緯です:

  • 0x140005330からの256バイト配列は、AESのS-boxです。これは、配列最初の方の63 7C 77 7B F2 6B 6F C5でGoogle検索するとRijndael S-box - Wikipediaがヒットしたことで分かりました。
  • 0x140005530からの16バイト配列は、AESのrconと呼ばれているもののようです。これは、配列最初の方の8D 01 02 04 08 10 20 40をGoogle検索するとAES128 in Ruby 1.9.2がヒットしたことで分かりました。なおヒット結果では16バイトすべて非0ですが、本問題では後ろ5バイトが0x00です。「多分AES-128では今ある値だけで十分で、AES-192やAES-256では16バイト全部必要なんでしょう」と考えて流しました。
  • 0x140001000の関数は、AESの鍵展開を行う関数のようです。これは、関数中でS-boxもreconも使われており、処理内容もAES128 in Ruby 1.9.2のものと「なんとなく似ている気がする」と思って判断しました。
  • 0x1400016A0の関数は、AES-128で1ブロックを暗号化する関数のようです。これは、tiny-AES-c/aes.c at master · kokke/tiny-AES-cと見比べて「各段階を適切な順序で行っていそう」と思って判断しました。
  • 暗号利用モードはCBCのようです。これは0x140001FEE付近の以下の逆コンパイル結果をひたすら追跡して判断しました(同様の箇所が後ろに8個、全9個ありますが、すべて流れは同様のようです):
  addr_1 = Child_DeviceIoControl_0x224010();    // ここからしばらく、PAGE_NOACCESSへアクセスしようとする → どんな魔法なのかちゃんと取得できているらしい
  qmemcpy(&g_qwStr3, "BAT", 3);
  qmemcpy(&g_qwStr4, "COW", 3);
  pbyteArrayCurrentBlockSrc = (_OWORD *)(addr_1 + (g_qwStr4 << 12));// COW側から16バイト読み込み
  MayBe_AesKeySchedule(byteArrayExpandedKey, (const _BYTE *)(addr_1 + (g_qwStr3 << 12)));// BAT側からAES鍵の値を読み込み
  *(_OWORD *)byteArrayCurrentBlockSrc = *pbyteArrayCurrentBlockSrc;// COW側から16バイト読み込み、直接代入
  pbyteArrayCurrentBlockDest = pAllocatedSize0x1000;
  pbyteArrayCurrentBlockForXor = byteArrayCurrentBlockSrcCopy;// 単なるアドレスの代入だけ
  dwRestBlockCount1 = 4LL;
  *(_OWORD *)byteArrayCurrentBlockSrcCopy = *(_OWORD *)byteArrayCurrentBlockSrc;
  do
  {
    pbyteArrayCurrentBlockDest_1 = pbyteArrayCurrentBlockDest;// 16ずつ4回増える
    qwOffset = pbyteArrayCurrentBlockForXor - pbyteArrayCurrentBlockDest;
    dwRestCount2 = 16LL;
    do
    {
      *pbyteArrayCurrentBlockDest_1 ^= pbyteArrayCurrentBlockDest_1[qwOffset];// offset側加算結果を合わせて、実質「pbyteArrayCurrentBlockForXor[i]」相当
      ++pbyteArrayCurrentBlockDest_1;
      --dwRestCount2;
    }
    while ( dwRestCount2 );
    MayBe_Aes128Encrypt(pbyteArrayCurrentBlockDest, byteArrayExpandedKey);
    pbyteArrayCurrentBlockForXor = pbyteArrayCurrentBlockDest;// これCBCモードでは?
    pbyteArrayCurrentBlockDest += 16;           // 16ずつ4回増える
    --dwRestBlockCount1;
  }
  while ( dwRestBlockCount1 );
  *(_OWORD *)byteArrayCurrentBlockSrcCopy = *(_OWORD *)pbyteArrayCurrentBlockForXor;// この代入なにか意味ある?この後別の代入で上書きされてないか?
  DeviceIoControl(g_Child_hDevice, 0x224014u, 0LL, 0, 0LL, 0, 0LL, 0LL);
  • AES-128-CBCと仮定して、入力を暗号化する手順を再現したら、確認結果のバイト列と一致しました。

なお、最終的な「入力内容を使ったフラグの復号」では、入力内容のMD5をAES鍵の一部に使っているようでした。そのため「フラグ復号結果がそれらしくなるように入力内容を逆算」は不可能だと思います。

ソルバーと実行結果とフラグ

最終的な、「64文字のaを入力として与えた場合の変換結果が想定したものになることの検証」「正解となる入力の逆算」を行うスクリプトです(試行錯誤中内容もそのまま掲載します):

#!/usr/bin/env python3

from Crypto.Cipher import AES

test_input_a64 = bytearray(b"a" * 64)

assert len(test_input_a64) == 64

# 制御コード`0x224010`経由で取得する内容
extracted_keys = [
    # (COW, BAT)などが9回
    (bytes([0x62, 0x4e, 0xb3, 0x30, 0xd7, 0x63, 0x9b, 0x94, 0x5c, 0xec, 0x45, 0x15, 0xc7, 0xf5, 0x53, 0x58,]), bytes([0xd9, 0xae, 0xcb, 0x5a, 0xd8, 0xfc, 0x84, 0xf3, 0xaa, 0xc5, 0x8b, 0x9d, 0x08, 0x2d, 0x1d, 0x8f,])),
    (bytes([0xf5, 0xd1, 0x5c, 0x5a, 0xb2, 0xe9, 0x6b, 0x21, 0xe7, 0x2c, 0x74, 0xfa, 0x11, 0x00, 0x02, 0xdc,]), bytes([0xf5, 0x18, 0xb9, 0x72, 0x69, 0x4c, 0x79, 0xcc, 0xc4, 0x13, 0x8b, 0xe0, 0x39, 0x66, 0x9e, 0x59,])),
    (bytes([0xe4, 0x49, 0x63, 0x70, 0xe9, 0x36, 0x53, 0x8d, 0x9c, 0x14, 0xca, 0xf0, 0x03, 0xbc, 0x2b, 0x3d,]), bytes([0xcf, 0x74, 0x4d, 0xef, 0x49, 0xbb, 0x74, 0xaa, 0x96, 0x6e, 0xe7, 0xf5, 0x63, 0x09, 0x67, 0x89,])),
    (bytes([0x9e, 0xd6, 0x67, 0xb3, 0x8d, 0x13, 0x8d, 0xe7, 0x49, 0x73, 0x52, 0xf8, 0xb4, 0x56, 0xf8, 0x58,]), bytes([0x69, 0x9e, 0xbb, 0x61, 0x9f, 0xab, 0x1c, 0x14, 0x3c, 0x8e, 0x09, 0xc3, 0x36, 0xfc, 0xf8, 0xf8,])),
    (bytes([0xbb, 0xbd, 0xaa, 0x45, 0x34, 0xcf, 0x75, 0xca, 0xd7, 0xd3, 0x1c, 0x13, 0x05, 0x3e, 0x63, 0x4e,]), bytes([0x60, 0x3a, 0xe7, 0x7b, 0x24, 0xda, 0x70, 0x6f, 0x99, 0x2a, 0xee, 0xb2, 0x1a, 0x96, 0xc6, 0x33,])),
    (bytes([0x01, 0x50, 0xb0, 0x60, 0x47, 0xe2, 0x0c, 0xf6, 0xa3, 0xbd, 0x9c, 0x41, 0xb0, 0x61, 0x9e, 0x34,]), bytes([0x6e, 0x31, 0xfa, 0x55, 0xe0, 0x39, 0x27, 0xfa, 0x2b, 0x90, 0x78, 0xb6, 0xc6, 0x72, 0x69, 0xdf,])),
    (bytes([0xef, 0xaf, 0xa1, 0x44, 0x7b, 0xc8, 0xdd, 0xf8, 0x1f, 0x9c, 0x3a, 0xf8, 0xcc, 0xc2, 0x57, 0x8b,]), bytes([0xcb, 0xdd, 0x86, 0x3e, 0x1f, 0x03, 0xe5, 0x2f, 0xa3, 0xb1, 0xd7, 0xf4, 0x6e, 0xa8, 0x30, 0xd0,])),
    (bytes([0x71, 0x29, 0x2c, 0x0e, 0x6b, 0x2f, 0x3f, 0x2f, 0xd9, 0x71, 0x11, 0xb2, 0x4b, 0x63, 0x42, 0xf4,]), bytes([0x2e, 0x44, 0xd1, 0x84, 0xe7, 0x05, 0x4a, 0x7a, 0xa7, 0x67, 0x53, 0x8a, 0xd3, 0xca, 0x61, 0xd9,])),
    (bytes([0xfb, 0xa7, 0x3c, 0xa3, 0xe8, 0x8d, 0x3c, 0x74, 0xda, 0xcb, 0x32, 0x84, 0x85, 0xec, 0x2a, 0xed,]), bytes([0x72, 0x5e, 0xa8, 0x4d, 0x26, 0x47, 0x8d, 0xaf, 0xa6, 0x8e, 0xad, 0x9b, 0xfd, 0xbe, 0x52, 0x6f,])),
    ]


def encrypt(test_input):
    for (index, (iv_for_ecnrypt, key_for_encrypt)) in enumerate(extracted_keys):
        print(f"{index = }")

        assert len(test_input) == 64
        assert len(iv_for_ecnrypt) == 16
        assert len(key_for_encrypt) == 16

        cipher_for_encrypt = AES.new(key_for_encrypt, AES.MODE_CBC, iv=iv_for_ecnrypt)
        test_input = bytearray(cipher_for_encrypt.encrypt(test_input))
    return test_input

encrypted_test_input_a64 = encrypt(test_input_a64)
# a64個与えた場合のmemcmp第1引数の内容
last_expected_a64 = bytes([0xa8, 0x7d, 0xf8, 0xac, 0x32, 0xd3, 0x77, 0x1a, 0x35, 0x8b, 0x21, 0x83, 0x4a, 0xd1, 0x1c, 0x92, 0xd0, 0x92, 0x16, 0x20, 0x6a, 0x07, 0x17, 0x57, 0xc1, 0xc0, 0xdd, 0x14, 0x54, 0x10, 0x1e, 0x23, 0xbf, 0x7c, 0x07, 0xe8, 0xea, 0xd9, 0x64, 0x69, 0x0a, 0x13, 0x74, 0x4f, 0xb4, 0x65, 0x56, 0x72, 0x0b, 0x70, 0xfb, 0xa7, 0xb2, 0xc6, 0xce, 0x06, 0xe7, 0x5e, 0x22, 0xb4, 0x2c, 0x01, 0xa4, 0x6f,])
assert len(last_expected_a64)

print(f"{encrypted_test_input_a64.hex() = }")
print(f"{last_expected_a64.hex() = }")
print(f"{(encrypted_test_input_a64 == last_expected_a64) = }")
print()
print()
print()

def decrypt(input_for_decrypt):
    for (iv_for_decrypt, key_for_decrypt) in reversed(extracted_keys):
        cipher_for_decrypt = AES.new(key_for_decrypt, AES.MODE_CBC, iv_for_decrypt)
        input_for_decrypt = bytearray(cipher_for_decrypt.decrypt(input_for_decrypt))
    return input_for_decrypt

# memcmpの第2引数の内容
last_expected_flag = bytearray([0xe2, 0x42, 0xf1, 0x42, 0x1d, 0x2a, 0xae, 0xc7, 0xc1, 0xec, 0x19, 0x6a, 0x43, 0x45, 0x77, 0xe2, 0x05, 0xd3, 0x91, 0xe3, 0x72, 0x59, 0x6b, 0xaa, 0x96, 0x41, 0x08, 0x4d, 0x8e, 0x91, 0x46, 0xf3, 0x5f, 0x86, 0xb2, 0x2a, 0x05, 0xb2, 0x2a, 0x8a, 0x08, 0x9b, 0xfc, 0x66, 0x2b, 0x07, 0xe4, 0x3d, 0xec, 0x57, 0xfa, 0x1c, 0x8a, 0xfd, 0xbb, 0x08, 0x78, 0x06, 0xdb, 0x78, 0x35, 0x4f, 0x5b, 0xe0,])

a = bytearray(last_expected_a64)
a = decrypt(a)
print(a == test_input_a64)


print(decrypt(last_expected_flag))

実行しました:

$ ./solve.py
index = 0
index = 1
index = 2
index = 3
index = 4
index = 5
index = 6
index = 7
index = 8
encrypted_test_input_a64.hex() = 'a87df8ac32d3771a358b21834ad11c92d09216206a071757c1c0dd1454101e23bf7c07e8ead964690a13744fb46556720b70fba7b2c6ce06e75e22b42c01a46f'
last_expected_a64.hex() = 'a87df8ac32d3771a358b21834ad11c92d09216206a071757c1c0dd1454101e23bf7c07e8ead964690a13744fb46556720b70fba7b2c6ce06e75e22b42c01a46f'
(encrypted_test_input_a64 == last_expected_a64) = True



True
bytearray(b'H4VIn9_7Hi5_KEY_ME4n5_you_4rE_che47In9_ON_me_7f6301e1920cb86cf8e')
$

逆算できた正解の入力を実際に与えました:

c:\Users\User\Desktop\work>BrownFlagChecker.exe
Welcome! Give me the key and I will give you the flag: H4VIn9_7Hi5_KEY_ME4n5_you_4rE_che47In9_ON_me_7f6301e1920cb86cf8e
Correct. Here is your flag
Flag: LINECTF{72f9fc0fdf5129a4930286e5b9794e10}

c:\Users\User\Desktop\work>

フラグを入手できました: LINECTF{72f9fc0fdf5129a4930286e5b9794e10}

感想

  • Web問題に非常に注力しているコンテストで驚きました。
    • ただ私が読んだ問題はwelcome問題とrev問題だけです。他の問題は全く見れていません。すみません……。
  • 今回のrev問題で、初めてカーネルモードドライバーを読みました。新鮮でした!
  • KMD関連の知識がなさすぎて、どのAPIや処理が重要なのかも何も分かっておらず、使っているAPIを全部調べました。初々しい感覚を味わいました。
  • rev問題の耐解析機構が山盛りだったので、想定解法含めて、どのような解法があるのか気になりました。
    • Discordの書き込みによると、cheat engine's DBVMというものでブレークポイントを設定できて、レジスタ内容を確認できたとのことです。また、WinDbgでカーネルデバッグを試みた方もおられるようです。
  • 親プロセスのメモリ領域を、子プロセスから参照しているようで、ちょっと全く理解が追いつきませんでした!
    • 親プロセス側の仮想アドレスを物理アドレスへ変換して、子プロセス側で物理アドレスを仮想アドレスに戻していたのでしょうか?カーネルモードは非常に強大な力を持っていそうです!
  • Binary Ninjaを初めて使いました。キーボードショートカットの多くがIDAと共通だったのもあり、すぐに使えて便利でした。機能面の比較はおまけとして後述します。

おまけ: Binary Ninja FreeとIDA Freeの、機能面や逆コンパイル結果の比較

rev問題のBrownProtector.sys解析のために、Free版Binary NinjaFree版IDAを併用しました。初めてBinary Ninjaを使ったので、メモがてら、触り心地やIDAとの比較を紹介します。前述している通り、以下のバージョンを使用しています。

  • IDA Free Version 8.4.240320 Windows x64 (64-bit address size)
  • Binary Ninja Free 4.0.4958-Stable

Binary Ninjaのオフライン逆コンパイルが嬉しい

Binary NinjaはFree版でも逆コンパイル機能があります。また、オフラインで実行しているらしく、逆コンパイル表示も一瞬でしてくれます。

IDAのFree版の逆コンパイル機能はクラウドベースです。そのためサーバーと通信する待ち時間が必要です。また、時折No Responseなどのエラーが発生して、逆コンパイルできないときがあります。そのような場合でもしばらく待ったり、IDAを再起動したりすればまた逆コンパイルできるようになりますが、たまに待たされる点は注意が必要ですし、集中できているときに食らったりすると険しい顔になったりします。

Binary NinjaもIDAもキーボードショートカットはだいたい同じ、一部違うので注意

Binary Ninjaの初期設定状態でも、以下のキーボードショートカットはIDAと同様に使えて助かりました:

  • n: 変数名等の変更
  • y: 変数や関数への型付け
  • *: 変数の配列サイズの設定
  • ;: コメントの追加
  • r: カーソル位置の定数を文字型に変更
  • m: カーソル位置の定数を列挙型に変更
  • g: 指定アドレスへジャンプ
  • Esc: ジャンプ元へ戻る
  • F5: 疑似Cコードへの逆コンパイル
  • x: Binary Ninjaでは左下のCross Refferences箇所にフォーカスが移ります。Cross Refferences箇所には、常にカーソル位置のシンボルの相互参照が一覧表示されていますし、そこからジャンプできます。そのため、IDAの「カーソル位置の相互参照一覧を表示し、そこからジャンプ」と同様に操作できます。

なお、初期状態では以下のキーボードショートカットはIDAとは異なりました:

  • h: Binary Ninjaでは、Hex Dump画面への行き来に使用します。IDAでは、カーソル位置の定数の基数を変更します。IDAのノリで押して、うっかりHex Dumpを見るたびに笑顔になれます。Binary Ninjaでの基数変換は、定数を右クリックして、Display asメニュー以下から設定できます。
  • Ctrl+W: Binary Ninjaではおそらく未割り当てです。IDAでは作業状況の保存です。手癖で入力しても何も起こらず安心です。
  • Ctrl+S: Binary Ninjaでは作業状態を保存します。IDAではジャンプ先セグメントの選択です。これはBinary Ninjaの方が直感的に思います。

Binary NinjaはNTOSKRNL関係の構造体定義を持っている

Binary Ninjaは、BrownProtector.sysのエントリーポイントであるNTSTATUS _start(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)を正しく認識しました。DRIVER_OBJECT構造体の定義も正しく認識しているようで、0x140001047付近のDRIVER_OBJECTの初期化処理も、ファイル読み込み直後から正しく認識してくれました:

140001047      arg1->DriverUnload = sub_1400010e0
14000105b      arg1->MajorFunction[0xe] = sub_140001180
140001069      arg1->MajorFunction[0] = sub_140001160
14000106d      arg1->MajorFunction[2] = sub_140001160

なお、Binary Ninjaでも、WdfVersionBindで使用するWDF_BIND_INFO構造体など、WDFLDR由来のデータ構造の定義は持っていないようです。

Free版IDAでは、どうにもDRIVER_OBJECT構造体含めて、NTOSKRNL由来のデータ構造の定義を持っていないようです。

Binary NinjaはIMAGE_DOS_HEADER構造体の定義を持っていない?

IDAでは、IMAGE_DOS_HEADER構造体等、PE関連のデータ構造の定義を持っています。一方で、Binary NinjaはIMAGE_DOS_HEADER構造体を使おうとしても、Error parsing specified typeエラーで認識しませんでした:

自前で定義を与えるか、タイプライブラリ等を追加することで認識させられると思いますが、少なくとも初期状態では認識してくれないようです。

Binary Ninjaは逆コンパイル結果にもアドレス表示があって嬉しい

たまに「逆コンパイル結果のここはどのアドレスに対応するのか」を知りたいときがあります。Binary Ninjaでは、逆コンパイル結果画面の左端に、おおよそ対応するアドレスが表示されているのですぐに分かります。

IDAの場合は、逆コンパイル画面そのものにはアドレス表示はありません。ただ、逆コンパイル画面(Pseudocode-Aタブ等)の右クリックメニューでSynchronized with→IDA View-Aを選択しておくと、逆コンパイル画面の選択業と、逆アセンブル画面での選択行が同期するようになるため、タブ切り替えですぐ分かりはします。

Binary Ninjaはnot命令の逆コンパイルを!演算子としてしまっている

0x14000102c周辺に、以下の処理があります:

14000102c  41f7d1             not     r9d
14000102f  418bc1             mov     eax, r9d
140001032  c3                 retn

Binary Ninjaの逆コンパイル結果では、return !(dwHash);と、not命令をC言語の論理否定である!演算子と表示してしまっています。逆コンパイル結果だけを見ると重大な誤解をしてしまいそうです。

IDAの逆コンパイル結果では、return ~dwHash;と、not命令をC言語の1の補数にする~演算子に、正しく表示しています。

Binary Ninjaは1つの文中に多くの関数呼び出しを含めることがある

0x140001c6a付近の処理を、Binary Ninjaは以下のように込み入った1つの文で表示します:

int64_t rcx_10 = *(uint64_t*)(MmGetVirtualForPhysical((*(uint64_t*)(MmGetVirtualForPhysical((__mov_cr_gpr64_cr(cr3) & 0xfffffffff000)) + (((qwUserspaceAddr >> 0x27) & 0x1ff) << 3)) & 0xfffffffff000)) + (((qwUserspaceAddr >> 0x1e) & 0x1ff) << 3));

IDAは以下のように、関数呼び出し単位ごとに分割して表示します:

qwCr3 = __readcr3();
VirtualForPhysical = MmGetVirtualForPhysical(qwCr3 & 0xFFFFFFFFF000LL);
v4 = *(_QWORD *)(MmGetVirtualForPhysical(*(_QWORD *)(VirtualForPhysical + 8 * ((qwUserspaceAddr >> 39) & 0x1FF)) & 0xFFFFFFFFF000LL)
               + 8 * ((qwUserspaceAddr >> 30) & 0x1FF));

Binary Ninjaは構造体ポインター経由の多段アクセスのメンバー認識がちょっと弱そう

0x1400016ed付近に、PROCESS_BASIC_INFORMATION::PebBaseAddressを経由して、PEB::ImageBaseAddressにアクセスする処理があります:

1400016ed  488b442448         mov     rax, qword [rsp+0x48 {var_38.PebBaseAddress}]
1400016f2  488b4010           mov     rax, qword [rax+0x10]

Binary Ninjaの逆コンパイル表示はrax_2 = *(uint64_t*)(pbi.PebBaseAddress + 0x10);です。PROCESS_BASIC_INFORMATION::PebBaseAddressまでのアクセスを認識していますが、PEB経由でのアクセス先を認識できていません。

IDAの逆コンパイル表示はreturn pbi.PebBaseAddress->ImageBaseAddress;と、構造体メンバーの多段アクセスも適切に認識できています。

Binary Ninjaの多段分岐表示が読みやすいかも

0x140001180(DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]へ登録している関数)で、以下の分岐があります:

1400011c0  81ea00402200       sub     edx, 0x224000
1400011c6  0f8458020000       je      0x140001424  {"L9=U1"}

1400011cc  83ea04             sub     edx, 0x4
1400011cf  0f8403020000       je      0x1400013d8

1400011d5  83ea04             sub     edx, 0x4
1400011d8  0f849c010000       je      0x14000137a

1400011de  83ea04             sub     edx, 0x4
1400011e1  0f84f7000000       je      0x1400012de

1400011e7  83ea04             sub     edx, 0x4
1400011ea  746c               je      0x140001258

1400011ec  83fa04             cmp     edx, 0x4
1400011ef  0f859c020000       jne     0x140001491

Binary Ninjaの逆コンパイル結果は、アセンブリ内容を素直に翻訳しているのかもしれませんが、以下のように素直に見やすいif-else分岐です:

1400011c6      if (dwMayBeCode == 0x224000)
1400011c6      { // 内容中略
1400011c6      }
1400011c6      else
1400011c6      {
1400011cf          if (dwMayBeCode == 0x224004)
1400011cf          { // 内容中略
1400011cf          }
1400011d8          if (dwMayBeCode == 0x224008)
1400011d8          { // 内容中略
1400011d8          }
1400011d8          else
1400011d8          {
1400011e1              if (dwMayBeCode == 0x22400c)
1400011e1              { // 内容中略
1400011ea              }
1400011ea              else if (((dwMayBeCode == 0x224014 && PsGetCurrentProcessId() == g_hCurrentProcess_WhenCode0x224004_MustBeChild) && (PsGetCurrentThreadId() == g_hCurrentThread_MustBeChild && g_bSomeFlag_SetWhenCode0x224010 != 0)))
1400011ef              { // 内容中略
1400011ef              }
1400011d8          }
1400011c6      }

IDAの逆コンパイル結果は、「この分岐はどの場合の処理なのか」が分かりづらく、しばしばコメントで補足したくなる分岐です:

  if ( *(_DWORD *)(v2 + 24) == 0x224000 )
  { // 内容中略
  }
  if ( *(_DWORD *)(v2 + 24) == 0x224004 )
  { // 内容中略
  }
  if ( *(_DWORD *)(v2 + 24) != 0x224008 )
  {
    if ( *(_DWORD *)(v2 + 24) != 0x22400C )
    {
      if ( *(_DWORD *)(v2 + 24) != 0x224010 )
      {
        if ( *(_DWORD *)(v2 + 24) == 0x224014
          && PsGetCurrentProcessId() == g_currentProcessId2
          && PsGetCurrentThreadId() == g_currentThreadId2
          && g_bSomeFlag_SetWhenCode0x224010 )
        { // 内容中略
        }
        goto LABEL_43;
      }
      // 内容省略、0x224010に対応する処理
    }
    // 内容省略、0x22400Cに対応する処理
  }
  // 内容省略、0x224008に対応する処理

Binary Ninjaのグローバル変数や配列の表示が分かりやすい

0x140004000以降にはCRC32用のテーブルがあります。Biniary Ninjaでは、indexごとに値を表示してくれて読みやすいです:

.data section started  {0x140004000-0x140005200}
140004000  uint32_t g_dwCrc32TableSize256[0x100] =
140004000  {
140004000      [0x00] =  0x00000000
140004004      [0x01] =  0x77073096
140004008      [0x02] =  0xee0e612c
14000400c      [0x03] =  0x990951ba
(後略)

IDAでは型付けこそできますが、逆アセンブル表示のままのせいか、「どのindexがどの数値だろう」と思うことがあります:

.data:0000000140004000                                      ; _DWORD g_dwCrc32TableSize256[256]
.data:0000000140004000 00 00 00 00 96 30 07 77 2C 61 0E     g_dwCrc32TableSize256 dd 0, 77073096h, 0EE0E612Ch, 990951BAh, 76DC419h, 706AF48Fh
.data:0000000140004000 EE BA 51 09 99 19 C4 6D 07 8F F4…                                            ; DATA XREF: CalculateCrc32+1B↑o
.data:0000000140004018 35 A5 63 E9 A3 95 64 9E 32 88 DB…                    dd 0E963A535h, 9E6495A3h, 0EDB8832h, 79DCB8A4h, 0E0D5E91Eh
.data:000000014000402C 88 D9 D2 97 2B 4C B6 09 BD 7C B1…                    dd 97D2D988h, 9B64C2Bh, 7EB17CBDh, 0E7B82D07h, 90BF1D91h
(後略)

同様に、0x140004400にはUNICODE_STRING型のグローバル変数があります。Binary Ninjaでは、型を与えることで、メンバー名と内容を対応して表示してくれます:

140004400  UNICODE_STRING g_UnicodeStringDeviceName =
140004400  {
140004400      USHORT Length = 0x38
140004402      USHORT MaximumLength = 0x3a
140004408      PWCH Buffer = g_wstrDeviceName {u"\Device\BrownProtectorDevice"}
140004410  }

IDAでは、型を設定することで1まとめには表示してくれますが、「どのメンバーが何の値を取るんだろう」と思うことがあります:

.data:0000000140004400                                      ; UNICODE_STRING g_UnicodeStringDeviceName
.data:0000000140004400 38 00 3A 00 00 00 00 00 60 31 00     g_UnicodeStringDeviceName UNICODE_STRING <38h, 3Ah, offset aDeviceBrownpro>
.data:0000000140004400 40 01 00 00 00                                                               ; DATA XREF: InitializeDriverObjectAndDevice+17↑o
.data:0000000140004400                                                                              ; InitializeDriverObjectAndDevice+76↑o
.data:0000000140004400                                                                              ; "\\Device\\BrownProtectorDevice"