ブログの名前なんて適当で良いのでは

説明を求めるな、記事を読め

Rustの勉強がてらVirusTotal Public APIを利用したライブラリを作ってみる

最近あまりブログというものを書いていないので文章の感じがわからないですが,とりあえず今僕はインフルエンザB型にかかって安静にしています.

さて,本題ですが,最近というのも半年ぐらい前からたまにRustを書いています. 最初は弊団体Cpawの夏合宿で,Rustを0から勉強するというところが発端で,参加した数人はもうRustは嫌で書いてなさそうですが,僕は割と体にあったのでちょこちょこ書いています. 以前は,RustでCASL2/COMET2周りを書いたりしました.

encry1024.hatenablog.com

今回は,HTTP周りを触りつつ,Rust製が見当たらなさそうなものを何か作りたいなぁと思って見つけたのがVirusTotal APIを利用したライブラリでした.Google検索でRust VirusTotalで調べると以下が出てきましたが,見ての通り中身が何もない開発が止まっているリポジトリです.

github.com

本当に無いのかは,もう検索するのがだるくてやらなかったですが,さっさと土台を作ってしまいましょう. ということで,VirusTotalのURLの検査だけ完成したので公開しました.

github.com

このライブラリを使うと,こんな感じでURLのスキャンとそのレポートを見るコードを書くことができます.

extern crate virustotal;

use virustotal::*;

fn main() {

    let api = "Your API KEY";
    let url = "The URL you want to check";
    let res = url::scan(api, url);
    println!("{:?}", url::report(api, &res.scan_id.unwrap()));
    
}

まだテストコードも無くて雑な感じですが,public apiは個数が少ないので,そのうち完成しそうという未来が見える開発という点で気持が楽ですね.ぶっちゃけあまりRustをわからず雰囲気で書いているので,書き方のコツとかあったら教えてください.

新年なのでRustでComet IIエミュレータを書いた

あけましておめでとうございます.今年もよろしくお願いします.

さて,昨日は大晦日だったので海老天蕎麦を食ったり,大掃除したりといろいろしていたんですが,最終的には夜からガキ使を見ながらだらだらとRustでComet IIのエミュレータを書いていました.去年の冬コミケ(C93)ではRustでWebアプリケーションフレームワークIronを使った同人誌を売ったりしていたのもあり,自分の中でRust熱が強いのでこれを書いてみました.

git.alicemacs.com

一応仕様に記載されている命令(SVC以外)は実装しました.ただシフト系の命令はゴリ押し実装しているのでミスってる感じあると思うのでぼちぼち修正していきます.上記のexampleにもありますが,現状は機械語を実行するだけなので,ハンドアセンブルした機械語を格納する必要があります.まだリファクタリングをしていないので変数名やモジュール分けが綺麗ではないですが,ざっくりとexampleのコードと共に実装の裏側を紹介して行きたいと思います.上記に掲載されているexampleコードを張っておきます.

extern crate rust_casl2;
use rust_casl2::emu::Emu;

fn main() {
    let mut emu = Emu::new();
    emu.gr[1] = 0x0;
    emu.gr[2] = 0xdead;
    emu.memory[0] = 0x1412; // LD GR1, GR2
    let code = emu.fetch();
    emu.execute(code);
    println!("{:x}", gr[1]); //=> 0xdead
    println!("{:x}", gr[2]); //=> 0xdead
}

冒頭では,初めにミュータブルなEmu構造体のインスタンを生成しています.現状memoryフィールドに機械語を突っ込んだりするので,mutを付けています.

Emu構造体は,src/emu.rs にあります.

pub struct Emu{
    
    pub memory: [u16; 65536],
    
    // General Register
    pub gr: [u16; 8],
    
    // Program Register
    pub pr: u16,
    
    // Stack Pointer
    pub sp: Vec<u16>,
    
    // Flag Register [OF, SF, ZF]
    fr: [bool; 3], 
}

Comet IIでは基本的に符号なし16bitな処理なので,フィールドも基本的にはそうしています.各種フィールドの使い方はコメント通りです.
フラグレジスタは,当初u8の末尾3bitを使う方針でいましたが,なんか気持悪かった(使わない上位ビットが余っていて)ので,[bool; 3]にしましたが,3つのboolフィールドを持つ構造体を作って使いやすいメソッドを付けたほうが良い気がしてきました.

