だるろぐ

明日できることは、今日しない。

VirusTotal API を使ってみる

WPF で VirusToral API を利用するサンプルを書いてみました(書いた後に気付いたのですが、あんまり WPF 要素なかったです)。

VirusTotal とは

指定したファイルをいろんなウイルス対策エンジンでスキャンしてくれるサービスです。

結構利用制限はきついのですが、一応 API も用意されています。

これを使えば、比較的簡単にアプリケーションへウイルススキャン機能を追加できますね。

準備

f:id:daruyanagi:20141024021938p:plain

まずはアカウントを作成し、API キーを取得します。API キーには Public と Private がありますが、今回は Public を利用します。Private なキーのほうが制限が少ないのですが、個別にリクエストしなければならないようです。

VirusToral にファイルのスキャンをお願いする

VirusToral にファイルのスキャンをお願いするには、以下の URL に API キーとファイルを Post します。

https://www.virustotal.com/vtapi/v2/file/scan

今回は HttpClient を利用して、こんなコードにしてみました。本当ならばちゃんと帰ってきたデータをクラスにマッピングして……ということをすべきなのでしょうが、めんどくさいので DynamicJson(NuGet で入手しましょう)でパースして dynamic 型の変数として返しています。

public async Task<dynamic> RequestFileScan(string filename)
{
    using (var http = new HttpClient())
    {
        var uri = "https://www.virustotal.com/vtapi/v2/file/scan";
        var multipart = new MultipartFormDataContent();
        multipart.Add(new StringContent(APIKey), "apikey");
        multipart.Add(new ByteArrayContent(File.ReadAllBytes(filename)), "file", Path.GetFileName(filename));
        var response = await http.PostAsync(uri, multipart);
        var content = await response.Content.ReadAsStringAsync();
        var json = DynamicJson.Parse(content);

        return json;
    }
}

HttpClient の使い方、これであってるのかな……。まだ使い慣れてなくて、あんまりよく分かってません。GetAsync()、PostAsync() で簡単に非同期リクエストが投げられるのはわかりやすいのですけど、ファイルを送りたいとか、Cookie をセットしたいとか、ちょっとヒネったことをしようとするとすぐに Google 先生に泣きつかなきゃいけませんね。……僕だけですか、そうですか。Windows ストア アプリなんかではもっぱらこちらを使うようですので、慣れていきたいものです。

f:id:daruyanagi:20141024024314p:plain

ちなみに、返り値の Json の中身はこんな感じです。

{"response_code": 1,
 "verbose_msg": "Scan request successfully queued, come back later for the report",
 "resource": "999f7d93aa3d4a1a94cccfb4ea96bc2e28fd48020a481aa2dc7e215f3ce27bc0",
 "scan_id": "999f7d93aa3d4a1a94cccfb4ea96bc2e28fd48020a481aa2dc7e215f3ce27bc0-1324376258",
 "permalink": "https://www.virustotal.com/file/999f7d93aa3d4a1a94cccfb4ea96bc2e28fd48020a481aa2dc7e215f3ce27bc0/analysis/1324376258/",
 "sha256": "999f7d93aa3d4a1a94cccfb4ea96bc2e28fd48020a481aa2dc7e215f3ce27bc0",
 "sha1": "2cc875bca8030d745adfd14388b8c001471c2474",
 "md5": "4a00e1a3a14e4fec6f2b353b4f20bb73"}

response_code が 1 であればとりあえず成功。verbose_msg に「スキャンしているから後でレポートを取りに来てね」というメッセージが格納されます。

f:id:daruyanagi:20141024023747p:plain

レポートは scan_id で問い合わせますので、どこかに保存しておきましょう。

VirusToral のスキャンレポートを取得する

お次はスキャンレポートの取得です。

https://www.virustotal.com/vtapi/v2/file/repor

さっきの要領で、この URL を叩きます。

public async Task<dynamic> GetFileScanResults(string id)
{
    using (var http = new HttpClient())
    {
        var uri = "https://www.virustotal.com/vtapi/v2/file/report";
        var parameters = new Dictionary<string, string>
        {
            { "resource", id },
            { "apikey", APIKey },
        };
        var response = await http.PostAsync(uri, new FormUrlEncodedContent(parameters));
        var content = await response.Content.ReadAsStringAsync();
        var json = DynamicJson.Parse(content);

        return json;
    }
}

スキャンのリクエスト処理よりも簡単でした。成功すると、こんな Json が返ってきます。

