Szarny.io

There should be one-- and preferably only one --obvious way to do it.

tan(x/2)=t と置換して解く f(sinx,cosx) の積分

良く忘れるのでメモ

結論

 \displaystyle
t = tan{\frac{x}{2}}

とおくと,以下のように置換積分が可能になる.

 \displaystyle
\int f(sin{x}, cos{x}) dx = \int f(\frac{2t}{1+t^2}, \frac{1-t^2}{1+t^2}) \cdot \frac{2}{1+t^2} dt

導出

前提

 \displaystyle
t = tan{\frac{x}{2}}

tanxの導出

2倍角の公式を用います.
 \displaystyle \begin{align*}
tanx &= tan{2 \cdot \frac{x}{2}} \\ \\
&= \frac{2tan\frac{x}{2}}{1-tan^2{\frac{x}{2}}} \\ \\
&= \frac{2t}{1-t^2}
\end{align*}

cosxの導出

2倍角の公式を用いた後,cosxをtanxで置き換えます.
 \displaystyle \begin{align*}
cosx &= cos{2 \cdot \frac{x}{2}} \\ \\
&= 2cos^2{\frac{x}{2}} - 1 \\ \\
&= \frac{2}{1 + tan^2{\frac{x}{2}}} - 1 \\ \\
&= \frac{2}{1+t^2} - \frac{1+t^2}{1+t^2} \\ \\
&= \frac{1-t^2}{1+t^2}
\end{align*}

sinxの導出

すでに導出したtanxとcosxを用います.
 \displaystyle \begin{align*}
sinx &= cosx \cdot tanx \\ \\
&= \frac{1-t^2}{1+t^2} \cdot \frac{2t}{1-t^2} \\ \\
&= \frac{2t}{1+t^2} \\ \\
\end{align*}

dxの導出

商の微分法を用いて微分した後,cosxをtanxで置き換えます.
 \displaystyle \begin{align*}
\frac{dt}{dx} &= (tan{\frac{x}{2}})^{\prime} \\ \\
&= (\frac{sin{\frac{x}{2}}}{cos{\frac{x}{2}}})^{\prime} \\ \\
&= \frac{\frac{1}{2}cos^2{\frac{x}{2}} - (-\frac{1}{2}sin^2{\frac{x}{2}})}{cos^2{\frac{x}{2}}} \\ \\
&= \frac{1}{cos^2{\frac{x}{2}}} \\ \\
&= \frac{1 + tan^2{\frac{x}{2}}}{2} \\ \\
&= \frac{1+t^2}{2} \\ \\
&\therefore dx = \frac{2}{1+t^2}dt
\end{align*}

例題を解く

 
\displaystyle
\int \frac{1}{sinx} dx \\ \\
t = tan{\frac{x}{2}} とすると\\ \\
\begin{align*}
\int \frac{1}{sinx} dx &= \int \frac{1+t^2}{2t} \cdot \frac{2}{1+t^2} dt \\ \\
&= \int \frac{1}{t} dt \\ \\
&= log{ |t| } + C\\ \\
&= log{ |tan{\frac{x}{2}}| } + C
\end{align*}

Pythonで実装する素数判定アルゴリズムと実行効率の比較

はじめに

Pythonで5種の素数判定アルゴリズムを実装します.

後で効率を比較するために,各関数は  [2, limit] の範囲において,各数が素数であるかを計算します.そして,その範囲における素数の総数を戻り値として設定します.

ごり押し判定法

説明

ある数 k素数かどうか判定するために, [2, k-1]の範囲において約数が存在するかどうか調べます.
もし,その範囲内に約数が存在しないなら,素数であると判断します.

ソースコード

def normal(limit):
    n = 0
    for k in range(2, limit+1):
        factor = 0
        
        for divisor in range(2, k):
            # 約数が存在するか?
            if k % divisor == 0:
                factor += 1
                
        # 約数が存在しないなら素数
        if factor == 0:
            n += 1
            
    return n

ちょっと改善した判定法

説明

ある数 k について, [\lceil\frac{k+1}{2}\ \rceil, k-1]の範囲に約数が存在しないことは明らかです.
そのため,k素数かどうか判定するために, [2, \lfloor\frac{k}{2}\ \rfloor]の範囲において約数が存在するかどうか調べます.
もし,その範囲内に約数が存在しないなら,素数であると判断します.

