Base64デコーダーを作る

本記事は以前筆者のqiitaに投稿していた記事を移動させたものです。

Base64デコーダーを作る - Qiita

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

概要

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

Base64の仕組み

エンコード

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

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

デコード

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

  1. 付け足された"="を削除
    • SG9nZUhvZ2U=SG9nZUhvZ2U
  2. 変換表を用いて文字をバイナリに変換してつなげる.
    • SG9nZUhvZ2U010010 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に変換
01001000 → H
01101111 → o
01100111 → g
01100101 → e
01001000 → H
01101111 → o
01100111 → g
01100101 → e
  1. デコード完了!

プログラムにしてみる

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()

使い方

$ python3 customBase64Decoder.py <Base64テキスト>
$ python3 customBase64Decoder.py -k <Custom辞書> <Base64テキスト>
$ python3 customBase64Decoder.py SG9nZUhvZ2U=
$ python3 customBase64Decoder.py -k xEPOKnvADqeG0m1VkZ47CM653jrtbzLsTc2ypoYUSWJ9ludQig+awf8XF/RNHBhI 4vBUjCcQj8C=

HogeHoge

まとめ

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

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

参考文献

base64ってなんぞ??理解のために実装してみた - qiita https://qiita.com/PlanetMeron/items/2905e2d0aa7fe46a36d41