malwareとコールバックサーバーの繋がりの可視化

#malwareとコールバックサーバの繋がりの可視化

##これは
networkxの練習のためmalwareとコールバックサーバの繋がりの可視化について語った記事です。

##環境
Kali Linux 
python 2.2.17
##大まかな実装の流れ
1.引数の設定
2.suffixの整理
3.hostname取得の関数
4.targetディレクトリの走査
5.ネットワーク作成
##必要なライブラリを読み込み
```python
import pefile
import sys
import argparse
import os
import pprint
import networkx
import re
from networkx.drawing.nx_agraph import write_dot
import collections
from networkx.algorithms import bipartite
```
##コマンドライン引数の設定
```python
args = argparse.ArgumentParser()
args.add_argument("target")
args.add_argument("filename")
args.add_argument("malware_pro")
args.add_argument("hostname_pro")
args = args.parse_args()
network = networkx.Graph()
```
## suffixの整理
 ```python
suffixes = map(lambda string: string.strip(), open("suffixes.txt"))
suffixes = set(suffixes)
 ```
<font color="SlateGray">最後２行でmapとラムダ式を使いながらsuffixes.txtのsuffixを整理。suffixes.txtは自分で作ってもいいがめんどい。今回は[Malware Data Science](https://www.malwaredatascience.com/)からお借りする。また、target引数に渡すマルウェアサンプルも同じく拝借。まずはsuffixes.txt内の改行コードが邪魔なのでstrip()。python3だとmap関数の返り値がmap関数のオブジェクトになるので注意。</font>
<br>

```python
def get_hostnames(string):
    tmp = re.findall(r'(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]{,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}', string)
    hostnames = filter(lambda hostname: hostname.split(".")[-1].lower() in suffixes, tmp)
    return hostnames
```
<font color="SlateGray">正規表現でドメインを特定し、"."で区切る。１番最後の要素を小文字にし、filterとラムダ式を使ってsuffixesと一致するかチェック。ここもpython3だと...</font>
<br>

```python
for root,dirs,files in os.walk(args.target):
    for file in files:
    try:
         pe = pefile.PE(os.path.join(root, file))
    except pefile.PEFormatError:
        continue
        f_path = os.path.join(root, file)
        contents = os.popen("strings '{0}'".format(f_path)).read()
        hostnames = get_hostnames(contents)
        if len(hostnames):
            network.add_node(file,label=file ,color='blue', penwidth=3,bipartite=0)
        for hostname in hostnames:
            network.add_node(hostname,label=hostname,color='purple', penwidth=10,bipartite=1)
            network.add_edge(hostname, file ,penwidth=2)
    if hostnames:
        print "Extracted hostname from:", file
        pprint.pprint(hostname)
```
<font color="SlateGray">walkで目的のディレクトリを走査しfor文でルートディレクトリ、サブディレクトリ、ファイルパスを取得しtryでpefile可動化をチェック。違ったら次のループへ。f_pathにpeファイルのフルパスを格納し、印字化可能文字列を引数としてget_hostnames関数に渡す。
ホスト名を取得できたら、contentsalwareとホストのネットワークをそれぞれ作成。次にhost名が見つかればそのファイルパスを２部ネットワークの片方に、ホスト名自体を全て２部ネットワークのもう片方に登録。最後のif文で実行時にコマンドライン上にホスト名が取得できたファイルパスを表示</font>
<br>

```python
write_dot(network, args.filename)
codes= set(n for n,d in network.nodes(data=True) if d['bipartite']==0)
hostname = set(network)-codes
```
<font color="SlateGray">１行目でネットワークをfilenameに書き出す。先ほどbipartite=0としたマルウェア側をcodeに格納。network.nodes(data=True)とすることで、ノード名とノードのもつ属性の辞書からなるタプルが返ってくる。同じくhostnameにホスト名をいれる。<font>
<br>

```python
codes = bipartite.projected_graph(network, codes)
hostname = bipartite.projected_graph(network, hostname)
```
<font color="SlateGray">マルウェアとホストそれぞれについて射影を作成する。射影とはここでは２部ネットワークを簡素化したもので、例えばマルウェア(codes)の場合、ホスト名が共通しているマルウェア同士を結ぶ。</font>
<br>

```python
write_dot(codes ,args.malware_pro)
write_dot(hostname ,args.hostname_pro)
```
<font color="SlateGray">作成した射影をそれぞれファイルに書き出す。</font>
##fdpで可視化
```python
fdp filename.dot -T png -o filename.png -Goverlap=false
fdp malware_pro.dot -T png -o malwre_pro.png -Goverlap=false
fdp hostname_pro.dot -T png -o hostname_pro.png -Goverlap=false
```
<font color="SlateGray">fdpとは力指向に基づいてネットワークを可視化してくれるツールです。他にもsfdpなど他のツールもありますが、今回は割愛。実行してみる。<font>

```python
filename.png
malware_pro.pnf
hostname_pro.png
```
<font color="SlateGray">という画像ファイルでできる。<font>
<br>

```python
eog filename.png
```

<font color="SlateGray">開いてみると<font>
<br>
![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197716/f7a2fcfb-5935-609c-6991-a25f7444b2b4.png)

<font color="SlateGray">できた^^<font>
<br>

##ちなみに力指向って？
<font color="SlateGray">ネットワークをレイアウトする際に問題なのがエッジの長さです。ノードの重みが同じならエッジの長さも同じであることが望ましい。しかしノードの数が４つ以上になると絶対にノードの長さを全て同じにすることはできないですよね？したがって、この歪みを最小にしようと考えます。そこで出てくるのが力指向アルゴリズム。エッジをバネに見立ててシミュレートするとエッジが自動的にノード間の距離をできるだけ均一にしようとします。バネ偉大ですね。では。</font>