次のexampleの数行では,各種レジスタやメモリーにexample用の値を設定しています.今回の例ではLD命令を使ってレジスタ間での値のロードをしようとしています.具体的にはGR2に入っている0xdeadをGR1にロードします.LD命令のレジスタ間のオペコードは0x14なので,0x1412で LD GR1 GR2 を表しています.

値の設定後は,fetchメソッドでProgram Registerが指すmemoryから値を取ってきて,executeメソッドに渡して実行します.よくあるパイプラインの処理では,fetch -> decode -> executeとかですが,decodeな処理を書こうとすると冗長な気がした(たぶん正確にここらへんの区別がわかってないからかもしれない)ので,一気にexecuteという表現にしてしまっています. では,命令の実行部分を見てみましょう.executeメソッドの内部では,命令の上位1byteを見て愚直にmatchで分岐させています.分岐後の呼び出す関数は,無駄にsrc/instruction に小分けにして書いてあります.例としてsrc/instruction/ld.rs のLD命令を見てみましょう.

pub fn ld_r1_r2(emu: &mut Emu, code: u16) {

    let r1 = ((code & 0xf0) >> 4) as usize;
    let r2 = (code & 0xf) as usize;

    emu.gr[r1] = emu.gr[r2];

    let v = emu.gr[r1];
    emu.set_fr(ZF, v == 0);
    emu.set_fr(SF, is_set_msb(v));
    emu.set_fr(OF, false);

}

引数に設定されているcodeはfetchで取り出してきた2byteで,その中から1つ目,2つ目のレジスタに対応する値を取り出してr1, r2に格納します(上位1byte使っていないのが気になるのでu8とかに変えても良い気がしてきた).注意点としてこれをgrレジスタ配列の添字として使うので,u8等ではダメで,usizeに変換する必要があります. LD命令では,フラグレジスタを変更する必要があるので,各種ZF, SF, OFを設定しています.is_set_msb関数はsrc/util.rsで定義していて最上位bitが立っているかどうかのboolを返す関数となっています. そして,executeメソッド後ではgr[1]が0xdeadのgr[2]で書き換わっているのでprintln!で出力してみるとgr[1]の方で0xdeadが出力されるという感じです.

一応,各種命令のソースコードと共に単体テストを書いていますが,機械語自体にミスがあったりするかもしれないし,ハンドアセンブルするのも慣れてきてしまってはいますが,楽するためにもCASL2アセンブルするプログラムを作ったほうが良いという結論になり作っています.本当はそれ含めて公開する予定でしたが,間に合いませんでした.これはまた次の機会に公開したいと思います.

皆様にとって良い一年になりますように

2018-01-12 追記:

上記で言ってたCASL2アセンブラが完成しました.相変わらず突貫工事なのでミスはありそうですが,とりあえずツールとして動くレベルになりました.それと同時にいろいろ改造してコマンドラインツールとしてアセンブルとエミュレートが実行できるようになりました.

git.alicemacs.com

あとはRustでのテストやドキュメントの書き方とか含めリファクタリングをしながら理解を深めていこうかなと考えています.

画像として捉えるマルウェア検知分類の研究など

はじめに

少し前に突如現れて,様々な人を賑わせたこの記事を覚えているだろうか.

itpro.nikkeibp.co.jp

Twitterなどでいろんな人の意見を見てると批判的な意見が多かった記憶がある.しかし私としてはあまりそういう気持ではなかった.というのも本来画像ではない対象を画像として捉えて処理をするという研究を頻繁に見ていたからである.今更だが,この記事は情報セキュリティ系論文紹介 Advent Calendar 2017の22日目の記事である.したがって,情報セキュリティに関係あるマルウェアを題材として,マルウェアを画像化して検知・分類する研究例を簡単に紹介していきたいと思う.

Malware Images: Visualization and Automatic Classification(2011)

