簡単な実行ファイルをリバースエンジニアリングしてみた

## 背景

https://play.picoctf.org/practice/challenge/79

ツールの使い方の備忘録として残したかった

## Ghidraのインストール

ここから落とす

https://ghidra-sre.org/

Javaのランタイムが必要なので入れてなかったら入れる

https://www.oracle.com/java/technologies/downloads/

## Ghidraでの解析

`ghidraRun.bat`で起動できる

起動したらドラゴンくんを選択する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1086867/2efb3c6d-2694-132f-d58d-4aa74212b8fa.png)

importFileで解析したいバイナリをインポート

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1086867/33668de5-7a4b-9b84-242b-6f9c51d1d38f.png)

いろいろ出るがとりあえずそのままOK押してくと解析してくれる

解析が終わると、ウィンドウ左のSymbolTreeのFunctionsを見てみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1086867/f5b857ea-208d-0dc8-d86e-8c135d28de27.png)

これがバイナリ内部の関数だ。
それらしい関数名はないので、main関数を選択する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1086867/d17592f4-b055-783b-91ec-a6e616cf646a.png)

真ん中のウィンドウはアセンブリなので今回は見ない。
右のDecompileウィンドウを見てみると、アセンブリをc言語に（機械的に）戻したものが表示される。
あくまでアセンブリになったものを無理くりcにしているので、変数名は適当なものが入る。

```c:before

void main(void)

{
  size_t sVar1;
  char local_58 [23];
  char local_41;
  int local_2c;
  FILE *local_28;
  FILE *local_20;
  uint local_14;
  int local_10;
  char local_9;
  
  local_20 = fopen("flag.txt","r");
  local_28 = fopen("rev_this","a");
  if (local_20 == (FILE *)0x0) {
    puts("No flag found, please make sure this is run on the server");
  }
  if (local_28 == (FILE *)0x0) {
    puts("please run this on the server");
  }
  sVar1 = fread(local_58,0x18,1,local_20);
  local_2c = (int)sVar1;
  if ((int)sVar1 < 1) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  for (local_10 = 0; local_10 < 8; local_10 = local_10 + 1) {
    local_9 = local_58[local_10];
    fputc((int)local_9,local_28);
  }
  for (local_14 = 8; (int)local_14 < 0x17; local_14 = local_14 + 1) {
    if ((local_14 & 1) == 0) {
      local_9 = local_58[(int)local_14] + '\x05';
    }
    else {
      local_9 = local_58[(int)local_14] + -2;
    }
    fputc((int)local_9,local_28);
  }
  local_9 = local_41;
  fputc((int)local_41,local_28);
  fclose(local_28);
  fclose(local_20);
  return;
}
```

ここまでがGhidraでの操作。

## 解析したソースからフラグを推定する

ここからが本当のリバースエンジニアリング作業。
このままでは見づらいので、テキストエディタで変数名などを整形する。
たとえば、

```c
local_20 = fopen("flag.txt","r");
local_28 = fopen("rev_this","a");
```

なんかは`fopen`しているので入出力ファイルだとわかる。

```c
sVar1 = fread(local_58,0x18,1,local_20);
```
`fread`しているので、`local_58`は入力ファイルの文字列が入ると推測できる。


そんなこんなで整形した結果が以下。

```c:after
void main(void)

{
  size_t sVar1;
  char input_char_array [23];
  char local_41;
  int local_2c;
  FILE *output_file;
  FILE *input_file;
  uint j;
  int i;
  char enc_char;
  
  input_file = fopen("flag.txt","r");
  output_file = fopen("rev_this","a");
  if (input_file == (FILE *)0x0) {
    puts("No flag found, please make sure this is run on the server");
  }
  if (output_file == (FILE *)0x0) {
    puts("please run this on the server");
  }
  sVar1 = fread(input_char_array,0x18,1,input_file);
  local_2c = (int)sVar1;

  // 読み込んだ文字がないとき
  if ((int)sVar1 < 1) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }

// 暗号化処理
  for (i = 0; i < 8; i = i + 1) { // 8回ループ
  // 1文字目~8文字目まで
    enc_char = input_char_array[i];
    fputc((int)enc_char,output_file);
  }
  for (j = 8; (int)j < 0x17; j = j + 1) {
    // 8文字目~23文字目まで
    if ((j & 1) == 0) { // 下位1ビット以外をマスクして0のとき=偶数インデックスのとき
    // 奇数文字目
      enc_char = input_char_array[(int)j] + '\x05';
    }
    else { // 奇数インデックス
    // 偶数文字目
      enc_char = input_char_array[(int)j] + -2;
    }
    fputc((int)enc_char,output_file);
  }
  enc_char = local_41;
  fputc((int)local_41,output_file);
  fclose(output_file);
  fclose(input_file);
  return;
```

1~8文字目はprefixなのでそのまま。

このプログラムの暗号化アルゴリズムは

- 奇数文字目：文字コードを+5する
- 偶数文字目：文字コードを-2する

ということが分かったので、

アウトプットのファイル`rev_this`の文字列を、

- 奇数文字目：文字コードを-5する
- 偶数文字目：文字コードを+2する

してあげればいい。


以上。