加えて,2以外の偶数は確実に素数ではないため,スキップします.

ソースコード

def improve(limit):
    n = 0
    for k in range(2, limit+1):
        factor = 0
        
        # 2以外の偶数は素数ではないので無視する
        if k % 2 == 0 and k != 2:
            continue
        
        # 繰り返しの上限を半分にする
        for divisor in range(2, k//2):
            if k % divisor == 0:
                factor += 1
                
        if factor == 0:
            n += 1
            
    return n

素数の性質を利用した判定法

説明

合成数 xp <= \sqrt{x} を満たす素因子 p をもつ

素数判定 | アルゴリズムとデータ構造 | Aizu Online Judge

という性質を利用します.

つまり,ある数 k素数かどうか判定するために, [2, \sqrt{k}]の範囲において約数が存在するかどうか調べれば十分であるということです.
もし,その範囲内に約数が存在しないなら,素数であると判断します.

前項で実装した,偶数をスキップするアルゴリズムも採用します.

ソースコード

def using_sqrt(limit):
    n = 0
    for k in range(2, limit+1):
        factor = 0
        
        # 2以外の偶数は素数ではないので無視する
        if k % 2 == 0 and k != 2:
            continue
        
        # 繰り返しの上限を対象の平方根にする
        for divisor in range(2, math.floor(math.sqrt(k))+1):
            if k % divisor == 0:
                factor += 1
                
        if factor == 0:
            n += 1
            
    return n

フェルマーの小定理を利用した判定法

説明

ap が互いに素な自然数のとき
a^{p-1} \equiv 1 \mod p

フェルマーの小定理の証明と例題 | 高校数学の美しい物語

というフェルマーの小定理を利用して,素数判定を行います.

非常に高速ですが,確率的素数判定法であるため,素数でない数(擬素数)を素数であると判断することがあります.

ソースコード

def fermat(limit):
    n = 0
    for k in range(2, limit+1):
        # 2以外の偶数は素数ではないので無視する
        if k % 2 == 0 and k != 2:
            continue
            
        if pow(2, k-1, k) == 1:
            n += 1
            
    return n

エラトステネスの篩

説明

  1.  [2, limit-1]の範囲に含まれる整数を列挙した集合Aを用意する
  2. Aの中で最小の数 p素数とし,素数リストPに追加する
  3. Aの中で,pの倍数である要素を削除する
  4. p > \sqrt{limit}となった時点で終了し,残ったAの全要素をPに追加する

以上のようにして,素数を篩い分けるアルゴリズムです.

ソースコード

def eratosthenes(limit):
    A = [i for i in range(2, limit+1)]
    P = []
    time = 0
    
    while True:
        prime = min(A)
        
        if prime > math.sqrt(limit):
            break
            
        P.append(prime)
            
        i = 0
        while i < len(A):
            if A[i] % prime == 0:
                A.pop(i)
                continue
            i += 1
            
    for a in A:
        P.append(a)
            
    return len(P)

実行時間の比較

説明

以上,5つのアルゴリズムを,上限を変えながらそれぞれ実行し,実行時間の比較を行います.
今回は上限を 1000, 5000, 10000, 100000 と設定しました.

実験用のソースコード

# 上限
limits = [1000, 5000, 10000, 100000]

# 適用する関数名
labels = ["Eratosthenes", "Fermat", "SQRT", "Improve", "Normal"]

# 関数本体
functions = [normal, improve, using_sqrt, fermat, eratosthenes]

for answer, limit in zip(answers, limits):
    records = []
    
    for t_f, f in enumerate(functions):
        start = time.time()
        result = f(limit)
        end = time.time()
        records.append(round(end - start, 5))
    
    records = list(reversed(records))
    
    plt.barh(range(5), records)
    plt.yticks(range(5), (labels))
    plt.title("limit is " + str(limit))
    plt.ylabel("Algorithm")
    plt.xlabel("Time(sec)")
    plt.axis(xmax=max(records)*1.2)
    
    for x,y in zip(records, [0,1,2,3,4]):
        plt.text(x*1.01, y, records[y])
    
    plt.show()

実行結果

結果はそれぞれ以下のようになりました.
上限が小さいときはそれほど差はありませんが,大きくなるにつれて差がどんどん広がっていきました.

グラフからわかる通り,実行時間が最も短いのはフェルマーの小定理を利用したアルゴリズムですが,やはり結果は不正確です.
正確かつ高速な判定が行いたいときは,平方根を上限として判定を行うアルゴリズムが最適かと思います.

f:id:Szarny:20170921231446p:plain
f:id:Szarny:20170921231457p:plain
f:id:Szarny:20170921231504p:plain
f:id:Szarny:20170921231940p:plain

pythonのtqdmモジュールで進捗状況を表示する

はじめに

tqdmモジュールを使うことで,プログラムの進捗状況を可視化することができるようです.
実際に使いながら見ていきます.

モジュールのインストール

# pip install tqdm

Anaconda環境の方はこちら

# conda install tqdm

使用例

簡単な例

関数do_somethingでは,time.sleep関数を用いて時間のかかる処理を模倣しています.

import tqdm
import time

def do_something():
    time.sleep(0.1)
    print("done.")
    
for i in tqdm.tqdm(range(10)):
    do_something()
  0%|                                | 0/10 [00:00<?, ?it/s]done.
 10%|██▍                     | 1/10 [00:00<00:00,  9.91it/s]done.
 20%|████▊                   | 2/10 [00:00<00:00,  9.90it/s]done.
 30%|███████▏                | 3/10 [00:00<00:00,  9.89it/s]done.
 40%|█████████▌              | 4/10 [00:00<00:00,  9.90it/s]done.
 50%|████████████            | 5/10 [00:00<00:00,  9.91it/s]done.
 60%|██████████████▍         | 6/10 [00:00<00:00,  9.90it/s]done.
 70%|████████████████▊       | 7/10 [00:00<00:00,  9.91it/s]done.
 80%|███████████████████▏    | 8/10 [00:00<00:00,  9.92it/s]done.
 90%|█████████████████████▌  | 9/10 [00:00<00:00,  9.92it/s]done.
100%|███████████████████████| 10/10 [00:01<00:00,  9.93it/s]

関数と組み合わせる

import time
import tqdm

def add(a, b):
    time.sleep(0.1)
    return "{} + {} = {}".format(a, b, a+b)

def sub(a, b):
    time.sleep(0.1)
    return "{} - {} = {}".format(a, b, a-b)

def mul(a, b):
    time.sleep(0.1)
    return "{} * {} = {}".format(a, b, a*b)

def div(a, b):
    time.sleep(0.1)
    return "{} / {} = {}".format(a, b, a/b)

# 進捗状況付きの関数リスト
func_with_progress = tqdm.tqdm([add, sub, mul, div])

for func in func_with_progress:
    print(func(6,2))

print("program done.")
  0%|                                 | 0/4 [00:00<?, ?it/s]6 + 2 = 8
 25%|██████▎                  | 1/4 [00:00<00:00,  9.92it/s]6 - 2 = 4
 50%|████████████▌            | 2/4 [00:00<00:00,  9.90it/s]6 * 2 = 12
 75%|██████████████████▊      | 3/4 [00:00<00:00,  9.91it/s]6 / 2 = 3.0
100%|█████████████████████████| 4/4 [00:00<00:00,  9.92it/s]
program done.

通信処理と出力のカスタマイズ

処理に時間がかかりがちで,実際に実行が続いているか不安になることが多い通信処理を含むプログラムと組み合わせるとよさそうですね.

本ブログの最新記事のタイトルを取得し,表示するプログラムです.
各チェックポイント毎に,進捗状況をカスタマイズして表示させています.

import tqdm
import requests
from bs4 import BeautifulSoup as bs

progress = tqdm.tqdm(total=3)

page = requests.get("http://szarny.hatenablog.com/")
progress.set_description("[HTTP GET Request]")
progress.update(1)

soup = bs(page.text, "lxml")
progress.set_description("[HTML Parse]")
progress.update(1)

title_list = soup.find_all("a", class_="entry-title-link")
progress.set_description("[Finding title]")
progress.update(1)
progress.close()

for i, title in enumerate(title_list, start=1):
    print("[{}] {}".format(i, title.text))
※実際には以下のプログレスバーがシームレスに表示されます
                      0%|          | 0/3 [00:00<?, ?it/s]
[HTTP GET Request]:  33%|███▎      | 1/3 [00:00<00:00,  4.61it/s]
[HTML Parse]:        67%|██████▋   | 2/3 [00:00<00:00,  3.88it/s]      
[Finding title]:    100%|██████████| 3/3 [00:00<00:00,  4.60it/s]

[1] Metasploitのsmb_versionモジュールを使ってWindowsマシンを特定する
[2] 高性能ポートスキャナ「Nmap」を使ってポートスキャン - 通信方式・オプション・実行例ほか
[3] Base64 Encoder + Decoder をPythonで実装する
[4] SANS NETWARSトーナメント2017 に参加しました
[5] バッファオーバーフローを実験する(deadbeefとeip奪取)
[6] VMware Playerでインターネット接続が突然切れた
[7] 簡単なプログラムをgdb-pedaで解析する

Metasploitのsmb_versionモジュールを使ってWindowsマシンを特定する

SMBとは

Server Message Block (SMB) は、主にWindowsで使用されているOSI参照モデル第7層アプリケーション層部分の独自通信プロトコルの総称。 LANを通じてファイル共有やプリンタ共有などの実現に使用される。


Server Message Block - Wikipediaより

実践

msfconsoleの起動

# msfconsole
msf > 

モジュールの検索

msf > search smb_version

Matching Modules
================
   Name                               Disclosure Date  Rank    Description
   ----                               ---------------  ----    -----------
   auxiliary/scanner/smb/smb_version                   normal  SMB Version Detection

モジュールの設定

msf > use scanner/smb/smb_version
msf auxiliary(smb_version) > show options

Module options (auxiliary/scanner/smb/smb_version):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   RHOSTS                      yes       The target address range or CIDR identifier
   SMBDomain  .                no        The Windows domain to use for authentication
   SMBPass                     no        The password for the specified username
   SMBUser                     no        The username to authenticate as
   THREADS    1                yes       The number of concurrent threads

msf auxiliary(smb_version) > set RHOSTS 192.168.0.3
RHOSTS => 192.168.0.3

実行と確認

msf auxiliary(smb_version) > run

[+] 192.168.0.3:445       - Host is running Windows 10 Home (build:XXXXX) (name:DESKTOP-XXXXXXX)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

sf auxiliary(smb_version) > hosts
続きを読む

高性能ポートスキャナ「Nmap」を使ってポートスキャン - 通信方式・オプション・実行例ほか

ポートスキャンとは

ターゲットのコンピュータ上でどのようなサービスが稼働しているのか,つまりどのポートが開放されているのかを調査する手法です.
開放されているポートだけでなく,サービスを提供しているソフトウェアのバージョンOSのフィンガプリントを取得することもあります.

一般的には,まずターゲットに対して,宛先ポート番号を一定の範囲内で増分させたパケットを送信します.
そして,それに適切な応答があればポートが開放されていると判断します.加えて,返信内容からフィンガプリント等の各種情報を取得します.

例えば,宛先ポート番号を22番に設定したパケットに対する応答があった場合には,ターゲット上でSSHサービスが稼働していると分かります.また,80番に設定したパケットに対する応答があった場合には,HTTPサービスが稼働していると分かります.

逆に,応答がなかった場合には,当該サービスが稼働していない応答する設定がされていない,若しくはファイアウォール等によってパケットが遮断されていると判断できます.

f:id:Szarny:20170911011357p:plain


Nmap

Nmapとは

nmapはGordon Lyonによって書かれた、非常によく知られたセキュリティスキャナである。名前は、ネットワーク上にどのような機器やサービスが動いているかという、ネットワークの地図を作成すること (Network Mapper) に由来する。


nmap - Wikipedia より

いわゆる,高性能ポートスキャナです.
ペネトレーションテストIntelligence Gathering(情報収集)の段階において利用されるツールです.

通信の流れ

Nmapはポートスキャンに先立って,ターゲットとなるホストの発見を行います.
そして,発見したホストに対して,ポートスキャンを仕掛けていきます.
上記のホストの発見方法や,ポートスキャンの方法は,下記のオプションにて適宜設定可能です.

フォーマット

nmap [Scan Type(s)] [Options] {target specification}

Nmapのオプション

オプション(詳細表示編)

-A OSのフィンガプリント等の詳細情報を表示
-O OS名を表示
-sV ソフトウェア・サービスのバージョンを詳細表示
-v ホストスキャン情報を詳細表示
--packet-trace 通信の内容をダンプ

オプション(ホスト発見編)

-P0 ホスト発見を省略
-sP Pingを利用してホストを一覧表示
-PS TCP SYNを利用
-PA TCP ACKを利用
-PU UDPを利用
-PE Pingを利用

オプション(スキャン方法編)

-sS TCP SYNスキャン
-sT TCP Connectスキャン
-sN TCP Null(フラグなし)スキャン
-sF TCP FINスキャン
-sU UDPスキャン
-sX Xmaxスキャン(FIN+PSH+URG)

オプション(ポート指定編)

-p [X] 宛先ポート番号を X に設定
-p [X-Y] 宛先ポート番号を X ~ Y に範囲指定
-F 特定のポートに絞ってファストスキャン
-r ポート指定の順番をランダム化

オプション(スプーフィング編)

-S [IPaddr] IPアドレスを偽装
-e [iface] ネットワークインターフェースを指定
-g [port] 送信元ポート番号を指定
--data-length [num] num分のランダムなデータを付加
--spoof-mac [MACaddr] MACアドレスを偽装

オプション(ファイル出力編)

-oN [filename] Nmap標準形式で出力
-oX [filename] XML形式で出力
-oG [filename] Grepable(grepコマンドで抽出可能)形式で出力
-oA [filename] 上記3つ全部(オススメ)
-oS [filename] スクリプトキディ形式で出力

よく用いる実行例

外部に対して行うとマズいので,以下ではターゲットを自身(127.0.0.1)に設定して行います.

普通にポートスキャン

# nmap 127.0.0.1

ホスト発見を省略してスキャン

# nmap -P0 127.0.0.1

詳細表示設定でポートスキャン

# nmap -A -v -sV 127.0.0.1

宛先ポートを指定してスキャン

# nmap -p 8080 localhost

宛先ポートを絞って高速スキャン

# nmap -F 127.0.0.1

ウェルノウンポートのみスキャン

# nmap -p 0-1023 127.0.0.1

全てTCP SYNを用いてスキャン

# nmap -PS -sS 127.0.0.1

全出力形式を指定して詳細スキャン(特にオススメ)

output.nmap, output.gnmap, output.xmlが生成されます.

# nmap -A -v -sV -oA output 127.0.0.1

CIDRでIPアドレスを範囲指定してスキャン(例)

# nmap 127.0.0.0/24

スキャン結果の意味(STATE)

open 到達可能で,ポートが開放されていて,サービスが稼働している
closed 到達可能だが,ポートが閉じており,サービスは稼働していない
filtered FW等によりパケットフィルタリングが実施されている
unfiltered 到達可能だが,ポートの状態は不明である
open│filtered open状態か,filtered状態である
closed│filtered closed状態か,filtered状態である

スキャン結果の例

以下の例では,ホスト127.0.0.1において,ポート111,3306,5432,8080にてSERVICE欄で示されたサービスが検出され,それらはSTATE欄からopen状態であると分かります.

# nmap 127.0.0.1

Starting Nmap 7.12 ( https://nmap.org ) at 2017-09-11 02:17 JST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.0000020s latency).
Not shown: 996 closed ports
PORT     STATE SERVICE
111/tcp  open  rpcbind
3306/tcp open  mysql
5432/tcp open  postgresql
8080/tcp open  http-proxy

Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds

おわりに

以上より,ポートスキャンについて,また,ポートスキャナであるNmapについてまとめました.
自身のサービスに対して,もしくは,自組織のネットワークに対して行う分には問題ありませんが,外部のサーバ等に対して無許可で本手法を実施した場合,法律に抵触するおそれがありますので注意してください.

Base64 Encoder + Decoder をPythonで実装する

はじめに

よくマルチメディアデータのエンコーディングに使われており,目にすることが多いBase64.いまいち仕組みがよく分かっていなかったので,イチから実装してみました.

仕組み

Base64変換の手順を以下に挙げる。

  1. 元データを6ビットずつに分割。(6ビットに満たない分は0を追加して6ビットにする)
  1. 各6ビットの値を変換表を使って4文字ずつ変換。(4文字に満たない分は = 記号を追加して4文字にする)

Base64 - Wikipedia より

つまり,8bitのASCIIを受け取って,6bitにセパレートして,変換表で変換するわけですね.
"AB"をエンコードすると,以下のような流れになるでしょう.

f:id:Szarny:20170904150236p:plain

Pythonで実装

import sys

# Conversion table of base64
encode_table = {
    "110000": "w","110001": "x","110101": "1","110100": "0",
    "010100": "U","010101": "V","001100": "M","001101": "N",
    "011110": "e","011111": "f","001001": "J","001000": "I",
    "011011": "b","011010": "a","000110": "G","000111": "H",
    "000011": "D","000010": "C","100100": "k","100101": "l",
    "111100": "8","111101": "9","100010": "i","100011": "j",
    "101110": "u","101111": "v","111001": "5","111000": "4",
    "101011": "r","101010": "q","110011": "z","110010": "y",
    "010010": "S","010011": "T","010111": "X","010110": "W",
    "110110": "2","110111": "3","011000": "Y","011001": "Z",
    "001111": "P","001110": "O","011101": "d","011100": "c",
    "001010": "K","001011": "L","101101": "t","000000": "A",
    "000001": "B","100111": "n","100110": "m","000101": "F",
    "000100": "E","111111": "/","111110": "+","100001": "h",
    "100000": "g","010001": "R","010000": "Q","101100": "s",
    "111010": "6","111011": "7","101000": "o","101001": "p"
}

decode_table = {
    'w': '110000', 'x': '110001', '1': '110101', '0': '110100',
    'U': '010100', 'V': '010101', 'M': '001100', 'N': '001101',
    'e': '011110', 'f': '011111', 'J': '001001', 'I': '001000',
    'b': '011011', 'a': '011010', 'G': '000110', 'H': '000111',
    'D': '000011', 'C': '000010', 'k': '100100', 'l': '100101',
    '8': '111100', '9': '111101', 'i': '100010', 'j': '100011',
    'u': '101110', 'v': '101111', '5': '111001', '4': '111000',
    'r': '101011', 'q': '101010', 'z': '110011', 'y': '110010',
    'S': '010010', 'T': '010011', 'X': '010111', 'W': '010110',
    '2': '110110', '3': '110111', 'Y': '011000', 'Z': '011001',
    'P': '001111', 'O': '001110', 'd': '011101', 'c': '011100',
    'K': '001010', 'L': '001011', 't': '101101', 'A': '000000',
    'B': '000001', 'n': '100111', 'm': '100110', 'F': '000101',
    'E': '000100', '/': '111111', '+': '111110', 'h': '100001',
    'g': '100000', 'R': '010001', 'Q': '010000', 's': '101100',
    '6': '111010', '7': '111011', 'o': '101000', 'p': '101001'
}

# Format checker
def is_invalid_format(argv):
    if len(argv) != 3:
        return True
    elif argv[1] == "-h":
        return True
    elif argv[1] == "" or  (argv[1] != "-d" and argv[1] != "-e" and argv[1] != "-b"):
        return True
    elif argv[2] == "":
        return True
    else:
        return False

# ASCII character -> 8-digit binary
def chr2bin(letter):
    binary = ""
    cardinals = [128, 64, 32, 16, 8, 4, 2, 1]
    ascii_num = ord(letter)

    for car in cardinals:
        if ascii_num >= car:
            ascii_num -= car
            binary += "1"
        else:
            binary += "0"

    return binary

# 8-digit binary -> ASCII character
def bin2chr(binary):
    cardinal = 128
    decimal = 0

    for digit in binary:
        if int(digit) == 1:
            decimal += cardinal
        cardinal //= 2

    return chr(decimal)

# base64 encoder
def encode(data):
    binary = ""
    six_separated_binary = []
    base64 = ""

    # ASCII -> binary
    for letter in data:
        binary += chr2bin(letter)

    # Zero padding
    if len(binary) % 6 != 0:
        binary += "0" * (6 - (len(binary) % 6))

    # Separation into 6-digit binary
    for idx in range(0, len(binary), 6):
        six_separated_binary.append(binary[idx:idx+6])

    # 6-digit binary -> base64 character
    for six_digit in six_separated_binary:
        base64 += encode_table[six_digit]

    # = padding
    if len(base64) % 4 != 0:
        base64 += "=" * (4 - (len(base64) % 4))

    print("[Encode]", base64)

# base64 decoder
def decode(base64):
    data = ""
    binary = ""

    # Remove = padding
    base64 = base64.replace("=","")

    # base64 character -> 6-digit binary
    for base64char in base64:
        binary += decode_table[base64char]

    # Remove zero padding
    char_len = len(binary) // 8
    if len(binary) % 8 != 0:
        binary = binary[0:char_len*8]

    start = 0
    end   = 8
    while end <= len(binary):
        data += bin2chr(binary[start:end])
        start += 8
        end += 8

    print("[Decode]", data)

def main():
    if is_invalid_format(sys.argv):
        print("[*] Usage")
        print("python3 base64tool.py [-d decode | -e encode | -b both] \"data\"")
        sys.exit(0)

    print("[Origin]", sys.argv[2])

    if sys.argv[1] == "-e":
        encode(sys.argv[2])
    elif sys.argv[1] == "-d":
        decode(sys.argv[2])
    else:
        encode(sys.argv[2])
        decode(sys.argv[2])

if __name__ == '__main__':
    main()

基本的には,上記の手順を1つ1つ実行しているだけです.
シフト演算等は用いず,なるべく分かりやすい形でコーディングしました.

ASCIIとか進数変換のあたりで若干詰まってしまいましたが,何とかなりました.(組み込み関数はないようですね)

テスト

# python base64tool.py 
[*] Usage
python3 base64tool.py [-d decode | -e encode | -b both] "data"

# python base64tool.py -e "AB"
[Origin] AB
[Encode] QUI=

# python base64tool.py -d "QUI="
[Origin] QUI=
[Decode] AB

# python base64tool.py -e "This is python program."
[Origin] This is python program.
[Encode] VGhpcyBpcyBweXRob24gcHJvZ3JhbS4=

# python base64tool.py -d "VGhpcyBpcyBweXRob24gcHJvZ3JhbS4="
[Origin] VGhpcyBpcyBweXRob24gcHJvZ3JhbS4=
[Decode] This is python program.

動きました\(^o^)/
いつも使ってるものでも,敢えてイチから作り直してみると楽しいですね!

残念ながらマルチバイト文字には対応していないので,今後の課題としたいと思います.

SANS NETWARSトーナメント2017 に参加しました

SANS NETWARS トーナメント 2017

参加してきました.
SANS NETWARSトーナメント2017 | イベントのご案内 | 情報セキュリティのNRIセキュア

内容や問題の公開は禁止されているのでそれ以外のことを.

着くまで

まず,会場が結構大きなビル(株式会社サンケイビル|もっとひとりひとりのなかへ。)で,田舎者の自分でもさすがに迷うことはないと思ってたんですけど,案の定迷いました.

少し早めに大手町駅に着いたので,コーヒーでも飲んでから行こうと思い,ドトールに入店しました.開場時間が近づいてきたので,
念を押してGoogle Mapで経路検索してから言ったのですが,一向に着かない.
おかしいなと思い,Google Map見直してみると,出発地点のドトールが隣のビルだったことに気付く(;^_^A
田舎民からすると,ドトールが隣接しているという概念が無いんですよね...

f:id:Szarny:20170902122632p:plain

調べなおすと3店舗もありましたorz

トレーニング

始めて同時通訳なるものを聞きました.
通訳なしだと,聞き取れるところ半分,聞き取れないところ半分という感じでした.もっと英語聞き取れるようにならないとダメですね~

CTF

今回からチーム制になったらしく,初心者の自分には助かりました.
教えつつ,教えられつつ,情報共有しつつCTFをしたのが初めてだったのですが,1人でやるよりいいな~と思いました.

結果は,YasaiRamenで8位でした!
途中4位くらいまで上がっていたんですが,Levelが上がるにつれてどんどん解けなくなってしまってしまいました.
が,チームの方々に助けられ,予想以上に健闘できたので良かったです.来年参加できれば,ぜひ入賞圏内を狙いたいです.

おわりに

2日間のトレーニングを通して,独学では知ることができないような知識・技術をたっぷりと身に着けることができたと思います.
最後に,主催してくださったNRIセキュアテクノロジーズ様,講師ならびに翻訳の方々に感謝いたします<(_ _)>