Base64デコーダーを作る

# Base64デコーダーを作る

とあるCrackmeでcustom_base64なるものでエンコードされた文字列が出てきた...  
Flagを手に入れるためにはこれをデコードして元の文字列を見つけなきゃいけない．
custom_base64とはなんなのか...答えを見つけるために，我々はアマゾンの奥地へと...

## 概要

Base64の仕組みの中で000000 ~ 111111を文字に置き換えた辞書が存在する．
通常のBase64では順番に `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/` をあてはめた辞書が使われている．
これを指定した辞書に入れ替えたものがcustom_base64関数の中身であった．
[Base64を実装する記事][1]を参考に本記事ではPythonでデコーダーを実装する．

## Base64の仕組み

### エンコード

大まかにBase64のエンコードの処理を確認します．詳しくは他サイトを参照．

1. 変更したい文字列(ASCII)をバイナリ(2進数)に変換
2. バイナリを6bitづつに分割
3. 分割した際に最後が6bitより少なくなるため，6bitになるように0を追加する
4. 変換表を用いて6bitを文字に変換
5. 4biteずつBase64では出力するために文字数が4の倍数文字になるよう"="を付け足す
6. base64の文字列の完成!!

### デコード

エンコードの仕組みがわかればデコードは簡単！基本的には逆の手順を踏むだけ！

1. 付け足された"="を削除
   - `SG9nZUhvZ2U=` → `SG9nZUhvZ2U`
2. 変換表を用いて文字をバイナリに変換してつなげる．
   - `SG9nZUhvZ2U` → `010010 000110 111101 100111 011001 010100 100001 101111 011001 110110 010100`
3. バイナリを8bitずつに分割，エンコード3. で付け足された0が余るのでそれらを削除
   - `01001000 01101111 01100111 01100101 01001000 01101111 01100111 01100101 00`
4. 2進数bitをASCIIに変換

```2進数から文字に変換
01001000 → H
01101111 → o
01100111 → g
01100101 → e
01001000 → H
01101111 → o
01100111 → g
01100101 → e
```

5. デコード完了！

## プログラムにしてみる

```custom_base64_decoder.py
import sys
import argparse
BYTE_SIZE = 8

# 000000 -> 111111まで1文字ずつ辞書型リストを作成する関数
def makeDict(base64Dict_seed):
    dictionary = {}

    for i in range(0, 64):
        dictionary[format(i, '06b')] = base64Dict_seed[i]

    return dictionary


# 文字列s をn文字で区切ってリスト化する関数
def split(string, n):
    split_list = []

    for i in range(0, len(string), n):
        split_list.append(string[i:i+n])

    return split_list


# 文字列がn文字なかったらn文字になるように`c`を足す
def fillBlank(s, n, c):
    mod = len(s) % n

    if mod == 0:
        return s
    else:
        margin = n - mod
        return s + c * margin


# 辞書の値を渡すと辞書のキーを返す
def getValue(key, items):
    for v in items.items():
        # print(v[1])
        if v[1] == key:
            # print(v)
            return v[0]
    return ''


def main():
    # -kをつけるとカスタム辞書を入力できる
    parser = argparse.ArgumentParser(
    description='custom Base64 Decoder')
    parser.add_argument('-k', '--key', help="Use custom Seed to encrypt in base64 ", \
        default="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
    parser.add_argument('text', help='base text')
    args = parser.parse_args()


    # 0. 辞書を作る
    base64Dict = makeDict(args.key)


    # 1. '='をはずす
    text = args.text.replace("=", '')

    binStr = ""


    # 2. 変換表を用いて文字をバイナリに変換してつなげる．
    for i in text:
        binStr += getValue(i, base64Dict)

    # 3. バイナリを8bitずつに分割，エンコード3. で付け足された0が余るのでそれらを削除
    splitCount = 8
    s = split(binStr, splitCount)

    if (len(s[-1]) != 8):
        s.pop(-1)


    # 4. 2進数bitをASCIIに変換
    result =""

    for c in s:
        print(c + " → " + chr(int(c, 2)))
        result += chr(int(c, 2))

    print(result)

if __name__ == "__main__":
    main()
```

### 使い方

```Usage
$ python3 customBase64Decoder.py <Base64テキスト>
```

```custom_Base64
$ python3 customBase64Decoder.py -k <Custom辞書> <Base64テキスト>
```

```例
$ python3 customBase64Decoder.py SG9nZUhvZ2U=
```

```例
$ python3 customBase64Decoder.py -k xEPOKnvADqeG0m1VkZ47CM653jrtbzLsTc2ypoYUSWJ9ludQig+awf8XF/RNHBhI 4vBUjCcQj8C=

HogeHoge
```

## まとめ

Base64完全に理解した．
これでオリジナルBase64つくって秘密の通信ができちゃうね，やったね

サンプルコードはGitHubに置いてあります

- エンコーダー
    - [https://github.com/itiB/sandpit/blob/master/tools/customBase64Encoder.py](https://github.com/itiB/sandpit/blob/master/tools/customBase64Encoder.py)
- デコーダー
    - [https://github.com/itiB/sandpit/blob/master/tools/customBase64Decoder.py](https://github.com/itiB/sandpit/blob/master/tools/customBase64Decoder.py)

## 参考文献

base64ってなんぞ？？理解のために実装してみた - qiita
[https://qiita.com/PlanetMeron/items/2905e2d0aa7fe46a36d4][1]

[1]:https://qiita.com/PlanetMeron/items/2905e2d0aa7fe46a36d4