2016年12月20日火曜日

Raspberry Pi から A/D コンバータ ADC0832 を使う

最近 Raspberry Pi (RPi) で電子工作を勉強し始めた.『カラー図解 最新 Raspberry Pi で学ぶ電子工作—作って動かしてしくみがわかる—』を参考に勉強をしているのだけれど,対応するキットを買わなかったことによりパーツ特有の接続・制御の部分で少し苦労したので,その時のメモ.

重要: 電子工作について確かな知識も十分な経験もないので,間違ったことを書いている可能性が他の記事と比べても高いので,ご注意ください.

1. 環境と経緯

環境というか,買ったものは以下.
  • Raspberry Pi 1 Model B
    買ったの自体はずいぶん昔だったので,Raspberry Pi 1. (今は) Raspbian をインストールして使用している.
  • cocopar®最新Ultimateスターターキット 初心者専用実験キット 基本部品セットNew Ultimate Starter Learning Kit for Raspberry Pi 3 Model B/2B/B+ Python (https://www.amazon.co.jp/dp/B01EFJ70HC/)
    こちらは最近電子工作を勉強するために買った.
よく見ると,完全に失敗した買い物だった.RPi 1 Model B は GPIO ピンが 26 ピンなのに対し,RPi 2, 3 は GPIO 40 ピン,さらに上記スターターキットは  RPi 2, 3 用ということで 40 ピン用のパーツなどが用意されている.また,参照している『Raspberry Pi で学ぶ電子工作』とも使用パーツが完全に一致はしないので,電子工作初心者としてのチョイスとしては完全に間違いである.でも悔しいのでパーツの買い足しなどはせず,できるところまではそのまま突き進むことにした.

その結果最初に困ったのが,書籍 6 章の A/D 変換である.書籍では MCP3208 という 8 入力,12 ビット A/D コンバータが使用されているが,上記キットに含まれていた A/D コンバータは ADC0832 という 2 入力,8 ビットのもの.なので,配線や A/D コンバータを操作する処理のプログラムは MCP3208 用から ADC0832 用に変更する必要があるのだけれども,不運なことに書籍ではまさに当該部分の処理の説明が「当面は,特に初学者の間はこの実装を理解する必要はない」ということでばっさり省略されていた.これは意地でもなんとか ADC0832 で動かさねばならない,ということで頑張った.きっとこういうのが勉強になるんだと思う.

2.接続

書籍 6 章と同じように,半固定抵抗と A/D コンバータを接続することにより A/D コンバータの動作を確認することにする.書籍には MCP3208 用の接続が書かれているので,AD0832 での接続はデータシート (http://www.ti.com/lit/ds/symlink/adc0832-n.pdf) を参照して,書籍と似たような感じで接続する.また,データシートは検索して一番上位に出てくる Texas Instruments のサイトを参照しているが,おそらく OEM されているからか,いろいろな会社名から同じ製品名で販売されており,どこが本当の一次情報なのかわからない.

結果として,以下のような接続を行った.

写真で見るとこんな感じ.

3. プログラム

これは正直手がかりが全くつかめなかったのだが,Gist に ADC0832 用のコードを上げている方がいて助かった (https://gist.github.com/HeinrichHartmann/27f33798d12317575c6c).

とりあえず最終的なコードを示すと,以下のようになった.
# 6-1_adc0832.py
import RPi.GPIO as GPIO
from time import sleep

def readadc(adcnum, clockpin, mosipin, misopin, cspin):
    if adcnum > 2 or adcnum < 0:
        return -1

    GPIO.output(cspin, GPIO.HIGH)
    GPIO.output(cspin, GPIO.LOW)

    GPIO.output(clockpin, GPIO.LOW)

    for i in [1, 1, adcnum]:
        if i == 1:
            GPIO.output(mosipin, GPIO.HIGH)
        else:
            GPIO.output(mosipin, GPIO.LOW)
        GPIO.output(clockpin, GPIO.HIGH)
        GPIO.output(clockpin, GPIO.LOW)

    adcout = 0
    for i in range(8):
        GPIO.output(clockpin, GPIO.HIGH)
        GPIO.output(clockpin, GPIO.LOW)
        adcout <<= 1
        if GPIO.input(misopin) == GPIO.HIGH:
            adcout |= 0x1

    GPIO.output(cspin, GPIO.HIGH)
    return adcout


GPIO.setmode(GPIO.BCM)
SPICLK = 11
SPIMOSI = 10
SPIMISO = 9
SPICS = 8
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICS, GPIO.OUT)

try:
    while True:
        inputVal0 = readadc(0, SPICLK, SPIMOSI, SPIMISO, SPICS)
        inputVal1 = readadc(1, SPICLK, SPIMOSI, SPIMISO, SPICS)
        print("ADC[0]: {}\t ADC[1]: {}".format(inputVal0, inputVal1))
        sleep(0.2)

except KeyboardInterrupt:
    pass

GPIO.cleanup()
肝の部分は readadc 関数のところで,この中身が書籍の MCP3208 用から ADC0832 用に書き換えてある (ほぼ Gist の方のコードのまま) .また,main 部分では入力 2 チャンネルの両方の値を毎回出力するようにした.

このコードとデータシートを睨んでいると,ようやくコードの意味がわかってきた.まず見るべきは,Timing Diagram の図だと思う.
(データシートより引用)

この図を見ると,各ピンがどのタイミングでどの状態になるのかを理解できる.そのとおりに RPi 側のコードを書いてやれば良い.具体的には以下の流れ.

1. CHIP SELECT (CS) ピンを HIGH -> LOW にセット
    GPIO.output(cspin, GPIO.HIGH)
    GPIO.output(cspin, GPIO.LOW)

    GPIO.output(clockpin, GPIO.LOW)
CS ピンの制御により 1 回の値読み取りプロセスが初期化される (のだと理解した).また,各ピンの値の入力・出力は CLOCK (CLK) ピンの状態変化のタイミング (図中に矢印が書かれている場所) で行われる (と推測している).初回の入出力は DATA IN (DI) の START BIT の入力で,CLK ピンが LOW -> HIGH に変化した時に発生するはずなので初期化プロセスとして CLK ピンも LOW にセットしておく.

2. DI に各パラメータをセット
ADDRESS MUX と書かれている部分で, ここでは 3 つのパラメータ START BIT, SGL/DIF, ODD/SIGN を DI ピンにセットし,CLK ピンを操作することによりシーケンスを進めていく.これらのパラメータにより,どの入力チャネルの値を取得するかといったことを指定できる.
    for i in [1, 1, adcnum]:
        if i == 1:
            GPIO.output(mosipin, GPIO.HIGH)
        else:
            GPIO.output(mosipin, GPIO.LOW)
        GPIO.output(clockpin, GPIO.HIGH)
        GPIO.output(clockpin, GPIO.LOW)
ここで,1, 1, adcnum (0 or 1) という値を送っている.この値の選択は,Multiplexer Addressing の表を見ると良いのだと理解した.ここでは純粋に adcnum に指定された入力チャネルの値を取得するように設定したが,2 番目の SGL/DIF に 0 を設定することで 2 チャネルの差分を取得するようにもできるようだ (試してはいない; 理由は後述).
(データシートより引用)

3. DATA OUT (DO) から値の読み取り
DI を使用した値の設定と逆のパターンで,DO から 1 ビットずつ値を読み込んでいく.
    adcout = 0
    for i in range(8):
        GPIO.output(clockpin, GPIO.HIGH)
        GPIO.output(clockpin, GPIO.LOW)
        adcout <<= 1
        if GPIO.input(misopin) == GPIO.HIGH:
            adcout |= 0x1
ここからは,CLK ピンが HIGH -> LOW に変化したタイミングで DO の値がセットされていく.最初の 8 ビットは MSB FIRST DATA, その後の 8 ビットが LSB FIRST DATA ということで同じ値を 1 ビットずつ,CLK ピンの電圧変化のタイミングで読み出していくことができるようだ (LSB の読み取りは試していない).

4. 読み取りプロセスの終了
最後に CS ピンを LOW -> HIGH に戻すことにより,1 回の読み取りプロセスを終了させる.
    GPIO.output(cspin, GPIO.HIGH)

4. 実行

実際に実行してみたところ,完璧とはいかなかった.
  • チャネル 0 の入力が読み取れず,常に値が 0 になってしまう.同じ接続をチャネル 1 に変更すると値を読み取ることができるので,おそらくコンバータが壊れているのではないかと推測する.
  • チャネル 1 の入力は読み取れるが,安定しない.半固定抵抗に触らない状態でも読み取られる値が結構変動する.ジャンパ線をちょっと触っても値が変動するくらいなので,おそらく流れる電流が不安定だからではないかと推測する.
以下に,半固定抵抗の出力をチャネル 0, 1 の双方に入力した時 (最初に示した接続図からは接続を追加している) のコード実行結果を示す.
pi@raspberrypi:~ $ python 6-1_adc0832.py
ADC[0]: 0        ADC[1]: 192
ADC[0]: 0        ADC[1]: 156
ADC[0]: 0        ADC[1]: 128
ADC[0]: 0        ADC[1]: 158
ADC[0]: 0        ADC[1]: 144
ADC[0]: 0        ADC[1]: 144
ADC[0]: 0        ADC[1]: 144
ADC[0]: 0        ADC[1]: 156
ADC[0]: 0        ADC[1]: 144
ADC[0]: 0        ADC[1]: 152
ADC[0]: 0        ADC[1]: 128
ADC[0]: 0        ADC[1]: 144
ADC[0]: 0        ADC[1]: 128
## snip ##
こんな感じ.

参考

2016年7月26日火曜日

Linux/DDoS-Xor.A リバージング

先日の Cuckoo によるマルウェア解析の続きで,少しだけれどもリバージングにチャレンジしてみる.

1. 対象マルウェア

前回と同じく sha256: da8140274788214c9451d950e0f323c0c51188c5bf847bafef9f48c023e91a55 のファイルを対象とする.前回やったときは VirusTotal の結果はなかったけれど,今検索してみると Linux/DDoS-Xor.A などと結果が出てくる (https://www.virustotal.com/en/file/da8140274788214c9451d950e0f323c0c51188c5bf847bafef9f48c023e91a55/analysis/).

2. Cuckoo による解析の続き

前回の投稿では Linux マルウェアでは strace や子プロセスの追跡ができないと書いたが,実行のタイミングを調整することでうまく動作させることができた.(そして Pull Request デビューした: https://github.com/cuckoosandbox/cuckoo/pull/1007)

トレースありの Cuckoo レポートを https://drmn.jp/blog/20160726-reversing/2.zip にアップロードした.一応 PCAP 等含むのでパスワード “infected” をかけている.

トレースが見れると非常にはかどる.このマルウェアは何回もプロセスを作ったり消したりする動作をしていたのだけれど,たとえば外部ホストに接続しに行こうとしたプロセスのトレースは以下.

わかりづらく表記された .org のドメインの名前解決をして当該 IP アドレスに接続しているように見受けられる.ではこの接続先を strings などで探すことができないかと考えたが,できなかった.
cuckoo@cactus /data/cuckoo/storage/analyses/2 $ strings binary | grep org
C/o Keld Simonsen, Skt. Jorgens Alle 8, DK-1615 Kobenhavn V
TLS generation counter wrapped!  Please report as described in <http://www.gnu.org/software/libc/bugs.html>.
DNS に使用されるアドレスのほうは出てくる.
cuckoo@cactus /data/cuckoo/storage/analyses/2 $ strings binary | grep -F 114.114.114.114
114.114.114.114
ということで,strings だけでは得られない情報を求めてリバージングしていく.できれば動作全体の解明をしていきたいのだけれど,まだまだ始めたばかりなので…….

3. リバージング

リバージングには Radare2 を使用している.

main の逆アセンブル結果を見ていくと,最初のほうにこんなパートが出てくる.
PATH 環境変数をセットしているのは良いとして,0x0804b67f 以降,謎の文字列をセットして dec_conf が呼び出されている.これは怪しい.次に dec_conf を見てみる.
いろいろと値のコピーなどを行っているようだが,結局のところ encrypt_code が呼び出されていて,これが肝のようだ.
encrypt_code 自体は小さな関数で,さらにそこからどこかにも飛んでいない純粋な処理を行う関数のようなので,ここを丹念に見ることにする.しかし,decrypt_conf の中で encrypt_code という名前の関数が呼び出されるということは……2 回処理にかけたら元に戻るアルゴリズム? XOR 的な? という想像が働いてくる.コードの中にもちらっと見えるし.読んだ結果を以下に書き込んだ.
どうやら,この関数は文字列とその長さを引数に受けとり,その文字列と “BB2FA36AAA9541F0” (の繰り返し) の ASCII コードで 1 文字ずつ XOR をかける処理を行うもののようだ.

ということで,同等の関数を Python で書いてみた.main とのあいだには memmove している dec_conf 関数があるが,たぶん特に何も考えず,dec_conf への引数を渡してみたら良いのではないかとやってみる.
# decrypt.py
import sys

def decrypt(code):
    div = 0x10 # len(key)
    key = "BB2FA36AAA9541F0"

    result = ""
    for i in range(len(code)):
        result = result + chr(ord(code[i]) ^ ord(key[i % div]))

    return result

for code in sys.stdin:
    print(decrypt(code.rstrip()))
main で渡されていた文字列をいくつか試してみる.
cuckoo@cactus ~/work $ python decrypt.py
m ])5
/boot
m4S4nAC/n2_AD
/var/run/sftp
m.[$nFR$7nLQQGF
/lib/udev/udev
良さそう!

もともとのドメインを探して strings を全部喰わせてみる:
cuckoo@cactus ~/work $ strings /data/cuckoo/storage/analyses/2/binary | python decrypt.py | grep -a org
2.org
2.org
まだもう少しちゃんとコードを追う必要がありそうだけれども,まあまあいい線行っているのではないか.

参考

2016年7月13日水曜日

Cuckoo で Linux マルウェアの解析

マルウェアの動的解析を行うための Cuckoo Sandbox をセットアップしたときのメモ.

1. 動機と結論

最近運用しはじめたハニーポットにマルウェアがひっかかってきている.いずれは静的解析も含めて解析を行っていきたいと考えているが,まずはお手軽なサンドボックス解析をできる環境を整えようと思った.使用しているハニーポットの種類の問題かもしれないが意外と Linux 向けのマルウェアがかかっていること,また個人的な趣向により Linux 向けマルウェアの解析を目標とする (ゲストの VM に Linux を使用する).

実際にセットアップした感想としては,Cuckoo は Linux ゲストの対応がまだ発展途上にあり,Windows ゲストでの解析に比べまだレベルが落ちると感じた.自分が正しく理解しているならば,以下のような事項はデフォルトでは実現できない.
  • 検体が fork した子プロセスの追跡
  • システムコールのトレース (strace; SystemTap を使用すれば検体の親プロセスのトレースはできていそう)
  • ファイル作成等アクティビティの検出
  • Volatility によるメモリフォレンジック
とはいえ,実際にサンドボックス内で検体を一定時間実行させることにより発生するネットワークトラフィックの PCAP や,そのメモリダンプを取得してある程度手動ででもその解析を行うことができるのは非常に魅力的であった.

2. 環境,方針

自宅に VMware ESXi サーバがあるのでそれを利用することとする.
  • ホスト OS
    (Hardened) Gentoo. 通常であれば Ubuntu あたりを使用するのが楽なのだろうけど,リソースに限りがある自宅サーバで使用するという状況においては,不要なコンポーネントをできる限り排除できるという理由で Gentoo を選んだ.Gentoo であれば Qt をインストールしなくても VirtualBox が使用できる! Gentoo とは選択です (ステマ).Hardened の意味は特になくて,SELinux や grsecurity による制御は使用していない.なんとなくの選択.ホスト OS と言っても ESXi の上で動いている VM であるため,ここで Cuckoo を使用するとなると Nested VM を使用することとなる.
  • 仮想化ソフトウェア
    VirtualBox. 最近の Cuckoo のバージョンは仮想化ソフトウェアに依存しておらず VMware や KVM でも動作するそうだが,特にこだわりはなかったので一番実績がありデフォルトである VirtualBox を使用する.
  • ゲスト OS
    Ubuntu Desktop 16.04 LTS. 一番メジャーなディストリビューションを.もちろんひとつに限定する必要はないが,今回の記事ではこれだけを対象とする.
  • virtualenv
    Cuckoo をはじめとして,関連ツールは大きく Python に依存している.しかも Python 2.7 (Gentoo は現在デフォルトで 3.4 を使用している.もちろん 2.7 も選択できるが.Gentoo ですから). 必要なモジュールを普通にシステムにインストールするとなると Gentoo のパッケージ管理システムである Portage とだいぶ相性が悪いのと,メンテナンスがしづらくなることが考えられるので,Cuckoo 関連用に virtualenv を用意し,その中で作業を行うこととする.
Cuckoo の Web インターフェイス利用までは今回の記事では行かない.あくまでコアと考えられる部分を対象とする.

3. ホスト OS の準備

ホスト OS の上でさらに VM を走らせる必要があるために,それなりのリソースを確保する必要がある.ホスト OS はこのような構成にした.
  • CPU: 2 コア
  • メモリ: 4GB
  • HDD: 64GB (OS, ソフトウェア用)  + 1TB (データ用; マウントポイント /data)
また,ESXi で Nested VM を使用する場合,(ホスト OS の) VM の設定として Virtual Hardware-Assisted Virtualzation (VHV) を有効にする必要がある.vSphere Web Client (要 vCenter) からであれば普通に設定できるようだが,自宅ユーザが普通に使える vSphere Client にはその設定がなく,SSH コンソールから .vmx ファイルを直接編集する必要がある.ちなみに,今回ホスト OS の名前は “cactus.”

- cactus.vmx
vhv.enable = "TRUE"

この設定は vSphere Client から設定を変更するたびに消えてしまうことに注意.そのつど手動で追加してやる必要がある.

Gentoo のインストールは普通に.profile は hardened/linux/amd64 で USE フラグもそんなにいじってないが,CPU 機能の有効化 (cpuinfo2cpuflags の結果) と,acl, caps, icu は有効にした (caps は後に必要なになってくる.他はいつもの癖).

Cuckoo に必要なソフトウェアをドキュメント (http://docs.cuckoosandbox.org/en/latest/installation/host/requirements/) を参考にインストールする.Portage から (システムとして) 以下をインストールした.バージョンは現時点のもので参考まで.
  • app-emulation/virtualbox-5.0.20 -qt4 +headless
  • app-emulation/virtualbox-extpack-oracle-5.0.20.106931
  • net-analyzer/tcpdump-4.7.4
  • dev-vcs/git-2.7.3-r1
  • dev-python/virtualenv-13.1.2
  • dev-lang/python-2.7.10-r1 +sqlite
  • net-dns/dnsmasq-2.75 (オプション; 後述)
少し USE フラグの調整をする.VirtualBox はこのあと全てコマンドラインで扱うことになるので,GUI は不要.“-qt4 +headless” すると依存コンポーネントがかなり減る.また Python は 2.7 系も最初からインストールされていたが,sqlite フラグを立ててアップデート.Volatility が Python の sqlite3 モジュールに依存するため.また,VirtualBox 関連のパッケージは package.accept_keywords に記載して新しいバージョン (5.x 系) を入れることにした.

ドキュメントでは Volatility のインストールも必要とされていて,標準で ebuild も提供されているが,virtualenv を使用するとシステムにインストールされたモジュール (/usr/lib64/python2.7/site-packages 以下) が参照されないこともあり,virtualenv のほうで自前でインストールすることとする.

このあたりからドキュメントの流れには沿わずにやっていく.

ガイドにしたがって Cuckoo 用ユーザが tcpdump を使用できるように cabality を設定.caps フラグがここで効いてきている.
dr@cactus ~ $ sudo setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump
dr@cactus ~ $ sudo getcap /usr/sbin/tcpdump
/usr/sbin/tcpdump = cap_net_admin,cap_net_raw+eip

Cuckoo 用ユーザを作成.通常のデーモン用ユーザのようにログインシェルを無効化して作成したいところだが,当該ユーザのシェルからやることがたくさんあるので,ほぼ普通のユーザとして作る.
dr@cactus ~ $ sudo useradd -r -m cuckoo
dr@cactus ~ $ sudo gpasswd -a cuckoo vboxusers

今後複数のユーザを行き来して作業を行う.ますは Cuckoo ユーザで virtualenv の作成.Python 2.7 を指定すること.
dr@cactus ~ $ sudo su - cuckoo
cuckoo@cactus ~ $ virtualenv --python=python2.7 ~/cuckoo
cuckoo@cactus ~ $ source ~/cuckoo/bin/activate
(cuckoo)cuckoo@cactus ~ $

4. Volatility

まずは Volatility のインストールから.pip には提供されていない (と思う) ので,GitHub レポジトリから取得し,(virtualenv に) インストール.あと関連パッケージも pip でインストール.参考: https://github.com/volatilityfoundation/volatility/wiki/Installation
(cuckoo)cuckoo@cactus ~ $ cd volatility/
(cuckoo)cuckoo@cactus ~/volatility $ python setup.py install
(cuckoo)cuckoo@cactus ~/volatility $ pip install distorm3 pycrypto OpenPyxl ujson

PIL (Python Imaging Library) は現在は Pillow としてメンテナンスされていると理解している.libjpeg に依存していたので,それもインストール.
dr@cactus ~ $ sudo emerge -av media-libs/libjpeg-turbo
(cuckoo)cuckoo@cactus ~/volatility $ pip install Pillow

yara も書かれているのだけど,実際インストールすると Cuckoo 起動時にエラーが出るようになるのでアンインストールした.

Volatility のインストールはここでしておくが,実際には Cuckoo から利用できる解析内容は Windows 向けの内容に限られており Linux ゲストのための解析を (自動で) かけることは現時点ではできない.でも,Cuckoo 実行時にメモリダンプを取得すればあとからの解析はできてとても興味深いので…….

5. Cuckoo インストール

GitHub から取得.解析結果のレポート等はこの cuckoo ディレクトリの下に保存されることになるので,データ用のパーティションに配置する.
dr@cactus /data $ sudo git clone https://github.com/cuckoosandbox/cuckoo.git
dr@cactus /data $ sudo chown -Rv cuckoo:cuckoo cuckoo

(cuckoo)cuckoo@cactus /data/cuckoo $ pip install -r requirements.txt
ここで設定なしに実行してみようとすると,Windows モニタ用のバイナリ (使わないけど),およびシグニチャをダウンロードしてくるように言われるので従ってダウンロード.

(cuckoo)cuckoo@cactus /data/cuckoo $./utils/community.py -waf

ここまでで Cuckoo が起動するようになる.ただし,これからゲスト VM の作成や設定などが待っている.

6. ゲスト OS の準備

前述のとおり,Ubuntu Desktop 16.04 を VirtualBox で動作させることとする.少しネットワーク周りの設定がややこしい.
  • “hostonly” ネットワークを使用する.必要に応じてホスト OS で NAT し,インターネットに出られるようにする.
  • ホスト OS の IP アドレスアサインに DHCP は使用せず,固定アドレスを設定する.これは Cuckoo が固定の IP アドレスを対象にパケットキャプチャを行うため.
  • ホスト OS で dnsmasq を動作させて,ゲスト OS が名前解決できるようにする.
NAT や DNS は,少なくともセットアップ時にはインターネット接続が使用できた便利だが,解析を行う時に有効にしたままだと,検体 (マルウェア) のトラフィックがインターネットに出ていくことになるので注意が必要.

ここから先はとにかく慣れない VirtualBox CLI で頑張る.

“hostonly” ネットワークを使用するためのホスト OS 側インターフェイスの作成:
(cuckoo)cuckoo@cactus ~ $ VBoxManage hostonlyif create
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Interface 'vboxnet0' was successfully created
cuckoo@cactus ~ $ VBoxManage hostonlyif ipconfig vboxnet0 --ip 192.168.56.1
cuckoo@cactus ~ $ ip addr show dev vboxnet0
5: vboxnet0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
    link/ether 0a:00:27:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 192.168.56.1/24 brd 192.168.56.255 scope global vboxnet0
       valid_lft forever preferred_lft forever

NAT 設定:
dr@cactus ~ $ sudo sysctl -w net.ipv4.ip_forward=1
dr@cactus ~ $ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
-A FORWARD -s 192.168.56.0/24 -i vboxnet0 -o tun0 -m conntrack --ctstate NEW -j ACCEPT
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
dr@cactus ~ $ sudo iptables -S -t nat
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-A POSTROUTING -j MASQUERADE
あと必要に応じて /etc/sysctl.conf にも記述.

VirtualBox の DHCP サーバ機能停止:
(cuckoo)cuckoo@cactus ~ $ VBoxManage list dhcpservers
NetworkName:    HostInterfaceNetworking-vboxnet0
IP:             192.168.56.100
NetworkMask:    255.255.255.0
lowerIPAddress: 192.168.56.101
upperIPAddress: 192.168.56.254
Enabled:        Yes

(cuckoo)cuckoo@cactus ~ $ VBoxManage dhcpserver remove -netname HostInterfaceNetworking-vboxnet0
dnsmasq の設定と起動.DHCP 機能を切ることを忘れずに:
# /etc/dnsmasq.conf
interface=vboxnet0
no-dhcp-interface=vboxnet0
bind-interfaces

dr@cactus ~ $ rc-service dnsmasq start

続いてゲスト VM の作成.1 点ポイントとしては,VRAM の容量を増やしてやらないと VRDE で RDP 接続したときの表示がうまくいかなかった.

(cuckoo)cuckoo@cactus ~ $ VBoxManage createvm -name "ubuntu-16.04_x64" --ostype Ubuntu_64 --basefolder /data/vm --register
Virtual machine 'ubuntu-16.04_x64' is created and registered.
UUID: eb33474a-6f34-4165-8ee8-9828bf9672a8
Settings file: '/data/vm/ubuntu-16.04_x64/ubuntu-16.04_x64.vbox'
(cuckoo)cuckoo@cactus ~ $ VBoxManage modifyvm "ubuntu-16.04_x64" --memory 2048 --vram 64 --nic1 hostonly --hostonlyadapter1 vboxnet0
(cuckoo)cuckoo@cactus ~ $ VBoxManage createhd --filename /data/vm/ubuntu-16.04_x64/ubuntu-16.04_x64.vdi --size 25600
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Medium created. UUID: 3ade2e5c-6277-4a00-b5a5-09921a84a8ac
(cuckoo)cuckoo@cactus ~ $ VBoxManage storagectl "ubuntu-16.04_x64" --name sata --add sata --portcount 2 --bootable on
(cuckoo)cuckoo@cactus ~ $ VBoxManage storageattach "ubuntu-16.04_x64" --storagectl sata --port 1 --type hdd --medium /data/vm/ubuntu-16.04_x64/ubuntu-16.04_x64.vdi
(cuckoo)cuckoo@cactus ~ $ VBoxManage storageattach "ubuntu-16.04_x64" --storagectl sata --port 2 --type dvddrive --medium /home/cuckoo/ubuntu-16.04-desktop-amd64.iso
(cuckoo)cuckoo@cactus ~ $ VBoxHeadless --startvm "ubuntu-16.04_x64" --vrde on

Oracle VM VirtualBox Headless Interface 5.0.20_Gentoo
(C) 2008-2016 Oracle Corporation
All rights reserved.

VRDE server is listening on port 3389.
RDP で接続して OS インストール作業.終わったらインストールメディアの detatch.
(cuckoo)cuckoo@cactus ~ $ VBoxManage storageattach "ubuntu-16.04_x64" --storagectl sata --port 2 --medium emptydrive

ゲスト OS の中ではいくつかの設定を.
  • 自動アップデート,diag 情報送信の無効化
  • ネットワーク設定 (今回自分は 192.168.56.101/24, ゲートウェイと DNS は 192.168.56.1 (ホスト OS の vboxnet0) を設定)
  • Cuckoo エージェント (agent/agent.{py,sh}) のダウンロード・実行.起動時に agent.sh が実行されるように /etc/rc.local に登録しておくと良い
  • タイムゾーン (今回ホスト,ゲスト OS ともに UTC を設定した)
一通り終わったら,スナップショットを取って終了.この状態から検体が実行されることになる.
cuckoo@cactus ~ $ VBoxManage snapshot "ubuntu-16.04_x64" take "initial" --pause
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Snapshot taken. UUID: 27d7efba-187d-47fd-a3ab-caaa0a0052c5
cuckoo@cactus ~ $ VBoxManage controlvm "ubuntu-16.04_x64" poweroff
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
cuckoo@cactus ~ $ VBoxManage snapshot "ubuntu-16.04_x64" restorecurrent
Restoring snapshot 27d7efba-187d-47fd-a3ab-caaa0a0052c5
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%

7. Cuckoo の設定

基本的にはドキュメントとファイルを眺めて,ということろだが必要最小限な設定について.
# conf/cuckoo.conf
[cuckoo]
version_check = off
machinery = virtualbox
memory_dump = off
max_machines_count = 1

[routing]
route = none
internet = none

[resultserver]
ip = 192.168.99.60
routing の設定に注意.コメントを読むと route = none を設定しておくとトラフィックは外部に出ていかないように読めるのだが,実際には,「Cuckoo 側では特にネットワーク関連の制御を行わない」という意味である.そのため,上で行ったホスト OS での NAT の設定が残っている状態だと,検体解析時のゲスト OS (つまりマルウェアの) トラフィックはインターネットに出ていくことになる.また,memory_dump はデフォルトの off のままにしているが,これを on にすれば実行時のメモリダンプが生成される.ただし,フルメモリダンプになるため容量を食うので注意 (今回作成した VM だと 2GB).でも,それを Volatility で見てみるといろいろなことがわかる.resultserver の IP アドレスはホスト OS の IP アドレス.デフォルトでは vboxnet0 のデフォルトアドレスが設定されているが,VM が立ち上がっていないときにリンクダウンしたりするので,その他のアドレスの指定が推奨されている.

# conf/virtualbox.conf
[virtualbox]
machines = ubuntu-16.04_x64

[ubuntu-16.04_x64]
label = ubuntu-16.04_x64
platform = linux
ip = 192.168.56.101
ここまで設定すると,ようやく解析ができるようになる.

8. 解析

cuckoo.py を実行すると,
(cuckoo)cuckoo@cactus /data/cuckoo $ python cuckoo.py

  .-----------------.
  | Cuckoo Sandbox? |
  |     OH NOES!    |\  '-.__.-'
  '-----------------' \  /oo |--.--,--,--.
                         \_.-'._i__i__i_.'
                               """""""""

 Cuckoo Sandbox 2.0-dev
 www.cuckoosandbox.org
 Copyright (c) 2010-2015

2016-07-12 10:46:08,338 [lib.cuckoo.core.scheduler] INFO: Using "virtualbox" as machine manager
2016-07-12 10:46:11,059 [lib.cuckoo.core.scheduler] INFO: Loaded 1 machine/s
2016-07-12 10:46:11,086 [lib.cuckoo.core.scheduler] INFO: Waiting for analysis tasks.
と待ち状態になるので,別コンソールから util/submit.py で検体を提出:
(cuckoo)cuckoo@cactus /data/cuckoo $ ./utils/submit.py /data/malware/da8140274788214c9451d950e0f323c0c51188c5bf847bafef9f48c023e91a55
Success: File "/data/malware/da8140274788214c9451d950e0f323c0c51188c5bf847bafef9f48c023e91a55" added as task with ID 1


すると Cuckoo コンソール側ではこんな出力が出てくる:
2016-07-12 10:48:39,473 [lib.cuckoo.core.scheduler] INFO: Starting analysis of FILE "da8140274788214c9451d950e0f323c0c51188c5bf847bafef9f48c023e91a55" (task #1, options "")
2016-07-12 10:48:39,580 [lib.cuckoo.core.scheduler] INFO: Task #1: acquired machine ubuntu-16.04_x64 (label=ubuntu-16.04_x64)
2016-07-12 10:48:39,721 [modules.auxiliary.sniffer] INFO: Started sniffer with PID 22438 (interface=vboxnet0, host=192.168.56.101, pcap=/data/cuckoo/storage/analyses/1/dump.pcap)
tcpdump: listening on vboxnet0, link-type EN10MB (Ethernet), capture size 262144 bytes
2016-07-12 10:48:48,464 [lib.cuckoo.core.guest] INFO: Starting analysis on guest (id=ubuntu-16.04_x64, ip=192.168.56.101)
2016-07-12 10:49:00,126 [lib.cuckoo.core.guest] INFO: ubuntu-16.04_x64: analysis completed successfully
63 packets captured
67 packets received by filter
0 packets dropped by kernel
2016-07-12 10:49:02,226 [lib.cuckoo.common.objects] WARNING: Unable to import yara (please compile from sources)
2016-07-12 10:49:08,274 [lib.cuckoo.core.scheduler] INFO: Task #1: reports generation completed (path=/data/cuckoo/storage/analyses/1)
2016-07-12 10:49:08,305 [lib.cuckoo.core.scheduler] INFO: Task #1: analysis procedure completed

そしてレポートやログが示されたディレクトリ (/data/cuckoo/storage/analyses/1) に保存されている.

9. 感想

まず,上記のタイムスタンプを見てみると 12 秒間しか実行されていない.短い.これは多分,検体自身の親プロセスしか追えていなくて,親プロセスの終了とともに解析が終了しているのだと思う.実際の挙動をもっと見るために解析タイムアウト (デフォルト 120 秒) まで続ける場合には,util/submit.py--enforce-timeout オプションが使える.また,レポート (reports/report.json) の内容は少し期待外れで,ネットワークアクティビティと strings, VirusTotal でのスキャン結果くらいしか得られていない.これは Windows 向けマルウェアを malwr.com に投げたときとはずいぶん状況が違うので,Linux ゲスト対応がまだ発展途上なのであるのだと思う.
参考までに,当該ディレクトリの ls -lRreport.jsonhttps://drmn.jp/blog/20160713-cuckoo/ にアップロードした.

参考

2016年3月7日月曜日

Gephi ことはじめ

グラフとかビジュアライゼーションとかやってみたい,ということで Gephi を触ってみた.試してみるネタとして,手元の Gentoo にインストールされているパッケージの依存関係をグラフにするというもの.

1. データの準備

現在インストールされているパッケージは 1,100 ほど.eix と equery を使用して,それぞれのパッケージの依存関係をすべて CSV に落とす.
# eix-installed -q all | while read pkg; do equery -q d $pkg | sed -e "s|$|,$pkg|"; done | tee data.csv
# wc -l data.csv
9783 data.csv
# head data.csv
x11-libs/gtk+-3.18.7,app-accessibility/at-spi2-atk-2.18.1
app-accessibility/at-spi2-atk-2.18.1,app-accessibility/at-spi2-core-2.18.3
app-accessibility/speech-dispatcher-0.8.1,app-accessibility/espeak-1.47.11-r1
www-client/chromium-49.0.2623.75,app-accessibility/speech-dispatcher-0.8.1
app-eselect/eselect-blas-0.1,app-admin/eselect-1.4.4
app-eselect/eselect-cblas-0.1,app-admin/eselect-1.4.4
app-eselect/eselect-ctags-1.18,app-admin/eselect-1.4.4
app-eselect/eselect-fontconfig-1.1,app-admin/eselect-1.4.4
app-eselect/eselect-java-0.1.0,app-admin/eselect-1.4.4
app-eselect/eselect-lib-bin-symlink-0.1.1,app-admin/eselect-1.4.4
それなりの規模のデータになった.

2. グラフの作成

Gephi のチュートリアルを見ながら試行錯誤してみた結果が以下.
SVG 版: https://drmn.jp/blog/20160307-portage.svg

何かの目的を持ってグラフを作成したわけではないのだけれど,簡単なことは見えてくる.
  • pkgconfig が依存され度ナンバーワン
  • Python, Perl, Java, Haskell 関連のパッケージは当該言語内の依存関係が強い (当然).そのなかでも Haskell は孤高の存在
  • 依存によるコミュニティ分類が,パッケージの種類に対応していそうなところと,混ざっていそうなところがある.メディア関連ライブラリはかなり独立しているのに対し,X 関連と開発関連が混じっているという点が見られる

参考

2016年2月14日日曜日

xmonad いじり

3 年ぶりくらいに xmonad をいじって,なかなか快適になったので忘れないようにメモしておく.3 年前の記事が dr's tech memo: Haskell に興味がある人向け xmonad 設定ガイド にあるため,その後どういった変更を行ったかについてまとめる.現在の動作環境は以下.
  • もちろん Gentoo
  • ghc-7.8.4
  • xmonad-0.11-r1
  • xmonad-contrib-0.11.2

1. startupHook

startupHook を利用して,従来 .xinitrc に書いていたものを少し xmonad のほうに持ってきた.とはいえ .xinitrc との棲み分けをちゃんとできているわけではなく,適当.
import XMonad.Util.Cursor               -- setDefaultCursor
import XMonad.Util.Run                  -- unsafeSpawn, spawnPipe, hPutStrLn

main = do
    myStatusBar <- spawnPipe "xmobar"
    xmonad $ ewmh
           $ defaultConfig {
                   layoutHook      = myLayoutHook
                 , manageHook      = myManageHook
                 , handleEventHook = myHandleEventHook
                 , modMask         = myModMask
                 , logHook         = myLogHook myStatusBar
                 , startupHook     = myStartupHook
                 }
                 `additionalKeysP` myAdditionalKeysP

myStartupHook = do
    setDefaultCursor xC_left_ptr
    unsafeSpawn "thunderbird"
    unsafeSpawn "pidgin"
startupHookX () の型として定義されている.
ghci> :m +XMonad
ghci> :i startupHook
data XConfig (l :: * -> *)
  = XConfig {..., startupHook :: !X (), ...}
        -- Defined in ‘XMonad.Core’
xmonad における 「アクション」 はこれを満たす関数 (モナド) になっているので,スタートアップ時に実行したいアクションを (do でつなげて) 列挙してやれば良い.上記の setDefaultCursor はその名のとおりポインタを設定するもの.従来 xsetroot コマンドで行っていたけれど,たまたま見つけたので使ってみたというレベルのもの.なお参考までに,.xinitrc は以下のようになっている.
# ~/.xinitrc
xrdb -merge $HOME/.Xresources

xmodmap $HOME/.Xmodmap

autocutsel &
autocutsel -selection PRIMARY &
dispad

export GTK_IM_MODULE="uim"
export QT_IM_MODULE="uim"
uim-xim &
export XMODIFIERS="@im=uim"

exec xmonad
他のものも xmonad のほうに移せるだろうけれど,とりあえず残している.特に明確な基準はない.
それから,startupHook を使用せずとも,xmonad 関数を呼び出す前の xmobar を spawn しているところにアクションを列挙していけば同様のことができると想像している (試していないけれど).ただ,せっかく startupHook という機構を用意してくれているので,戻り値が必要な特殊なケースを除けば,別のところにまとめておいたほうが見通しが良くなるのだと思う.

2. additionalKeysP

前回はキーバインドの追加に additionalKeys を使用していたが,同様に XMonad.Util.EZConfig に用意されている additionalKeysP を使用することにした.従来は xbindkeys で割り当てていたものを xmonad に持ってこようとしたときに,additionalKeysP のほうが手軽に利用できるキーが多いようだったため.
import XMonad.Util.Run                  -- unsafeSpawn
import XMonad.Util.EZConfig             -- additionalKeysP

myAdditionalKeysP = [
    --snip --
    
    -- volume control
    , ("<XF86AudioRaiseVolume>", unsafeSpawn "amixer set Master playback 10+")
    , ("<XF86AudioLowerVolume>", unsafeSpawn "amixer set Master playback 10-")
    , ("<XF86AudioMute>",        unsafeSpawn "amixer set Master toggle")
    ]
利用できるキーは XMonad.Util.EZConfig で参照可能.

3. xmonad と相性の悪いアプリケーションへの対応

3.1. mpv (MPlayer)

MPlayer の時からそうだったけれど,mpv でフルスクリーン表示にならないという問題があった.これは manageHook で mpv のウィンドウを float させてやることで解決する.
import XMonad.Hooks.ManageHelpers       -- isFullscreen, doFullFloat

myManageHook =   manageDocks
             <+> composeAll [
                        isFullscreen                      --> doFullFloat
                      , className =? "mpv"                --> doFloat
                      -- snip --
                      ]

3.2. Java Swing アプリケーション

JabRef など,Swing を利用した Java アプリケーションはうまく表示が行えないという問題があった.まさかウィンドウマネージャとの相性の問題だとは…….と思ったら FAQ にあった.

4. その他

4.1. XMonad.Prompt.Shell

dmenu の代わりに XMonad.Prompt.Shell. 外部プログラムへの依存が減る,カスタマイズ性が高いといったところがメリット.
import XMonad.Prompt                    -- XPConfig
import XMonad.Prompt.Shell              -- shellPrompt

myAdditionalKeysP = [
      ("M-p",   shellPrompt myXPConfig)
      -- snip --
    ]

myXPConfig = defaultXPConfig {
                   font              = "xft:sans-serif:size=6"
                 , bgColor           = "black"
                 , fgColor           = "grey"
                 , promptBorderWidth = 0
                 , position          = Top
                 , alwaysHighlight   = True
                 , height            = 30
                 }

4.2. XMonad.Layout.HintedTile/XMonad.Layout.Renamed

Tall の代わりに XMonad.Layout.HintedTile. ターミナルのようなアプリケーションで 1 行以下の 「余り」 が出ないように,ウィンドウサイズのほうを調整 (縮小) する.場合によっては並んだウィンドウの高さや幅ががたがたになるので,Tall とどっちが良いかは好みによりそう.XMonad.Layout.Spacing も併用すると綺麗になることを期待したのだけれど,どうやら HintedTile とは相性が悪そう (ヒンティングしたあとにスペースを入れることになり,結果として全てのウィンドウでヒンティングが合わなくなる).
それから,レイアウトに機能を足していくと名前が長くなってくるので,それをリネームするための XMonad.Layout.Remaned.
import XMonad hiding (Tall)
import XMonad.Layout.HintedTile
import XMonad.Layout.Renamed

myLayoutHook =   renamed [CutWordsLeft 2]
                 $ smartBorders $ avoidStruts $ maximize $ minimize
                 $ (hintedTile Tall ||| hintedTile Wide)
             ||| (avoidStruts $ noBorders Full)
    where
        hintedTile = HintedTile nmaster delta ratio TopLeft
        nmaster    = 1
        ratio      = 1/2
        delta      = 3/100

4.3. XMonad.Layout.LayoutScreens

スクリーンのなかで workspace を切るという機能.最近 31.5 インチのディスプレイを買ったので,便利に使えるかどうかを試してみている.
import XMonad.Layout.LayoutScreens      -- layoutSplitScreen
import XMonad.Layout.TwoPane

myAdditionalKeysP = [
    -- snip --
    -- split screen
    , ("M-M1-<Space>",   layoutSplitScreen 2 (TwoPane (3/100) 0.7))
    , ("M-M1-S-<Space>", rescreen)
    ]

4.4. XMonad.Hooks.InsertPosition

insertPosition を使用して,新しいウィンドウがアクティブなウィンドウの次に来る,すなわち新しいウィンドウにマスタを取られないようにした.
import XMonad.Hooks.InsertPosition

myManageHook =   insertPosition Below Newer
             <+> manageDocks
             <+> composeAll [ -- snip -- ]

5. スクリーンショット

高解像度っぷりのアピールを含め.

6. 現在の xmonad.hs

 -- ~/.xmonad/xmonad.hs
import XMonad hiding (Tall)
import XMonad.Hooks.DynamicLog          -- logHook-related
import XMonad.Hooks.EwmhDesktops        -- ewmh, fullscreenEventHook
import XMonad.Hooks.InsertPosition
import XMonad.Hooks.ManageDocks         -- manageDocks, avoidStruts, docksEventHook
import XMonad.Hooks.ManageHelpers       -- isFullscreen, doFullFloat
import XMonad.Layout.HintedTile
import XMonad.Layout.LayoutScreens      -- layoutSplitScreen
import XMonad.Layout.Maximize
import XMonad.Layout.Minimize
import XMonad.Layout.NoBorders          -- smartBorders, noBorders
import XMonad.Layout.Renamed
import XMonad.Layout.TwoPane
import XMonad.Prompt                    -- XPConfig
import XMonad.Prompt.Shell              -- shellPrompt
import XMonad.Util.Cursor               -- setDefaultCursor
import XMonad.Util.EZConfig             -- additionalKeysP
import XMonad.Util.Run                  -- unsafeSpawn, spawnPipe, hPutStrLn
import XMonad.Util.WorkspaceCompare     -- getSortByXineramaRule

main = do
    myStatusBar <- spawnPipe "xmobar"
    xmonad $ ewmh
           $ defaultConfig {
                   layoutHook      = myLayoutHook
                 , manageHook      = myManageHook
                 , handleEventHook = myHandleEventHook
                 , modMask         = myModMask
                 , logHook         = myLogHook myStatusBar
                 , startupHook     = myStartupHook
                 }
                 `additionalKeysP` myAdditionalKeysP

myModMask = mod4Mask

myLayoutHook =   renamed [CutWordsLeft 2]
                 $ smartBorders $ avoidStruts $ maximize $ minimize
                 $ (hintedTile Tall ||| hintedTile Wide)
             ||| (avoidStruts $ noBorders Full)
    where
        hintedTile = HintedTile nmaster delta ratio TopLeft
        nmaster    = 1
        ratio      = 1/2
        delta      = 3/100

myManageHook =   insertPosition Below Newer
             <+> manageDocks
             <+> composeAll [
                        isFullscreen                      --> doFullFloat
                      , className =? "mpv"                --> doFloat
                      , className =? "Gimp"               --> doFloat
                      , className =? "Pidgin"             --> doShift "9"
                      , className =? "Thunderbird"        --> doShift "9"
                      ]

myHandleEventHook =   docksEventHook
                  <+> fullscreenEventHook

myLogHook h = dynamicLogWithPP xmobarPP {
                    ppSep    = " | "
                  , ppTitle  = xmobarColor "green" "" . shorten 80
                  , ppOutput = hPutStrLn h
                  , ppSort   = getSortByXineramaRule
                  }

myStartupHook = do
    setDefaultCursor xC_left_ptr
    unsafeSpawn "thunderbird"
    unsafeSpawn "pidgin"

myAdditionalKeysP = [
      ("M-p",   shellPrompt myXPConfig)
    , ("M-m",   withFocused (sendMessage . maximizeRestore))
    , ("M-n",   withFocused minimizeWindow)
    , ("M-S-n", sendMessage RestoreNextMinimizedWin)

    -- toggle dock visibility
    , ("M-b", sendMessage ToggleStruts)

    -- split screen
    , ("M-M1-<Space>",   layoutSplitScreen 2 (TwoPane (3/100) 0.7))
    , ("M-M1-S-<Space>", rescreen)

    -- volume control
    , ("<XF86AudioRaiseVolume>", unsafeSpawn "amixer set Master playback 10+")
    , ("<XF86AudioLowerVolume>", unsafeSpawn "amixer set Master playback 10-")
    , ("<XF86AudioMute>",        unsafeSpawn "amixer set Master toggle")

    -- other commands
    , ("M-S-l", unsafeSpawn "alock -auth pam -bg blank")
    , ("M-c",   unsafeSpawn "chromium --incognito --force-device-scale-factor=1.6")
    ]

myXPConfig = defaultXPConfig {
                   font              = "xft:sans-serif:size=6"
                 , bgColor           = "black"
                 , fgColor           = "grey"
                 , promptBorderWidth = 0
                 , position          = Top
                 , alwaysHighlight   = True
                 , height            = 30
                 }

2015年8月23日日曜日

著作権法と DVD と Linux

先日,以下のニュースを知った.DVD リッピングを行うソフトウェアを配布していた人物が著作権法違反により逮捕されたというものである.
  • JVA ニュースリリース リッピングソフトをアップロードしていた者を著作権法違反で初めて検挙 及び、出版社従業員等を著作権法違反の幇助でも検挙
    http://jva-net.or.jp/news/news_150819/news.pdf
DVD リッピングのソフトウェアなんて,特に Linux を使っていればごく普通に入手できるものである.なぜこれが違法になるのだと疑問に思い調べ始めたところ,2012 年の著作権法改正によってこのあたりが大きく変わり,ソフトウェア配布のみならず私的使用における複製も違法であることがわかった.そんなことはこれまでまったく知らなかったので,3 年遅れで整理しようというエントリである.

なお,できるかぎり正確な記述をしようと努力はしているが,ちゃんと法律を勉強したわけでもないので,エントリの内容に誤りがないことの保証はできないのでご了承ください.

1. 前提: DVD のスクランブルと Linux における扱い

市場に流通している大多数の DVD には Content Scramble System (CSS) というスクランブルがかかっており,視聴時にはプレイヤーがスクランブルを解除する動作となっている.スクランブル解除のために必要な CSS の仕様は DVD Copy Control Association (DVD CCA) にライセンスを受けることで入手できるが,ライセンスにより当該仕様の情報公開が禁止される.PC において DVD 再生を行うにあたり,スクランブル解除を担っているのは DVD ドライブではなくソフトウェアであり,正規のソフトウェアは DVD CCA にライセンスを受けスクランブル解除のコードを利用するということとなる.

Linux 等ではオープンソースソフトウェアが標準的に利用されている.オープンソースソフトウェアは,上記の情報公開禁止条項を満たすことができないため正規に DVD CCA のライセンスを受けることができずスクランブルを解除できないということになる.だが実際にはオープンソースソフトウェアでも DVD の再生を行うことができる.これはライセンスを受けずに CSS を解析した技術を利用しているからである.当該技術は最初は DeCSS というプログラムによって公開され,現在は CSS 解除ライブラリ libdvdcss として標準的に利用されている.Linux 等で DVD 再生を行うことができるメディアプレイヤーは現在ほぼすべてこの技術を利用している.

CSS 仕様が DVD CCA のライセンス範囲に閉じていればコピープロテクションとしての機能を果たすことができるが,DeCSS により CSS 解除機能を持つソフトウェアを自由に作ることができるようになった結果,DVD の複製が可能となった.

2. 著作権法の理解

著作権法における,上記 DVD スクランブルと Linux における扱いに関連する箇所に関してまとめる.なお,引用は現行の著作権法 (2015 年改正) から.

まず,2012 年の著作権法改正によって,CSS が 「技術的保護手段」 に含まれるようになった.
第二条  この法律において、次の各号に掲げる用語の意義は、当該各号に定めるところによる。
二十  技術的保護手段 電子的方法、磁気的方法その他の人の知覚によつて認識することができない方法(次号において「電磁的方法」という。)により、第十七条第一項に規定する著作者人格権若しくは著作権又は第八十九条第一項に規定する実演家人格権若しくは同条第六項に規定する著作隣接権(以下この号、第三十条第一項第二号及び第百二十条の二第一号において「著作権等」という。)を侵害する行為の防止又は抑止(著作権等を侵害する行為の結果に著しい障害を生じさせることによる当該行為の抑止をいう。第三十条第一項第二号において同じ。)をする手段(著作権等を有する者の意思に基づくことなく用いられているものを除く。)であつて、著作物、実演、レコード、放送又は有線放送(次号において「著作物等」という。)の利用(著作者又は実演家の同意を得ないで行つたとしたならば著作者人格権又は実演家人格権の侵害となるべき行為を含む。)に際し、これに用いられる機器が特定の反応をする信号を著作物、実演、レコード若しくは放送若しくは有線放送に係る音若しくは影像とともに記録媒体に記録し、若しくは送信する方式又は当該機器が特定の変換を必要とするよう著作物、実演、レコード若しくは放送若しくは有線放送に係る音若しくは影像を変換して記録媒体に記録し、若しくは送信する方式によるものをいう。
「当該機器が特定の変換を必要とするよう著作物、実演、レコード若しくは放送若しくは有線放送に係る音若しくは影像を変換して記録媒体に記録」 の部分は 2012 年の改正により追加された部分となる.CSS 解除は 「特定の変換」 に該当するため CSS は 「技術的保護手段」 に含まれることになった.

CSS が 「技術的保護手段」 に含まれることによるユーザへの影響として,以下条文により CSS を回避した複製が私的使用においても違法となる (罰則はなし).
第三十条  著作権の目的となつている著作物(以下この款において単に「著作物」という。)は、個人的に又は家庭内その他これに準ずる限られた範囲内において使用すること(以下「私的使用」という。)を目的とするときは、次に掲げる場合を除き、その使用する者が複製することができる。
二  技術的保護手段の回避(第二条第一項第二十号に規定する信号の除去若しくは改変(記録又は送信の方式の変換に伴う技術的な制約による除去又は改変を除く。)を行うこと又は同号に規定する特定の変換を必要とするよう変換された著作物、実演、レコード若しくは放送若しくは有線放送に係る音若しくは影像の復元(著作権等を有する者の意思に基づいて行われるものを除く。)を行うことにより、当該技術的保護手段によつて防止される行為を可能とし、又は当該技術的保護手段によつて抑止される行為の結果に障害を生じないようにすることをいう。第百二十条の二第一号及び第二号において同じ。)により可能となり、又はその結果に障害が生じないようになつた複製を、その事実を知りながら行う場合
なお,CSS 回避を行った視聴については問題ないと理解している.DVD は視聴するものであることを否定する人はいないだろうから,視聴行為自体は 「当該技術的保護手段によつて防止される行為」 には当然含まれない.

であるから,Linux ユーザとしては,以下のようになる.
  • libdvdcss を使用するソフトウェアで DVD を再生することは合法
  • libdvdcss を使用するソフトウェアで DVD をリッピングすることは違法

また,ソフトウェア開発者などへの影響として,以下条文により CSS を回避して複製を行うプログラムの作成・配布を行うことができなくなる (罰則あり,非親告罪).
第百二十条の二  次の各号のいずれかに該当する者は、三年以下の懲役若しくは三百万円以下の罰金に処し、又はこれを併科する。
一  技術的保護手段の回避を行うことをその機能とする装置(当該装置の部品一式であつて容易に組み立てることができるものを含む。)若しくは技術的保護手段の回避を行うことをその機能とするプログラムの複製物を公衆に譲渡し、若しくは貸与し、公衆への譲渡若しくは貸与の目的をもつて製造し、輸入し、若しくは所持し、若しくは公衆の使用に供し、又は当該プログラムを公衆送信し、若しくは送信可能化する行為(当該装置又は当該プログラムが当該機能以外の機能を併せて有する場合にあつては、著作権等を侵害する行為を技術的保護手段の回避により可能とする用途に供するために行うものに限る。)をした者
冒頭に挙げた事例は,当該ソフトウェアをアップロードしていた人物が著作権法違反で,当該サイトにリンクを張っていた人物が著作権法違反の幇助で逮捕された事例であり,上記条文の行為に該当していることになる.

そうすると Linux ディストリビューション / パッケージのミラーを行っているサイトなどが問題になるのではないかと思ったのだが,国内ミラーサーバの Gentoo / Arch Linux のパッケージを確認したところ,libdvdcss とソフトウェアが分離された (ごく一般的な) かたちでソース,バイナリの双方での配布が行われているようだ.一方,Linux Mint は “No codecs” バージョンのみの配布となっている.状況から考えると,以下のような整理になるのだろうかと推測する
  • libdvdcss は CSS 回避の機能のみを持ち,単体での配布は 「著作権等を侵害する行為」 である複製の用途に供するとの判断はできない
  • libdvdcss に依存する DVD 複製ソフトウェアはそれ自身では CSS 回避の機能を持たず,「技術的保護手段の回避」 に該当しない
  • libdvdcss に依存するメディアプレイヤーは,「著作権等を侵害する行為」「技術的保護手段の回避」 の双方に該当しない
  • libdvdcss に依存しない,もしくはバンドル済みの DVD 複製ソフトウェアは,「著作権等を侵害する行為を技術的保護手段の回避により可能とする用途に供する」 に該当する
  • libdvdcss に依存しない,もしくはバンドル済みのメディアプレイヤーは,「著作権等を侵害する行為」 に該当しない
なお,これらの配布に関しては 2012 年以前から不正競争防止法も関連するもよう.ここでは触れない.

これら条文の根底にある考えはこうなのだと思う: 「DVD を複製できないようにスクランブルをかけたのだから,複製してくれるな.複製の手段を配布することも認めない.スクランブルを回避しても目的が視聴のみであれば,いたしかたあるまい」

3. 個人の感想

恥ずかしながら,私的使用における複製も違法であるということを今までまったく知らず,そのことを知ったときにはかなりのショックだった.それまでは音楽 CD と同じようなものと思っていた.音楽 CD は私的使用の範囲内での複製が認められているため吸い出したコンテンツを携帯プレイヤーで楽しむといったことに問題がないが,CSS が利用された DVD にはそのような選択肢がなくなったということになる.
DRM 一般について言えることだが,利用者の選択を制限する方向性の技術は嫌いだ.

以下に著作権法に関して持った疑問を 2 点挙げておく.
  • 私的使用まで踏み込んで違法化することの有効性
    2012 年改正で CSS が 「技術的保護手段」 に含まれるようになったことにより,私的使用においても CSS を回避して複製することが違法となった.ただし,2012 年改正以前においても,複製コンテンツを不特定多数がダウンロードできるようにするといった行為は私的使用の範疇を超えた違法行為であった.実際問題として,CSS を回避した DVD の複製は海外では特に制限対象となっておらず,海外のリソースを参照すればソフトウェアも情報もたやすく入手できる.私的使用まで踏み込んだ制限を行うことで,著作権者の利益をどれだけ守れることになるのだろうか.
  • スクランブルは複製を防止するための手段なのか
    まあこれは,2012 年改正から認められるようになったということなのだけれど.地デジ放送は? などと考え始めると,あまり納得感はない.

参考


2015年2月8日日曜日

感想文: The Visual Display of Quantitative Information

Edward R. Tufte, The Visual Display of Quantitative Information を読んだので感想文を書こうと思う。

そもそも本書を知ったきっかけは Cytoscape の開発者である Ono 氏のブログだったと思うのだけれど、見つからなかったので SlideShare へのリンクを以下に。

『繋がり』を見る: Cytoscapeと周辺ツールを使ったグラフデータ可視化入門
http://www.slideshare.net/keiono/cytoscape

また、Tufte および本自体については以下のブログがわかりやすかった。

タフテ、タフテという呪文 - 三上のブログ
http://d.hatena.ne.jp/elmikamino/20080402/1207139139


洋書である。私は英語が大得意ではないので、洋書というといつも要所要所しか読まない (読めない) のだが、本書はかなり平易な英語になっていると感じた。また、絵図が多用されており、これらからもかなりの意味を読み取ることができ、読み進めるにあたりさほど苦労はなかった。たまに過去の文章の引用があったりすると、時代なのか、イギリスだからなのか、論文だからなのか、とたんに難しくなる。

本書は二部構成になっている。第一部は 「実践」 として、過去の優れた、また優れていないグラフを数多く論じる。第二部は 「理論」 であり、情報を伝えるグラフを描くためのいくつかの原則を多数の例とともに明快に示していく。その根底にあるものは一貫している。
グラフはデータを示すものだから、グラフ自体が示すデータの密度を最大化していくべきである。
原則のひとつとして、データを表さないもの (「インク」) を可能な限り排するというものがある。装飾的な表現はもちろん、今まで疑問を持たず引いていた座標軸も、データそのものを表すものではない。そういった要素を大胆に削減し、グラフの中の情報密度を相対的に増加させていく。あまりに大胆であるため、正直やりすぎで逆にわかりづらい、と感じる箇所も多分にある。しかし Tufte はこう書く。
さらに言うと、よくあることだが、グラフを見る読者の能力を低く見積もるのは間違いだ。あなたが理解できることなのだから、読者もできるでしょう。グラフはその絵図自体が、テキストがなくても理解できるくらい、洗練されているべきなのだ。(p.136, 意訳)
まあ実際に示された大胆な例ほどの 「改善」 をやる勇気は今のところないのだけれども、それはそれで良いかと思う。

そんなふうに、ふんふん、へえ面白いと読んでいくのだが、その最後 (から 3 番目くらい) に以下の原則が示され、はっとした。
グラフはデータに関する段落であり、またそのように扱われるべきだ。(p.181, 意訳)
こういう心構え的な内容が最後に出てくるのも変わっているなと感じたのだが、これを読んだ時に今まで示されてきた数々の原則 / 方法論が急に整理されてストンと落ちた。そしてそのまま、こう続ける。
言葉も、グラフも表も、形式は違うがいずれも情報の伝達という目的は共通している。同じ事項に関する情報が、ページ上の別々の場所に分断されて配置されるべきなどということはない。
(2 ページほど略)
このように、データ解析のグラフにおいて言葉が使われる時には、(デザインが複雑な場合に) 読者がそれを 「どうやって」 読み解くべきかを示すべきであって、「何を」 読み解くべきかを示すものではない。(p.181-182, 意訳)
これをダビンチの論文を引用して話すのがまたたまらないのだが、ふと思うと、本書をこれまで読んで絵図を見て、これなんだっけ、と思ったことがまるでなかったことに気付かされた。絵図を見るためにページをめくる必要があったことすら、数えるほどしかない。そして (英語力のせいで) 文章が多少わからなくとも、その絵図自体が明快に語っている。本書に示されたいくつかの原則、またその根底にある情報を伝えるという目的を、目の前にあるその本がそのまま体現しているということに感動すら覚えた。

本書はタイポグラフィについても定評がある。Tufte にとっては、いかにして受け手に情報を伝えるかということを突き詰めていった結果、グラフも、タイポグラフィも、自信はないが文章も、こだわらざるを得なかった (自らの出版社を立ち上げる程度に) ことなのだろうと感じた。あとプレゼンテーションもすごいらしい。

 参考