Windowsマルウェアを画像化して分類する研究としては,かなり多くの論文に引用されている有名な研究である.手法としては至って簡単で,まず初めにPEバイナリ自体を8bitグレースケール画像として変換する.画像化する際には,ファイルサイズに応じて,画像の横幅を決めている.実際にマルウェアを画像化してみた結果が見たければ著者たちがサイトで公開している. 特徴量としては,画像のテクスチャ認識等で利用されているGIST特徴量を用いて,その特徴量をK最近傍法で分類するといったものだ.実験結果のConfusion matrixを以下に示す.これを見る限りかなり高いレベルで分類分けに成功していることがわかる.
f:id:encry1024:20171221092730p:plain
2011年以降にじわじわと画像化したWindowsマルウェアの論文がいくつかと見つかる.そして時代はAndroidに・・・

Detecting Android Malware by Applying Classification Techniques on Images Patterns(2017)

こちらは,Androidマルウェアを対象としたものである.PEバイナリは画像化してみると人間でもわかるような特徴が見て取れるのだが(さっきのリンクを見てみて),Androidアプリのファイル形式であるAPKだと以下の図のようで,普通の人間には何もわからないだろう. f:id:encry1024:20171221101026p:plain
APKを解析した事がある人なら知っているだろうが,APKファイル自体はunzipコマンド等で簡単に解凍して中身を取り出すことができる.中に格納されているいくつかのファイルを繋げて画像化してみると以下のようになる.これだと特徴が見て取れる. f:id:encry1024:20171221102543p:plain
本研究では,上記の得られた画像のGIST特徴量を用いて,Random Forestで分類する. データセットとしては,マルウェアAndroid Drebin Projectと,グッドウェアにダウンロードしてきたAPKをVirusTotalに通して安全だったものを採用し,合計1280体のうち70%を学習用に,30%をテストデータとして使って実験を行っている.結果は以下の図.こちらも高い精度がでていることがわかる.ただ,データセットがかなり小さいのでそこは微妙な気がする. f:id:encry1024:20171221104501p:plain

おわりに

WindowsAndroidマルウェアの2種類を画像化して分類する研究例を紹介した.両者とも高い精度を出しており画像化して識別する手法は良いように思えるが,単純に考えて画像化する際にどうサイズを決定するのかなどもどうするべきかという問題点があったり,理論的な面からもなぜ画像化するのが良いのか等についての根拠が曖昧だったりするので,様々な問題点があるのも事実である.こういったところが解明されていけば,画像化する手法も有用な手法になりうるかもしれないですね. 実はこの他にもAPKのDEXファイルをRGB画像にしてCNNする研究があったりするので,気になったらGoogle Scholarで"malware image" とか検索かけると面白いです.

ansibleを体験してみる(2)

前回の記事では,とりあえず普通のansibleコマンドを使ってみた.今回はansible-playbookで遊んでみる.環境については前回と同じ.

encry1024.hatenablog.com

目標

2つのVMにたいして,slコマンドをインストールしてみる.

Playbookを書く

まずはじめに,playbookを適用する対象となるホストを設定する.今回はtargetグループである.becomeでその後切り替わるユーザを指定することができる.これをyesにしておくことでrootになれる.次に実際のやることを明示するためにtaskでグルーピングし,この処理の概要を記したnameを埋めたら,実際に行う作業について記述する.今回はaptでslを入れるだけなのだが,他にオプションがついているので見ていこう.state=presentとは,インストール済みならそのままにするというオプションで,update_cache=yesが,aptのインストール時に,予めアップデートするための設定.たぶんapt updateが走っているのかな.

- hosts: target
  become: yes
  tasks:
    - name: install sl via apt
      apt: name=sl state=present update_cache=yes

これをinstall_setup.yamlとして保存しておいたので,以下のようにコマンドを打つ.

ansible-playbook install_sl.yaml -i inventory

実行後,以下のような感じの画面がでれば成功(これは二回目だからちょっと違うところあり)

PLAY [target] ************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************
ok: [vm1]
ok: [vm2]

TASK [install sl via apt] ************************************************************************************************************************************
ok: [vm1]
ok: [vm2]

PLAY RECAP ***************************************************************************************************************************************************
vm1                        : ok=2    changed=0    unreachable=0    failed=0
vm2                        : ok=2    changed=0    unreachable=0    failed=0

感想

yamlでかけるので,わかりやすくて良いなと思った.かなりモジュール命な気がしてきたので,便利そうなモジュールを全部探してみる.複数台管理するときには,良いと感じた.まだまだ「Ansibleで安心ぶる」ができない知識量なので,実践投入していろいろ試してみようと思った.