{
 "response_code": 1,
 "verbose_msg": "Scan finished, scan information embedded in this object",

 "resource": "99017f6eebbac24f351415dd410d522d",
 "scan_id": "52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c-1273894724",
 "md5": "99017f6eebbac24f351415dd410d522d",
 "sha1": "4d1740485713a2ab3a4f5822a01f645fe8387f92",
 "sha256": "52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c",

 "scan_date": "2010-05-15 03:38:44",

 "positives": 40,
 "total": 40,

 "scans": {"nProtect": {"detected": true, "version": "2010-05-14.01", "result": "Trojan.Generic.3611249", "update": "20100514"},
           "CAT-QuickHeal": {"detected": true, "version": "10.00", "result": "Trojan.VB.acgy", "update": "20100514"},
           "McAfee": {"detected": true, "version": "5.400.0.1158", "result": "Generic.dx!rkx", "update": "20100515"},
           "TheHacker": {"detected": true, "version": "6.5.2.0.280", "result": "Trojan/VB.gen", "update": "20100514"},
           "VirusBuster": {"detected": true, "version": "5.0.27.0", "result": "Trojan.VB.JFDE", "update": "20100514"},
           "NOD32": {"detected": true, "version": "5115", "result": "a variant of Win32/Qhost.NTY", "update": "20100514"},
           "F-Prot": {"detected": false, "version": "4.5.1.85", "result": null, "update": "20100514"},
           "Symantec": {"detected": true, "version": "20101.1.0.89", "result": "Trojan.KillAV", "update": "20100515"},
           "Norman": {"detected": true, "version": "6.04.12", "result": "W32/Smalltroj.YFHZ", "update": "20100514"},
           "TrendMicro-HouseCall": {"detected": true, "version": "9.120.0.1004", "result": "TROJ_VB.JVJ", "update": "20100515"},
           "Avast": {"detected": true, "version": "4.8.1351.0", "result": "Win32:Malware-gen", "update": "20100514"},
           "eSafe": {"detected": true, "version": "7.0.17.0", "result": "Win32.TRVB.Acgy", "update": "20100513"},
           "ClamAV": {"detected": false, "version": "0.96.0.3-git", "result": null, "update": "20100514"},
           "Kaspersky": {"detected": true, "version": "7.0.0.125", "result": "Trojan.Win32.VB.acgy", "update": "20100515"},
           "BitDefender": {"detected": true, "version": "7.2", "result": "Trojan.Generic.3611249", "update": "20100515"},
           "Comodo": {"detected": true, "version": "4842", "result": "Heur.Suspicious", "update": "20100515"},
           "F-Secure": {"detected": true, "version": "9.0.15370.0", "result": "Trojan.Generic.3611249", "update": "20100514"},
           "DrWeb": {"detected": true, "version": "5.0.2.03300", "result": "Trojan.Hosts.37", "update": "20100515"},
           "AntiVir": {"detected": true, "version": "8.2.1.242", "result": "TR/VB.acgy.1", "update": "20100514"},
           "TrendMicro": {"detected": true, "version": "9.120.0.1004", "result": "TROJ_VB.JVJ", "update": "20100514"},
           "McAfee-GW-Edition": {"detected": true, "version": "2010.1", "result": "Generic.dx!rkx", "update": "20100515"},
           "Sophos": {"detected": true, "version": "4.53.0", "result": "Troj/VBHost-A", "update": "20100515"},
           "eTrust-Vet": {"detected": true, "version": "35.2.7490", "result": "Win32/ASuspect.HDBBD", "update": "20100515"},
           "Authentium": {"detected": false, "version": "5.2.0.5", "result": null, "update": "20100514"},
           "Jiangmin": {"detected": true, "version": "13.0.900", "result": "Trojan/VB.yqh", "update": "20100514"}, [...] },

 "permalink": "https://www.virustotal.com/file/52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c/analysis/1273894724/"
}

permalink はレポートページの URL です。こういうページですな。

f:id:daruyanagi:20141024025040p:plain

total はスキャンに利用したウイルス対策エンジンの数、positives はそのなかで指定したファイルがマルウェアだと答えた(陽性)エンジンの数です。ウイルス対策エンジンのバージョンや、検知したウイルスを彼らが何と呼んでいるかも取得できますね。

f:id:daruyanagi:20141024023722p:plain

ウイルススキャンをリクエストしてレポートが出来上がるまでには少し時間がかかるので、気を付けましょう。まだレポートができあがっていない場合は、「おとといこいや(意訳)」という verbose_msg がいただけます(response_code は -2 かな)。

f:id:daruyanagi:20141024024537p:plain

あとは適当にデータバインディングして、それっぽく UI を作ってください。簡単ですけど、今日はこの辺で。

だるやなぎはマスコットアプリ文化祭 2014を応援しています

謎の「だるやなぎ部門」には素敵なプレゼントを用意しています。ぜひ参加してみてくださいね。

f:id:daruyanagi:20141024024829p:plain