ansibleを体験してみる(1)

以前ansibleに指先だけ触れたけど,その記憶すらすべて消えたので,もう一回自分なりに使ってみる.

目標

2つのVMを作成し,ansibleコマンドを使って,それらのIPアドレスを確認してみる.(ソフトのインストールは待ち時間が生じてめんどくさいけど,pingだけだとつまらないので)

環境

  • Ansible: 2.4.2.0
  • Ubuntu Server 16.04 LTS

2つのVM作成

まずはじめにansibleのターゲットとなる二つのVMを作成する.ここでは,vm1, vm2というホスト名を付けることにする.作成はめんどくさいので,vagrant経由でやる.以下,Vagrantfile.

Vagrant.configure(2) do |config|
  
  config.vm.box = "ubuntu/xenial64"

  config.vm.define :vm1 do |vm1|
    vm1.vm.network :private_network, ip: "192.168.33.101"
  end

  config.vm.define :vm2 do |vm1|
    vm1.vm.network :private_network, ip: "192.168.33.102"
  end
  
end

vagrant upが終わったら, vagrant ssh-configを, ~/.ssh/configに追記しておく.(環境汚れが嫌なのでこの記事終わったらすぐに消した)

インベントリファイルを作成する

ansibleでは,構成対象となるホストや、各種変数を設定するインベントリを必要とする. inventoryというファイル名で今回は作成する. どうやらこのファイルに記述する内容は,~/.ssh/configも考慮してくれるみたいなので,前述した方法で~/.ssh/configを書き換えている場合,以下のように設定することができる. また,このファイルはINI形式なので,グルーピングができるため,わかりやすいようにtargetとして2つのVMをくくっておいた.さらに少しハマった点として,Ubuntu Server 16.04 LTS(あるいはこの使ったvagrant boxかな?)には,python2系が入っていなかった.そこでpythonインタプリタ用の変数の設定をしておく必要がある.最後の2行がその設定となっており,これで標準で入っているpython3を使ってくれるようだ.ここでは,targetというグループにしているので,target:varsのグループにしている.

[target]
vm1
vm2
[target:vars]
ansible_python_interpreter=/usr/bin/python3

いざ,実験

ここまで来たら,後は以下のコマンドを打つだけだ. ansibleコマンドでは,-mで様々なモジュールを利用することができる.今回はコマンドを実行したかったので,commandモジュールを利用した(しかしながら,デフォルトのモジュールはcommandになっているらしいので,ここのオプションはいらない).また,引数は-aオプションで渡す.

ansible target -i inventory -m command -a 'hostname -I'

上記をすると以下の画面になると思います.

vm1 | SUCCESS | rc=0 >>
10.0.2.15 192.168.33.101

vm2 | SUCCESS | rc=0 >>
10.0.2.15 192.168.33.102 

それぞれ,Vagrantfileで設定したIPアドレスが含まれていることがわかる.成功っぽい.

感想

たしかにリモートサーバに対してsshさえ通っていれば構成管理ができるのはとても魅力的で,なおかつINI形式のファイルだけでサクッと一括にコマンド実行とかができるのは良いなと思った. 今回は,一行のコマンド実行のためだけにAnsibleという良いソフトウェアをつい利用してしまったが,次は,もっと一般的な構成管理を想定して,Playbookと呼ばれるyamlのファイルを使って遊んでみる.

vagrantのssh接続を早くする

Vagrantを利用している人ならば,vagrant sshが遅くてしんどい気持ちになったことは多いと思います.そこで早くできないかとぐぐってみると以下の記事を見つけると思います.

Vagrant ssh-configでvagrant sshを快適にする - Qiita

ここでは~/.ssh/configvagrantで管理してるVMの情報を追記(vagrant ssh-configをそのまま流し込んでいるだけ)して,vagrant sshではなく普通のsshでやると早いということが書かれているのですが,実際にどれぐらい早いかが書いていなかったので,実際に検証してみました.

方法 時間
vagrant ssh 5.38 real 2.39 user 0.47 sys
ssh 2.11 real 0.01 user 0.00 sys

この結果を見る限り普通のsshのほうが早いので,これからは普通のsshを使おうという気持ちになりました.